@@ -4,6 +4,7 @@ const path = require('path');
44const fs = require ( 'graceful-fs' ) ;
55const retry = require ( 'retry' ) ;
66const onExit = require ( 'signal-exit' ) ;
7+ const mtimePrecision = require ( './mtime-precision' ) ;
78
89const locks = { } ;
910
@@ -22,11 +23,24 @@ function resolveCanonicalPath(file, options, callback) {
2223}
2324
2425function acquireLock ( file , options , callback ) {
26+ const lockfilePath = getLockFile ( file , options ) ;
27+
2528 // Use mkdir to create the lockfile (atomic operation)
26- options . fs . mkdir ( getLockFile ( file , options ) , ( err ) => {
27- // If successful, we are done
29+ options . fs . mkdir ( lockfilePath , ( err ) => {
2830 if ( ! err ) {
29- return options . fs . stat ( getLockFile ( file , options ) , callback ) ;
31+ // At this point, we acquired the lock!
32+ // Probe the mtime precision
33+ return mtimePrecision . probe ( lockfilePath , options . fs , ( err , mtime , mtimePrecision ) => {
34+ // If it failed, try to remove the lock..
35+ /* istanbul ignore if */
36+ if ( err ) {
37+ options . fs . rmdir ( lockfilePath , ( ) => { } ) ;
38+
39+ return callback ( err ) ;
40+ }
41+
42+ callback ( null , mtime , mtimePrecision ) ;
43+ } ) ;
3044 }
3145
3246 // If error is not EEXIST then some other error occurred while locking
@@ -39,7 +53,7 @@ function acquireLock(file, options, callback) {
3953 return callback ( Object . assign ( new Error ( 'Lock file is already being held' ) , { code : 'ELOCKED' , file } ) ) ;
4054 }
4155
42- options . fs . stat ( getLockFile ( file , options ) , ( err , stat ) => {
56+ options . fs . stat ( lockfilePath , ( err , stat ) => {
4357 if ( err ) {
4458 // Retry if the lockfile has been removed (meanwhile)
4559 // Skip stale check to avoid recursiveness
@@ -95,8 +109,9 @@ function updateLock(file, options) {
95109 lock . updateTimeout = setTimeout ( ( ) => {
96110 lock . updateTimeout = null ;
97111
98- // Check if mtime is still ours if it is we can still recover from a system sleep or a busy event loop
99- options . fs . stat ( getLockFile ( file , options ) , ( err , stat ) => {
112+ // Stat the file to check if mtime is still ours
113+ // If it is, we can still recover from a system sleep or a busy event loop
114+ options . fs . stat ( lock . lockfilePath , ( err , stat ) => {
100115 const isOverThreshold = lock . lastUpdate + options . stale < Date . now ( ) ;
101116
102117 // If it failed to update the lockfile, keep trying unless
@@ -111,7 +126,7 @@ function updateLock(file, options) {
111126 return updateLock ( file , options ) ;
112127 }
113128
114- const isMtimeOurs = lock . mtimeChecker ( lock . mtime , stat . mtime ) ;
129+ const isMtimeOurs = lock . mtime . getTime ( ) === stat . mtime . getTime ( ) ;
115130
116131 if ( ! isMtimeOurs ) {
117132 return setLockAsCompromised (
@@ -123,9 +138,9 @@ function updateLock(file, options) {
123138 ) ) ;
124139 }
125140
126- const mtime = new Date ( ) ;
141+ const mtime = mtimePrecision . getMtime ( lock . mtimePrecision ) ;
127142
128- options . fs . utimes ( getLockFile ( file , options ) , mtime , mtime , ( err ) => {
143+ options . fs . utimes ( lock . lockfilePath , mtime , mtime , ( err ) => {
129144 const isOverThreshold = lock . lastUpdate + options . stale < Date . now ( ) ;
130145
131146 // Ignore if the lock was released
@@ -215,7 +230,7 @@ function lock(file, options, callback) {
215230 const operation = retry . operation ( options . retries ) ;
216231
217232 operation . attempt ( ( ) => {
218- acquireLock ( file , options , ( err , stat ) => {
233+ acquireLock ( file , options , ( err , mtime , mtimePrecision ) => {
219234 if ( operation . retry ( err ) ) {
220235 return ;
221236 }
@@ -226,10 +241,11 @@ function lock(file, options, callback) {
226241
227242 // We now own the lock
228243 const lock = locks [ file ] = {
229- mtime : stat . mtime ,
244+ lockfilePath : getLockFile ( file , options ) ,
245+ mtime,
246+ mtimePrecision,
230247 options,
231248 lastUpdate : Date . now ( ) ,
232- mtimeChecker : createMtimeChecker ( ) ,
233249 } ;
234250
235251 // We must keep the lock fresh to avoid staleness
@@ -310,29 +326,6 @@ function getLocks() {
310326 return locks ;
311327}
312328
313- function createMtimeChecker ( ) {
314- let precision ;
315-
316- return ( lockMtime , statMtime ) => {
317- // If lock time was not on the second we can determine precision
318- if ( ! precision && lockMtime % 1000 !== 0 ) {
319- precision = statMtime % 1000 === 0 ? 's' : 'ms' ;
320- }
321-
322- if ( precision === 's' ) {
323- const lockTs = lockMtime . getTime ( ) ;
324- const statTs = statMtime . getTime ( ) ;
325-
326- // Maybe the file system truncates or rounds...
327- return Math . trunc ( lockTs / 1000 ) === Math . trunc ( statTs / 1000 ) ||
328- Math . round ( lockTs / 1000 ) === Math . round ( statTs / 1000 ) ;
329- }
330-
331- // Must be ms or lockMtime was on the second
332- return lockMtime . getTime ( ) === statMtime . getTime ( ) ;
333- } ;
334- }
335-
336329// Remove acquired locks on exit
337330/* istanbul ignore next */
338331onExit ( ( ) => {
0 commit comments