github.com/sandwichdev/go-internals@v0.0.0-20210605002614-12311ac6b2c5/poll/fd_mutex_test.go (about)

     1  // Copyright 2013 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package poll_test
     6  
     7  import (
     8  	"math/rand"
     9  	"runtime"
    10  	"strings"
    11  	"testing"
    12  	"time"
    13  
    14  	. "github.com/SandwichDev/go-internals/poll"
    15  )
    16  
    17  func TestMutexLock(t *testing.T) {
    18  	var mu FDMutex
    19  
    20  	if !mu.Incref() {
    21  		t.Fatal("broken")
    22  	}
    23  	if mu.Decref() {
    24  		t.Fatal("broken")
    25  	}
    26  
    27  	if !mu.RWLock(true) {
    28  		t.Fatal("broken")
    29  	}
    30  	if mu.RWUnlock(true) {
    31  		t.Fatal("broken")
    32  	}
    33  
    34  	if !mu.RWLock(false) {
    35  		t.Fatal("broken")
    36  	}
    37  	if mu.RWUnlock(false) {
    38  		t.Fatal("broken")
    39  	}
    40  }
    41  
    42  func TestMutexClose(t *testing.T) {
    43  	var mu FDMutex
    44  	if !mu.IncrefAndClose() {
    45  		t.Fatal("broken")
    46  	}
    47  
    48  	if mu.Incref() {
    49  		t.Fatal("broken")
    50  	}
    51  	if mu.RWLock(true) {
    52  		t.Fatal("broken")
    53  	}
    54  	if mu.RWLock(false) {
    55  		t.Fatal("broken")
    56  	}
    57  	if mu.IncrefAndClose() {
    58  		t.Fatal("broken")
    59  	}
    60  }
    61  
    62  func TestMutexCloseUnblock(t *testing.T) {
    63  	c := make(chan bool, 4)
    64  	var mu FDMutex
    65  	mu.RWLock(true)
    66  	for i := 0; i < 4; i++ {
    67  		go func() {
    68  			if mu.RWLock(true) {
    69  				t.Error("broken")
    70  				return
    71  			}
    72  			c <- true
    73  		}()
    74  	}
    75  	// Concurrent goroutines must not be able to read lock the mutex.
    76  	time.Sleep(time.Millisecond)
    77  	select {
    78  	case <-c:
    79  		t.Fatal("broken")
    80  	default:
    81  	}
    82  	mu.IncrefAndClose() // Must unblock the readers.
    83  	for i := 0; i < 4; i++ {
    84  		select {
    85  		case <-c:
    86  		case <-time.After(10 * time.Second):
    87  			t.Fatal("broken")
    88  		}
    89  	}
    90  	if mu.Decref() {
    91  		t.Fatal("broken")
    92  	}
    93  	if !mu.RWUnlock(true) {
    94  		t.Fatal("broken")
    95  	}
    96  }
    97  
    98  func TestMutexPanic(t *testing.T) {
    99  	ensurePanics := func(f func()) {
   100  		defer func() {
   101  			if recover() == nil {
   102  				t.Fatal("does not panic")
   103  			}
   104  		}()
   105  		f()
   106  	}
   107  
   108  	var mu FDMutex
   109  	ensurePanics(func() { mu.Decref() })
   110  	ensurePanics(func() { mu.RWUnlock(true) })
   111  	ensurePanics(func() { mu.RWUnlock(false) })
   112  
   113  	ensurePanics(func() { mu.Incref(); mu.Decref(); mu.Decref() })
   114  	ensurePanics(func() { mu.RWLock(true); mu.RWUnlock(true); mu.RWUnlock(true) })
   115  	ensurePanics(func() { mu.RWLock(false); mu.RWUnlock(false); mu.RWUnlock(false) })
   116  
   117  	// ensure that it's still not broken
   118  	mu.Incref()
   119  	mu.Decref()
   120  	mu.RWLock(true)
   121  	mu.RWUnlock(true)
   122  	mu.RWLock(false)
   123  	mu.RWUnlock(false)
   124  }
   125  
   126  func TestMutexOverflowPanic(t *testing.T) {
   127  	defer func() {
   128  		r := recover()
   129  		if r == nil {
   130  			t.Fatal("did not panic")
   131  		}
   132  		msg, ok := r.(string)
   133  		if !ok {
   134  			t.Fatalf("unexpected panic type %T", r)
   135  		}
   136  		if !strings.Contains(msg, "too many") || strings.Contains(msg, "inconsistent") {
   137  			t.Fatalf("wrong panic message %q", msg)
   138  		}
   139  	}()
   140  
   141  	var mu1 FDMutex
   142  	for i := 0; i < 1<<21; i++ {
   143  		mu1.Incref()
   144  	}
   145  }
   146  
   147  func TestMutexStress(t *testing.T) {
   148  	P := 8
   149  	N := int(1e6)
   150  	if testing.Short() {
   151  		P = 4
   152  		N = 1e4
   153  	}
   154  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(P))
   155  	done := make(chan bool, P)
   156  	var mu FDMutex
   157  	var readState [2]uint64
   158  	var writeState [2]uint64
   159  	for p := 0; p < P; p++ {
   160  		go func() {
   161  			defer func() {
   162  				done <- !t.Failed()
   163  			}()
   164  			r := rand.New(rand.NewSource(rand.Int63()))
   165  			for i := 0; i < N; i++ {
   166  				switch r.Intn(3) {
   167  				case 0:
   168  					if !mu.Incref() {
   169  						t.Error("broken")
   170  						return
   171  					}
   172  					if mu.Decref() {
   173  						t.Error("broken")
   174  						return
   175  					}
   176  				case 1:
   177  					if !mu.RWLock(true) {
   178  						t.Error("broken")
   179  						return
   180  					}
   181  					// Ensure that it provides mutual exclusion for readers.
   182  					if readState[0] != readState[1] {
   183  						t.Error("broken")
   184  						return
   185  					}
   186  					readState[0]++
   187  					readState[1]++
   188  					if mu.RWUnlock(true) {
   189  						t.Error("broken")
   190  						return
   191  					}
   192  				case 2:
   193  					if !mu.RWLock(false) {
   194  						t.Error("broken")
   195  						return
   196  					}
   197  					// Ensure that it provides mutual exclusion for writers.
   198  					if writeState[0] != writeState[1] {
   199  						t.Error("broken")
   200  						return
   201  					}
   202  					writeState[0]++
   203  					writeState[1]++
   204  					if mu.RWUnlock(false) {
   205  						t.Error("broken")
   206  						return
   207  					}
   208  				}
   209  			}
   210  		}()
   211  	}
   212  	for p := 0; p < P; p++ {
   213  		if !<-done {
   214  			t.FailNow()
   215  		}
   216  	}
   217  	if !mu.IncrefAndClose() {
   218  		t.Fatal("broken")
   219  	}
   220  	if !mu.Decref() {
   221  		t.Fatal("broken")
   222  	}
   223  }