github.com/FlowerWrong/netstack@v0.0.0-20191009141956-e5848263af28/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)
    24  }
    25  
    26  // Callback implements EntryCallback.Callback.
    27  func (c *callbackStub) Callback(e *Entry) {
    28  	c.f(e)
    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) { 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) { 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) {
   105  				cnt++
   106  				if entry != &e {
   107  					t.Errorf("entry = %p, want %p", entry, &e)
   108  				}
   109  			}}
   110  
   111  			// Wait for notification, then register.
   112  			<-ch1
   113  			q.EventRegister(&e, EventIn|EventErr)
   114  
   115  			// Tell main goroutine that we're done registering.
   116  			ch2 <- struct{}{}
   117  
   118  			// Wait for notification, then unregister.
   119  			<-ch3
   120  			q.EventUnregister(&e)
   121  
   122  			// Tell main goroutine that we're done unregistering.
   123  			ch2 <- struct{}{}
   124  		}()
   125  	}
   126  
   127  	// Let the goroutines register.
   128  	close(ch1)
   129  	for i := 0; i < concurrency; i++ {
   130  		<-ch2
   131  	}
   132  
   133  	// Issue a notification.
   134  	q.Notify(EventIn)
   135  	if cnt != concurrency {
   136  		t.Errorf("cnt = %d, want %d", cnt, concurrency)
   137  	}
   138  
   139  	// Let the goroutine unregister.
   140  	close(ch3)
   141  	for i := 0; i < concurrency; i++ {
   142  		<-ch2
   143  	}
   144  
   145  	// Issue a notification.
   146  	q.Notify(EventIn)
   147  	if cnt != concurrency {
   148  		t.Errorf("cnt = %d, want %d", cnt, concurrency)
   149  	}
   150  }
   151  
   152  func TestConcurrentNotification(t *testing.T) {
   153  	var q Queue
   154  	var cnt int32
   155  	const concurrency = 1000
   156  	const waiterCount = 1000
   157  
   158  	// Register waiters.
   159  	for i := 0; i < waiterCount; i++ {
   160  		var e Entry
   161  		e.Callback = &callbackStub{func(entry *Entry) {
   162  			atomic.AddInt32(&cnt, 1)
   163  			if entry != &e {
   164  				t.Errorf("entry = %p, want %p", entry, &e)
   165  			}
   166  		}}
   167  
   168  		q.EventRegister(&e, EventIn|EventErr)
   169  	}
   170  
   171  	// Launch notifiers.
   172  	ch1 := make(chan struct{})
   173  	ch2 := make(chan struct{})
   174  	for i := 0; i < concurrency; i++ {
   175  		go func() {
   176  			<-ch1
   177  			q.Notify(EventIn)
   178  			ch2 <- struct{}{}
   179  		}()
   180  	}
   181  
   182  	// Let notifiers go.
   183  	close(ch1)
   184  	for i := 0; i < concurrency; i++ {
   185  		<-ch2
   186  	}
   187  
   188  	// Check the count.
   189  	if cnt != concurrency*waiterCount {
   190  		t.Errorf("cnt = %d, want %d", cnt, concurrency*waiterCount)
   191  	}
   192  }