2026-04-15 15:24:36 +08:00
|
|
|
package notify
|
|
|
|
|
|
2026-04-15 19:52:45 +08:00
|
|
|
import (
|
|
|
|
|
"strconv"
|
|
|
|
|
"sync"
|
|
|
|
|
)
|
2026-04-15 15:24:36 +08:00
|
|
|
|
|
|
|
|
type recordRuntime struct {
|
|
|
|
|
mu sync.RWMutex
|
|
|
|
|
handler func(RecordAcceptInfo) error
|
2026-04-15 19:52:45 +08:00
|
|
|
records map[string]*recordStream
|
2026-04-15 15:24:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func newRecordRuntime() *recordRuntime {
|
2026-04-15 19:52:45 +08:00
|
|
|
return &recordRuntime{
|
|
|
|
|
records: make(map[string]*recordStream),
|
|
|
|
|
}
|
2026-04-15 15:24:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *recordRuntime) setHandler(fn func(RecordAcceptInfo) error) {
|
|
|
|
|
if r == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
r.mu.Lock()
|
|
|
|
|
r.handler = fn
|
|
|
|
|
r.mu.Unlock()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *recordRuntime) handlerSnapshot() func(RecordAcceptInfo) error {
|
|
|
|
|
if r == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
r.mu.RLock()
|
|
|
|
|
defer r.mu.RUnlock()
|
|
|
|
|
return r.handler
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *ClientCommon) getRecordRuntime() *recordRuntime {
|
|
|
|
|
if c == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return c.recordRuntime
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *ServerCommon) getRecordRuntime() *recordRuntime {
|
|
|
|
|
if s == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return s.recordRuntime
|
|
|
|
|
}
|
2026-04-15 19:52:45 +08:00
|
|
|
|
|
|
|
|
func (r *recordRuntime) register(record *recordStream) {
|
|
|
|
|
if r == nil || record == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
key := record.runtimeRegistryKey()
|
|
|
|
|
if key == "" {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
r.mu.Lock()
|
|
|
|
|
r.records[key] = record
|
|
|
|
|
r.mu.Unlock()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *recordRuntime) remove(key string) {
|
|
|
|
|
if r == nil || key == "" {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
r.mu.Lock()
|
|
|
|
|
delete(r.records, key)
|
|
|
|
|
r.mu.Unlock()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *recordRuntime) snapshots() []RecordSnapshot {
|
|
|
|
|
if r == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
r.mu.RLock()
|
|
|
|
|
records := make([]*recordStream, 0, len(r.records))
|
|
|
|
|
for _, record := range r.records {
|
|
|
|
|
if record == nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
records = append(records, record)
|
|
|
|
|
}
|
|
|
|
|
r.mu.RUnlock()
|
|
|
|
|
snapshots := make([]RecordSnapshot, 0, len(records))
|
|
|
|
|
for _, record := range records {
|
|
|
|
|
snapshots = append(snapshots, record.snapshot())
|
|
|
|
|
}
|
|
|
|
|
sortRecordSnapshots(snapshots)
|
|
|
|
|
return snapshots
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func bindRecordRuntime(record RecordStream, runtime *recordRuntime) {
|
|
|
|
|
if runtime == nil || record == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
rs, ok := record.(*recordStream)
|
|
|
|
|
if !ok {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
rs.bindRuntime(runtime)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *recordStream) bindRuntime(runtime *recordRuntime) {
|
|
|
|
|
if r == nil || runtime == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
key := r.runtimeRegistryKey()
|
|
|
|
|
if key == "" {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
r.mu.Lock()
|
|
|
|
|
r.runtime = runtime
|
|
|
|
|
r.runtimeKey = key
|
|
|
|
|
r.mu.Unlock()
|
|
|
|
|
runtime.register(r)
|
|
|
|
|
r.runtimeWatchOnce.Do(func() {
|
|
|
|
|
go func() {
|
|
|
|
|
streamCtx := r.stream.Context()
|
|
|
|
|
if streamCtx == nil {
|
|
|
|
|
<-r.ctx.Done()
|
|
|
|
|
} else {
|
|
|
|
|
select {
|
|
|
|
|
case <-r.ctx.Done():
|
|
|
|
|
case <-streamCtx.Done():
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
r.detachRuntime()
|
|
|
|
|
}()
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *recordStream) detachRuntime() {
|
|
|
|
|
if r == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
r.runtimeDetachOnce.Do(func() {
|
|
|
|
|
r.mu.Lock()
|
|
|
|
|
runtime := r.runtime
|
|
|
|
|
key := r.runtimeKey
|
|
|
|
|
r.runtime = nil
|
|
|
|
|
r.runtimeKey = ""
|
|
|
|
|
r.mu.Unlock()
|
|
|
|
|
if runtime != nil {
|
|
|
|
|
runtime.remove(key)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *recordStream) runtimeRegistryKey() string {
|
|
|
|
|
if r == nil || r.stream == nil {
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
scope := ""
|
|
|
|
|
dataID := uint64(0)
|
|
|
|
|
if stream, ok := r.stream.(*streamHandle); ok {
|
|
|
|
|
scope = normalizeFileScope(stream.runtimeScope)
|
|
|
|
|
dataID = stream.dataID
|
|
|
|
|
}
|
|
|
|
|
key := scope + "\x00" + r.stream.ID()
|
|
|
|
|
if dataID != 0 {
|
|
|
|
|
key += "\x01" + strconv.FormatUint(dataID, 10)
|
|
|
|
|
}
|
|
|
|
|
return key
|
|
|
|
|
}
|