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 }