package audio import ( "sync/atomic" "time" "unsafe" ) // MicrophoneContentionManager manages microphone access with cooldown periods type MicrophoneContentionManager struct { // Atomic fields MUST be first for ARM32 alignment (int64 fields need 8-byte alignment) lastOpNano int64 cooldownNanos int64 operationID int64 lockPtr unsafe.Pointer } func NewMicrophoneContentionManager(cooldown time.Duration) *MicrophoneContentionManager { return &MicrophoneContentionManager{ cooldownNanos: int64(cooldown), } } type OperationResult struct { Allowed bool RemainingCooldown time.Duration OperationID int64 } func (mcm *MicrophoneContentionManager) TryOperation() OperationResult { now := time.Now().UnixNano() cooldown := atomic.LoadInt64(&mcm.cooldownNanos) lastOp := atomic.LoadInt64(&mcm.lastOpNano) elapsed := now - lastOp if elapsed >= cooldown { if atomic.CompareAndSwapInt64(&mcm.lastOpNano, lastOp, now) { opID := atomic.AddInt64(&mcm.operationID, 1) return OperationResult{ Allowed: true, RemainingCooldown: 0, OperationID: opID, } } // Retry once if CAS failed lastOp = atomic.LoadInt64(&mcm.lastOpNano) elapsed = now - lastOp if elapsed >= cooldown && atomic.CompareAndSwapInt64(&mcm.lastOpNano, lastOp, now) { opID := atomic.AddInt64(&mcm.operationID, 1) return OperationResult{ Allowed: true, RemainingCooldown: 0, OperationID: opID, } } } remaining := time.Duration(cooldown - elapsed) if remaining < 0 { remaining = 0 } return OperationResult{ Allowed: false, RemainingCooldown: remaining, OperationID: atomic.LoadInt64(&mcm.operationID), } } func (mcm *MicrophoneContentionManager) SetCooldown(cooldown time.Duration) { atomic.StoreInt64(&mcm.cooldownNanos, int64(cooldown)) } func (mcm *MicrophoneContentionManager) GetCooldown() time.Duration { return time.Duration(atomic.LoadInt64(&mcm.cooldownNanos)) } func (mcm *MicrophoneContentionManager) GetLastOperationTime() time.Time { nanos := atomic.LoadInt64(&mcm.lastOpNano) if nanos == 0 { return time.Time{} } return time.Unix(0, nanos) } func (mcm *MicrophoneContentionManager) GetOperationCount() int64 { return atomic.LoadInt64(&mcm.operationID) } func (mcm *MicrophoneContentionManager) Reset() { atomic.StoreInt64(&mcm.lastOpNano, 0) atomic.StoreInt64(&mcm.operationID, 0) } var ( globalMicContentionManager unsafe.Pointer micContentionInitialized int32 ) func GetMicrophoneContentionManager() *MicrophoneContentionManager { ptr := atomic.LoadPointer(&globalMicContentionManager) if ptr != nil { return (*MicrophoneContentionManager)(ptr) } if atomic.CompareAndSwapInt32(&micContentionInitialized, 0, 1) { manager := NewMicrophoneContentionManager(GetConfig().MicContentionTimeout) atomic.StorePointer(&globalMicContentionManager, unsafe.Pointer(manager)) return manager } ptr = atomic.LoadPointer(&globalMicContentionManager) if ptr != nil { return (*MicrophoneContentionManager)(ptr) } return NewMicrophoneContentionManager(GetConfig().MicContentionTimeout) } func TryMicrophoneOperation() OperationResult { return GetMicrophoneContentionManager().TryOperation() } func SetMicrophoneCooldown(cooldown time.Duration) { GetMicrophoneContentionManager().SetCooldown(cooldown) }