notify/file_ack.go
starainrt 09d972c7b7
feat(notify): 重构通信内核并补齐 stream/bulk/record/transfer 能力
- 引入 LogicalConn/TransportConn 分层,ClientConn 保留兼容适配层
  - 新增 Stream、Bulk、RecordStream 三条数据面能力及对应控制路径
  - 完成 transfer/file 传输内核与状态快照、诊断能力
  - 补齐 reconnect、inbound dispatcher、modern psk 等基础模块
  - 增加大规模回归、并发与基准测试覆盖
  - 更新依赖库
2026-04-15 15:24:36 +08:00

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
}