Skip to content

Commit

Permalink
fix #129 install process listeners safely
Browse files Browse the repository at this point in the history
fix #139 flaky fd tests
  • Loading branch information
silkentrance committed Jul 8, 2017
1 parent f8db66b commit b700519
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 37 deletions.
60 changes: 50 additions & 10 deletions lib/tmp.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,14 @@ function _isUndefined(obj) {
*/
function _parseArguments(options, callback) {
if (typeof options == 'function') {
return [callback || {}, options];
return [{}, options];
}

if (_isUndefined(options)) {
return [{}, callback];
}

return [options, callback];
return [options, callback];
}

/**
Expand All @@ -121,6 +121,7 @@ function _generateTmpName(opts) {

// mkstemps like template
if (opts.template) {
// FIXME expects user to provide a working path
return opts.template.replace(TEMPLATE_PATTERN, _randomChars(6));
}

Expand Down Expand Up @@ -154,6 +155,8 @@ function tmpName(options, callback) {
if (opts.template && !opts.template.match(TEMPLATE_PATTERN))
return cb(new Error('Invalid template provided'));

// FIXME get rid of recursion
// FIXME unnecessary iterations when using a fixed name
(function _getUniqueName() {
const name = _generateTmpName(opts);

Expand Down Expand Up @@ -189,6 +192,8 @@ function tmpNameSync(options) {
if (opts.template && !opts.template.match(TEMPLATE_PATTERN))
throw new Error('Invalid template provided');

// FIXME get rid of duplicate code
// FIXME unnecessary iterations when using a fixed name
do {
const name = _generateTmpName(opts);
try {
Expand Down Expand Up @@ -517,19 +522,54 @@ const version = process.versions.node.split('.').map(function (value) {
return parseInt(value, 10);
});

if (version[0] === 0 && (version[1] < 9 || version[1] === 9 && version[2] < 5)) {
process.addListener('uncaughtException', function _uncaughtExceptionThrown(err) {
_uncaughtException = true;
_garbageCollector();
// safely install listeners
function _tmp$uncaught_exception(err) {
_uncaughtException = true;
_garbageCollector();

throw err;
});
throw err;
}

process.addListener('exit', function _exit(code) {
function _tmp$exit(code) {
// TODO:setting uncaught exception here is a no-no as it prevents
// garbage collection for a process that exited gracefully
if (code) _uncaughtException = true;
_garbageCollector();
});
}

function _is_legacy_listener(name) {
return name == '_exit' || name == '_uncaughtExceptionThrown';
}

function _safely_install_listener(evt, listener) {
var listeners = process.listeners(evt);

for (var i = 0, length = listeners.length; i < length; i++) {
var lstnr = listeners[i];
if (lstnr.name == listener.name || _is_legacy_listener(listener.name)) {
if (evt == 'exit') {
// make sure that the handler cleans up
try {
lstnr();
}
catch (err) {
; // just catch
}
}
process.removeListener(evt, lstnr);
break;
}
}

process.addListener(evt, listener);
}

if (version[0] === 0 && (version[1] < 9 || version[1] === 9 && version[2] < 5)) {
_safely_install_listener('uncaughtException', _tmp$uncaught_exception);
}

_safely_install_listener('exit', _tmp$exit);


/**
* Configuration options.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"lib/"
],
"scripts": {
"test": "vows test/*-test.js",
"test": "vows --spec test/*-test.js",
"doc": "jsdoc -c .jsdoc.json"
}
}
4 changes: 1 addition & 3 deletions test/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ var
existsSync = fs.existsSync || path.existsSync,
tmp = require('../lib/tmp');

var _cleanup_fn = undefined;

// FIXME:does not seem to work at all
// FIXME does not work at all
// make sure that we do not test spam the global tmp
tmp.TMP_DIR = './tmp';

Expand Down
44 changes: 21 additions & 23 deletions test/file-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,24 @@ function _testFile(mode, fdTest) {
return function _testFileGenerated(err, name, fd) {
assert.ok(existsSync(name), 'should exist');

var stat = fs.statSync(name);
assert.equal(stat.size, 0, 'should have zero size');
assert.ok(stat.isFile(), 'should be a file');

Test.testStat(stat, mode);

var fstat = null;
// check with fstat as well (fd checking)
if (fdTest) {
var fstat = fs.fstatSync(fd);
assert.deepEqual(fstat, stat, 'fstat results should be the same');
console.log('fd: ' + fd);
fstat = fs.fstatSync(fd);

var data = new Buffer('something');
assert.equal(fs.writeSync(fd, data, 0, data.length, 0), data.length, 'should be writable');
assert.ok(!fs.closeSync(fd), 'should not return with error');
}

var stat = fs.statSync(name);
assert.equal(stat.size, 0, 'should have zero size');
assert.ok(stat.isFile(), 'should be a file');
if (fdTest) {
assert.deepEqual(fstat, stat, 'fstat results should be the same');
}
Test.testStat(stat, mode);
};
}

Expand All @@ -48,18 +51,16 @@ function _testFileNoDescriptor(mode) {
};
}

function _testFileAfterDetachRemove(mode) {
return function _testFileAfterDetachRemove(err, name, fd) {
assert.ok(!existsSync(name), 'File should be removed');
function _testFileAfterDetachDescriptor(mode) {
return function _testFileAfterDetachDescriptorImpl(err, name, fd) {
assert.ok(existsSync(name), 'should exist');

var fstat = fs.fstatSync(fd);
assert.equal(fstat.size, 0, 'should have zero size');
assert.ok(fstat.isFile(), 'should be a file');
Test.testStat(fstat, mode);
var stat = fs.fstatSync(fd);
assert.equal(stat.size, 0, 'should have zero size');
assert.ok(stat.isFile(), 'should be a file');
Test.testStat(stat, mode);

var data = new Buffer('something');
assert.equal(fs.writeSync(fd, data, 0, data.length, 0), data.length, 'should be writable');
assert.ok(!fs.closeSync(fd), 'should not return with error');
assert.ok(fd != undefined, 'fd must be defined');
};
}

Expand Down Expand Up @@ -144,15 +145,12 @@ vows.describe('File creation').addBatch({
'when using detachDescriptor': {
topic: function () {
var cb = this.callback;
tmp.file({ detachDescriptor: true }, function (err, name, fd, removeCallback) {
removeCallback();
return cb(err, name, fd);
});
tmp.file({ detachDescriptor: true }, this.callback)
},

'should not return with an error': assert.isNull,
'should return with a name': Test.assertName,
'should have working descriptor after removeCallback': _testFileAfterDetachRemove(0100600),
'should have working a working file': _testFileAfterDetachDescriptor(0100600),
'should have been cleaned up': Test.testCleanup
},

Expand Down

0 comments on commit b700519

Please sign in to comment.