]> git.ipfire.org Git - thirdparty/gcc.git/blob - libgo/go/cmd/go/internal/lockedfile/internal/filelock/filelock_test.go
libgo: update to Go1.14beta1
[thirdparty/gcc.git] / libgo / go / cmd / go / internal / lockedfile / internal / filelock / filelock_test.go
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 // +build !js,!plan9
6
7 package filelock_test
8
9 import (
10 "fmt"
11 "internal/testenv"
12 "io/ioutil"
13 "os"
14 "os/exec"
15 "path/filepath"
16 "runtime"
17 "testing"
18 "time"
19
20 "cmd/go/internal/lockedfile/internal/filelock"
21 )
22
23 func lock(t *testing.T, f *os.File) {
24 t.Helper()
25 err := filelock.Lock(f)
26 t.Logf("Lock(fd %d) = %v", f.Fd(), err)
27 if err != nil {
28 t.Fail()
29 }
30 }
31
32 func rLock(t *testing.T, f *os.File) {
33 t.Helper()
34 err := filelock.RLock(f)
35 t.Logf("RLock(fd %d) = %v", f.Fd(), err)
36 if err != nil {
37 t.Fail()
38 }
39 }
40
41 func unlock(t *testing.T, f *os.File) {
42 t.Helper()
43 err := filelock.Unlock(f)
44 t.Logf("Unlock(fd %d) = %v", f.Fd(), err)
45 if err != nil {
46 t.Fail()
47 }
48 }
49
50 func mustTempFile(t *testing.T) (f *os.File, remove func()) {
51 t.Helper()
52
53 base := filepath.Base(t.Name())
54 f, err := ioutil.TempFile("", base)
55 if err != nil {
56 t.Fatalf(`ioutil.TempFile("", %q) = %v`, base, err)
57 }
58 t.Logf("fd %d = %s", f.Fd(), f.Name())
59
60 return f, func() {
61 f.Close()
62 os.Remove(f.Name())
63 }
64 }
65
66 func mustOpen(t *testing.T, name string) *os.File {
67 t.Helper()
68
69 f, err := os.OpenFile(name, os.O_RDWR, 0)
70 if err != nil {
71 t.Fatalf("os.Open(%q) = %v", name, err)
72 }
73
74 t.Logf("fd %d = os.Open(%q)", f.Fd(), name)
75 return f
76 }
77
78 const (
79 quiescent = 10 * time.Millisecond
80 probablyStillBlocked = 10 * time.Second
81 )
82
83 func mustBlock(t *testing.T, op string, f *os.File) (wait func(*testing.T)) {
84 t.Helper()
85
86 desc := fmt.Sprintf("%s(fd %d)", op, f.Fd())
87
88 done := make(chan struct{})
89 go func() {
90 t.Helper()
91 switch op {
92 case "Lock":
93 lock(t, f)
94 case "RLock":
95 rLock(t, f)
96 default:
97 panic("invalid op: " + op)
98 }
99 close(done)
100 }()
101
102 select {
103 case <-done:
104 t.Fatalf("%s unexpectedly did not block", desc)
105 return nil
106
107 case <-time.After(quiescent):
108 t.Logf("%s is blocked (as expected)", desc)
109 return func(t *testing.T) {
110 t.Helper()
111 select {
112 case <-time.After(probablyStillBlocked):
113 t.Fatalf("%s is unexpectedly still blocked", desc)
114 case <-done:
115 }
116 }
117 }
118 }
119
120 func TestLockExcludesLock(t *testing.T) {
121 t.Parallel()
122
123 f, remove := mustTempFile(t)
124 defer remove()
125
126 other := mustOpen(t, f.Name())
127 defer other.Close()
128
129 lock(t, f)
130 lockOther := mustBlock(t, "Lock", other)
131 unlock(t, f)
132 lockOther(t)
133 unlock(t, other)
134 }
135
136 func TestLockExcludesRLock(t *testing.T) {
137 t.Parallel()
138
139 f, remove := mustTempFile(t)
140 defer remove()
141
142 other := mustOpen(t, f.Name())
143 defer other.Close()
144
145 lock(t, f)
146 rLockOther := mustBlock(t, "RLock", other)
147 unlock(t, f)
148 rLockOther(t)
149 unlock(t, other)
150 }
151
152 func TestRLockExcludesOnlyLock(t *testing.T) {
153 t.Parallel()
154
155 f, remove := mustTempFile(t)
156 defer remove()
157 rLock(t, f)
158
159 f2 := mustOpen(t, f.Name())
160 defer f2.Close()
161
162 doUnlockTF := false
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)
170 unlock(t, f)
171 lockF2(t)
172 default:
173 rLock(t, f2)
174 doUnlockTF = true
175 }
176
177 other := mustOpen(t, f.Name())
178 defer other.Close()
179 lockOther := mustBlock(t, "Lock", other)
180
181 unlock(t, f2)
182 if doUnlockTF {
183 unlock(t, f)
184 }
185 lockOther(t)
186 unlock(t, other)
187 }
188
189 func TestLockNotDroppedByExecCommand(t *testing.T) {
190 testenv.MustHaveExec(t)
191
192 f, remove := mustTempFile(t)
193 defer remove()
194
195 lock(t, f)
196
197 other := mustOpen(t, f.Name())
198 defer other.Close()
199
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)
206 }
207
208 lockOther := mustBlock(t, "Lock", other)
209 unlock(t, f)
210 lockOther(t)
211 unlock(t, other)
212 }