mirror of https://github.com/jetkvm/kvm.git
158 lines
5.0 KiB
Go
158 lines
5.0 KiB
Go
package audio
|
|
|
|
import (
|
|
"sync/atomic"
|
|
"time"
|
|
"unsafe"
|
|
)
|
|
|
|
// MicrophoneContentionManager provides optimized microphone operation locking
|
|
// with reduced contention using atomic operations and conditional locking
|
|
type MicrophoneContentionManager struct {
|
|
// Atomic fields (must be 64-bit aligned on 32-bit systems)
|
|
lastOpNano int64 // Unix nanoseconds of last operation
|
|
cooldownNanos int64 // Cooldown duration in nanoseconds
|
|
operationID int64 // Incremental operation ID for tracking
|
|
|
|
// Lock-free state flags (using atomic.Pointer for lock-free updates)
|
|
lockPtr unsafe.Pointer // *sync.Mutex - conditionally allocated
|
|
}
|
|
|
|
// NewMicrophoneContentionManager creates a new microphone contention manager
|
|
func NewMicrophoneContentionManager(cooldown time.Duration) *MicrophoneContentionManager {
|
|
return &MicrophoneContentionManager{
|
|
cooldownNanos: int64(cooldown),
|
|
}
|
|
}
|
|
|
|
// OperationResult represents the result of attempting a microphone operation
|
|
type OperationResult struct {
|
|
Allowed bool
|
|
RemainingCooldown time.Duration
|
|
OperationID int64
|
|
}
|
|
|
|
// TryOperation attempts to perform a microphone operation with optimized contention handling
|
|
func (mcm *MicrophoneContentionManager) TryOperation() OperationResult {
|
|
now := time.Now().UnixNano()
|
|
cooldown := atomic.LoadInt64(&mcm.cooldownNanos)
|
|
|
|
// Fast path: check if we're clearly outside cooldown period using atomic read
|
|
lastOp := atomic.LoadInt64(&mcm.lastOpNano)
|
|
elapsed := now - lastOp
|
|
|
|
if elapsed >= cooldown {
|
|
// Attempt atomic update without locking
|
|
if atomic.CompareAndSwapInt64(&mcm.lastOpNano, lastOp, now) {
|
|
opID := atomic.AddInt64(&mcm.operationID, 1)
|
|
return OperationResult{
|
|
Allowed: true,
|
|
RemainingCooldown: 0,
|
|
OperationID: opID,
|
|
}
|
|
}
|
|
}
|
|
|
|
// Slow path: potential contention, check remaining cooldown
|
|
currentLastOp := atomic.LoadInt64(&mcm.lastOpNano)
|
|
currentElapsed := now - currentLastOp
|
|
|
|
if currentElapsed >= cooldown {
|
|
// Race condition: another operation might have updated lastOpNano
|
|
// Try once more with CAS
|
|
if atomic.CompareAndSwapInt64(&mcm.lastOpNano, currentLastOp, now) {
|
|
opID := atomic.AddInt64(&mcm.operationID, 1)
|
|
return OperationResult{
|
|
Allowed: true,
|
|
RemainingCooldown: 0,
|
|
OperationID: opID,
|
|
}
|
|
}
|
|
// If CAS failed, fall through to cooldown calculation
|
|
currentLastOp = atomic.LoadInt64(&mcm.lastOpNano)
|
|
currentElapsed = now - currentLastOp
|
|
}
|
|
|
|
remaining := time.Duration(cooldown - currentElapsed)
|
|
if remaining < 0 {
|
|
remaining = 0
|
|
}
|
|
|
|
return OperationResult{
|
|
Allowed: false,
|
|
RemainingCooldown: remaining,
|
|
OperationID: atomic.LoadInt64(&mcm.operationID),
|
|
}
|
|
}
|
|
|
|
// SetCooldown updates the cooldown duration atomically
|
|
func (mcm *MicrophoneContentionManager) SetCooldown(cooldown time.Duration) {
|
|
atomic.StoreInt64(&mcm.cooldownNanos, int64(cooldown))
|
|
}
|
|
|
|
// GetCooldown returns the current cooldown duration
|
|
func (mcm *MicrophoneContentionManager) GetCooldown() time.Duration {
|
|
return time.Duration(atomic.LoadInt64(&mcm.cooldownNanos))
|
|
}
|
|
|
|
// GetLastOperationTime returns the time of the last operation
|
|
func (mcm *MicrophoneContentionManager) GetLastOperationTime() time.Time {
|
|
nanos := atomic.LoadInt64(&mcm.lastOpNano)
|
|
if nanos == 0 {
|
|
return time.Time{}
|
|
}
|
|
return time.Unix(0, nanos)
|
|
}
|
|
|
|
// GetOperationCount returns the total number of successful operations
|
|
func (mcm *MicrophoneContentionManager) GetOperationCount() int64 {
|
|
return atomic.LoadInt64(&mcm.operationID)
|
|
}
|
|
|
|
// Reset resets the contention manager state
|
|
func (mcm *MicrophoneContentionManager) Reset() {
|
|
atomic.StoreInt64(&mcm.lastOpNano, 0)
|
|
atomic.StoreInt64(&mcm.operationID, 0)
|
|
}
|
|
|
|
// Global instance for microphone contention management
|
|
var (
|
|
globalMicContentionManager unsafe.Pointer // *MicrophoneContentionManager
|
|
micContentionInitialized int32
|
|
)
|
|
|
|
// GetMicrophoneContentionManager returns the global microphone contention manager
|
|
func GetMicrophoneContentionManager() *MicrophoneContentionManager {
|
|
ptr := atomic.LoadPointer(&globalMicContentionManager)
|
|
if ptr != nil {
|
|
return (*MicrophoneContentionManager)(ptr)
|
|
}
|
|
|
|
// Initialize on first use
|
|
if atomic.CompareAndSwapInt32(&micContentionInitialized, 0, 1) {
|
|
manager := NewMicrophoneContentionManager(200 * time.Millisecond)
|
|
atomic.StorePointer(&globalMicContentionManager, unsafe.Pointer(manager))
|
|
return manager
|
|
}
|
|
|
|
// Another goroutine initialized it, try again
|
|
ptr = atomic.LoadPointer(&globalMicContentionManager)
|
|
if ptr != nil {
|
|
return (*MicrophoneContentionManager)(ptr)
|
|
}
|
|
|
|
// Fallback: create a new manager (should rarely happen)
|
|
return NewMicrophoneContentionManager(200 * time.Millisecond)
|
|
}
|
|
|
|
// TryMicrophoneOperation provides a convenient global function for microphone operations
|
|
func TryMicrophoneOperation() OperationResult {
|
|
manager := GetMicrophoneContentionManager()
|
|
return manager.TryOperation()
|
|
}
|
|
|
|
// SetMicrophoneCooldown updates the global microphone cooldown
|
|
func SetMicrophoneCooldown(cooldown time.Duration) {
|
|
manager := GetMicrophoneContentionManager()
|
|
manager.SetCooldown(cooldown)
|
|
} |