summaryrefslogtreecommitdiff
path: root/deps/npm/node_modules/lockfile/lockfile.js
diff options
context:
space:
mode:
Diffstat (limited to 'deps/npm/node_modules/lockfile/lockfile.js')
-rw-r--r--deps/npm/node_modules/lockfile/lockfile.js137
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
}