Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions graceful-fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ function patch (fs) {

function go$readFile (path, options, cb, startTime) {
return fs$readFile(path, options, function (err) {
if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
if (err && (err.code === 'EMFILE' || err.code === 'ENFILE' || err.code === 'EAGAIN'))
enqueue([go$readFile, [path, options, cb], err, startTime || Date.now(), Date.now()])
else {
if (typeof cb === 'function')
Expand All @@ -136,7 +136,7 @@ function patch (fs) {

function go$writeFile (path, data, options, cb, startTime) {
return fs$writeFile(path, data, options, function (err) {
if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
if (err && (err.code === 'EMFILE' || err.code === 'ENFILE' || err.code === 'EAGAIN'))
enqueue([go$writeFile, [path, data, options, cb], err, startTime || Date.now(), Date.now()])
else {
if (typeof cb === 'function')
Expand All @@ -157,7 +157,7 @@ function patch (fs) {

function go$appendFile (path, data, options, cb, startTime) {
return fs$appendFile(path, data, options, function (err) {
if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
if (err && (err.code === 'EMFILE' || err.code === 'ENFILE' || err.code === 'EAGAIN'))
enqueue([go$appendFile, [path, data, options, cb], err, startTime || Date.now(), Date.now()])
else {
if (typeof cb === 'function')
Expand All @@ -179,7 +179,7 @@ function patch (fs) {

function go$copyFile (src, dest, flags, cb, startTime) {
return fs$copyFile(src, dest, flags, function (err) {
if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
if (err && (err.code === 'EMFILE' || err.code === 'ENFILE' || err.code === 'EAGAIN'))
enqueue([go$copyFile, [src, dest, flags, cb], err, startTime || Date.now(), Date.now()])
else {
if (typeof cb === 'function')
Expand Down Expand Up @@ -212,7 +212,7 @@ function patch (fs) {

function fs$readdirCallback (path, options, cb, startTime) {
return function (err, files) {
if (err && (err.code === 'EMFILE' || err.code === 'ENFILE'))
if (err && (err.code === 'EMFILE' || err.code === 'ENFILE' || err.code === 'EAGAIN'))
enqueue([
go$readdir,
[path, options, cb],
Expand Down Expand Up @@ -355,7 +355,7 @@ function patch (fs) {

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'))
if (err && (err.code === 'EMFILE' || err.code === 'ENFILE' || err.code === 'EAGAIN'))
enqueue([go$open, [path, flags, mode, cb], err, startTime || Date.now(), Date.now()])
else {
if (typeof cb === 'function')
Expand Down
118 changes: 118 additions & 0 deletions test/eagain.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
'use strict'

var importFresh = require('import-fresh')
var path = require('path')
var realFs = require('fs')
var test = require('tap').test

var EAGAIN = Object.assign(new Error('FAKE EAGAIN'), { code: 'EAGAIN' })

test('EAGAIN readFile is retried and eventually succeeds', function (t) {
var readFile = realFs.readFile
var eagainCount = 0

t.teardown(function () {
realFs.readFile = readFile
})

realFs.readFile = function (path, options, cb) {
process.nextTick(function () {
if (eagainCount < 3) {
eagainCount++
return cb(EAGAIN)
}
// succeed after 3 retries
cb(null, Buffer.from('test content'))
})
}

var fs = importFresh(path.dirname(__dirname))
fs.readFile('literally anything', function (err, data) {
t.notOk(err, 'should not get an error')
t.equal(eagainCount, 3, 'should have retried 3 times')
t.equal(data.toString(), 'test content', 'should get the content')
t.end()
})
})

test('EAGAIN eventually times out and returns error', function (t) {
var readFile = realFs.readFile
var realNow = Date.now

t.teardown(function () {
realFs.readFile = readFile
Date.now = realNow
})

realFs.readFile = function (path, options, cb) {
process.nextTick(function () {
cb(EAGAIN)
// hijack Date.now _after_ we call the callback, the callback will
// call it when adding the job to the queue, we want to capture it
// any time after that first call so we can pretend it's been 60s
Date.now = function () {
return realNow() + 60000
}
})
}

var fs = importFresh(path.dirname(__dirname))
fs.readFile('literally anything', function (err) {
t.equal(err.code, 'EAGAIN', 'eventually got the EAGAIN')
t.end()
})
})

test('EAGAIN open is retried and eventually succeeds', function (t) {
var open = realFs.open
var eagainCount = 0

t.teardown(function () {
realFs.open = open
})

realFs.open = function (path, flags, mode, cb) {
process.nextTick(function () {
if (eagainCount < 2) {
eagainCount++
return cb(EAGAIN)
}
// succeed after 2 retries with a fake fd
cb(null, 42)
})
}

var fs = importFresh(path.dirname(__dirname))
fs.open('literally anything', 'r', function (err, fd) {
t.notOk(err, 'should not get an error')
t.equal(eagainCount, 2, 'should have retried 2 times')
t.equal(fd, 42, 'should get the fd')
t.end()
})
})

test('EAGAIN writeFile is retried and eventually succeeds', function (t) {
var writeFile = realFs.writeFile
var eagainCount = 0

t.teardown(function () {
realFs.writeFile = writeFile
})

realFs.writeFile = function (path, data, options, cb) {
process.nextTick(function () {
if (eagainCount < 2) {
eagainCount++
return cb(EAGAIN)
}
cb(null)
})
}

var fs = importFresh(path.dirname(__dirname))
fs.writeFile('literally anything', 'data', function (err) {
t.notOk(err, 'should not get an error')
t.equal(eagainCount, 2, 'should have retried 2 times')
t.end()
})
})