Skip to content

Commit e7604b1

Browse files
xkry
authored andcommitted
Retain buffers in fs.read/write()
Closes GH-814. Closes GH-827.
1 parent 2a65d29 commit e7604b1

5 files changed

Lines changed: 172 additions & 9 deletions

File tree

lib/fs.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,12 @@ fs.read = function(fd, buffer, offset, length, position, callback) {
240240
};
241241
}
242242

243-
binding.read(fd, buffer, offset, length, position, callback || noop);
243+
function wrapper(err, bytesRead) {
244+
// Retain a reference to buffer so that it can't be GC'ed too soon.
245+
callback && callback(err, bytesRead || 0, buffer);
246+
}
247+
248+
binding.read(fd, buffer, offset, length, position, wrapper);
244249
};
245250

246251
fs.readSync = function(fd, buffer, offset, length, position) {
@@ -285,7 +290,12 @@ fs.write = function(fd, buffer, offset, length, position, callback) {
285290
return;
286291
}
287292

288-
binding.write(fd, buffer, offset, length, position, callback || noop);
293+
function wrapper(err, written) {
294+
// Retain a reference to buffer so that it can't be GC'ed too soon.
295+
callback && callback(err, written || 0, buffer);
296+
}
297+
298+
binding.write(fd, buffer, offset, length, position, wrapper);
289299
};
290300

291301
fs.writeSync = function(fd, buffer, offset, length, position) {

src/node_file.cc

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -707,9 +707,6 @@ static Handle<Value> Write(const Arguments& args) {
707707
Local<Value> cb = args[5];
708708

709709
if (cb->IsFunction()) {
710-
// Grab a reference to buffer so it isn't GCed
711-
Local<Object> cb_obj = cb->ToObject();
712-
cb_obj->Set(buf_symbol, buffer_obj);
713710

714711
ASYNC_CALL(write, cb, fd, buf, len, pos)
715712
} else {
@@ -775,10 +772,6 @@ static Handle<Value> Read(const Arguments& args) {
775772
cb = args[5];
776773

777774
if (cb->IsFunction()) {
778-
// Grab a reference to buffer so it isn't GCed
779-
// TODO: need test coverage
780-
Local<Object> cb_obj = cb->ToObject();
781-
cb_obj->Set(buf_symbol, buffer_obj);
782775

783776
ASYNC_CALL(read, cb, fd, buf, len, pos);
784777
} else {

test/common.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ process.on('exit', function() {
6060
process,
6161
global];
6262

63+
if (global.gc) {
64+
knownGlobals.push(gc);
65+
}
66+
6367
if (global.DTRACE_HTTP_SERVER_RESPONSE) {
6468
knownGlobals.push(DTRACE_HTTP_SERVER_RESPONSE);
6569
knownGlobals.push(DTRACE_HTTP_SERVER_REQUEST);

test/pummel/test-regress-GH-814.js

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Flags: --expose_gc
2+
3+
function newBuffer(size, value) {
4+
var buffer = new Buffer(size);
5+
while (size--) {
6+
buffer[size] = value;
7+
}
8+
//buffer[buffer.length-2]= 0x0d;
9+
buffer[buffer.length - 1] = 0x0a;
10+
return buffer;
11+
}
12+
13+
14+
var common = require('../common');
15+
var fs = require('fs');
16+
var testFileName = require('path').join(common.tmpDir, 'GH-814_testFile.txt');
17+
var testFileFD = fs.openSync(testFileName, 'w');
18+
console.log(testFileName);
19+
20+
21+
22+
var kBufSize = 128 * 1024;
23+
var PASS = true;
24+
var neverWrittenBuffer = newBuffer(kBufSize, 0x2e); //0x2e === '.'
25+
var bufPool = [];
26+
27+
28+
29+
var tail = require('child_process').spawn('tail', ['-f', testFileName]);
30+
tail.stdout.on('data', tailCB);
31+
32+
function tailCB(data) {
33+
PASS = data.toString().indexOf('.') < 0;
34+
}
35+
36+
37+
38+
var timeToQuit = Date.now() + 8e3; //Test during no more than this seconds.
39+
(function main() {
40+
41+
if (PASS) {
42+
fs.write(testFileFD, newBuffer(kBufSize, 0x61), 0, kBufSize, -1, cb);
43+
gc();
44+
var nuBuf = new Buffer(kBufSize);
45+
neverWrittenBuffer.copy(nuBuf);
46+
if (bufPool.push(nuBuf) > 100) {
47+
bufPool.length = 0;
48+
}
49+
}
50+
else {
51+
throw Error("Buffer GC'ed test -> FAIL");
52+
}
53+
54+
if (Date.now() < timeToQuit) {
55+
process.nextTick(main);
56+
}
57+
else {
58+
tail.kill();
59+
console.log("Buffer GC'ed test -> PASS (OK)");
60+
}
61+
62+
})();
63+
64+
65+
function cb(err, written) {
66+
if (err) {
67+
throw err;
68+
}
69+
}
70+
71+
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Flags: --expose_gc
2+
3+
var common = require('../common');
4+
5+
var fs = require('fs');
6+
var testFileName = require('path').join(common.tmpDir, 'GH-814_test.txt');
7+
var testFD = fs.openSync(testFileName, 'w');
8+
console.error(testFileName + '\n');
9+
10+
11+
var tailProc = require('child_process').spawn('tail', ['-f', testFileName]);
12+
tailProc.stdout.on('data', tailCB);
13+
14+
function tailCB(data) {
15+
PASS = data.toString().indexOf('.') < 0;
16+
17+
if (PASS) {
18+
//console.error('i');
19+
} else {
20+
console.error('[FAIL]\n DATA -> ');
21+
console.error(data);
22+
console.error('\n');
23+
throw Error('Buffers GC test -> FAIL');
24+
}
25+
}
26+
27+
28+
var PASS = true;
29+
var bufPool = [];
30+
var kBufSize = 16 * 1024 * 1024;
31+
var neverWrittenBuffer = newBuffer(kBufSize, 0x2e); //0x2e === '.'
32+
33+
var timeToQuit = Date.now() + 5e3; //Test should last no more than this.
34+
writer();
35+
36+
function writer() {
37+
38+
if (PASS) {
39+
if (Date.now() > timeToQuit) {
40+
setTimeout(function() {
41+
process.kill(tailProc.pid);
42+
console.error('\nBuffers GC test -> PASS (OK)\n');
43+
}, 555);
44+
} else {
45+
fs.write(testFD, newBuffer(kBufSize, 0x61), 0, kBufSize, -1, writerCB);
46+
gc();
47+
gc();
48+
gc();
49+
gc();
50+
gc();
51+
gc();
52+
var nuBuf = new Buffer(kBufSize);
53+
neverWrittenBuffer.copy(nuBuf);
54+
if (bufPool.push(nuBuf) > 100) {
55+
bufPool.length = 0;
56+
}
57+
process.nextTick(writer);
58+
//console.error('o');
59+
}
60+
}
61+
62+
}
63+
64+
function writerCB(err, written) {
65+
//console.error('cb.');
66+
if (err) {
67+
throw err;
68+
}
69+
}
70+
71+
72+
73+
74+
// ******************* UTILITIES
75+
76+
77+
function newBuffer(size, value) {
78+
var buffer = new Buffer(size);
79+
while (size--) {
80+
buffer[size] = value;
81+
}
82+
buffer[buffer.length - 1] = 0x0d;
83+
buffer[buffer.length - 1] = 0x0a;
84+
return buffer;
85+
}

0 commit comments

Comments
 (0)