github.com/egonelbre/exp@v0.0.0-20240430123955-ed1d3aa93911/sync2/biasedmutex_test.go (about) 1 package sync2 2 3 import ( 4 "fmt" 5 "strings" 6 "sync" 7 "sync/atomic" 8 "testing" 9 ) 10 11 func TestBiasedMutexProgress(t *testing.T) { 12 for _, rt := range []int{1, 2, 4, 8} { 13 for _, wt := range []int{1, 2, 4, 8} { 14 name := fmt.Sprintf("r%vw%v", rt, wt) 15 16 t.Run(name, func(t *testing.T) { 17 m := NewBiasedMutex() 18 m.SetReaderThreshold(rt) 19 m.SetWriterThreshold(wt) 20 21 testProgress(t, m) 22 }) 23 } 24 } 25 } 26 27 func TestStandardRWMutex(t *testing.T) { 28 testProgress(t, &sync.RWMutex{}) 29 } 30 31 type RWMutex interface { 32 Lock() 33 Unlock() 34 RLock() 35 RUnlock() 36 } 37 38 func testProgress(t *testing.T, m RWMutex) { 39 const STEPS = 10000 40 const R, W = 100, 100 41 42 var totalProgress int64 43 var readerProgress int64 44 var writerProgress int64 45 46 var sharedVariable int 47 48 var activeWriters int64 49 var activeReaders int64 50 51 var wg sync.WaitGroup 52 53 var mufatal sync.Mutex 54 var fatals []string 55 fatal := func(err string) { 56 mufatal.Lock() 57 fatals = append(fatals, err) 58 mufatal.Unlock() 59 atomic.AddInt64(&totalProgress, STEPS) 60 } 61 62 order := make([]byte, STEPS) 63 64 wg.Add(R) 65 for i := 0; i < R; i++ { 66 go func() { 67 defer wg.Done() 68 69 localTotal := 0 70 for { 71 m.RLock() 72 73 orderx := atomic.AddInt64(&totalProgress, 1) 74 if orderx >= STEPS { 75 m.RUnlock() 76 break 77 } 78 order[orderx-1] = 'R' 79 80 atomic.AddInt64(&activeReaders, 1) 81 if atomic.LoadInt64(&activeWriters) > 0 { 82 fatal("writer active during reading") 83 atomic.AddInt64(&activeReaders, -1) 84 m.RUnlock() 85 break 86 } 87 atomic.AddInt64(&readerProgress, 1) 88 localTotal += sharedVariable 89 atomic.AddInt64(&activeReaders, -1) 90 m.RUnlock() 91 } 92 }() 93 } 94 95 wg.Add(W) 96 for i := 0; i < W; i++ { 97 go func(i int) { 98 defer wg.Done() 99 100 step := 0 101 for { 102 m.Lock() 103 104 orderx := atomic.AddInt64(&totalProgress, 1) 105 if orderx >= STEPS { 106 m.Unlock() 107 break 108 } 109 order[orderx-1] = 'W' 110 111 if atomic.AddInt64(&activeWriters, 1) > 1 { 112 fatal("more than one writer") 113 atomic.AddInt64(&activeWriters, -1) 114 m.Unlock() 115 break 116 } 117 if atomic.LoadInt64(&activeReaders) > 0 { 118 fatal("reader active during writing") 119 atomic.AddInt64(&activeWriters, -1) 120 m.Unlock() 121 break 122 } 123 writerProgress++ 124 sharedVariable += step * i 125 step++ 126 atomic.AddInt64(&activeWriters, -1) 127 m.Unlock() 128 } 129 }(i) 130 } 131 132 wg.Wait() 133 134 if len(fatals) > 0 { 135 t.Fatal("invalid semantics\n" + strings.Join(fatals, "\n")) 136 } 137 138 if readerProgress == 0 || writerProgress == 0 { 139 t.Fatalf("no progress reader:%v writer:%v", readerProgress, writerProgress) 140 } 141 x := STEPS 142 if x > 500 { 143 x = 500 144 } 145 t.Logf("reader:%v writer:%v\n%v", readerProgress, writerProgress, string(order[:x])) 146 }