diff options
Diffstat (limited to 'deps/npm/node_modules/lockfile/lockfile.js')
-rw-r--r-- | deps/npm/node_modules/lockfile/lockfile.js | 137 |
1 files changed, 88 insertions, 49 deletions
diff --git a/deps/npm/node_modules/lockfile/lockfile.js b/deps/npm/node_modules/lockfile/lockfile.js index b82f1f0d65..0c2c6f597a 100644 --- a/deps/npm/node_modules/lockfile/lockfile.js +++ b/deps/npm/node_modules/lockfile/lockfile.js @@ -7,9 +7,9 @@ if (process.version.match(/^v0\.[0-6]/)) { } var os = require('os') -var filetime = 'ctime' +exports.filetime = 'ctime' if (os.platform() == "win32") { - filetime = 'mtime' + exports.filetime = 'mtime' } var debug @@ -92,7 +92,7 @@ exports.check = function (path, opts, cb) { }) fs.close(fd, function (er) { - var age = Date.now() - st[filetime].getTime() + var age = Date.now() - st[exports.filetime].getTime() return cb(er, age <= opts.stale) }) }) @@ -125,29 +125,39 @@ exports.checkSync = function (path, opts) { } finally { fs.closeSync(fd) } - var age = Date.now() - st[filetime].getTime() + var age = Date.now() - st[exports.filetime].getTime() return (age <= opts.stale) } } -var req = 0 +var req = 1 exports.lock = function (path, opts, cb) { if (typeof opts === 'function') cb = opts, opts = {} opts.req = opts.req || req++ debug('lock', path, opts) + opts.start = opts.start || Date.now() if (typeof opts.retries === 'number' && opts.retries > 0) { - cb = (function (orig) { return function (er, fd) { - if (!er) return orig(er, fd) - var newRT = opts.retries - 1 - opts_ = Object.create(opts, { retries: { value: newRT }}) - debug('lock retry', path, newRT) - if (opts.retryWait) setTimeout(function() { - exports.lock(path, opts_, orig) - }, opts.retryWait) - else exports.lock(path, opts_, orig) + debug('has retries', opts.retries) + var retries = opts.retries + opts.retries = 0 + cb = (function (orig) { return function cb (er, fd) { + debug('retry-mutated callback') + retries -= 1 + if (!er || retries < 0) return orig(er, fd) + + debug('lock retry', path, opts) + + if (opts.retryWait) setTimeout(retry, opts.retryWait) + else retry() + + function retry () { + opts.start = Date.now() + debug('retrying', opts.start) + exports.lock(path, opts, cb) + } }})(cb) } @@ -167,30 +177,57 @@ exports.lock = function (path, opts, cb) { if (er.code !== 'EEXIST') return cb(er) // someone's got this one. see if it's valid. - if (opts.stale) fs.stat(path, function (statEr, st) { - if (statEr) { - if (statEr.code === 'ENOENT') { - // expired already! - var opts_ = Object.create(opts, { stale: { value: false }}) - debug('lock stale enoent retry', path, opts_) - exports.lock(path, opts_, cb) - return - } - return cb(statEr) + if (!opts.stale) return notStale(er, path, opts, cb) + + return maybeStale(er, path, opts, false, cb) + }) +} + + +// Staleness checking algorithm +// 1. acquire $lock, fail +// 2. stat $lock, find that it is stale +// 3. acquire $lock.STALE +// 4. stat $lock, assert that it is still stale +// 5. unlink $lock +// 6. link $lock.STALE $lock +// 7. unlink $lock.STALE +// On any failure, clean up whatever we've done, and raise the error. +function maybeStale (originalEr, path, opts, hasStaleLock, cb) { + fs.stat(path, function (statEr, st) { + if (statEr) { + if (statEr.code === 'ENOENT') { + // expired already! + opts.stale = false + debug('lock stale enoent retry', path, opts) + exports.lock(path, opts, cb) + return } + return cb(statEr) + } - var age = Date.now() - st[filetime].getTime() - if (age > opts.stale) { - debug('lock stale', path, opts_) - exports.unlock(path, function (er) { - if (er) return cb(er) - var opts_ = Object.create(opts, { stale: { value: false }}) - debug('lock stale retry', path, opts_) - exports.lock(path, opts_, cb) + var age = Date.now() - st[exports.filetime].getTime() + if (age <= opts.stale) return notStale(originalEr, path, opts, cb) + + debug('lock stale', path, opts) + if (hasStaleLock) { + exports.unlock(path, function (er) { + if (er) return cb(er) + debug('lock stale retry', path, opts) + fs.link(path + '.STALE', path, function (er) { + fs.unlink(path + '.STALE', function () { + // best effort. if the unlink fails, oh well. + cb(er) + }) }) - } else notStale(er, path, opts, cb) - }) - else notStale(er, path, opts, cb) + }) + } else { + debug('acquire .STALE file lock', opts) + exports.lock(path + '.STALE', opts, function (er) { + if (er) return cb(er) + maybeStale(originalEr, path, opts, true, cb) + }) + } }) } @@ -201,20 +238,22 @@ function notStale (er, path, opts, cb) { if (typeof opts.wait !== 'number' || opts.wait <= 0) return cb(er) - // console.error('wait', path, opts.wait) - // wait for some ms for the lock to clear - var start = Date.now() + // poll for some ms for the lock to clear + var now = Date.now() + var start = opts.start || now var end = start + opts.wait - function retry () { - debug('notStale retry', path, opts) - var now = Date.now() - var newWait = end - now - var newOpts = Object.create(opts, { wait: { value: newWait }}) - exports.lock(path, newOpts, cb) - } + if (end <= now) + return cb(er) - var timer = setTimeout(retry, 100) + debug('now=%d, wait until %d (delta=%d)', start, end, end-start) + var wait = Math.min(end - start, opts.pollPeriod || 100) + var timer = setTimeout(poll, wait) + + function poll () { + debug('notStale, polling', path, opts) + exports.lock(path, opts, cb) + } } exports.lockSync = function (path, opts) { @@ -236,7 +275,7 @@ exports.lockSync = function (path, opts) { if (opts.stale) { var st = fs.statSync(path) - var ct = st[filetime].getTime() + var ct = st[exports.filetime].getTime() if (!(ct % 1000) && (opts.stale % 1000)) { // probably don't have subsecond resolution. // round up the staleness indicator. @@ -264,8 +303,8 @@ function retryThrow (path, opts, er) { if (typeof opts.retries === 'number' && opts.retries > 0) { var newRT = opts.retries - 1 debug('retryThrow', path, opts, newRT) - var opts_ = Object.create(opts, { retries: { value: newRT }}) - return exports.lockSync(path, opts_) + opts.retries = newRT + return exports.lockSync(path, opts) } throw er } |