178 lines
3.4 KiB
Go
178 lines
3.4 KiB
Go
|
|
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
|
||
|
|
}
|