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.
20 "cmd/go/internal/lockedfile/internal/filelock"
23 func lock(t *testing.T, f *os.File) {
25 err := filelock.Lock(f)
26 t.Logf("Lock(fd %d) = %v", f.Fd(), err)
32 func rLock(t *testing.T, f *os.File) {
34 err := filelock.RLock(f)
35 t.Logf("RLock(fd %d) = %v", f.Fd(), err)
41 func unlock(t *testing.T, f *os.File) {
43 err := filelock.Unlock(f)
44 t.Logf("Unlock(fd %d) = %v", f.Fd(), err)
50 func mustTempFile(t *testing.T) (f *os.File, remove func()) {
53 base := filepath.Base(t.Name())
54 f, err := ioutil.TempFile("", base)
56 t.Fatalf(`ioutil.TempFile("", %q) = %v`, base, err)
58 t.Logf("fd %d = %s", f.Fd(), f.Name())
66 func mustOpen(t *testing.T, name string) *os.File {
69 f, err := os.OpenFile(name, os.O_RDWR, 0)
71 t.Fatalf("os.Open(%q) = %v", name, err)
74 t.Logf("fd %d = os.Open(%q)", f.Fd(), name)
79 quiescent = 10 * time.Millisecond
80 probablyStillBlocked = 10 * time.Second
83 func mustBlock(t *testing.T, op string, f *os.File) (wait func(*testing.T)) {
86 desc := fmt.Sprintf("%s(fd %d)", op, f.Fd())
88 done := make(chan struct{})
97 panic("invalid op: " + op)
104 t.Fatalf("%s unexpectedly did not block", desc)
107 case <-time.After(quiescent):
108 t.Logf("%s is blocked (as expected)", desc)
109 return func(t *testing.T) {
112 case <-time.After(probablyStillBlocked):
113 t.Fatalf("%s is unexpectedly still blocked", desc)
120 func TestLockExcludesLock(t *testing.T) {
123 f, remove := mustTempFile(t)
126 other := mustOpen(t, f.Name())
130 lockOther := mustBlock(t, "Lock", other)
136 func TestLockExcludesRLock(t *testing.T) {
139 f, remove := mustTempFile(t)
142 other := mustOpen(t, f.Name())
146 rLockOther := mustBlock(t, "RLock", other)
152 func TestRLockExcludesOnlyLock(t *testing.T) {
155 f, remove := mustTempFile(t)
159 f2 := mustOpen(t, f.Name())
163 switch runtime.GOOS {
164 case "aix", "illumos", "solaris":
165 // When using POSIX locks (as on Solaris), we can't safely read-lock the
166 // same inode through two different descriptors at the same time: when the
167 // first descriptor is closed, the second descriptor would still be open but
168 // silently unlocked. So a second RLock must block instead of proceeding.
169 lockF2 := mustBlock(t, "RLock", f2)
177 other := mustOpen(t, f.Name())
179 lockOther := mustBlock(t, "Lock", other)
189 func TestLockNotDroppedByExecCommand(t *testing.T) {
190 testenv.MustHaveExec(t)
192 f, remove := mustTempFile(t)
197 other := mustOpen(t, f.Name())
200 // Some kinds of file locks are dropped when a duplicated or forked file
201 // descriptor is unlocked. Double-check that the approach used by os/exec does
202 // not accidentally drop locks.
203 cmd := exec.Command(os.Args[0], "-test.run=^$")
204 if err := cmd.Run(); err != nil {
205 t.Fatalf("exec failed: %v", err)
208 lockOther := mustBlock(t, "Lock", other)