notify/file_ack.go

178 lines
3.4 KiB
Go
Raw Normal View History

package notify
import (
"errors"
"strconv"
"sync"
"time"
)
var (
errFileAckCanceled = errors.New("file ack canceled")
errFileAckTimeout = errors.New("file ack timeout")
)
type fileAckWait struct {
key string
scope string
pool *fileAckPool
reply chan FileEvent
closeOnce sync.Once
}
type fileAckPool struct {
pool sync.Map
}
func newFileAckPool() *fileAckPool {
return &fileAckPool{}
}
func fileAckKey(scope string, fileID string, stage string, offset int64) string {
return normalizeFileScope(scope) + "|" + fileID + "|" + stage + "|" + formatInt(offset)
}
func (p *fileAckPool) prepare(scope string, fileID string, stage string, offset int64) *fileAckWait {
scope = normalizeFileScope(scope)
wait := &fileAckWait{
key: fileAckKey(scope, fileID, stage, offset),
scope: scope,
pool: p,
reply: make(chan FileEvent, 1),
}
p.pool.Store(wait.key, wait)
return wait
}
func (p *fileAckPool) deliver(scope string, event FileEvent) bool {
return p.deliverAny([]string{scope}, event)
}
func (p *fileAckPool) deliverAny(scopes []string, event FileEvent) bool {
if p == nil {
return false
}
for _, scope := range scopes {
key := fileAckKey(scope, event.Packet.FileID, event.Packet.Stage, event.Packet.Offset)
data, ok := p.pool.LoadAndDelete(key)
if !ok {
continue
}
wait := data.(*fileAckWait)
wait.deliver(event)
return true
}
return false
}
func (w *fileAckWait) cancel() {
if w == nil {
return
}
if w.pool != nil {
w.pool.pool.Delete(w.key)
}
w.closeReply()
}
func (w *fileAckWait) deliver(event FileEvent) {
if w == nil {
return
}
w.closeOnce.Do(func() {
select {
case w.reply <- event:
default:
}
close(w.reply)
})
}
func (w *fileAckWait) closeReply() {
if w == nil {
return
}
w.closeOnce.Do(func() {
close(w.reply)
})
}
func (p *fileAckPool) waitPrepared(wait *fileAckWait, timeout time.Duration) error {
if timeout <= 0 {
timeout = defaultFileAckTimeout
}
timer := time.NewTimer(timeout)
defer timer.Stop()
select {
case event, ok := <-wait.reply:
if !ok {
return errFileAckCanceled
}
if event.Err != nil {
return event.Err
}
if event.Packet.Error != "" {
return errors.New(event.Packet.Error)
}
return nil
case <-timer.C:
wait.cancel()
return errFileAckTimeout
}
}
func (p *fileAckPool) wait(scope string, fileID string, stage string, offset int64, timeout time.Duration) error {
wait := p.prepare(scope, fileID, stage, offset)
return p.waitPrepared(wait, timeout)
}
func (p *fileAckPool) closeAll() {
if p == nil {
return
}
p.pool.Range(func(_, value interface{}) bool {
value.(*fileAckWait).cancel()
return true
})
}
func (p *fileAckPool) closeScope(scope string) {
if p == nil {
return
}
scope = normalizeFileScope(scope)
p.pool.Range(func(_, value interface{}) bool {
wait := value.(*fileAckWait)
if wait.scope == scope {
wait.cancel()
}
return true
})
}
func (p *fileAckPool) closeScopeFamily(scope string) {
if p == nil {
return
}
base := normalizeFileScope(scope)
p.pool.Range(func(_, value interface{}) bool {
wait := value.(*fileAckWait)
if scopeBelongsToServerFileScope(wait.scope, base) {
wait.cancel()
}
return true
})
}
func formatInt(v int64) string {
return strconv.FormatInt(v, 10)
}
func (c *ClientCommon) getFileAckPool() *fileAckPool {
return c.getLogicalSessionState().fileAckWaits
}
func (s *ServerCommon) getFileAckPool() *fileAckPool {
return s.getLogicalSessionState().fileAckWaits
}