Skip to content

Commit

Permalink
Fix problems with graceful-fs bug
Browse files Browse the repository at this point in the history
Summary:
public
This fixes the problem that graceful-fs exposes an unpatched `close()` method, that will never trigger retries.
This behavior deadlocks the packager when trying to use graceful-fs’s `open()`/`read()`/`close()` methods.

See: isaacs/node-graceful-fs#56

Reviewed By: cpojer

Differential Revision: D2885512

fb-gh-sync-id: 71112d2488929bf1775fe9f8566fa03fd66b6bea
  • Loading branch information
davidaurelio authored and facebook-github-bot-7 committed Feb 1, 2016
1 parent f618756 commit f04882f
Showing 1 changed file with 61 additions and 35 deletions.
96 changes: 61 additions & 35 deletions packager/react-packager/src/DependencyResolver/fastfs.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ const {EventEmitter} = require('events');
const fs = require('graceful-fs');
const path = require('path');

const open = Promise.denodeify(fs.open);
// workaround for https://github.com/isaacs/node-graceful-fs/issues/56
// fs.close is patched, whereas graceful-fs.close is not.
const fsClose = require('fs').close;

const readFile = Promise.denodeify(fs.readFile);
const stat = Promise.denodeify(fs.stat);

Expand Down Expand Up @@ -251,41 +254,12 @@ class File {
}

readWhile(predicate) {
const CHUNK_SIZE = 512;
let result = '';

return open(this.path, 'r').then(fd => {
/* global Buffer: true */
const buffer = new Buffer(CHUNK_SIZE);
const p = new Promise((resolve, reject) => {
let counter = 0;
const callback = (error, bytesRead) => {
if (error) {
reject();
return;
}

const chunk = buffer.toString('utf8', 0, bytesRead);
result += chunk;
if (bytesRead > 0 && predicate(chunk, counter++, result)) {
readChunk(fd, buffer, callback);
} else {
if (bytesRead === 0 && !this._read) { // reached EOF
this._read = Promise.resolve(result);
}
resolve(result);
}
};
readChunk(fd, buffer, callback);
});

p.catch(() => fs.close(fd));
return p;
return readWhile(this.path, predicate).then(({result, completed}) => {
if (completed && !this._read) {
this._read = Promise.resolve(result);
}
return result;
});

function readChunk(fd, buffer, callback) {
fs.read(fd, buffer, 0, CHUNK_SIZE, null, callback);
}
}

stat() {
Expand Down Expand Up @@ -365,6 +339,58 @@ class File {
}
}

function readWhile(filePath, predicate) {
return new Promise((resolve, reject) => {
fs.open(filePath, 'r', (openError, fd) => {
if (openError) {
reject(openError);
return;
}

read(
fd,
/*global Buffer: true*/
new Buffer(512),
makeReadCallback(fd, predicate, (readError, result, completed) => {
if (readError) {
reject(readError);
} else {
resolve({result, completed});
}
})
);
});
});
}

function read(fd, buffer, callback) {
fs.read(fd, buffer, 0, buffer.length, -1, callback);
}

function close(fd, error, result, complete, callback) {
fsClose(fd, closeError => callback(error || closeError, result, complete));
}

function makeReadCallback(fd, predicate, callback) {
let result = '';
let index = 0;
return function readCallback(error, bytesRead, buffer) {
if (error) {
close(fd, error, undefined, false, callback);
return;
}

const completed = bytesRead === 0;
const chunk = completed ? '' : buffer.toString('utf8', 0, bytesRead);
result += chunk;
if (completed || !predicate(chunk, index++, result)) {
close(fd, null, result, completed, callback);
} else {
read(fd, buffer, readCallback);
}
};
}

function isDescendant(root, child) {
return path.relative(root, child).indexOf('..') !== 0;
}
Expand Down

0 comments on commit f04882f

Please sign in to comment.