| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448 | var fs = require('fs')var polyfills = require('./polyfills.js')var legacy = require('./legacy-streams.js')var clone = require('./clone.js')var util = require('util')/* istanbul ignore next - node 0.x polyfill */var gracefulQueuevar previousSymbol/* istanbul ignore else - node 0.x polyfill */if (typeof Symbol === 'function' && typeof Symbol.for === 'function') {  gracefulQueue = Symbol.for('graceful-fs.queue')  // This is used in testing by future versions  previousSymbol = Symbol.for('graceful-fs.previous')} else {  gracefulQueue = '___graceful-fs.queue'  previousSymbol = '___graceful-fs.previous'}function noop () {}function publishQueue(context, queue) {  Object.defineProperty(context, gracefulQueue, {    get: function() {      return queue    }  })}var debug = noopif (util.debuglog)  debug = util.debuglog('gfs4')else if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || ''))  debug = function() {    var m = util.format.apply(util, arguments)    m = 'GFS4: ' + m.split(/\n/).join('\nGFS4: ')    console.error(m)  }// Once time initializationif (!fs[gracefulQueue]) {  // This queue can be shared by multiple loaded instances  var queue = global[gracefulQueue] || []  publishQueue(fs, queue)  // Patch fs.close/closeSync to shared queue version, because we need  // to retry() whenever a close happens *anywhere* in the program.  // This is essential when multiple graceful-fs instances are  // in play at the same time.  fs.close = (function (fs$close) {    function close (fd, cb) {      return fs$close.call(fs, fd, function (err) {        // This function uses the graceful-fs shared queue        if (!err) {          resetQueue()        }        if (typeof cb === 'function')          cb.apply(this, arguments)      })    }    Object.defineProperty(close, previousSymbol, {      value: fs$close    })    return close  })(fs.close)  fs.closeSync = (function (fs$closeSync) {    function closeSync (fd) {      // This function uses the graceful-fs shared queue      fs$closeSync.apply(fs, arguments)      resetQueue()    }    Object.defineProperty(closeSync, previousSymbol, {      value: fs$closeSync    })    return closeSync  })(fs.closeSync)  if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || '')) {    process.on('exit', function() {      debug(fs[gracefulQueue])      require('assert').equal(fs[gracefulQueue].length, 0)    })  }}if (!global[gracefulQueue]) {  publishQueue(global, fs[gracefulQueue]);}module.exports = patch(clone(fs))if (process.env.TEST_GRACEFUL_FS_GLOBAL_PATCH && !fs.__patched) {    module.exports = patch(fs)    fs.__patched = true;}function patch (fs) {  // Everything that references the open() function needs to be in here  polyfills(fs)  fs.gracefulify = patch  fs.createReadStream = createReadStream  fs.createWriteStream = createWriteStream  var fs$readFile = fs.readFile  fs.readFile = readFile  function readFile (path, options, cb) {    if (typeof options === 'function')      cb = options, options = null    return go$readFile(path, options, cb)    function go$readFile (path, options, cb, startTime) {      return fs$readFile(path, options, function (err) {        if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))          enqueue([go$readFile, [path, options, cb], err, startTime || Date.now(), Date.now()])        else {          if (typeof cb === 'function')            cb.apply(this, arguments)        }      })    }  }  var fs$writeFile = fs.writeFile  fs.writeFile = writeFile  function writeFile (path, data, options, cb) {    if (typeof options === 'function')      cb = options, options = null    return go$writeFile(path, data, options, cb)    function go$writeFile (path, data, options, cb, startTime) {      return fs$writeFile(path, data, options, function (err) {        if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))          enqueue([go$writeFile, [path, data, options, cb], err, startTime || Date.now(), Date.now()])        else {          if (typeof cb === 'function')            cb.apply(this, arguments)        }      })    }  }  var fs$appendFile = fs.appendFile  if (fs$appendFile)    fs.appendFile = appendFile  function appendFile (path, data, options, cb) {    if (typeof options === 'function')      cb = options, options = null    return go$appendFile(path, data, options, cb)    function go$appendFile (path, data, options, cb, startTime) {      return fs$appendFile(path, data, options, function (err) {        if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))          enqueue([go$appendFile, [path, data, options, cb], err, startTime || Date.now(), Date.now()])        else {          if (typeof cb === 'function')            cb.apply(this, arguments)        }      })    }  }  var fs$copyFile = fs.copyFile  if (fs$copyFile)    fs.copyFile = copyFile  function copyFile (src, dest, flags, cb) {    if (typeof flags === 'function') {      cb = flags      flags = 0    }    return go$copyFile(src, dest, flags, cb)    function go$copyFile (src, dest, flags, cb, startTime) {      return fs$copyFile(src, dest, flags, function (err) {        if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))          enqueue([go$copyFile, [src, dest, flags, cb], err, startTime || Date.now(), Date.now()])        else {          if (typeof cb === 'function')            cb.apply(this, arguments)        }      })    }  }  var fs$readdir = fs.readdir  fs.readdir = readdir  var noReaddirOptionVersions = /^v[0-5]\./  function readdir (path, options, cb) {    if (typeof options === 'function')      cb = options, options = null    var go$readdir = noReaddirOptionVersions.test(process.version)      ? function go$readdir (path, options, cb, startTime) {        return fs$readdir(path, fs$readdirCallback(          path, options, cb, startTime        ))      }      : function go$readdir (path, options, cb, startTime) {        return fs$readdir(path, options, fs$readdirCallback(          path, options, cb, startTime        ))      }    return go$readdir(path, options, cb)    function fs$readdirCallback (path, options, cb, startTime) {      return function (err, files) {        if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))          enqueue([            go$readdir,            [path, options, cb],            err,            startTime || Date.now(),            Date.now()          ])        else {          if (files && files.sort)            files.sort()          if (typeof cb === 'function')            cb.call(this, err, files)        }      }    }  }  if (process.version.substr(0, 4) === 'v0.8') {    var legStreams = legacy(fs)    ReadStream = legStreams.ReadStream    WriteStream = legStreams.WriteStream  }  var fs$ReadStream = fs.ReadStream  if (fs$ReadStream) {    ReadStream.prototype = Object.create(fs$ReadStream.prototype)    ReadStream.prototype.open = ReadStream$open  }  var fs$WriteStream = fs.WriteStream  if (fs$WriteStream) {    WriteStream.prototype = Object.create(fs$WriteStream.prototype)    WriteStream.prototype.open = WriteStream$open  }  Object.defineProperty(fs, 'ReadStream', {    get: function () {      return ReadStream    },    set: function (val) {      ReadStream = val    },    enumerable: true,    configurable: true  })  Object.defineProperty(fs, 'WriteStream', {    get: function () {      return WriteStream    },    set: function (val) {      WriteStream = val    },    enumerable: true,    configurable: true  })  // legacy names  var FileReadStream = ReadStream  Object.defineProperty(fs, 'FileReadStream', {    get: function () {      return FileReadStream    },    set: function (val) {      FileReadStream = val    },    enumerable: true,    configurable: true  })  var FileWriteStream = WriteStream  Object.defineProperty(fs, 'FileWriteStream', {    get: function () {      return FileWriteStream    },    set: function (val) {      FileWriteStream = val    },    enumerable: true,    configurable: true  })  function ReadStream (path, options) {    if (this instanceof ReadStream)      return fs$ReadStream.apply(this, arguments), this    else      return ReadStream.apply(Object.create(ReadStream.prototype), arguments)  }  function ReadStream$open () {    var that = this    open(that.path, that.flags, that.mode, function (err, fd) {      if (err) {        if (that.autoClose)          that.destroy()        that.emit('error', err)      } else {        that.fd = fd        that.emit('open', fd)        that.read()      }    })  }  function WriteStream (path, options) {    if (this instanceof WriteStream)      return fs$WriteStream.apply(this, arguments), this    else      return WriteStream.apply(Object.create(WriteStream.prototype), arguments)  }  function WriteStream$open () {    var that = this    open(that.path, that.flags, that.mode, function (err, fd) {      if (err) {        that.destroy()        that.emit('error', err)      } else {        that.fd = fd        that.emit('open', fd)      }    })  }  function createReadStream (path, options) {    return new fs.ReadStream(path, options)  }  function createWriteStream (path, options) {    return new fs.WriteStream(path, options)  }  var fs$open = fs.open  fs.open = open  function open (path, flags, mode, cb) {    if (typeof mode === 'function')      cb = mode, mode = null    return go$open(path, flags, mode, cb)    function go$open (path, flags, mode, cb, startTime) {      return fs$open(path, flags, mode, function (err, fd) {        if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))          enqueue([go$open, [path, flags, mode, cb], err, startTime || Date.now(), Date.now()])        else {          if (typeof cb === 'function')            cb.apply(this, arguments)        }      })    }  }  return fs}function enqueue (elem) {  debug('ENQUEUE', elem[0].name, elem[1])  fs[gracefulQueue].push(elem)  retry()}// keep track of the timeout between retry() callsvar retryTimer// reset the startTime and lastTime to now// this resets the start of the 60 second overall timeout as well as the// delay between attempts so that we'll retry these jobs soonerfunction resetQueue () {  var now = Date.now()  for (var i = 0; i < fs[gracefulQueue].length; ++i) {    // entries that are only a length of 2 are from an older version, don't    // bother modifying those since they'll be retried anyway.    if (fs[gracefulQueue][i].length > 2) {      fs[gracefulQueue][i][3] = now // startTime      fs[gracefulQueue][i][4] = now // lastTime    }  }  // call retry to make sure we're actively processing the queue  retry()}function retry () {  // clear the timer and remove it to help prevent unintended concurrency  clearTimeout(retryTimer)  retryTimer = undefined  if (fs[gracefulQueue].length === 0)    return  var elem = fs[gracefulQueue].shift()  var fn = elem[0]  var args = elem[1]  // these items may be unset if they were added by an older graceful-fs  var err = elem[2]  var startTime = elem[3]  var lastTime = elem[4]  // if we don't have a startTime we have no way of knowing if we've waited  // long enough, so go ahead and retry this item now  if (startTime === undefined) {    debug('RETRY', fn.name, args)    fn.apply(null, args)  } else if (Date.now() - startTime >= 60000) {    // it's been more than 60 seconds total, bail now    debug('TIMEOUT', fn.name, args)    var cb = args.pop()    if (typeof cb === 'function')      cb.call(null, err)  } else {    // the amount of time between the last attempt and right now    var sinceAttempt = Date.now() - lastTime    // the amount of time between when we first tried, and when we last tried    // rounded up to at least 1    var sinceStart = Math.max(lastTime - startTime, 1)    // backoff. wait longer than the total time we've been retrying, but only    // up to a maximum of 100ms    var desiredDelay = Math.min(sinceStart * 1.2, 100)    // it's been long enough since the last retry, do it again    if (sinceAttempt >= desiredDelay) {      debug('RETRY', fn.name, args)      fn.apply(null, args.concat([startTime]))    } else {      // if we can't do this job yet, push it to the end of the queue      // and let the next iteration check again      fs[gracefulQueue].push(elem)    }  }  // schedule our next run if one isn't already scheduled  if (retryTimer === undefined) {    retryTimer = setTimeout(retry, 0)  }}
 |