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(&notifiedEvent, 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(&notifiedEvent) == 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  }