diff --git a/benchmark/fs/lstat.js b/benchmark/fs/lstat.js new file mode 100644 index 00000000000000..bf5c50661157c0 --- /dev/null +++ b/benchmark/fs/lstat.js @@ -0,0 +1,88 @@ +// Call fs.stat over and over again really fast. +// Then see how many times it got called. +// Yes, this is a silly benchmark. Most benchmarks are silly. + +var path = require('path'); +var common = require('../common.js'); +var fs = require('fs'); + +var FILES = [ + require.resolve('../../lib/assert.js'), + require.resolve('../../lib/console.js'), + require.resolve('../../lib/fs.js') +]; + +var VARIANTS = { + promise: createPromiseBasedTest(fs.lstat), + callback: createCallBackBasedTest(fs.lstat), +}; + +var bench = common.createBenchmark(main, { + dur: [5], + concurrent: [/*1, 10,*/ 100], + variant: Object.keys(VARIANTS) +}); + +function main(conf) { + var stat = VARIANTS[conf.variant]; + + if (stat == VARIANTS.promise && !fs.lstat('.')) { + bench.start(); + bench.end(0); + return; + } + + var calls = 0; + bench.start(); + setTimeout(function() { + bench.end(calls); + }, +conf.dur * 1000); + + var cur = +conf.concurrent; + while (cur--) run(); + + function run() { + var p = stat(next); + if (p) p.then(next); + } + + function next() { + calls++; + run(); + } +} + +function createCallBackBasedTest(stat) { + return function runStatViaCallbacks(cb) { + stat(FILES[0], function(err, data) { + if (err) throw err; + second(data); + }); + + function second(data) { + stat(FILES[1], function(err, data) { + if (err) throw err; + third(data); + }); + } + + function third(data) { + stat(FILES[2], function(err, data) { + if (err) throw err; + cb(data); + }); + } + }; +} + +function createPromiseBasedTest(stat) { + return function runStatViaPromises() { + return stat(FILES[0]) + .then(function secondP(data) { + return stat(FILES[1]); + }) + .then(function thirdP(data) { + return stat(FILES[2]); + }); + } +} diff --git a/benchmark/fs/stat.js b/benchmark/fs/stat.js new file mode 100644 index 00000000000000..b3565132c9b302 --- /dev/null +++ b/benchmark/fs/stat.js @@ -0,0 +1,88 @@ +// Call fs.stat over and over again really fast. +// Then see how many times it got called. +// Yes, this is a silly benchmark. Most benchmarks are silly. + +var path = require('path'); +var common = require('../common.js'); +var fs = require('fs'); + +var FILES = [ + require.resolve('../../lib/assert.js'), + require.resolve('../../lib/console.js'), + require.resolve('../../lib/fs.js') +]; + +var VARIANTS = { + promise: createPromiseBasedTest(fs.stat), + callback: createCallBackBasedTest(fs.stat), +}; + +var bench = common.createBenchmark(main, { + dur: [5], + concurrent: [/*1, 10,*/ 100], + variant: Object.keys(VARIANTS) +}); + +function main(conf) { + var stat = VARIANTS[conf.variant]; + + if (stat == VARIANTS.promise && !fs.stat('.')) { + bench.start(); + bench.end(0); + return; + } + + var calls = 0; + bench.start(); + setTimeout(function() { + bench.end(calls); + }, +conf.dur * 1000); + + var cur = +conf.concurrent; + while (cur--) run(); + + function run() { + var p = stat(next); + if (p) p.then(next); + } + + function next() { + calls++; + run(); + } +} + +function createCallBackBasedTest(stat) { + return function runStatViaCallbacks(cb) { + stat(FILES[0], function(err, data) { + if (err) throw err; + second(data); + }); + + function second(data) { + stat(FILES[1], function(err, data) { + if (err) throw err; + third(data); + }); + } + + function third(data) { + stat(FILES[2], function(err, data) { + if (err) throw err; + cb(data); + }); + } + }; +} + +function createPromiseBasedTest(stat) { + return function runStatViaPromises() { + return stat(FILES[0]) + .then(function secondP(data) { + return stat(FILES[1]); + }) + .then(function thirdP(data) { + return stat(FILES[2]); + }); + } +} diff --git a/lib/fs.js b/lib/fs.js index 66549ee5a7f7e5..828e889850e564 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -112,9 +112,12 @@ function nullCheck(path, callback) { var er = new Error('Path must be a string without null bytes.'); if (!callback) throw er; - process.nextTick(function() { - callback(er); - }); + if (callback.reject) + callback.reject(er); + else + process.nextTick(function() { + callback(er); + }); return false; } return true; @@ -780,12 +783,32 @@ fs.lstat = function(path, callback) { binding.lstat(pathModule._makeLong(path), req); }; -fs.stat = function(path, callback) { - callback = makeCallback(callback); - if (!nullCheck(path, callback)) return; +function makeOnComplete(cb) { + return function oncomplete() { + return cb.apply(null, arguments); + }; +} + +function Defer(callback) { var req = new FSReqWrap(); - req.oncomplete = callback; + if (util.isFunction(callback)) { + req.oncomplete = makeOnComplete(callback); + // Below causes stat/test to reduce from >58K to < 50K, why? + // req.oncomplete = function() { return callback.apply(null, arguments); }; + return req; + } + req.promise = new Promise(function(resolve, reject) { + req.resolve = resolve; + req.reject = reject; + }); + return req; +} + +fs.stat = function(path, callback) { + var req = Defer(callback); + if (!nullCheck(path, req)) return req.promise; binding.stat(pathModule._makeLong(path), req); + return req.promise; }; fs.fstatSync = function(fd) { diff --git a/src/env.h b/src/env.h index 76f7284f1edfd5..7cbac4af3c4bb5 100644 --- a/src/env.h +++ b/src/env.h @@ -185,8 +185,10 @@ namespace node { V(received_shutdown_string, "receivedShutdown") \ V(refresh_string, "refresh") \ V(regexp_string, "regexp") \ + V(reject_string, "reject") \ V(rename_string, "rename") \ V(replacement_string, "replacement") \ + V(resolve_string, "resolve") \ V(retry_string, "retry") \ V(rss_string, "rss") \ V(serial_string, "serial") \ diff --git a/src/node_file.cc b/src/node_file.cc index 1077929fcf47aa..fa52da6dac8ca4 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -247,7 +247,7 @@ static void After(uv_fs_t *req) { } } - req_wrap->MakeCallback(env->oncomplete_string(), argc, argv); + req_wrap->Fulfill(argc, argv); uv_fs_req_cleanup(&req_wrap->req_); delete req_wrap; diff --git a/src/req_wrap.h b/src/req_wrap.h index 22263fac7c40cb..d314e1cef6c0c6 100644 --- a/src/req_wrap.h +++ b/src/req_wrap.h @@ -53,6 +53,24 @@ class ReqWrap : public AsyncWrap { persistent().Reset(); } + inline v8::Handle Fulfill(int argc, v8::Handle* argv) { + // if (obj.oncomplete) .oncomplete(argv) + v8::Local cb_v = object()->Get(env()->oncomplete_string()); + if (cb_v->IsFunction()) { + return MakeCallback(cb_v.As(), argc, argv); + } + // else + // if (argv[0]) obj.reject(argv[0]) + // else obj.resolve(argv[1...]) + if (argc == 0) + return MakeCallback(env()->resolve_string(), 0, NULL); + + if (argv[0]->ToBoolean()->IsTrue()) // if(err) { + return MakeCallback(env()->reject_string(), 1, argv); + + return MakeCallback(env()->resolve_string(), argc-1, argv+1); + } + // Call this after the req has been dispatched. void Dispatched() { req_.data = this;