| // Monkey-patching the fs module. |
| // It's ugly, but there is simply no other way to do this. |
| var fs = module.exports = require('fs') |
| |
| var assert = require('assert') |
| |
| // fix up some busted stuff, mostly on windows and old nodes |
| require('./polyfills.js') |
| |
| // The EMFILE enqueuing stuff |
| |
| var util = require('util') |
| |
| function noop () {} |
| |
| var debug = noop |
| var util = require('util') |
| if (util.debuglog) |
| debug = util.debuglog('gfs') |
| else if (/\bgfs\b/i.test(process.env.NODE_DEBUG || '')) |
| debug = function() { |
| var m = util.format.apply(util, arguments) |
| m = 'GFS: ' + m.split(/\n/).join('\nGFS: ') |
| console.error(m) |
| } |
| |
| if (/\bgfs\b/i.test(process.env.NODE_DEBUG || '')) { |
| process.on('exit', function() { |
| debug('fds', fds) |
| debug(queue) |
| assert.equal(queue.length, 0) |
| }) |
| } |
| |
| |
| var originalOpen = fs.open |
| fs.open = open |
| |
| function open(path, flags, mode, cb) { |
| if (typeof mode === "function") cb = mode, mode = null |
| if (typeof cb !== "function") cb = noop |
| new OpenReq(path, flags, mode, cb) |
| } |
| |
| function OpenReq(path, flags, mode, cb) { |
| this.path = path |
| this.flags = flags |
| this.mode = mode |
| this.cb = cb |
| Req.call(this) |
| } |
| |
| util.inherits(OpenReq, Req) |
| |
| OpenReq.prototype.process = function() { |
| originalOpen.call(fs, this.path, this.flags, this.mode, this.done) |
| } |
| |
| var fds = {} |
| OpenReq.prototype.done = function(er, fd) { |
| debug('open done', er, fd) |
| if (fd) |
| fds['fd' + fd] = this.path |
| Req.prototype.done.call(this, er, fd) |
| } |
| |
| |
| var originalReaddir = fs.readdir |
| fs.readdir = readdir |
| |
| function readdir(path, cb) { |
| if (typeof cb !== "function") cb = noop |
| new ReaddirReq(path, cb) |
| } |
| |
| function ReaddirReq(path, cb) { |
| this.path = path |
| this.cb = cb |
| Req.call(this) |
| } |
| |
| util.inherits(ReaddirReq, Req) |
| |
| ReaddirReq.prototype.process = function() { |
| originalReaddir.call(fs, this.path, this.done) |
| } |
| |
| ReaddirReq.prototype.done = function(er, files) { |
| Req.prototype.done.call(this, er, files) |
| onclose() |
| } |
| |
| |
| var originalClose = fs.close |
| fs.close = close |
| |
| function close (fd, cb) { |
| debug('close', fd) |
| if (typeof cb !== "function") cb = noop |
| delete fds['fd' + fd] |
| originalClose.call(fs, fd, function(er) { |
| onclose() |
| cb(er) |
| }) |
| } |
| |
| |
| var originalCloseSync = fs.closeSync |
| fs.closeSync = closeSync |
| |
| function closeSync (fd) { |
| try { |
| return originalCloseSync(fd) |
| } finally { |
| onclose() |
| } |
| } |
| |
| |
| // Req class |
| function Req () { |
| // start processing |
| this.done = this.done.bind(this) |
| this.failures = 0 |
| this.process() |
| } |
| |
| Req.prototype.done = function (er, result) { |
| var tryAgain = false |
| if (er) { |
| var code = er.code |
| var tryAgain = code === "EMFILE" |
| if (process.platform === "win32") |
| tryAgain = tryAgain || code === "OK" |
| } |
| |
| if (tryAgain) { |
| this.failures ++ |
| enqueue(this) |
| } else { |
| var cb = this.cb |
| cb(er, result) |
| } |
| } |
| |
| var queue = [] |
| |
| function enqueue(req) { |
| queue.push(req) |
| debug('enqueue %d %s', queue.length, req.constructor.name, req) |
| } |
| |
| function onclose() { |
| var req = queue.shift() |
| if (req) { |
| debug('process', req.constructor.name, req) |
| req.process() |
| } |
| } |