github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/waiter/waiter_test.go (about)

     1  // Copyright 2018 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 waiter
    16  
    17  import (
    18  	"sync/atomic"
    19  	"testing"
    20  )
    21  
    22  type callbackStub struct {
    23  	f func(e *Entry, m EventMask)
    24  }
    25  
    26  // Callback implements EntryCallback.Callback.
    27  func (c *callbackStub) Callback(e *Entry, m EventMask) {
    28  	c.f(e, m)
    29  }
    30  
    31  func TestEmptyQueue(t *testing.T) {
    32  	var q Queue
    33  
    34  	// Notify the zero-value of a queue.
    35  	q.Notify(EventIn)
    36  
    37  	// Register then unregister a waiter, then notify the queue.
    38  	cnt := 0
    39  	e := Entry{Callback: &callbackStub{func(*Entry, EventMask) { cnt++ }}}
    40  	q.EventRegister(&e, EventIn)
    41  	q.EventUnregister(&e)
    42  	q.Notify(EventIn)
    43  	if cnt != 0 {
    44  		t.Errorf("Callback was called when it shouldn't have been")
    45  	}
    46  }
    47  
    48  func TestMask(t *testing.T) {
    49  	// Register a waiter.
    50  	var q Queue
    51  	var cnt int
    52  	e := Entry{Callback: &callbackStub{func(*Entry, EventMask) { cnt++ }}}
    53  	q.EventRegister(&e, EventIn|EventErr)
    54  
    55  	// Notify with an overlapping mask.
    56  	cnt = 0
    57  	q.Notify(EventIn | EventOut)
    58  	if cnt != 1 {
    59  		t.Errorf("Callback wasn't called when it should have been")
    60  	}
    61  
    62  	// Notify with a subset mask.
    63  	cnt = 0
    64  	q.Notify(EventIn)
    65  	if cnt != 1 {
    66  		t.Errorf("Callback wasn't called when it should have been")
    67  	}
    68  
    69  	// Notify with a superset mask.
    70  	cnt = 0
    71  	q.Notify(EventIn | EventErr | EventOut)
    72  	if cnt != 1 {
    73  		t.Errorf("Callback wasn't called when it should have been")
    74  	}
    75  
    76  	// Notify with the exact same mask.
    77  	cnt = 0
    78  	q.Notify(EventIn | EventErr)
    79  	if cnt != 1 {
    80  		t.Errorf("Callback wasn't called when it should have been")
    81  	}
    82  
    83  	// Notify with a disjoint mask.
    84  	cnt = 0
    85  	q.Notify(EventOut | EventHUp)
    86  	if cnt != 0 {
    87  		t.Errorf("Callback was called when it shouldn't have been")
    88  	}
    89  }
    90  
    91  func TestConcurrentRegistration(t *testing.T) {
    92  	var q Queue
    93  	var cnt int
    94  	const concurrency = 1000
    95  
    96  	ch1 := make(chan struct{})
    97  	ch2 := make(chan struct{})
    98  	ch3 := make(chan struct{})
    99  
   100  	// Create goroutines that will all register/unregister concurrently.
   101  	for i := 0; i < concurrency; i++ {
   102  		go func() {
   103  			var e Entry
   104  			e.Callback = &callbackStub{func(entry *Entry, mask EventMask) {
   105  				cnt++
   106  				if entry != &e {
   107  					t.Errorf("entry = %p, want %p", entry, &e)
   108  				}
   109  				if mask != EventIn {
   110  					t.Errorf("mask = %#x want %#x", mask, EventIn)
   111  				}
   112  			}}
   113  
   114  			// Wait for notification, then register.
   115  			<-ch1
   116  			q.EventRegister(&e, EventIn|EventErr)
   117  
   118  			// Tell main goroutine that we're done registering.
   119  			ch2 <- struct{}{}
   120  
   121  			// Wait for notification, then unregister.
   122  			<-ch3
   123  			q.EventUnregister(&e)
   124  
   125  			// Tell main goroutine that we're done unregistering.
   126  			ch2 <- struct{}{}
   127  		}()
   128  	}
   129  
   130  	// Let the goroutines register.
   131  	close(ch1)
   132  	for i := 0; i < concurrency; i++ {
   133  		<-ch2
   134  	}
   135  
   136  	// Issue a notification.
   137  	q.Notify(EventIn)
   138  	if cnt != concurrency {
   139  		t.Errorf("cnt = %d, want %d", cnt, concurrency)
   140  	}
   141  
   142  	// Let the goroutine unregister.
   143  	close(ch3)
   144  	for i := 0; i < concurrency; i++ {
   145  		<-ch2
   146  	}
   147  
   148  	// Issue a notification.
   149  	q.Notify(EventIn)
   150  	if cnt != concurrency {
   151  		t.Errorf("cnt = %d, want %d", cnt, concurrency)
   152  	}
   153  }
   154  
   155  func TestConcurrentNotification(t *testing.T) {
   156  	var q Queue
   157  	var cnt int32
   158  	const concurrency = 1000
   159  	const waiterCount = 1000
   160  
   161  	// Register waiters.
   162  	for i := 0; i < waiterCount; i++ {
   163  		var e Entry
   164  		e.Callback = &callbackStub{func(entry *Entry, mask EventMask) {
   165  			atomic.AddInt32(&cnt, 1)
   166  			if entry != &e {
   167  				t.Errorf("entry = %p, want %p", entry, &e)
   168  			}
   169  			if mask != EventIn {
   170  				t.Errorf("mask = %#x want %#x", mask, EventIn)
   171  			}
   172  		}}
   173  
   174  		q.EventRegister(&e, EventIn|EventErr)
   175  	}
   176  
   177  	// Launch notifiers.
   178  	ch1 := make(chan struct{})
   179  	ch2 := make(chan struct{})
   180  	for i := 0; i < concurrency; i++ {
   181  		go func() {
   182  			<-ch1
   183  			q.Notify(EventIn)
   184  			ch2 <- struct{}{}
   185  		}()
   186  	}
   187  
   188  	// Let notifiers go.
   189  	close(ch1)
   190  	for i := 0; i < concurrency; i++ {
   191  		<-ch2
   192  	}
   193  
   194  	// Check the count.
   195  	if cnt != concurrency*waiterCount {
   196  		t.Errorf("cnt = %d, want %d", cnt, concurrency*waiterCount)
   197  	}
   198  }