Fix #258 - Queued fs calls hang when fs goes into error state due to provider.open error

This commit is contained in:
David Humphrey (:humph) david.humphrey@senecacollege.ca 2014-08-18 14:08:54 -04:00
parent 3e9b77a37a
commit 84112410ba
3 changed files with 98 additions and 2 deletions

View File

@ -223,8 +223,8 @@ function FileSystem(options, callback) {
fs.readyState = FS_ERROR;
} else {
fs.readyState = FS_READY;
runQueued();
}
runQueued();
callback(error, fs);
}
@ -301,6 +301,13 @@ FileSystem.providers = providers;
var error = fs.queueOrRun(function() {
var context = fs.provider.openReadWriteContext();
// Fail early if the filesystem is in an error state (e.g.,
// provider failed to open.
if(FS_ERROR === fs.readyState) {
var err = new Errors.EFILESYSTEMERROR('filesystem unavailable, operation canceled');
return callback.call(fs, err);
}
// Wrap the callback so we can explicitly close the context
function complete() {
context.close();

89
tests/bugs/issue258.js Normal file
View File

@ -0,0 +1,89 @@
var Filer = require('../..');
var util = require('../lib/test-utils.js');
var expect = require('chai').expect;
var setImmediate = require('../../lib/async.js').setImmediate;
describe('Queued operations should error when fs is in error state, issue 258', function() {
var provider;
// Provider that does nothing but fail on open.
function FailingProviderContext(){}
FailingProviderContext.prototype.clear = function(callback) {
this.failCallback(callback);
};
FailingProviderContext.prototype.getObject =
FailingProviderContext.prototype.getBuffer = function(key, callback) {
this.failCallback(callback);
};
FailingProviderContext.prototype.putObject =
FailingProviderContext.prototype.putBuffer = function(key, value, callback) {
this.failCallback(callback);
};
FailingProviderContext.prototype.delete = function(key, callback) {
this.failCallback(callback);
};
function FailingProvider() {
var self = this;
self.name = 'failure';
self.open = function(callback) {
// Wait until caller tells us to fail
self.failNow = function() {
self.failCallback(callback);
};
};
self.failCallback = function(callback) {
setImmediate(function() {
callback(new Error);
});
};
}
FailingProvider.prototype.getReadWriteContext =
FailingProvider.prototype.getReadWriteContext = function() {
return new FailingProviderContext();
};
beforeEach(function() {
provider = new FailingProvider();
});
afterEach(function() {
provider = null;
});
it('should get EFILESYSTEMERROR errors on callbacks to queued operations on provider error', function(done) {
var errCount = 0;
var fs = new Filer.FileSystem({provider: provider});
function maybeDone(err) {
expect(err).to.exist;
expect(err.code).to.equal('EFILESYSTEMERROR');
errCount++;
if(errCount === 2) {
done();
}
}
// Queue some fs operations, and expect them to fail
fs.mkdir('/tmp', maybeDone);
fs.writeFile('/file', 'data', maybeDone);
// Operations are queued, tell the provider to fail now.
provider.failNow();
});
it('should get EFILESYSTEMERROR errors on callbacks to queued operations after ready callback', function(done) {
var fs = new Filer.FileSystem({provider: provider}, function(err) {
expect(err).to.exist;
// Queue is drained, but new operations should also fail
fs.mkdir('/tmp', function(err) {
expect(err).to.exist;
expect(err.code).to.equal('EFILESYSTEMERROR');
done();
});
});
provider.failNow();
});
});

View File

@ -72,4 +72,4 @@ require("./bugs/issue249");
require("./bugs/ls-depth-bug");
require("./bugs/issue247.js");
require("./bugs/issue254.js");
require("./bugs/issue258.js");