Skip to content

Commit b70db24

Browse files
committed
add file lock test
1 parent 49ff130 commit b70db24

File tree

1 file changed

+173
-0
lines changed

1 file changed

+173
-0
lines changed
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"os/signal"
7+
"path/filepath"
8+
"syscall"
9+
"time"
10+
11+
"github.com/gofrs/flock"
12+
)
13+
14+
// This is a simple program that demonstrates lock behavior.
15+
// Run it manually to see the behavior:
16+
//
17+
// Terminal 1: go run test_lock_release_simple.go acquire
18+
// Terminal 2: go run test_lock_release_simple.go check
19+
// Terminal 1: (kill with Ctrl+C or kill -9)
20+
// Terminal 2: go run test_lock_release_simple.go check (now succeeds!)
21+
// Terminal 2: go run test_lock_release_simple.go cleanup
22+
23+
func main() {
24+
if len(os.Args) < 2 {
25+
printUsage()
26+
os.Exit(1)
27+
}
28+
29+
lockFile := filepath.Join(os.TempDir(), "flock-test.lock")
30+
fmt.Println(lockFile)
31+
32+
switch os.Args[1] {
33+
case "acquire":
34+
acquireLock(lockFile)
35+
case "check":
36+
checkLock(lockFile)
37+
case "status":
38+
showStatus(lockFile)
39+
case "cleanup":
40+
cleanup(lockFile)
41+
default:
42+
printUsage()
43+
os.Exit(1)
44+
}
45+
}
46+
47+
func printUsage() {
48+
fmt.Println("Usage:")
49+
fmt.Println(" go run test_lock_release_simple.go acquire - Acquire lock and hold it")
50+
fmt.Println(" go run test_lock_release_simple.go check - Try to acquire lock")
51+
fmt.Println(" go run test_lock_release_simple.go status - Check if lockfile exists")
52+
fmt.Println(" go run test_lock_release_simple.go cleanup - Remove lockfile")
53+
fmt.Println()
54+
fmt.Println("Test procedure:")
55+
fmt.Println(" 1. Terminal 1: run 'acquire' - holds lock indefinitely")
56+
fmt.Println(" 2. Terminal 2: run 'check' - should fail (lock held)")
57+
fmt.Println(" 3. Terminal 1: kill with Ctrl+C or kill -9")
58+
fmt.Println(" 4. Terminal 2: run 'check' again - should succeed!")
59+
fmt.Println(" 5. Terminal 2: run 'status' - lockfile still exists")
60+
fmt.Println(" 6. Terminal 2: run 'cleanup' - remove lockfile")
61+
}
62+
63+
func acquireLock(lockFile string) {
64+
fileLock := flock.New(lockFile)
65+
66+
fmt.Printf("[PID %d] Attempting to acquire lock on: %s\n", os.Getpid(), lockFile)
67+
68+
locked, err := fileLock.TryLock()
69+
if err != nil {
70+
fmt.Printf("❌ Error acquiring lock: %v\n", err)
71+
os.Exit(1)
72+
}
73+
74+
if !locked {
75+
fmt.Printf("❌ Could not acquire lock (already held by another process)\n")
76+
os.Exit(1)
77+
}
78+
79+
fmt.Printf("✅ Lock acquired successfully!\n")
80+
fmt.Printf(" Lockfile: %s\n", lockFile)
81+
fmt.Printf(" PID: %d\n", os.Getpid())
82+
fmt.Println()
83+
fmt.Println("Holding lock indefinitely. Press Ctrl+C to exit gracefully,")
84+
fmt.Println("or use 'kill -9' from another terminal to simulate a crash.")
85+
fmt.Println()
86+
fmt.Println("Commands to try in another terminal:")
87+
fmt.Printf(" go run test_lock_release_simple.go check # Try to acquire (should fail)\n")
88+
fmt.Printf(" kill -9 %d # Kill this process\n", os.Getpid())
89+
fmt.Printf(" go run test_lock_release_simple.go check # Try again (should succeed!)\n")
90+
fmt.Println()
91+
92+
// Set up graceful shutdown
93+
sigChan := make(chan os.Signal, 1)
94+
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
95+
96+
go func() {
97+
<-sigChan
98+
fmt.Println("\n\nReceived interrupt signal, releasing lock...")
99+
fileLock.Unlock()
100+
fmt.Println("Lock released. Exiting.")
101+
os.Exit(0)
102+
}()
103+
104+
// Hold lock forever
105+
for {
106+
fmt.Printf(".")
107+
time.Sleep(2 * time.Second)
108+
}
109+
}
110+
111+
func checkLock(lockFile string) {
112+
fileLock := flock.New(lockFile)
113+
114+
fmt.Printf("[PID %d] Attempting to acquire lock on: %s\n", os.Getpid(), lockFile)
115+
116+
locked, err := fileLock.TryLock()
117+
if err != nil {
118+
fmt.Printf("❌ Error: %v\n", err)
119+
os.Exit(1)
120+
}
121+
122+
if !locked {
123+
fmt.Printf("❌ Lock is currently held by another process\n")
124+
fmt.Printf(" This means another process has the lock\n")
125+
os.Exit(1)
126+
}
127+
128+
fmt.Printf("✅ Successfully acquired lock!\n")
129+
fmt.Printf(" This means no other process holds the lock\n")
130+
fmt.Printf(" (The lock was either never acquired, or the holding process died)\n")
131+
132+
// Release it immediately
133+
fileLock.Unlock()
134+
fmt.Println("\nLock released.")
135+
}
136+
137+
func showStatus(lockFile string) {
138+
fmt.Printf("Lockfile path: %s\n", lockFile)
139+
140+
info, err := os.Stat(lockFile)
141+
if os.IsNotExist(err) {
142+
fmt.Println("Status: ❌ Lockfile does not exist")
143+
return
144+
}
145+
146+
if err != nil {
147+
fmt.Printf("Error checking file: %v\n", err)
148+
return
149+
}
150+
151+
fmt.Printf("Status: ✅ Lockfile exists\n")
152+
fmt.Printf(" Size: %d bytes\n", info.Size())
153+
fmt.Printf(" Modified: %s\n", info.ModTime().Format(time.RFC3339))
154+
fmt.Println("\nNote: The lockfile existing does NOT mean the lock is held.")
155+
fmt.Println(" Run 'check' to see if the lock can be acquired.")
156+
}
157+
158+
func cleanup(lockFile string) {
159+
fmt.Printf("Removing lockfile: %s\n", lockFile)
160+
161+
err := os.Remove(lockFile)
162+
if os.IsNotExist(err) {
163+
fmt.Println("✅ Lockfile doesn't exist (already clean)")
164+
return
165+
}
166+
167+
if err != nil {
168+
fmt.Printf("❌ Error removing file: %v\n", err)
169+
os.Exit(1)
170+
}
171+
172+
fmt.Println("✅ Lockfile removed successfully")
173+
}

0 commit comments

Comments
 (0)