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