github.com/haraldrudell/parl@v0.4.176/counter/access-manager_test.go (about) 1 /* 2 © 2023–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 package counter 7 8 import ( 9 "sync" 10 "sync/atomic" 11 "testing" 12 "time" 13 ) 14 15 const shortTime = time.Millisecond 16 17 func TestAccessManagerAtomic(t *testing.T) { 18 var exp1 = ticketDelta 19 var exp2 = uint64(0) 20 21 var manager accessManager 22 var actual uint64 23 24 // start atomic operation 25 var isLockAccess = manager.RequestAccess() 26 if isLockAccess { 27 t.Error("isLockAccess true") 28 } 29 actual = atomic.LoadUint64(&manager.before) 30 if actual != exp1 { 31 t.Errorf("before1: %d exp %d", actual, exp1) 32 } 33 34 // end atomic operation 35 manager.RelinquishAccess(isLockAccess) 36 actual = atomic.LoadUint64(&manager.before) 37 if actual != exp2 { 38 t.Errorf("before2: %d exp %d", actual, exp2) 39 } 40 } 41 42 type UseLockTester struct { 43 lockIsReady, waitToReleaseLock, lockDone sync.WaitGroup 44 hasLock chan struct{} 45 requestIsReady, requestDone sync.WaitGroup 46 hasTicket chan struct{} 47 } 48 49 func NewUseLockTester() (lockTester *UseLockTester) { return &UseLockTester{} } 50 func (u *UseLockTester) Lock(manager *accessManager) { 51 u.lockIsReady.Add(1) 52 u.waitToReleaseLock.Add(1) 53 u.lockDone.Add(1) 54 u.hasLock = make(chan struct{}) 55 go u.lock(manager) 56 } 57 func (u *UseLockTester) lock(manager *accessManager) { 58 defer u.lockDone.Done() 59 60 u.lockIsReady.Done() 61 manager.Lock() 62 close(u.hasLock) 63 u.waitToReleaseLock.Wait() 64 manager.Unlock() 65 } 66 func (u *UseLockTester) Request(manager *accessManager) { 67 u.requestIsReady.Add(1) 68 u.requestDone.Add(1) 69 u.hasTicket = make(chan struct{}) 70 go u.request(manager) 71 } 72 func (u *UseLockTester) request(manager *accessManager) { 73 defer u.requestDone.Done() 74 75 u.requestIsReady.Done() 76 var ticket = manager.RequestAccess() 77 close(u.hasTicket) 78 manager.RelinquishAccess(ticket) 79 } 80 81 func TestAccessManagerLock(t *testing.T) { 82 83 var manager accessManager 84 var lockTester = NewUseLockTester() 85 var timer *time.Timer 86 87 // have ongoing atomic access: blocks Lock 88 var ticket = manager.RequestAccess() 89 90 // have thread wait for lock access 91 lockTester.Lock(&manager) 92 lockTester.lockIsReady.Wait() 93 select { 94 case <-lockTester.hasLock: 95 t.Error("hasLock prematurely") 96 default: 97 } 98 99 // end atomic access: enter lock access 100 manager.RelinquishAccess(ticket) 101 timer = time.NewTimer(shortTime) 102 select { 103 case <-timer.C: 104 t.Error("hasLock not enabled") 105 case <-lockTester.hasLock: 106 timer.Stop() 107 } 108 109 // attempt lock access: should be blocked 110 lockTester.Request(&manager) 111 lockTester.requestIsReady.Wait() 112 select { 113 case <-lockTester.hasTicket: 114 t.Error("hasTicket prematurely") 115 default: 116 } 117 118 // make lock available: lock access should happen 119 lockTester.waitToReleaseLock.Done() 120 timer = time.NewTimer(shortTime) 121 select { 122 case <-timer.C: 123 t.Error("hasTicket not enabled") 124 case <-lockTester.hasTicket: 125 timer.Stop() 126 } 127 128 lockTester.lockDone.Wait() 129 lockTester.requestDone.Wait() 130 }