github.com/FlowerWrong/netstack@v0.0.0-20191009141956-e5848263af28/gate/gate_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 gate_test
    16  
    17  import (
    18  	"sync"
    19  	"testing"
    20  	"time"
    21  
    22  	"github.com/FlowerWrong/netstack/gate"
    23  )
    24  
    25  func TestBasicEnter(t *testing.T) {
    26  	var g gate.Gate
    27  
    28  	if !g.Enter() {
    29  		t.Fatalf("Failed to enter when it should be allowed")
    30  	}
    31  
    32  	g.Leave()
    33  
    34  	g.Close()
    35  
    36  	if g.Enter() {
    37  		t.Fatalf("Allowed to enter when it should fail")
    38  	}
    39  }
    40  
    41  func enterFunc(t *testing.T, g *gate.Gate, enter, leave, reenter chan struct{}, done1, done2, done3 *sync.WaitGroup) {
    42  	// Wait until instructed to enter.
    43  	<-enter
    44  	if !g.Enter() {
    45  		t.Errorf("Failed to enter when it should be allowed")
    46  	}
    47  
    48  	done1.Done()
    49  
    50  	// Wait until instructed to leave.
    51  	<-leave
    52  	g.Leave()
    53  
    54  	done2.Done()
    55  
    56  	// Wait until instructed to reenter.
    57  	<-reenter
    58  	if g.Enter() {
    59  		t.Errorf("Allowed to enter when it should fail")
    60  	}
    61  	done3.Done()
    62  }
    63  
    64  func TestConcurrentEnter(t *testing.T) {
    65  	var g gate.Gate
    66  	var done1, done2, done3 sync.WaitGroup
    67  
    68  	// Create 1000 worker goroutines.
    69  	enter := make(chan struct{})
    70  	leave := make(chan struct{})
    71  	reenter := make(chan struct{})
    72  	done1.Add(1000)
    73  	done2.Add(1000)
    74  	done3.Add(1000)
    75  	for i := 0; i < 1000; i++ {
    76  		go enterFunc(t, &g, enter, leave, reenter, &done1, &done2, &done3)
    77  	}
    78  
    79  	// Tell them all to enter, then leave.
    80  	close(enter)
    81  	done1.Wait()
    82  
    83  	close(leave)
    84  	done2.Wait()
    85  
    86  	// Close the gate, then have the workers try to enter again.
    87  	g.Close()
    88  	close(reenter)
    89  	done3.Wait()
    90  }
    91  
    92  func closeFunc(g *gate.Gate, done chan struct{}) {
    93  	g.Close()
    94  	close(done)
    95  }
    96  
    97  func TestCloseWaits(t *testing.T) {
    98  	var g gate.Gate
    99  
   100  	// Enter 10 times.
   101  	for i := 0; i < 10; i++ {
   102  		if !g.Enter() {
   103  			t.Fatalf("Failed to enter when it should be allowed")
   104  		}
   105  	}
   106  
   107  	// Launch closer. Check that it doesn't complete.
   108  	done := make(chan struct{})
   109  	go closeFunc(&g, done)
   110  
   111  	for i := 0; i < 10; i++ {
   112  		select {
   113  		case <-done:
   114  			t.Fatalf("Close function completed too soon")
   115  		case <-time.After(100 * time.Millisecond):
   116  		}
   117  
   118  		g.Leave()
   119  	}
   120  
   121  	// Now the closer must complete.
   122  	<-done
   123  }
   124  
   125  func TestMultipleSerialCloses(t *testing.T) {
   126  	var g gate.Gate
   127  
   128  	// Enter 10 times.
   129  	for i := 0; i < 10; i++ {
   130  		if !g.Enter() {
   131  			t.Fatalf("Failed to enter when it should be allowed")
   132  		}
   133  	}
   134  
   135  	// Launch closer. Check that it doesn't complete.
   136  	done := make(chan struct{})
   137  	go closeFunc(&g, done)
   138  
   139  	for i := 0; i < 10; i++ {
   140  		select {
   141  		case <-done:
   142  			t.Fatalf("Close function completed too soon")
   143  		case <-time.After(100 * time.Millisecond):
   144  		}
   145  
   146  		g.Leave()
   147  	}
   148  
   149  	// Now the closer must complete.
   150  	<-done
   151  
   152  	// Close again should not block.
   153  	done = make(chan struct{})
   154  	go closeFunc(&g, done)
   155  
   156  	select {
   157  	case <-done:
   158  	case <-time.After(2 * time.Second):
   159  		t.Fatalf("Second Close is blocking")
   160  	}
   161  }
   162  
   163  func worker(g *gate.Gate, done *sync.WaitGroup) {
   164  	for {
   165  		if !g.Enter() {
   166  			break
   167  		}
   168  		g.Leave()
   169  	}
   170  	done.Done()
   171  }
   172  
   173  func TestConcurrentAll(t *testing.T) {
   174  	var g gate.Gate
   175  	var done sync.WaitGroup
   176  
   177  	// Launch 1000 goroutines to concurrently enter/leave.
   178  	done.Add(1000)
   179  	for i := 0; i < 1000; i++ {
   180  		go worker(&g, &done)
   181  	}
   182  
   183  	// Wait for the goroutines to do some work, then close the gate.
   184  	time.Sleep(2 * time.Second)
   185  	g.Close()
   186  
   187  	// Wait for all of them to complete.
   188  	done.Wait()
   189  }