package notify import ( "strconv" "sync" ) type recordRuntime struct { mu sync.RWMutex handler func(RecordAcceptInfo) error records map[string]*recordStream } func newRecordRuntime() *recordRuntime { return &recordRuntime{ records: make(map[string]*recordStream), } } 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 } 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 }