Skip to content

Commit 941fc61

Browse files
committed
Merge pull request nightlyone#12 from stewi1014/master
Added relevant and go-compatable tests to testing file. Fixed dead PID on windows.
2 parents fbe4e7f + eb87890 commit 941fc61

File tree

4 files changed

+148
-41
lines changed

4 files changed

+148
-41
lines changed

lockfile.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,21 @@ func (l Lockfile) GetOwner() (*os.Process, error) {
4444

4545
// try hard for pids. If no pid, the lockfile is junk anyway and we delete it.
4646
if pid > 0 {
47-
p, err := os.FindProcess(pid)
47+
running, err := isRunning(pid)
4848
if err != nil {
4949
return nil, err
5050
}
51-
return p, isProcessAlive(p)
51+
52+
if running {
53+
proc, err := os.FindProcess(pid)
54+
if err != nil {
55+
return nil, err
56+
}
57+
return proc, nil
58+
} else {
59+
return nil, ErrDeadOwner
60+
}
61+
5262
} else {
5363
return nil, ErrInvalidPid
5464
}

lockfile_test.go

Lines changed: 108 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
1-
package lockfile_test
1+
package lockfile
22

33
import (
4-
lockfile "."
54
"fmt"
5+
"io/ioutil"
6+
"math/rand"
67
"os"
78
"path/filepath"
9+
"strconv"
10+
"testing"
811
)
912

1013
func ExampleLockfile() {
11-
lock, err := lockfile.New(filepath.Join(os.TempDir(), "lock.me.now.lck"))
14+
lock, err := New(filepath.Join(os.TempDir(), "lock.me.now.lck"))
1215
if err != nil {
1316
fmt.Printf("Cannot init lock. reason: %v\n", err)
1417
panic(err)
@@ -26,3 +29,105 @@ func ExampleLockfile() {
2629
fmt.Println("Do stuff under lock")
2730
// Output: Do stuff under lock
2831
}
32+
33+
func TestLock(t *testing.T) {
34+
path, err := filepath.Abs("test_lockfile.pid")
35+
if err != nil {
36+
panic(err)
37+
}
38+
39+
lf, err := New(path)
40+
if err != nil {
41+
t.Fail()
42+
fmt.Println("Error making lockfile: ", err)
43+
return
44+
}
45+
46+
err = lf.TryLock()
47+
if err != nil {
48+
t.Fail()
49+
fmt.Println("Error locking lockfile: ", err)
50+
return
51+
}
52+
53+
err = lf.Unlock()
54+
if err != nil {
55+
t.Fail()
56+
fmt.Println("Error unlocking lockfile: ", err)
57+
return
58+
}
59+
}
60+
61+
func TestDeadPID(t *testing.T) {
62+
path, err := filepath.Abs("test_lockfile.pid")
63+
if err != nil {
64+
panic(err)
65+
}
66+
67+
pid := GetDeadPID()
68+
69+
ioutil.WriteFile(path, []byte(strconv.Itoa(pid)+"\n"), 0666)
70+
}
71+
72+
func GetDeadPID() int {
73+
for {
74+
pid := rand.Int() % 4096 //I have no idea how windows handles large PIDs, or if they even exist. Limit it to 4096 to be safe.
75+
running, err := isRunning(pid)
76+
if err != nil {
77+
fmt.Println("Error checking PID: ", err)
78+
continue
79+
}
80+
81+
if !running {
82+
return pid
83+
}
84+
}
85+
}
86+
87+
func TestBusy(t *testing.T) {
88+
path, err := filepath.Abs("test_lockfile.pid")
89+
if err != nil {
90+
t.Fail()
91+
fmt.Println(err)
92+
return
93+
}
94+
95+
lf1, err := New(path)
96+
if err != nil {
97+
t.Fail()
98+
fmt.Println(err)
99+
return
100+
}
101+
102+
err = lf1.TryLock()
103+
if err != nil {
104+
t.Fail()
105+
fmt.Println(err)
106+
return
107+
}
108+
109+
lf2, err := New(path)
110+
if err != nil {
111+
t.Fail()
112+
fmt.Println(err)
113+
return
114+
}
115+
116+
err = lf2.TryLock()
117+
if err == nil {
118+
t.Fail()
119+
fmt.Println("No error locking already locked lockfile!")
120+
return
121+
} else if err != ErrBusy {
122+
t.Fail()
123+
fmt.Println(err)
124+
return
125+
}
126+
127+
err = lf1.Unlock()
128+
if err != nil {
129+
t.Fail()
130+
fmt.Println(err)
131+
return
132+
}
133+
}

lockfile_unix.go

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,16 @@ import (
77
"syscall"
88
)
99

10-
func isProcessAlive(p *os.Process) error {
11-
err := p.Signal(os.Signal(syscall.Signal(0)))
12-
if err == nil {
13-
return nil
14-
}
15-
errno, ok := err.(syscall.Errno)
16-
if !ok {
17-
return ErrDeadOwner
18-
}
19-
20-
switch errno {
21-
case syscall.ESRCH:
22-
return ErrDeadOwner
23-
case syscall.EPERM:
24-
return nil
25-
default:
26-
return err
10+
func isRunning(pid int) (bool, error) {
11+
proc, err := os.FindProcess(pid)
12+
if err != nil {
13+
return false, err
14+
} else {
15+
err := proc.Signal(syscall.Signal(0))
16+
if err == nil {
17+
return true, nil
18+
} else {
19+
return false, nil
20+
}
2721
}
2822
}

lockfile_windows.go

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,30 @@
11
package lockfile
22

33
import (
4-
"os"
5-
"reflect"
64
"syscall"
75
)
86

9-
func isProcessAlive(p *os.Process) error {
10-
// Extract handle value from the os.Process struct to avoid the need
11-
// of a second, manually opened process handle.
12-
value := reflect.ValueOf(p)
13-
// Dereference *os.Process to os.Process
14-
value = value.Elem()
15-
field := value.FieldByName("handle")
16-
17-
handle := syscall.Handle(field.Uint())
7+
//For some reason these consts don't exist in syscall.
8+
const (
9+
error_invalid_parameter = 87
10+
code_still_active = 259
11+
)
1812

19-
var code uint32
20-
err := syscall.GetExitCodeProcess(handle, &code)
13+
func isRunning(pid int) (bool, error) {
14+
procHnd, err := syscall.OpenProcess(syscall.PROCESS_QUERY_INFORMATION, true, uint32(pid))
2115
if err != nil {
22-
return err
16+
if scerr, ok := err.(syscall.Errno); ok {
17+
if uintptr(scerr) == error_invalid_parameter {
18+
return false, nil
19+
}
20+
}
2321
}
2422

25-
// code will contain the exit code of the process or 259 (STILL_ALIVE)
26-
// if the process has not exited yet.
27-
if code == 259 {
28-
return nil
23+
var code uint32
24+
err = syscall.GetExitCodeProcess(procHnd, &code)
25+
if err != nil {
26+
return false, err
2927
}
3028

31-
return ErrDeadOwner
29+
return code == code_still_active, nil
3230
}

0 commit comments

Comments
 (0)