github.com/searKing/golang/go@v1.2.117/sync/filelock/filelock_test.go (about)

     1  // Copyright 2018 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  //go:build !js && !plan9 && !wasip1
     6  
     7  package filelock_test
     8  
     9  import (
    10  	"fmt"
    11  	"os"
    12  	"path/filepath"
    13  	"runtime"
    14  	"testing"
    15  	"time"
    16  
    17  	"github.com/searKing/golang/go/sync/filelock"
    18  )
    19  
    20  func lock(t *testing.T, f *os.File) {
    21  	t.Helper()
    22  	err := filelock.Lock(f)
    23  	t.Logf("Lock(fd %d) = %v", f.Fd(), err)
    24  	if err != nil {
    25  		t.Fail()
    26  	}
    27  }
    28  
    29  func rLock(t *testing.T, f *os.File) {
    30  	t.Helper()
    31  	err := filelock.RLock(f)
    32  	t.Logf("RLock(fd %d) = %v", f.Fd(), err)
    33  	if err != nil {
    34  		t.Fail()
    35  	}
    36  }
    37  
    38  func unlock(t *testing.T, f *os.File) {
    39  	t.Helper()
    40  	err := filelock.Unlock(f)
    41  	t.Logf("Unlock(fd %d) = %v", f.Fd(), err)
    42  	if err != nil {
    43  		t.Fail()
    44  	}
    45  }
    46  
    47  func mustTempFile(t *testing.T) (f *os.File, remove func()) {
    48  	t.Helper()
    49  
    50  	base := filepath.Base(t.Name())
    51  	f, err := os.CreateTemp("", base)
    52  	if err != nil {
    53  		t.Fatalf(`os.CreateTemp("", %q) = %v`, base, err)
    54  	}
    55  	t.Logf("fd %d = %s", f.Fd(), f.Name())
    56  
    57  	return f, func() {
    58  		f.Close()
    59  		os.Remove(f.Name())
    60  	}
    61  }
    62  
    63  func mustOpen(t *testing.T, name string) *os.File {
    64  	t.Helper()
    65  
    66  	f, err := os.OpenFile(name, os.O_RDWR, 0)
    67  	if err != nil {
    68  		t.Fatalf("os.Open(%q) = %v", name, err)
    69  	}
    70  
    71  	t.Logf("fd %d = os.Open(%q)", f.Fd(), name)
    72  	return f
    73  }
    74  
    75  const (
    76  	quiescent            = 10 * time.Millisecond
    77  	probablyStillBlocked = 10 * time.Second
    78  )
    79  
    80  func mustBlock(t *testing.T, op string, f *os.File) (wait func(*testing.T)) {
    81  	t.Helper()
    82  
    83  	desc := fmt.Sprintf("%s(fd %d)", op, f.Fd())
    84  
    85  	done := make(chan struct{})
    86  	go func() {
    87  		t.Helper()
    88  		switch op {
    89  		case "Lock":
    90  			lock(t, f)
    91  		case "RLock":
    92  			rLock(t, f)
    93  		default:
    94  			panic("invalid op: " + op)
    95  		}
    96  		close(done)
    97  	}()
    98  
    99  	select {
   100  	case <-done:
   101  		t.Fatalf("%s unexpectedly did not block", desc)
   102  		return nil
   103  
   104  	case <-time.After(quiescent):
   105  		t.Logf("%s is blocked (as expected)", desc)
   106  		return func(t *testing.T) {
   107  			t.Helper()
   108  			select {
   109  			case <-time.After(probablyStillBlocked):
   110  				t.Fatalf("%s is unexpectedly still blocked", desc)
   111  			case <-done:
   112  			}
   113  		}
   114  	}
   115  }
   116  
   117  func TestLockExcludesLock(t *testing.T) {
   118  	t.Parallel()
   119  
   120  	f, remove := mustTempFile(t)
   121  	defer remove()
   122  
   123  	other := mustOpen(t, f.Name())
   124  	defer other.Close()
   125  
   126  	lock(t, f)
   127  	lockOther := mustBlock(t, "Lock", other)
   128  	unlock(t, f)
   129  	lockOther(t)
   130  	unlock(t, other)
   131  }
   132  
   133  func TestLockExcludesRLock(t *testing.T) {
   134  	t.Parallel()
   135  
   136  	f, remove := mustTempFile(t)
   137  	defer remove()
   138  
   139  	other := mustOpen(t, f.Name())
   140  	defer other.Close()
   141  
   142  	lock(t, f)
   143  	rLockOther := mustBlock(t, "RLock", other)
   144  	unlock(t, f)
   145  	rLockOther(t)
   146  	unlock(t, other)
   147  }
   148  
   149  func TestRLockExcludesOnlyLock(t *testing.T) {
   150  	t.Parallel()
   151  
   152  	f, remove := mustTempFile(t)
   153  	defer remove()
   154  	rLock(t, f)
   155  
   156  	f2 := mustOpen(t, f.Name())
   157  	defer f2.Close()
   158  
   159  	doUnlockTF := false
   160  	switch runtime.GOOS {
   161  	case "aix", "solaris":
   162  		// When using POSIX locks (as on Solaris), we can't safely read-lock the
   163  		// same inode through two different descriptors at the same time: when the
   164  		// first descriptor is closed, the second descriptor would still be open but
   165  		// silently unlocked. So a second RLock must block instead of proceeding.
   166  		lockF2 := mustBlock(t, "RLock", f2)
   167  		unlock(t, f)
   168  		lockF2(t)
   169  	default:
   170  		rLock(t, f2)
   171  		doUnlockTF = true
   172  	}
   173  
   174  	other := mustOpen(t, f.Name())
   175  	defer other.Close()
   176  	lockOther := mustBlock(t, "Lock", other)
   177  
   178  	unlock(t, f2)
   179  	if doUnlockTF {
   180  		unlock(t, f)
   181  	}
   182  	lockOther(t)
   183  	unlock(t, other)
   184  }