180 lines
3.2 KiB
Go
180 lines
3.2 KiB
Go
// Copyright 2016 The Netstack Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package gate_test
|
|
|
|
import (
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"gvisor.googlesource.com/gvisor/pkg/gate"
|
|
)
|
|
|
|
func TestBasicEnter(t *testing.T) {
|
|
var g gate.Gate
|
|
|
|
if !g.Enter() {
|
|
t.Fatalf("Failed to enter when it should be allowed")
|
|
}
|
|
|
|
g.Leave()
|
|
|
|
g.Close()
|
|
|
|
if g.Enter() {
|
|
t.Fatalf("Allowed to enter when it should fail")
|
|
}
|
|
}
|
|
|
|
func enterFunc(t *testing.T, g *gate.Gate, enter, leave, reenter chan struct{}, done1, done2, done3 *sync.WaitGroup) {
|
|
// Wait until instructed to enter.
|
|
<-enter
|
|
if !g.Enter() {
|
|
t.Errorf("Failed to enter when it should be allowed")
|
|
}
|
|
|
|
done1.Done()
|
|
|
|
// Wait until instructed to leave.
|
|
<-leave
|
|
g.Leave()
|
|
|
|
done2.Done()
|
|
|
|
// Wait until instructed to reenter.
|
|
<-reenter
|
|
if g.Enter() {
|
|
t.Errorf("Allowed to enter when it should fail")
|
|
}
|
|
done3.Done()
|
|
}
|
|
|
|
func TestConcurrentEnter(t *testing.T) {
|
|
var g gate.Gate
|
|
var done1, done2, done3 sync.WaitGroup
|
|
|
|
// Create 1000 worker goroutines.
|
|
enter := make(chan struct{})
|
|
leave := make(chan struct{})
|
|
reenter := make(chan struct{})
|
|
done1.Add(1000)
|
|
done2.Add(1000)
|
|
done3.Add(1000)
|
|
for i := 0; i < 1000; i++ {
|
|
go enterFunc(t, &g, enter, leave, reenter, &done1, &done2, &done3)
|
|
}
|
|
|
|
// Tell them all to enter, then leave.
|
|
close(enter)
|
|
done1.Wait()
|
|
|
|
close(leave)
|
|
done2.Wait()
|
|
|
|
// Close the gate, then have the workers try to enter again.
|
|
g.Close()
|
|
close(reenter)
|
|
done3.Wait()
|
|
}
|
|
|
|
func closeFunc(g *gate.Gate, done chan struct{}) {
|
|
g.Close()
|
|
close(done)
|
|
}
|
|
|
|
func TestCloseWaits(t *testing.T) {
|
|
var g gate.Gate
|
|
|
|
// Enter 10 times.
|
|
for i := 0; i < 10; i++ {
|
|
if !g.Enter() {
|
|
t.Fatalf("Failed to enter when it should be allowed")
|
|
}
|
|
}
|
|
|
|
// Launch closer. Check that it doesn't complete.
|
|
done := make(chan struct{})
|
|
go closeFunc(&g, done)
|
|
|
|
for i := 0; i < 10; i++ {
|
|
select {
|
|
case <-done:
|
|
t.Fatalf("Close function completed too soon")
|
|
case <-time.After(100 * time.Millisecond):
|
|
}
|
|
|
|
g.Leave()
|
|
}
|
|
|
|
// Now the closer must complete.
|
|
<-done
|
|
}
|
|
|
|
func TestMultipleSerialCloses(t *testing.T) {
|
|
var g gate.Gate
|
|
|
|
// Enter 10 times.
|
|
for i := 0; i < 10; i++ {
|
|
if !g.Enter() {
|
|
t.Fatalf("Failed to enter when it should be allowed")
|
|
}
|
|
}
|
|
|
|
// Launch closer. Check that it doesn't complete.
|
|
done := make(chan struct{})
|
|
go closeFunc(&g, done)
|
|
|
|
for i := 0; i < 10; i++ {
|
|
select {
|
|
case <-done:
|
|
t.Fatalf("Close function completed too soon")
|
|
case <-time.After(100 * time.Millisecond):
|
|
}
|
|
|
|
g.Leave()
|
|
}
|
|
|
|
// Now the closer must complete.
|
|
<-done
|
|
|
|
// Close again should not block.
|
|
done = make(chan struct{})
|
|
go closeFunc(&g, done)
|
|
|
|
select {
|
|
case <-done:
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatalf("Second Close is blocking")
|
|
}
|
|
}
|
|
|
|
func worker(g *gate.Gate, done *sync.WaitGroup) {
|
|
for {
|
|
if !g.Enter() {
|
|
break
|
|
}
|
|
g.Leave()
|
|
}
|
|
done.Done()
|
|
}
|
|
|
|
func TestConcurrentAll(t *testing.T) {
|
|
var g gate.Gate
|
|
var done sync.WaitGroup
|
|
|
|
// Launch 1000 goroutines to concurrently enter/leave.
|
|
done.Add(1000)
|
|
for i := 0; i < 1000; i++ {
|
|
go worker(&g, &done)
|
|
}
|
|
|
|
// Wait for the goroutines to do some work, then close the gate.
|
|
time.Sleep(2 * time.Second)
|
|
g.Close()
|
|
|
|
// Wait for all of them to complete.
|
|
done.Wait()
|
|
}
|