github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/syncevent/waiter_test.go (about) 1 // Copyright 2020 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package syncevent 16 17 import ( 18 "sync/atomic" 19 "testing" 20 "time" 21 22 "github.com/SagerNet/gvisor/pkg/sleep" 23 "github.com/SagerNet/gvisor/pkg/sync" 24 ) 25 26 func TestWaiterAlreadyPending(t *testing.T) { 27 var w Waiter 28 w.Init() 29 want := Set(1) 30 w.Notify(want) 31 if got := w.Wait(); got != want { 32 t.Errorf("Waiter.Wait: got %#x, wanted %#x", got, want) 33 } 34 } 35 36 func TestWaiterAsyncNotify(t *testing.T) { 37 var w Waiter 38 w.Init() 39 want := Set(1) 40 go func() { 41 time.Sleep(100 * time.Millisecond) 42 w.Notify(want) 43 }() 44 if got := w.Wait(); got != want { 45 t.Errorf("Waiter.Wait: got %#x, wanted %#x", got, want) 46 } 47 } 48 49 func TestWaiterWaitFor(t *testing.T) { 50 var w Waiter 51 w.Init() 52 evWaited := Set(1) 53 evOther := Set(2) 54 w.Notify(evOther) 55 notifiedEvent := uint32(0) 56 go func() { 57 time.Sleep(100 * time.Millisecond) 58 atomic.StoreUint32(¬ifiedEvent, 1) 59 w.Notify(evWaited) 60 }() 61 if got, want := w.WaitFor(evWaited), evWaited|evOther; got != want { 62 t.Errorf("Waiter.WaitFor: got %#x, wanted %#x", got, want) 63 } 64 if atomic.LoadUint32(¬ifiedEvent) == 0 { 65 t.Errorf("Waiter.WaitFor returned before goroutine notified waited-for event") 66 } 67 } 68 69 func TestWaiterWaitAndAckAll(t *testing.T) { 70 var w Waiter 71 w.Init() 72 w.Notify(AllEvents) 73 if got := w.WaitAndAckAll(); got != AllEvents { 74 t.Errorf("Waiter.WaitAndAckAll: got %#x, wanted %#x", got, AllEvents) 75 } 76 if got := w.Pending(); got != NoEvents { 77 t.Errorf("Waiter.WaitAndAckAll did not ack all events: got %#x, wanted 0", got) 78 } 79 } 80 81 // BenchmarkWaiterX, BenchmarkSleeperX, and BenchmarkChannelX benchmark usage 82 // pattern X (described in terms of Waiter) with Waiter, sleep.Sleeper, and 83 // buffered chan struct{} respectively. When the maximum number of event 84 // sources is relevant, we use 3 event sources because this is representative 85 // of the kernel.Task.block() use case: an interrupt source, a timeout source, 86 // and the actual event source being waited on. 87 88 // Event set used by most benchmarks. 89 const evBench Set = 1 90 91 // BenchmarkXxxNotifyRedundant measures how long it takes to notify a Waiter of 92 // an event that is already pending. 93 94 func BenchmarkWaiterNotifyRedundant(b *testing.B) { 95 var w Waiter 96 w.Init() 97 w.Notify(evBench) 98 99 b.ResetTimer() 100 for i := 0; i < b.N; i++ { 101 w.Notify(evBench) 102 } 103 } 104 105 func BenchmarkSleeperNotifyRedundant(b *testing.B) { 106 var s sleep.Sleeper 107 var w sleep.Waker 108 s.AddWaker(&w, 0) 109 w.Assert() 110 111 b.ResetTimer() 112 for i := 0; i < b.N; i++ { 113 w.Assert() 114 } 115 } 116 117 func BenchmarkChannelNotifyRedundant(b *testing.B) { 118 ch := make(chan struct{}, 1) 119 ch <- struct{}{} 120 121 b.ResetTimer() 122 for i := 0; i < b.N; i++ { 123 select { 124 case ch <- struct{}{}: 125 default: 126 } 127 } 128 } 129 130 // BenchmarkXxxNotifyWaitAck measures how long it takes to notify a Waiter an 131 // event, return that event using a blocking check, and then unset the event as 132 // pending. 133 134 func BenchmarkWaiterNotifyWaitAck(b *testing.B) { 135 var w Waiter 136 w.Init() 137 138 b.ResetTimer() 139 for i := 0; i < b.N; i++ { 140 w.Notify(evBench) 141 w.Wait() 142 w.Ack(evBench) 143 } 144 } 145 146 func BenchmarkSleeperNotifyWaitAck(b *testing.B) { 147 var s sleep.Sleeper 148 var w sleep.Waker 149 s.AddWaker(&w, 0) 150 151 b.ResetTimer() 152 for i := 0; i < b.N; i++ { 153 w.Assert() 154 s.Fetch(true) 155 } 156 } 157 158 func BenchmarkChannelNotifyWaitAck(b *testing.B) { 159 ch := make(chan struct{}, 1) 160 161 b.ResetTimer() 162 for i := 0; i < b.N; i++ { 163 // notify 164 select { 165 case ch <- struct{}{}: 166 default: 167 } 168 169 // wait + ack 170 <-ch 171 } 172 } 173 174 // BenchmarkSleeperMultiNotifyWaitAck is equivalent to 175 // BenchmarkSleeperNotifyWaitAck, but also includes allocation of a 176 // temporary sleep.Waker. This is necessary when multiple goroutines may wait 177 // for the same event, since each sleep.Waker can wake only a single 178 // sleep.Sleeper. 179 // 180 // The syncevent package does not require a distinct object for each 181 // waiter-waker relationship, so BenchmarkWaiterNotifyWaitAck and 182 // BenchmarkWaiterMultiNotifyWaitAck would be identical. The analogous state 183 // for channels, runtime.sudog, is inescapably runtime-allocated, so 184 // BenchmarkChannelNotifyWaitAck and BenchmarkChannelMultiNotifyWaitAck would 185 // also be identical. 186 187 func BenchmarkSleeperMultiNotifyWaitAck(b *testing.B) { 188 var s sleep.Sleeper 189 // The sleep package doesn't provide sync.Pool allocation of Wakers; 190 // we do for a fairer comparison. 191 wakerPool := sync.Pool{ 192 New: func() interface{} { 193 return &sleep.Waker{} 194 }, 195 } 196 197 b.ResetTimer() 198 for i := 0; i < b.N; i++ { 199 w := wakerPool.Get().(*sleep.Waker) 200 s.AddWaker(w, 0) 201 w.Assert() 202 s.Fetch(true) 203 s.Done() 204 wakerPool.Put(w) 205 } 206 } 207 208 // BenchmarkXxxTempNotifyWaitAck is equivalent to NotifyWaitAck, but also 209 // includes allocation of a temporary Waiter. This models the case where a 210 // goroutine not already associated with a Waiter needs one in order to block. 211 // 212 // The analogous state for channels is built into runtime.g, so 213 // BenchmarkChannelNotifyWaitAck and BenchmarkChannelTempNotifyWaitAck would be 214 // identical. 215 216 func BenchmarkWaiterTempNotifyWaitAck(b *testing.B) { 217 b.ResetTimer() 218 for i := 0; i < b.N; i++ { 219 w := GetWaiter() 220 w.Notify(evBench) 221 w.Wait() 222 w.Ack(evBench) 223 PutWaiter(w) 224 } 225 } 226 227 func BenchmarkSleeperTempNotifyWaitAck(b *testing.B) { 228 // The sleep package doesn't provide sync.Pool allocation of Sleepers; 229 // we do for a fairer comparison. 230 sleeperPool := sync.Pool{ 231 New: func() interface{} { 232 return &sleep.Sleeper{} 233 }, 234 } 235 var w sleep.Waker 236 237 b.ResetTimer() 238 for i := 0; i < b.N; i++ { 239 s := sleeperPool.Get().(*sleep.Sleeper) 240 s.AddWaker(&w, 0) 241 w.Assert() 242 s.Fetch(true) 243 s.Done() 244 sleeperPool.Put(s) 245 } 246 } 247 248 // BenchmarkXxxNotifyWaitMultiAck is equivalent to NotifyWaitAck, but allows 249 // for multiple event sources. 250 251 func BenchmarkWaiterNotifyWaitMultiAck(b *testing.B) { 252 var w Waiter 253 w.Init() 254 255 b.ResetTimer() 256 for i := 0; i < b.N; i++ { 257 w.Notify(evBench) 258 if e := w.Wait(); e != evBench { 259 b.Fatalf("Wait: got %#x, wanted %#x", e, evBench) 260 } 261 w.Ack(evBench) 262 } 263 } 264 265 func BenchmarkSleeperNotifyWaitMultiAck(b *testing.B) { 266 var s sleep.Sleeper 267 var ws [3]sleep.Waker 268 for i := range ws { 269 s.AddWaker(&ws[i], i) 270 } 271 272 b.ResetTimer() 273 for i := 0; i < b.N; i++ { 274 ws[0].Assert() 275 if id, _ := s.Fetch(true); id != 0 { 276 b.Fatalf("Fetch: got %d, wanted 0", id) 277 } 278 } 279 } 280 281 func BenchmarkChannelNotifyWaitMultiAck(b *testing.B) { 282 ch0 := make(chan struct{}, 1) 283 ch1 := make(chan struct{}, 1) 284 ch2 := make(chan struct{}, 1) 285 286 b.ResetTimer() 287 for i := 0; i < b.N; i++ { 288 // notify 289 select { 290 case ch0 <- struct{}{}: 291 default: 292 } 293 294 // wait + clear 295 select { 296 case <-ch0: 297 // ok 298 case <-ch1: 299 b.Fatalf("received from ch1") 300 case <-ch2: 301 b.Fatalf("received from ch2") 302 } 303 } 304 } 305 306 // BenchmarkXxxNotifyAsyncWaitAck measures how long it takes to wait for an 307 // event while another goroutine signals the event. This assumes that a new 308 // goroutine doesn't run immediately (i.e. the creator of a new goroutine is 309 // allowed to go to sleep before the new goroutine has a chance to run). 310 311 func BenchmarkWaiterNotifyAsyncWaitAck(b *testing.B) { 312 var w Waiter 313 w.Init() 314 315 b.ResetTimer() 316 for i := 0; i < b.N; i++ { 317 go func() { 318 w.Notify(1) 319 }() 320 w.Wait() 321 w.Ack(evBench) 322 } 323 } 324 325 func BenchmarkSleeperNotifyAsyncWaitAck(b *testing.B) { 326 var s sleep.Sleeper 327 var w sleep.Waker 328 s.AddWaker(&w, 0) 329 330 b.ResetTimer() 331 for i := 0; i < b.N; i++ { 332 go func() { 333 w.Assert() 334 }() 335 s.Fetch(true) 336 } 337 } 338 339 func BenchmarkChannelNotifyAsyncWaitAck(b *testing.B) { 340 ch := make(chan struct{}, 1) 341 342 b.ResetTimer() 343 for i := 0; i < b.N; i++ { 344 go func() { 345 select { 346 case ch <- struct{}{}: 347 default: 348 } 349 }() 350 <-ch 351 } 352 } 353 354 // BenchmarkXxxNotifyAsyncWaitMultiAck is equivalent to NotifyAsyncWaitAck, but 355 // allows for multiple event sources. 356 357 func BenchmarkWaiterNotifyAsyncWaitMultiAck(b *testing.B) { 358 var w Waiter 359 w.Init() 360 361 b.ResetTimer() 362 for i := 0; i < b.N; i++ { 363 go func() { 364 w.Notify(evBench) 365 }() 366 if e := w.Wait(); e != evBench { 367 b.Fatalf("Wait: got %#x, wanted %#x", e, evBench) 368 } 369 w.Ack(evBench) 370 } 371 } 372 373 func BenchmarkSleeperNotifyAsyncWaitMultiAck(b *testing.B) { 374 var s sleep.Sleeper 375 var ws [3]sleep.Waker 376 for i := range ws { 377 s.AddWaker(&ws[i], i) 378 } 379 380 b.ResetTimer() 381 for i := 0; i < b.N; i++ { 382 go func() { 383 ws[0].Assert() 384 }() 385 if id, _ := s.Fetch(true); id != 0 { 386 b.Fatalf("Fetch: got %d, expected 0", id) 387 } 388 } 389 } 390 391 func BenchmarkChannelNotifyAsyncWaitMultiAck(b *testing.B) { 392 ch0 := make(chan struct{}, 1) 393 ch1 := make(chan struct{}, 1) 394 ch2 := make(chan struct{}, 1) 395 396 b.ResetTimer() 397 for i := 0; i < b.N; i++ { 398 go func() { 399 select { 400 case ch0 <- struct{}{}: 401 default: 402 } 403 }() 404 405 select { 406 case <-ch0: 407 // ok 408 case <-ch1: 409 b.Fatalf("received from ch1") 410 case <-ch2: 411 b.Fatalf("received from ch2") 412 } 413 } 414 }