Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

graceful-fs.js 8.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. var fs = require('fs')
  2. var polyfills = require('./polyfills.js')
  3. var legacy = require('./legacy-streams.js')
  4. var clone = require('./clone.js')
  5. var util = require('util')
  6. /* istanbul ignore next - node 0.x polyfill */
  7. var gracefulQueue
  8. var previousSymbol
  9. /* istanbul ignore else - node 0.x polyfill */
  10. if (typeof Symbol === 'function' && typeof Symbol.for === 'function') {
  11. gracefulQueue = Symbol.for('graceful-fs.queue')
  12. // This is used in testing by future versions
  13. previousSymbol = Symbol.for('graceful-fs.previous')
  14. } else {
  15. gracefulQueue = '___graceful-fs.queue'
  16. previousSymbol = '___graceful-fs.previous'
  17. }
  18. function noop () {}
  19. var debug = noop
  20. if (util.debuglog)
  21. debug = util.debuglog('gfs4')
  22. else if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || ''))
  23. debug = function() {
  24. var m = util.format.apply(util, arguments)
  25. m = 'GFS4: ' + m.split(/\n/).join('\nGFS4: ')
  26. console.error(m)
  27. }
  28. // Once time initialization
  29. if (!global[gracefulQueue]) {
  30. // This queue can be shared by multiple loaded instances
  31. var queue = []
  32. Object.defineProperty(global, gracefulQueue, {
  33. get: function() {
  34. return queue
  35. }
  36. })
  37. // Patch fs.close/closeSync to shared queue version, because we need
  38. // to retry() whenever a close happens *anywhere* in the program.
  39. // This is essential when multiple graceful-fs instances are
  40. // in play at the same time.
  41. fs.close = (function (fs$close) {
  42. function close (fd, cb) {
  43. return fs$close.call(fs, fd, function (err) {
  44. // This function uses the graceful-fs shared queue
  45. if (!err) {
  46. retry()
  47. }
  48. if (typeof cb === 'function')
  49. cb.apply(this, arguments)
  50. })
  51. }
  52. Object.defineProperty(close, previousSymbol, {
  53. value: fs$close
  54. })
  55. return close
  56. })(fs.close)
  57. fs.closeSync = (function (fs$closeSync) {
  58. function closeSync (fd) {
  59. // This function uses the graceful-fs shared queue
  60. fs$closeSync.apply(fs, arguments)
  61. retry()
  62. }
  63. Object.defineProperty(closeSync, previousSymbol, {
  64. value: fs$closeSync
  65. })
  66. return closeSync
  67. })(fs.closeSync)
  68. if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || '')) {
  69. process.on('exit', function() {
  70. debug(global[gracefulQueue])
  71. require('assert').equal(global[gracefulQueue].length, 0)
  72. })
  73. }
  74. }
  75. module.exports = patch(clone(fs))
  76. if (process.env.TEST_GRACEFUL_FS_GLOBAL_PATCH && !fs.__patched) {
  77. module.exports = patch(fs)
  78. fs.__patched = true;
  79. }
  80. function patch (fs) {
  81. // Everything that references the open() function needs to be in here
  82. polyfills(fs)
  83. fs.gracefulify = patch
  84. fs.createReadStream = createReadStream
  85. fs.createWriteStream = createWriteStream
  86. var fs$readFile = fs.readFile
  87. fs.readFile = readFile
  88. function readFile (path, options, cb) {
  89. if (typeof options === 'function')
  90. cb = options, options = null
  91. return go$readFile(path, options, cb)
  92. function go$readFile (path, options, cb) {
  93. return fs$readFile(path, options, function (err) {
  94. if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
  95. enqueue([go$readFile, [path, options, cb]])
  96. else {
  97. if (typeof cb === 'function')
  98. cb.apply(this, arguments)
  99. retry()
  100. }
  101. })
  102. }
  103. }
  104. var fs$writeFile = fs.writeFile
  105. fs.writeFile = writeFile
  106. function writeFile (path, data, options, cb) {
  107. if (typeof options === 'function')
  108. cb = options, options = null
  109. return go$writeFile(path, data, options, cb)
  110. function go$writeFile (path, data, options, cb) {
  111. return fs$writeFile(path, data, options, function (err) {
  112. if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
  113. enqueue([go$writeFile, [path, data, options, cb]])
  114. else {
  115. if (typeof cb === 'function')
  116. cb.apply(this, arguments)
  117. retry()
  118. }
  119. })
  120. }
  121. }
  122. var fs$appendFile = fs.appendFile
  123. if (fs$appendFile)
  124. fs.appendFile = appendFile
  125. function appendFile (path, data, options, cb) {
  126. if (typeof options === 'function')
  127. cb = options, options = null
  128. return go$appendFile(path, data, options, cb)
  129. function go$appendFile (path, data, options, cb) {
  130. return fs$appendFile(path, data, options, function (err) {
  131. if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
  132. enqueue([go$appendFile, [path, data, options, cb]])
  133. else {
  134. if (typeof cb === 'function')
  135. cb.apply(this, arguments)
  136. retry()
  137. }
  138. })
  139. }
  140. }
  141. var fs$readdir = fs.readdir
  142. fs.readdir = readdir
  143. function readdir (path, options, cb) {
  144. var args = [path]
  145. if (typeof options !== 'function') {
  146. args.push(options)
  147. } else {
  148. cb = options
  149. }
  150. args.push(go$readdir$cb)
  151. return go$readdir(args)
  152. function go$readdir$cb (err, files) {
  153. if (files && files.sort)
  154. files.sort()
  155. if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
  156. enqueue([go$readdir, [args]])
  157. else {
  158. if (typeof cb === 'function')
  159. cb.apply(this, arguments)
  160. retry()
  161. }
  162. }
  163. }
  164. function go$readdir (args) {
  165. return fs$readdir.apply(fs, args)
  166. }
  167. if (process.version.substr(0, 4) === 'v0.8') {
  168. var legStreams = legacy(fs)
  169. ReadStream = legStreams.ReadStream
  170. WriteStream = legStreams.WriteStream
  171. }
  172. var fs$ReadStream = fs.ReadStream
  173. if (fs$ReadStream) {
  174. ReadStream.prototype = Object.create(fs$ReadStream.prototype)
  175. ReadStream.prototype.open = ReadStream$open
  176. }
  177. var fs$WriteStream = fs.WriteStream
  178. if (fs$WriteStream) {
  179. WriteStream.prototype = Object.create(fs$WriteStream.prototype)
  180. WriteStream.prototype.open = WriteStream$open
  181. }
  182. Object.defineProperty(fs, 'ReadStream', {
  183. get: function () {
  184. return ReadStream
  185. },
  186. set: function (val) {
  187. ReadStream = val
  188. },
  189. enumerable: true,
  190. configurable: true
  191. })
  192. Object.defineProperty(fs, 'WriteStream', {
  193. get: function () {
  194. return WriteStream
  195. },
  196. set: function (val) {
  197. WriteStream = val
  198. },
  199. enumerable: true,
  200. configurable: true
  201. })
  202. // legacy names
  203. Object.defineProperty(fs, 'FileReadStream', {
  204. get: function () {
  205. return ReadStream
  206. },
  207. set: function (val) {
  208. ReadStream = val
  209. },
  210. enumerable: true,
  211. configurable: true
  212. })
  213. Object.defineProperty(fs, 'FileWriteStream', {
  214. get: function () {
  215. return WriteStream
  216. },
  217. set: function (val) {
  218. WriteStream = val
  219. },
  220. enumerable: true,
  221. configurable: true
  222. })
  223. function ReadStream (path, options) {
  224. if (this instanceof ReadStream)
  225. return fs$ReadStream.apply(this, arguments), this
  226. else
  227. return ReadStream.apply(Object.create(ReadStream.prototype), arguments)
  228. }
  229. function ReadStream$open () {
  230. var that = this
  231. open(that.path, that.flags, that.mode, function (err, fd) {
  232. if (err) {
  233. if (that.autoClose)
  234. that.destroy()
  235. that.emit('error', err)
  236. } else {
  237. that.fd = fd
  238. that.emit('open', fd)
  239. that.read()
  240. }
  241. })
  242. }
  243. function WriteStream (path, options) {
  244. if (this instanceof WriteStream)
  245. return fs$WriteStream.apply(this, arguments), this
  246. else
  247. return WriteStream.apply(Object.create(WriteStream.prototype), arguments)
  248. }
  249. function WriteStream$open () {
  250. var that = this
  251. open(that.path, that.flags, that.mode, function (err, fd) {
  252. if (err) {
  253. that.destroy()
  254. that.emit('error', err)
  255. } else {
  256. that.fd = fd
  257. that.emit('open', fd)
  258. }
  259. })
  260. }
  261. function createReadStream (path, options) {
  262. return new fs.ReadStream(path, options)
  263. }
  264. function createWriteStream (path, options) {
  265. return new fs.WriteStream(path, options)
  266. }
  267. var fs$open = fs.open
  268. fs.open = open
  269. function open (path, flags, mode, cb) {
  270. if (typeof mode === 'function')
  271. cb = mode, mode = null
  272. return go$open(path, flags, mode, cb)
  273. function go$open (path, flags, mode, cb) {
  274. return fs$open(path, flags, mode, function (err, fd) {
  275. if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
  276. enqueue([go$open, [path, flags, mode, cb]])
  277. else {
  278. if (typeof cb === 'function')
  279. cb.apply(this, arguments)
  280. retry()
  281. }
  282. })
  283. }
  284. }
  285. return fs
  286. }
  287. function enqueue (elem) {
  288. debug('ENQUEUE', elem[0].name, elem[1])
  289. global[gracefulQueue].push(elem)
  290. }
  291. function retry () {
  292. var elem = global[gracefulQueue].shift()
  293. if (elem) {
  294. debug('RETRY', elem[0].name, elem[1])
  295. elem[0].apply(null, elem[1])
  296. }
  297. }