@@ -32,44 +32,49 @@ func (fl *FileLock) Lock() error {
3232 defer ticker .Stop ()
3333
3434 for {
35- select {
36- case <- timeout :
37- return fmt .Errorf ("timeout waiting for file lock" )
38- case <- ticker .C :
39- // Try to create lock file exclusively
40- f , err := os .OpenFile (fl .lockPath , os .O_CREATE | os .O_EXCL | os .O_WRONLY , 0644 )
41- if err == nil {
42- // Successfully created lock file
43- _ , err = f .WriteString (strconv .Itoa (fl .pid ))
44- f .Close ()
45- // Check for write error
46- if err != nil {
47- // We got the lock but failed to write.
48- // Best effort to clean up, then return the error.
49- _ = os .Remove (fl .lockPath )
50- return fmt .Errorf ("failed to write pid to lock file: %v" , err )
51- }
52- // We hold the lock
53- return nil
35+ // Try to create lock file exclusively
36+ f , err := os .OpenFile (fl .lockPath , os .O_CREATE | os .O_EXCL | os .O_WRONLY , 0644 )
37+ if err == nil {
38+ // Successfully created lock file
39+ _ , err = f .WriteString (strconv .Itoa (fl .pid ))
40+ f .Close ()
41+ // Check for write error
42+ if err != nil {
43+ // We got the lock but failed to write.
44+ // Best effort to clean up, then return the error.
45+ _ = os .Remove (fl .lockPath )
46+ return fmt .Errorf ("failed to write pid to lock file: %v" , err )
5447 }
48+ // We hold the lock
49+ return nil
50+ }
5551
56- // If we're here, os.OpenFile failed, likely because the file exists.
57- // Check if lock file is stale (older than 10 seconds)
58- info , statErr := os .Stat (fl .lockPath )
59- if statErr == nil {
60- if time .Since (info .ModTime ()) > 10 * time .Second {
61- // Lock file is stale, try to remove it
62- err = os .Remove (fl .lockPath )
63-
64- if err != nil && ! os .IsNotExist (err ) {
65- // If we can't remove it (and it's not 'not exist'),
66- // something is wrong (e.g., permissions).
67- return fmt .Errorf ("unable to remove stale lock: %v" , err )
68- }
52+ // If we're here, os.OpenFile failed, likely because the file exists.
53+ // Check if lock file is stale (older than 10 seconds)
54+ info , statErr := os .Stat (fl .lockPath )
55+ if statErr == nil {
56+ if time .Since (info .ModTime ()) > 10 * time .Second {
57+ // Lock file is stale, try to remove it
58+ removeErr := os .Remove (fl .lockPath )
59+
60+ if removeErr != nil && ! os .IsNotExist (removeErr ) {
61+ // If we can't remove it (and it's not 'not exist'),
62+ // something is wrong (e.g., permissions).
63+ return fmt .Errorf ("unable to remove stale lock: %v" , removeErr )
6964 }
65+ // Successfully removed stale lock, retry immediately
66+ // This reduces the race window significantly
67+ continue
7068 }
71- // If stat failed (e.g., file removed between OpenFile and Stat)
72- // or lock is not stale, just loop and wait for next tick.
69+ }
70+
71+ // If stat failed (e.g., file removed between OpenFile and Stat)
72+ // or lock is not stale, wait for next tick
73+ select {
74+ case <- timeout :
75+ return fmt .Errorf ("timeout waiting for file lock" )
76+ case <- ticker .C :
77+ // Continue to next iteration
7378 }
7479 }
7580}
0 commit comments