# add dist

This commit is contained in:
Mario Romano
2016-04-21 11:56:31 +01:00
parent 5914688467
commit 07807e7bc3
13499 changed files with 1808930 additions and 5 deletions

View File

@@ -0,0 +1,15 @@
The ISC License
Copyright (c) Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@@ -0,0 +1,86 @@
# lockfile
A very polite lock file utility, which endeavors to not litter, and to
wait patiently for others.
## Usage
```javascript
var lockFile = require('lockfile')
// opts is optional, and defaults to {}
lockFile.lock('some-file.lock', opts, function (er) {
// if the er happens, then it failed to acquire a lock.
// if there was not an error, then the file was created,
// and won't be deleted until we unlock it.
// do my stuff, free of interruptions
// then, some time later, do:
lockFile.unlock('some-file.lock', function (er) {
// er means that an error happened, and is probably bad.
})
})
```
## Methods
Sync methods return the value/throw the error, others don't. Standard
node fs stuff.
All known locks are removed when the process exits. Of course, it's
possible for certain types of failures to cause this to fail, but a best
effort is made to not be a litterbug.
### lockFile.lock(path, [opts], cb)
Acquire a file lock on the specified path
### lockFile.lockSync(path, [opts])
Acquire a file lock on the specified path
### lockFile.unlock(path, cb)
Close and unlink the lockfile.
### lockFile.unlockSync(path)
Close and unlink the lockfile.
### lockFile.check(path, [opts], cb)
Check if the lockfile is locked and not stale.
Callback is called with `cb(error, isLocked)`.
### lockFile.checkSync(path, [opts])
Check if the lockfile is locked and not stale.
Returns boolean.
## Options
### opts.wait
A number of milliseconds to wait for locks to expire before giving up.
Only used by lockFile.lock. Poll for `opts.wait` ms. If the lock is
not cleared by the time the wait expires, then it returns with the
original error.
### opts.pollPeriod
When using `opts.wait`, this is the period in ms in which it polls to
check if the lock has expired. Defaults to `100`.
### opts.stale
A number of milliseconds before locks are considered to have expired.
### opts.retries
Used by lock and lockSync. Retry `n` number of times before giving up.
### opts.retryWait
Used by lock. Wait `n` milliseconds before retrying.

View File

@@ -0,0 +1,311 @@
var fs = require('fs')
var wx = 'wx'
if (process.version.match(/^v0\.[0-6]/)) {
var c = require('constants')
wx = c.O_TRUNC | c.O_CREAT | c.O_WRONLY | c.O_EXCL
}
var os = require('os')
exports.filetime = 'ctime'
if (os.platform() == "win32") {
exports.filetime = 'mtime'
}
var debug
var util = require('util')
if (util.debuglog)
debug = util.debuglog('LOCKFILE')
else if (/\blockfile\b/i.test(process.env.NODE_DEBUG))
debug = function() {
var msg = util.format.apply(util, arguments)
console.error('LOCKFILE %d %s', process.pid, msg)
}
else
debug = function() {}
var locks = {}
function hasOwnProperty (obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop)
}
process.on('exit', function () {
debug('exit listener')
// cleanup
Object.keys(locks).forEach(exports.unlockSync)
})
// XXX https://github.com/joyent/node/issues/3555
// Remove when node 0.8 is deprecated.
if (/^v0\.[0-8]\./.test(process.version)) {
debug('uncaughtException, version = %s', process.version)
process.on('uncaughtException', function H (er) {
debug('uncaughtException')
var l = process.listeners('uncaughtException').filter(function (h) {
return h !== H
})
if (!l.length) {
// cleanup
try { Object.keys(locks).forEach(exports.unlockSync) } catch (e) {}
process.removeListener('uncaughtException', H)
throw er
}
})
}
exports.unlock = function (path, cb) {
debug('unlock', path)
// best-effort. unlocking an already-unlocked lock is a noop
delete locks[path]
fs.unlink(path, function (unlinkEr) { cb() })
}
exports.unlockSync = function (path) {
debug('unlockSync', path)
// best-effort. unlocking an already-unlocked lock is a noop
try { fs.unlinkSync(path) } catch (er) {}
delete locks[path]
}
// if the file can be opened in readonly mode, then it's there.
// if the error is something other than ENOENT, then it's not.
exports.check = function (path, opts, cb) {
if (typeof opts === 'function') cb = opts, opts = {}
debug('check', path, opts)
fs.open(path, 'r', function (er, fd) {
if (er) {
if (er.code !== 'ENOENT') return cb(er)
return cb(null, false)
}
if (!opts.stale) {
return fs.close(fd, function (er) {
return cb(er, true)
})
}
fs.fstat(fd, function (er, st) {
if (er) return fs.close(fd, function (er2) {
return cb(er)
})
fs.close(fd, function (er) {
var age = Date.now() - st[exports.filetime].getTime()
return cb(er, age <= opts.stale)
})
})
})
}
exports.checkSync = function (path, opts) {
opts = opts || {}
debug('checkSync', path, opts)
if (opts.wait) {
throw new Error('opts.wait not supported sync for obvious reasons')
}
try {
var fd = fs.openSync(path, 'r')
} catch (er) {
if (er.code !== 'ENOENT') throw er
return false
}
if (!opts.stale) {
try { fs.closeSync(fd) } catch (er) {}
return true
}
// file exists. however, might be stale
if (opts.stale) {
try {
var st = fs.fstatSync(fd)
} finally {
fs.closeSync(fd)
}
var age = Date.now() - st[exports.filetime].getTime()
return (age <= opts.stale)
}
}
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) {
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)
}
// try to engage the lock.
// if this succeeds, then we're in business.
fs.open(path, wx, function (er, fd) {
if (!er) {
debug('locked', path, fd)
locks[path] = fd
return fs.close(fd, function () {
return cb()
})
}
// something other than "currently locked"
// maybe eperm or something.
if (er.code !== 'EEXIST') return cb(er)
// someone's got this one. see if it's valid.
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[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 {
debug('acquire .STALE file lock', opts)
exports.lock(path + '.STALE', opts, function (er) {
if (er) return cb(er)
maybeStale(originalEr, path, opts, true, cb)
})
}
})
}
function notStale (er, path, opts, cb) {
debug('notStale', path, opts)
// if we can't wait, then just call it a failure
if (typeof opts.wait !== 'number' || opts.wait <= 0)
return cb(er)
// poll for some ms for the lock to clear
var now = Date.now()
var start = opts.start || now
var end = start + opts.wait
if (end <= now)
return cb(er)
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) {
opts = opts || {}
opts.req = opts.req || req++
debug('lockSync', path, opts)
if (opts.wait || opts.retryWait) {
throw new Error('opts.wait not supported sync for obvious reasons')
}
try {
var fd = fs.openSync(path, wx)
locks[path] = fd
try { fs.closeSync(fd) } catch (er) {}
debug('locked sync!', path, fd)
return
} catch (er) {
if (er.code !== 'EEXIST') return retryThrow(path, opts, er)
if (opts.stale) {
var st = fs.statSync(path)
var ct = st[exports.filetime].getTime()
if (!(ct % 1000) && (opts.stale % 1000)) {
// probably don't have subsecond resolution.
// round up the staleness indicator.
// Yes, this will be wrong 1/1000 times on platforms
// with subsecond stat precision, but that's acceptable
// in exchange for not mistakenly removing locks on
// most other systems.
opts.stale = 1000 * Math.ceil(opts.stale / 1000)
}
var age = Date.now() - ct
if (age > opts.stale) {
debug('lockSync stale', path, opts, age)
exports.unlockSync(path)
return exports.lockSync(path, opts)
}
}
// failed to lock!
debug('failed to lock', path, opts, er)
return retryThrow(path, opts, er)
}
}
function retryThrow (path, opts, er) {
if (typeof opts.retries === 'number' && opts.retries > 0) {
var newRT = opts.retries - 1
debug('retryThrow', path, opts, newRT)
opts.retries = newRT
return exports.lockSync(path, opts)
}
throw er
}

View File

@@ -0,0 +1,89 @@
{
"_args": [
[
"lockfile@^1.0.1",
"/Users/mromano/dev/dev-platform-webcomponents/ng2-components/ng2-alfresco-documentslist/node_modules/typings/node_modules/typings-core"
]
],
"_from": "lockfile@>=1.0.1 <2.0.0",
"_id": "lockfile@1.0.1",
"_inCache": true,
"_installable": true,
"_location": "/lockfile",
"_nodeVersion": "2.0.1",
"_npmUser": {
"email": "isaacs@npmjs.com",
"name": "isaacs"
},
"_npmVersion": "2.10.0",
"_phantomChildren": {},
"_requested": {
"name": "lockfile",
"raw": "lockfile@^1.0.1",
"rawSpec": "^1.0.1",
"scope": null,
"spec": ">=1.0.1 <2.0.0",
"type": "range"
},
"_requiredBy": [
"/typings/typings-core"
],
"_resolved": "https://registry.npmjs.org/lockfile/-/lockfile-1.0.1.tgz",
"_shasum": "9d353ecfe3f54d150bb57f89d51746935a39c4f5",
"_shrinkwrap": null,
"_spec": "lockfile@^1.0.1",
"_where": "/Users/mromano/dev/dev-platform-webcomponents/ng2-components/ng2-alfresco-documentslist/node_modules/typings/node_modules/typings-core",
"author": {
"email": "i@izs.me",
"name": "Isaac Z. Schlueter",
"url": "http://blog.izs.me/"
},
"bugs": {
"url": "https://github.com/isaacs/lockfile/issues"
},
"dependencies": {},
"description": "A very polite lock file utility, which endeavors to not litter, and to wait patiently for others.",
"devDependencies": {
"tap": "~0.2.5",
"touch": "0"
},
"directories": {
"test": "test"
},
"dist": {
"shasum": "9d353ecfe3f54d150bb57f89d51746935a39c4f5",
"tarball": "https://registry.npmjs.org/lockfile/-/lockfile-1.0.1.tgz"
},
"gitHead": "9d338ed8e3e3a166955d051f6b5fb6bb1e563ca8",
"homepage": "https://github.com/isaacs/lockfile#readme",
"keywords": [
"lockfile",
"lock",
"file",
"fs",
"O_EXCL"
],
"license": "ISC",
"main": "lockfile.js",
"maintainers": [
{
"email": "trevorburnham@gmail.com",
"name": "trevorburnham"
},
{
"email": "i@izs.me",
"name": "isaacs"
}
],
"name": "lockfile",
"optionalDependencies": {},
"readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git://github.com/isaacs/lockfile.git"
},
"scripts": {
"test": "tap test/*.js"
},
"version": "1.0.1"
}

View File

@@ -0,0 +1,292 @@
var test = require('tap').test
var lockFile = require('../lockfile.js')
var path = require('path')
var fs = require('fs')
var touch = require('touch')
// On Unix systems, it uses ctime by default for staleness checks, since it's
// the most reliable. However, because this test artificially sets some locks
// to an earlier time to simulate staleness, we use mtime here.
lockFile.filetime = 'mtime'
test('setup', function (t) {
try { lockFile.unlockSync('basic-lock') } catch (er) {}
try { lockFile.unlockSync('sync-lock') } catch (er) {}
try { lockFile.unlockSync('never-forget') } catch (er) {}
try { lockFile.unlockSync('stale-lock') } catch (er) {}
try { lockFile.unlockSync('watch-lock') } catch (er) {}
try { lockFile.unlockSync('retry-lock') } catch (er) {}
try { lockFile.unlockSync('contentious-lock') } catch (er) {}
try { lockFile.unlockSync('stale-wait-lock') } catch (er) {}
try { lockFile.unlockSync('stale-windows-lock') } catch (er) {}
t.end()
})
test('lock contention', function (t) {
var gotlocks = 0;
var N = 200
var delay = 10
// allow for some time for each lock acquisition and release.
// note that raising N higher will mean that the overhead
// increases, because we're creating more and more watchers.
// irl, you should never have several hundred contenders for a
// single lock, so this situation is somewhat pathological.
var overhead = 200
var wait = N * overhead + delay
// first make it locked, so that everyone has to wait
lockFile.lock('contentious-lock', function(er, lock) {
t.ifError(er, 'acquiring starter')
if (er) throw er;
t.pass('acquired starter lock')
setTimeout(function() {
lockFile.unlock('contentious-lock', function (er) {
t.ifError(er, 'unlocking starter')
if (er) throw er
t.pass('unlocked starter')
})
}, delay)
})
for (var i=0; i < N; i++)
lockFile.lock('contentious-lock', { wait: wait }, function(er, lock) {
if (er) throw er;
lockFile.unlock('contentious-lock', function(er) {
if (er) throw er
gotlocks++
t.pass('locked and unlocked #' + gotlocks)
if (gotlocks === N) {
t.pass('got all locks')
t.end()
}
})
})
})
test('basic test', function (t) {
lockFile.check('basic-lock', function (er, locked) {
if (er) throw er
t.notOk(locked)
lockFile.lock('basic-lock', function (er) {
if (er) throw er
lockFile.lock('basic-lock', function (er) {
t.ok(er)
lockFile.check('basic-lock', function (er, locked) {
if (er) throw er
t.ok(locked)
lockFile.unlock('basic-lock', function (er) {
if (er) throw er
lockFile.check('basic-lock', function (er, locked) {
if (er) throw er
t.notOk(locked)
t.end()
})
})
})
})
})
})
})
test('sync test', function (t) {
var locked
locked = lockFile.checkSync('sync-lock')
t.notOk(locked)
lockFile.lockSync('sync-lock')
locked = lockFile.checkSync('sync-lock')
t.ok(locked)
lockFile.unlockSync('sync-lock')
locked = lockFile.checkSync('sync-lock')
t.notOk(locked)
t.end()
})
test('exit cleanup test', function (t) {
var child = require.resolve('./fixtures/child.js')
var node = process.execPath
var spawn = require('child_process').spawn
spawn(node, [child]).on('exit', function () {
setTimeout(function () {
var locked = lockFile.checkSync('never-forget')
t.notOk(locked)
t.end()
}, 100)
})
})
test('error exit cleanup test', function (t) {
var child = require.resolve('./fixtures/bad-child.js')
var node = process.execPath
var spawn = require('child_process').spawn
spawn(node, [child]).on('exit', function () {
setTimeout(function () {
var locked = lockFile.checkSync('never-forget')
t.notOk(locked)
t.end()
}, 100)
})
})
test('staleness test', function (t) {
lockFile.lock('stale-lock', function (er) {
if (er) throw er
// simulate 2s old
touch.sync('stale-lock', { time: new Date(Date.now() - 2000) })
var opts = { stale: 1 }
lockFile.check('stale-lock', opts, function (er, locked) {
if (er) throw er
t.notOk(locked)
lockFile.lock('stale-lock', opts, function (er) {
if (er) throw er
lockFile.unlock('stale-lock', function (er) {
if (er) throw er
t.end()
})
})
})
})
})
test('staleness sync test', function (t) {
var opts = { stale: 1 }
lockFile.lockSync('stale-lock')
// simulate 2s old
touch.sync('stale-lock', { time: new Date(Date.now() - 2000) })
var locked
locked = lockFile.checkSync('stale-lock', opts)
t.notOk(locked)
lockFile.lockSync('stale-lock', opts)
lockFile.unlockSync('stale-lock')
t.end()
})
test('retries', function (t) {
// next 5 opens will fail.
var opens = 5
fs._open = fs.open
fs.open = function (path, mode, cb) {
if (--opens === 0) {
fs.open = fs._open
return fs.open(path, mode, cb)
}
var er = new Error('bogus')
// to be, or not to be, that is the question.
er.code = opens % 2 ? 'EEXIST' : 'ENOENT'
process.nextTick(cb.bind(null, er))
}
lockFile.lock('retry-lock', { retries: opens }, function (er) {
if (er) throw er
t.equal(opens, 0)
lockFile.unlockSync('retry-lock')
t.end()
})
})
test('retryWait', function (t) {
// next 5 opens will fail.
var opens = 5
fs._open = fs.open
fs.open = function (path, mode, cb) {
if (--opens === 0) {
fs.open = fs._open
return fs.open(path, mode, cb)
}
var er = new Error('bogus')
// to be, or not to be, that is the question.
er.code = opens % 2 ? 'EEXIST' : 'ENOENT'
process.nextTick(cb.bind(null, er))
}
var opts = { retries: opens, retryWait: 100 }
lockFile.lock('retry-lock', opts, function (er) {
if (er) throw er
t.equal(opens, 0)
lockFile.unlockSync('retry-lock')
t.end()
})
})
test('retry sync', function (t) {
// next 5 opens will fail.
var opens = 5
fs._openSync = fs.openSync
fs.openSync = function (path, mode) {
if (--opens === 0) {
fs.openSync = fs._openSync
return fs.openSync(path, mode)
}
var er = new Error('bogus')
// to be, or not to be, that is the question.
er.code = opens % 2 ? 'EEXIST' : 'ENOENT'
throw er
}
var opts = { retries: opens }
lockFile.lockSync('retry-lock', opts)
t.equal(opens, 0)
lockFile.unlockSync('retry-lock')
t.end()
})
test('wait and stale together', function (t) {
// first locker.
var interval
lockFile.lock('stale-wait-lock', function(er) {
// keep refreshing the lock, so we keep it forever
interval = setInterval(function() {
touch.sync('stale-wait-lock')
}, 10)
// try to get another lock. this must fail!
var opt = { stale: 1000, wait: 2000, pollInterval: 1000 }
lockFile.lock('stale-wait-lock', opt, function (er) {
if (!er)
t.fail('got second lock? that unpossible!')
else
t.pass('second lock failed, as i have foreseen it')
clearInterval(interval)
t.end()
})
})
})
test('stale windows file tunneling test', function (t) {
// for windows only
// nt file system tunneling feature will make file creation time not updated
var opts = { stale: 1000 }
lockFile.lockSync('stale-windows-lock')
touch.sync('stale-windows-lock', { time: new Date(Date.now() - 3000) })
var locked
lockFile.unlockSync('stale-windows-lock')
lockFile.lockSync('stale-windows-lock', opts)
locked = lockFile.checkSync('stale-windows-lock', opts)
t.ok(locked, "should be locked and not stale")
lockFile.lock('stale-windows-lock', opts, function (er) {
if (!er)
t.fail('got second lock? impossible, windows file tunneling problem!')
else
t.pass('second lock failed, windows file tunneling problem fixed')
t.end()
})
})
test('cleanup', function (t) {
try { lockFile.unlockSync('basic-lock') } catch (er) {}
try { lockFile.unlockSync('sync-lock') } catch (er) {}
try { lockFile.unlockSync('never-forget') } catch (er) {}
try { lockFile.unlockSync('stale-lock') } catch (er) {}
try { lockFile.unlockSync('watch-lock') } catch (er) {}
try { lockFile.unlockSync('retry-lock') } catch (er) {}
try { lockFile.unlockSync('contentious-lock') } catch (er) {}
try { lockFile.unlockSync('stale-wait-lock') } catch (er) {}
try { lockFile.unlockSync('stale-windows-lock') } catch (er) {}
t.end()
})

View File

@@ -0,0 +1,5 @@
var lockFile = require('../../lockfile.js')
lockFile.lockSync('never-forget')
throw new Error('waaaaaaaaa')

View File

@@ -0,0 +1,3 @@
var lockFile = require('../../lockfile.js')
lockFile.lock('never-forget', function () {})

View File

@@ -0,0 +1,62 @@
// In these tests, we do the following:
// try for 200ms (rt=2)
// wait for 300ms
// try for 200ms (rt=1)
// wait for 300ms
// try for 200ms (rt=0)
// fail after 1200
// Actual time will be more like 1220-ish for setTimeout irregularity
// But it should NOT be as slow as 2000.
var lockFile = require('../')
var touch = require('touch')
var test = require('tap').test
var fs = require('fs')
var RETRYWAIT = 100
var WAIT = 100
var RETRIES = 2
var EXPECTTIME = (RETRYWAIT * RETRIES) + (WAIT * (RETRIES + 1))
var TOOLONG = EXPECTTIME * 1.1
test('setup', function (t) {
touch.sync('file.lock')
t.end()
})
var pollPeriods = [10, 100, 10000]
pollPeriods.forEach(function (pp) {
test('retry+wait, poll=' + pp, function (t) {
var ended = false
var timer = setTimeout(function() {
t.fail('taking too long!')
ended = true
t.end()
}, 2000)
timer.unref()
var start = Date.now()
lockFile.lock('file.lock', {
wait: WAIT,
retries: RETRIES,
retryWait: RETRYWAIT,
pollPeriod: pp
}, function (er) {
if (ended) return
var time = Date.now() - start
console.error('t=%d', time)
t.ok(time >= EXPECTTIME, 'should take at least ' + EXPECTTIME)
t.ok(time < TOOLONG, 'should take less than ' + TOOLONG)
clearTimeout(timer)
t.end()
})
})
})
test('cleanup', function (t) {
fs.unlinkSync('file.lock')
t.end()
setTimeout(function() {
process.exit(1)
}, 500).unref()
})

View File

@@ -0,0 +1,85 @@
var fs = require('fs')
var lockFile = require('../')
var test = require('tap').test
var path = require('path')
var lock = path.resolve(__dirname, 'stale.lock')
var touch = require('touch')
var spawn = require('child_process').spawn
var node = process.execPath
// We're using a lockfile with an artificially old date,
// so make it use that instead of ctime.
// Probably you should never do this in production!
lockFile.filetime = 'mtime'
if (process.argv[2] === 'child') {
return child()
}
function child () {
// Make fs.stat take 100ms to return its data
// This is important because, in a test scenario where
// we're statting the same exact file rapid-fire like this,
// it'll end up being cached by the FS, and never trigger
// the race condition we're trying to expose.
fs.stat = function (stat) { return function () {
var args = [].slice.call(arguments)
var cb = args.pop()
stat.apply(fs, args.concat(function(er, st) {
setTimeout(function () {
cb(er, st)
}, 100)
}))
}}(fs.stat)
lockFile.lock(lock, { stale: 100000 }, function (er) {
if (er && er.code !== 'EEXIST')
throw er
else if (er)
process.exit(17)
else
setTimeout(function(){}, 500)
})
}
test('create stale file', function (t) {
try { fs.unlinkSync(lock) } catch (er) {}
touch.sync(lock, { time: '1979-07-01T19:10:00.000Z' })
t.end()
})
test('contenders', function (t) {
var n = 10
var fails = 0
var wins = 0
var args = [ __filename, 'child' ]
var opt = { stdio: [0, "pipe", 2] }
for (var i = 0; i < n; i++) {
spawn(node, args, opt).on('close', then)
}
function then (code) {
if (code === 17) {
fails ++
} else if (code) {
t.fail("unexpected failure", code)
fails ++
} else {
wins ++
}
if (fails + wins === n) {
done()
}
}
function done () {
t.equal(wins, 1, "should have 1 lock winner")
t.equal(fails, n - 1, "all others should lose")
t.end()
}
})
test('remove stale file', function (t) {
try { fs.unlinkSync(lock) } catch (er) {}
t.end()
})