You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Various improvements to withLock and its tests
- fix windows race conditions/errors
- use second-level granularity when detecting lock compromise; this
resolves a sporadic floating point issue under APFS, and makes this
generally more robust no matter the underlying file system
- improve touchLock logic so that it doesn't compromise the lock for the
next holder or keep running the interval when cleanup fails
## Testing notes
Fixes were verified via a [modified GHA
workflow](https://github.com/jenseng/cli/actions/runs/17803264354) that
ran all the tests 100 times 😅
## References
Related to #8512
@@ -86,9 +86,18 @@ function acquireLock (lockPath) {
86
86
returnretry(err)
87
87
}
88
88
if(status==='stale'){
89
-
// there is a very tiny window where another process could also release the stale lock and acquire it before we release it here; the lock compromise checker should detect this and throw an error
90
-
deleteLock(lockPath,['ENOENT','EBUSY'])// on windows, EBUSY can happen if another process is creating the lock; we'll just retry
89
+
try{
90
+
// there is a very tiny window where another process could also release the stale lock and acquire it before we release it here; the lock compromise checker should detect this and throw an error
91
+
deleteLock(lockPath)
92
+
}catch(e){
93
+
// on windows, EBUSY/EPERM can happen if another process is (re)creating the lock; maybe we can acquire it on a subsequent attempt 🤞
94
+
if(e.code==='EBUSY'||e.code==='EPERM'){
95
+
returnretry(e)
96
+
}
97
+
throwe
98
+
}
91
99
}
100
+
// immediately attempt to acquire the lock (no backoff)
92
101
returnawaitacquireLock(lockPath)
93
102
}
94
103
try{
@@ -100,12 +109,12 @@ function acquireLock (lockPath) {
@@ -131,31 +140,33 @@ async function getLockStatus (lockPath) {
131
140
asyncfunctionmaintainLock(lockPath){
132
141
constcontroller=newAbortController()
133
142
conststats=awaitfs.stat(lockPath)
134
-
letmtimeMs=stats.mtimeMs
143
+
// fs.utimes operates on floating points seconds (directly, or via strings/Date objects), which may not match the underlying filesystem's mtime precision, meaning that we might read a slightly different mtime than we write. always round to the nearest second, since all filesystems support at least second precision
0 commit comments