github.com/haraldrudell/parl@v0.4.176/moderator-core_test.go (about) 1 /* 2 © 2022–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 package parl 7 8 import ( 9 "sync" 10 "sync/atomic" 11 "testing" 12 "time" 13 ) 14 15 const ( 16 shortTime = time.Millisecond 17 ) 18 19 type ModeratorStatus struct { 20 parallelism, active, waiting, transferBehindLock uint64 21 } 22 23 func NewModeratorStatus(m *ModeratorCore) (status *ModeratorStatus) { 24 s := ModeratorStatus{} 25 s.parallelism, s.active, s.waiting = m.Status() 26 s.transferBehindLock = m.transferBehindLock.Load() 27 return &s 28 } 29 30 type ModeratorLocker struct { 31 isReady, isDone sync.WaitGroup 32 mo *ModeratorCore 33 haveTicket atomic.Bool 34 returnTicket func() 35 t *testing.T 36 } 37 38 func NewModeratorLocker(mo *ModeratorCore, t *testing.T) (m *ModeratorLocker) { 39 return &ModeratorLocker{mo: mo, t: t} 40 } 41 func (m *ModeratorLocker) Ticket() { 42 m.isReady.Add(1) 43 m.isDone.Add(1) 44 go m.ticket() 45 } 46 func (m *ModeratorLocker) ticket() { 47 defer m.isDone.Done() 48 49 var t = m.t 50 m.isReady.Done() 51 t.Log("thread invoking Ticket") 52 m.returnTicket = m.mo.Ticket() 53 t.Log("thread got ticket") 54 m.haveTicket.Store(true) 55 } 56 57 func TestModeratorCore(t *testing.T) { 58 var parallelism = uint64(1) 59 var exp1_1Ticket = ModeratorStatus{ 60 parallelism: parallelism, 61 active: 1, 62 } 63 var exp2_Ticket2Wait = ModeratorStatus{ 64 parallelism: parallelism, 65 active: 1, 66 waiting: 1, 67 } 68 var exp3_1Returned = ModeratorStatus{ 69 parallelism: parallelism, 70 active: 1, 71 } 72 var exp4_2returned = ModeratorStatus{ 73 parallelism: parallelism, 74 } 75 76 var moderator *ModeratorCore 77 var returnTicket1 func() 78 var actual ModeratorStatus 79 80 // one thread should be atomic access 81 moderator = NewModeratorCore(parallelism) 82 returnTicket1 = moderator.Ticket() 83 if returnTicket1 == nil { 84 t.Fatal("returnTicket1 nil") 85 } 86 actual = *NewModeratorStatus(moderator) 87 if actual != exp1_1Ticket { 88 t.Errorf("1: one ticket:\n%#v exp:\n%#v", actual, exp1_1Ticket) 89 } 90 91 // thread2 should use lock mode and block 92 var ml = NewModeratorLocker(moderator, t) 93 ml.Ticket() 94 ml.isReady.Wait() // wait for thread Ticket invocation 95 // wait 1 ms for thread to reach m.queue.Wait 96 <-time.NewTimer(shortTime).C 97 if ml.haveTicket.Load() { 98 t.Errorf("ml.haveTicket true") 99 } 100 actual = *NewModeratorStatus(moderator) 101 if actual != exp2_Ticket2Wait { 102 t.Errorf("2: ticket2 waiting:\n%#v exp:\n%#v", actual, exp2_Ticket2Wait) 103 } 104 105 // return first ticket 106 returnTicket1() 107 ml.isDone.Wait() 108 if !ml.haveTicket.Load() { 109 t.Errorf("ml.haveTicket false") 110 } 111 actual = *NewModeratorStatus(moderator) 112 if actual != exp3_1Returned { 113 t.Errorf("3: after return1:\n%#v exp:\n%#v", actual, exp3_1Returned) 114 } 115 if ml.returnTicket == nil { 116 t.Fatal("returnTicket2 nil") 117 } 118 119 // return second ticket 120 ml.returnTicket() 121 actual = *NewModeratorStatus(moderator) 122 if actual != exp4_2returned { 123 t.Errorf("4: after return2:\n%#v exp:\n%#v", actual, exp4_2returned) 124 } 125 }