Skip to content

Commit d430f77

Browse files
author
Marius
authored
Merge pull request #3 from tus/upstream-changes
Merge changes from upstream
2 parents 24100e2 + 121dc15 commit d430f77

File tree

3 files changed

+73
-60
lines changed

3 files changed

+73
-60
lines changed

lockfile.go

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ func (t TemporaryError) Error() string { return string(t) }
2323

2424
// Temporary returns always true.
2525
// It exists, so you can detect it via
26+
//
2627
// if te, ok := err.(interface{ Temporary() bool }); ok {
27-
// fmt.Println("I am a temporay error situation, so wait and retry")
28+
// fmt.Println("I am a temporary error situation, so wait and retry")
2829
// }
2930
func (t TemporaryError) Temporary() bool { return true }
3031

@@ -43,6 +44,7 @@ func New(path string) (Lockfile, error) {
4344
if !filepath.IsAbs(path) {
4445
return Lockfile(""), ErrNeedAbsPath
4546
}
47+
4648
return Lockfile(path), nil
4749
}
4850

@@ -61,6 +63,7 @@ func (l Lockfile) GetOwner() (*os.Process, error) {
6163
if err != nil {
6264
return nil, err
6365
}
66+
6467
running, err := isRunning(pid)
6568
if err != nil {
6669
return nil, err
@@ -71,10 +74,11 @@ func (l Lockfile) GetOwner() (*os.Process, error) {
7174
if err != nil {
7275
return nil, err
7376
}
77+
7478
return proc, nil
7579
}
76-
return nil, ErrDeadOwner
7780

81+
return nil, ErrDeadOwner
7882
}
7983

8084
// TryLock tries to own the lock.
@@ -91,34 +95,38 @@ func (l Lockfile) TryLock() error {
9195
panic(ErrNeedAbsPath)
9296
}
9397

94-
tmplock, err := ioutil.TempFile(filepath.Dir(name), "")
98+
tmplock, cleanup, err := makePidFile(name, os.Getpid())
9599
if err != nil {
96100
return err
97101
}
98102

99-
cleanup := func() {
100-
_ = tmplock.Close()
101-
_ = os.Remove(tmplock.Name())
102-
}
103103
defer cleanup()
104104

105-
if err := writePidLine(tmplock, os.Getpid()); err != nil {
106-
return err
105+
// EEXIST and similar error codes, caught by os.IsExist, are intentionally ignored,
106+
// as it means that someone was faster creating this link
107+
// and ignoring this kind of error is part of the algorithm.
108+
// Then we will probably fail the pid owner check later, if this process is still alive.
109+
// We cannot ignore ALL errors, since failure to support hard links, disk full
110+
// as well as many other errors can happen to a filesystem operation
111+
// and we really want to abort on those.
112+
if err := os.Link(tmplock, name); err != nil {
113+
if !os.IsExist(err) {
114+
return err
115+
}
107116
}
108117

109-
// return value intentionally ignored, as ignoring it is part of the algorithm
110-
_ = os.Link(tmplock.Name(), name)
111-
112-
fiTmp, err := os.Lstat(tmplock.Name())
118+
fiTmp, err := os.Lstat(tmplock)
113119
if err != nil {
114120
return err
115121
}
122+
116123
fiLock, err := os.Lstat(name)
117124
if err != nil {
118125
// tell user that a retry would be a good idea
119126
if os.IsNotExist(err) {
120127
return ErrNotExist
121128
}
129+
122130
return err
123131
}
124132

@@ -155,7 +163,7 @@ func (l Lockfile) TryLock() error {
155163
return l.TryLock()
156164
}
157165

158-
// Unlock a lock again, if we owned it. Returns any error that happend during release of lock.
166+
// Unlock a lock again, if we owned it. Returns any error that happened during release of lock.
159167
func (l Lockfile) Unlock() error {
160168
proc, err := l.GetOwner()
161169
switch err {
@@ -179,11 +187,6 @@ func (l Lockfile) Unlock() error {
179187
}
180188
}
181189

182-
func writePidLine(w io.Writer, pid int) error {
183-
_, err := io.WriteString(w, fmt.Sprintf("%d\n", pid))
184-
return err
185-
}
186-
187190
func scanPidLine(content []byte) (int, error) {
188191
if len(content) == 0 {
189192
return 0, ErrInvalidPid
@@ -197,5 +200,25 @@ func scanPidLine(content []byte) (int, error) {
197200
if pid <= 0 {
198201
return 0, ErrInvalidPid
199202
}
203+
200204
return pid, nil
201205
}
206+
207+
func makePidFile(name string, pid int) (tmpname string, cleanup func(), err error) {
208+
tmplock, err := ioutil.TempFile(filepath.Dir(name), filepath.Base(name)+".")
209+
if err != nil {
210+
return "", nil, err
211+
}
212+
213+
cleanup = func() {
214+
_ = tmplock.Close()
215+
_ = os.Remove(tmplock.Name())
216+
}
217+
218+
if _, err := io.WriteString(tmplock, fmt.Sprintf("%d\n", pid)); err != nil {
219+
cleanup() // Do cleanup here, so call doesn't have to.
220+
return "", nil, err
221+
}
222+
223+
return tmplock.Name(), cleanup, nil
224+
}

lockfile_test.go

Lines changed: 29 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,19 @@ func ExampleLockfile() {
1616
fmt.Printf("Cannot init lock. reason: %v", err)
1717
panic(err) // handle properly please!
1818
}
19-
err = lock.TryLock()
2019

2120
// Error handling is essential, as we only try to get the lock.
22-
if err != nil {
21+
if err = lock.TryLock(); err != nil {
2322
fmt.Printf("Cannot lock %q, reason: %v", lock, err)
2423
panic(err) // handle properly please!
2524
}
2625

27-
defer lock.Unlock()
26+
defer func() {
27+
if err := lock.Unlock(); err != nil {
28+
fmt.Printf("Cannot unlock %q, reason: %v", lock, err)
29+
panic(err) // handle properly please!
30+
}
31+
}()
2832

2933
fmt.Println("Do stuff under lock")
3034
// Output: Do stuff under lock
@@ -61,7 +65,6 @@ func TestBasicLockUnlock(t *testing.T) {
6165
func GetDeadPID() int {
6266
// I have no idea how windows handles large PIDs, or if they even exist.
6367
// So limit it to be less or equal to 4096 to be safe.
64-
6568
const maxPid = 4095
6669

6770
// limited iteration, so we finish one day
@@ -71,7 +74,9 @@ func GetDeadPID() int {
7174
if seen[pid] {
7275
continue
7376
}
77+
7478
seen[pid] = true
79+
7580
running, err := isRunning(pid)
7681
if err != nil {
7782
fmt.Println("Error checking PID: ", err)
@@ -164,6 +169,7 @@ func TestRogueDeletionDeadPid(t *testing.T) {
164169
t.Fatal(err)
165170
return
166171
}
172+
167173
defer os.Remove(path)
168174

169175
err = lf.Unlock()
@@ -172,12 +178,12 @@ func TestRogueDeletionDeadPid(t *testing.T) {
172178
return
173179
}
174180

175-
if _, err := os.Stat(path); os.IsNotExist(err) {
176-
t.Fatal("lockfile should not be deleted by us, if we didn't create it")
177-
} else {
178-
if err != nil {
179-
t.Fatalf("unexpected error %v", err)
181+
if _, err := os.Stat(path); err != nil {
182+
if os.IsNotExist(err) {
183+
content, _ := ioutil.ReadFile(path)
184+
t.Fatalf("lockfile %q (%q) should not be deleted by us, if we didn't create it", path, content)
180185
}
186+
t.Fatalf("unexpected error %v", err)
181187
}
182188
}
183189

@@ -215,10 +221,12 @@ func TestInvalidPidLeadToReplacedLockfileAndSuccess(t *testing.T) {
215221
t.Fatal(err)
216222
return
217223
}
224+
218225
if err := ioutil.WriteFile(path, []byte("\n"), 0666); err != nil {
219226
t.Fatal(err)
220227
return
221228
}
229+
222230
defer os.Remove(path)
223231

224232
lf, err := New(path)
@@ -250,46 +258,27 @@ func TestScanPidLine(t *testing.T) {
250258
pid int
251259
xfail error
252260
}{
253-
{
254-
xfail: ErrInvalidPid,
255-
},
256-
{
257-
input: []byte(""),
258-
xfail: ErrInvalidPid,
259-
},
260-
{
261-
input: []byte("\n"),
262-
xfail: ErrInvalidPid,
263-
},
264-
{
265-
input: []byte("-1\n"),
266-
xfail: ErrInvalidPid,
267-
},
268-
{
269-
input: []byte("0\n"),
270-
xfail: ErrInvalidPid,
271-
},
272-
{
273-
input: []byte("a\n"),
274-
xfail: ErrInvalidPid,
275-
},
276-
{
277-
input: []byte("1\n"),
278-
pid: 1,
279-
},
261+
{xfail: ErrInvalidPid},
262+
{input: []byte(""), xfail: ErrInvalidPid},
263+
{input: []byte("\n"), xfail: ErrInvalidPid},
264+
{input: []byte("-1\n"), xfail: ErrInvalidPid},
265+
{input: []byte("0\n"), xfail: ErrInvalidPid},
266+
{input: []byte("a\n"), xfail: ErrInvalidPid},
267+
{input: []byte("1\n"), pid: 1},
280268
}
281269

282270
// test positive cases first
283271
for step, tc := range tests {
284272
if tc.xfail != nil {
285273
continue
286274
}
287-
want := tc.pid
275+
288276
got, err := scanPidLine(tc.input)
289277
if err != nil {
290278
t.Fatalf("%d: unexpected error %v", step, err)
291279
}
292-
if got != want {
280+
281+
if want := tc.pid; got != want {
293282
t.Errorf("%d: expected pid %d, got %d", step, want, got)
294283
}
295284
}
@@ -299,9 +288,9 @@ func TestScanPidLine(t *testing.T) {
299288
if tc.xfail == nil {
300289
continue
301290
}
302-
want := tc.xfail
291+
303292
_, got := scanPidLine(tc.input)
304-
if got != want {
293+
if want := tc.xfail; got != want {
305294
t.Errorf("%d: expected error %v, got %v", step, want, got)
306295
}
307296
}

lockfile_unix.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
1+
// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris aix
22

33
package lockfile
44

@@ -16,5 +16,6 @@ func isRunning(pid int) (bool, error) {
1616
if err := proc.Signal(syscall.Signal(0)); err != nil {
1717
return false, nil
1818
}
19+
1920
return true, nil
2021
}

0 commit comments

Comments
 (0)