@@ -105,7 +105,7 @@ t.test('stale lock takeover', async (t) => {
105105 const mtimeMs = Math . round ( Date . now ( ) / 1000 ) * 1000
106106 mockStat = async ( ) => {
107107 if ( ++ statCalls === 1 ) {
108- return { mtimeMs : mtimeMs - 10_000 }
108+ return { mtimeMs : mtimeMs - 120_000 }
109109 } else {
110110 return { mtimeMs, ino : 1 }
111111 }
@@ -146,7 +146,7 @@ t.test('EBUSY during stale lock takeover', async (t) => {
146146 const mtimeMs = Math . round ( Date . now ( ) / 1000 ) * 1000
147147 mockStat = async ( ) => {
148148 if ( ++ statCalls === 1 ) {
149- return { mtimeMs : mtimeMs - 10_000 }
149+ return { mtimeMs : mtimeMs - 120_000 }
150150 } else {
151151 return { mtimeMs, ino : 1 }
152152 }
@@ -168,7 +168,7 @@ t.test('concurrent stale lock takeover', async (t) => {
168168 const lockPath = path . join ( fs . mkdtempSync ( path . join ( getTempDir ( ) , 'test-' ) ) , 'concurrency.lock' )
169169 // make a stale lock
170170 await fs . promises . mkdir ( lockPath )
171- await fs . promises . utimes ( lockPath , new Date ( Date . now ( ) - 10_000 ) , new Date ( Date . now ( ) - 10_000 ) )
171+ await fs . promises . utimes ( lockPath , new Date ( Date . now ( ) - 120_000 ) , new Date ( Date . now ( ) - 120_000 ) )
172172
173173 const results = await Promise . allSettled ( [
174174 withLock ( lockPath , ( ) => 'lock1' ) ,
@@ -225,6 +225,27 @@ t.test('mtime floating point mismatch', async (t) => {
225225 } ) , 'should handle mtime floating point mismatches' )
226226} )
227227
228+ t . test ( 'slow fs.stat calls shouldn\'t cause a lock compromise false positive' , async ( t ) => {
229+ let mtimeMs = Math . round ( Date . now ( ) / 1000 ) * 1000
230+ let statCalls = 0
231+ mockStat = async ( ) => {
232+ const result = mtimeMs
233+ if ( ++ statCalls === 2 ) { // make it slow in the first touchLock callback
234+ await setTimeout ( 2000 )
235+ }
236+ return { mtimeMs : result , ino : 1 }
237+ }
238+ mockUtimes = async ( _ , nextMtimeSeconds ) => {
239+ console . log ( 'utimes' , nextMtimeSeconds )
240+ mtimeMs = nextMtimeSeconds * 1000
241+ }
242+ const lockPath = path . join ( fs . mkdtempSync ( path . join ( getTempDir ( ) , 'test-' ) ) , 'concurrency.lock' )
243+ t . ok ( await withLock ( lockPath , async ( ) => {
244+ await setTimeout ( 4000 )
245+ return true
246+ } ) , 'should handle slow fs.stat calls' )
247+ } )
248+
228249t . test ( 'unexpected errors' , async ( t ) => {
229250 t . test ( 'can\'t create lock' , async ( t ) => {
230251 const lockPath = '/these/parent/directories/do/not/exist/so/it/should/fail.lock'
@@ -259,7 +280,7 @@ t.test('unexpected errors', async (t) => {
259280 }
260281 // it's stale
261282 mockStat = async ( ) => {
262- return { mtimeMs : Date . now ( ) - 10_000 }
283+ return { mtimeMs : Date . now ( ) - 120_000 }
263284 }
264285 // but we can't release it
265286 mockRmdirSync = ( ) => {
@@ -301,7 +322,7 @@ t.test('lock released during maintenance', async (t) => {
301322 mockStat = async ( ...args ) => {
302323 const value = await fs . promises . stat ( ...args )
303324 if ( ++ statCalls > 1 ) {
304- // this runs during the setInterval ; release the lock so that we no longer hold it
325+ // this runs during the setTimeout callback ; release the lock so that we no longer hold it
305326 await releaseLock ( 'test value' )
306327 await setTimeout ( )
307328 }
@@ -314,7 +335,7 @@ t.test('lock released during maintenance', async (t) => {
314335 }
315336
316337 const lockPromise = withLock ( lockPath , ( ) => releaseLockPromise )
317- // since we unref the interval timeout, we need to wait to ensure it actually runs
338+ // since we unref the timeout, we need to wait to ensure it actually runs
318339 await setTimeout ( 2000 )
319340 t . equal ( await lockPromise , 'test value' , 'should acquire the lock' )
320341 t . equal ( utimesCalls , 0 , 'should never call utimes' )
0 commit comments