package dataext import ( "context" "sync" "sync/atomic" "testing" "time" ) func TestCASMutex_LockUnlock(t *testing.T) { m := NewCASMutex() m.Lock() m.Unlock() } func TestCASMutex_TryLock(t *testing.T) { m := NewCASMutex() if !m.TryLock() { t.Fatal("TryLock should succeed on fresh mutex") } if m.TryLock() { t.Fatal("TryLock should fail when already locked") } m.Unlock() if !m.TryLock() { t.Fatal("TryLock should succeed after Unlock") } m.Unlock() } func TestCASMutex_TryLockWithTimeout(t *testing.T) { m := NewCASMutex() m.Lock() start := time.Now() if m.TryLockWithTimeout(20 * time.Millisecond) { t.Fatal("TryLockWithTimeout should fail when locked") } if time.Since(start) < 15*time.Millisecond { t.Fatal("TryLockWithTimeout returned too quickly") } m.Unlock() if !m.TryLockWithTimeout(50 * time.Millisecond) { t.Fatal("TryLockWithTimeout should succeed when unlocked") } m.Unlock() } func TestCASMutex_TryLockWithContext_Cancel(t *testing.T) { m := NewCASMutex() m.Lock() defer m.Unlock() ctx, cancel := context.WithCancel(context.Background()) go func() { time.Sleep(10 * time.Millisecond) cancel() }() if m.TryLockWithContext(ctx) { t.Fatal("expected lock to fail after cancel") } } func TestCASMutex_RLockMultiple(t *testing.T) { m := NewCASMutex() if !m.RTryLock() { t.Fatal("RTryLock should succeed") } if !m.RTryLock() { t.Fatal("Second RTryLock should succeed") } if m.TryLock() { t.Fatal("Write TryLock should fail with read locks held") } m.RUnlock() m.RUnlock() if !m.TryLock() { t.Fatal("Write TryLock should succeed after read unlocks") } m.Unlock() } func TestCASMutex_RLocker(t *testing.T) { m := NewCASMutex() rl := m.RLocker() rl.Lock() rl.Unlock() } func TestCASMutex_Concurrent(t *testing.T) { m := NewCASMutex() var counter int64 const n = 50 var wg sync.WaitGroup wg.Add(n) for i := 0; i < n; i++ { go func() { defer wg.Done() m.Lock() atomic.AddInt64(&counter, 1) m.Unlock() }() } wg.Wait() if atomic.LoadInt64(&counter) != n { t.Fatalf("counter=%d want %d", counter, n) } } func TestCASMutex_RTryLockWithTimeout(t *testing.T) { m := NewCASMutex() m.Lock() if m.RTryLockWithTimeout(20 * time.Millisecond) { t.Fatal("RTryLockWithTimeout should fail when write-locked") } m.Unlock() if !m.RTryLockWithTimeout(20 * time.Millisecond) { t.Fatal("RTryLockWithTimeout should succeed when free") } m.RUnlock() }