Skip to content

Commit db0e991

Browse files
committed
fs: remove custom Buffer pool for streams
The performance benefit of using a custom pool are negligable. Furthermore, it causes problems with Workers and transferrable. Rather than further adding complexity for compat with Workers, just remove the pooling logic. Refs: #33880 (comment) Fixes: #31733 PR-URL: #33981 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
1 parent e202df8 commit db0e991

File tree

2 files changed

+33
-72
lines changed

2 files changed

+33
-72
lines changed

lib/internal/fs/streams.js

+30-70
Original file line numberDiff line numberDiff line change
@@ -26,29 +26,8 @@ const { toPathIfFileURL } = require('internal/url');
2626
const kIoDone = Symbol('kIoDone');
2727
const kIsPerformingIO = Symbol('kIsPerformingIO');
2828

29-
const kMinPoolSpace = 128;
3029
const kFs = Symbol('kFs');
3130

32-
let pool;
33-
// It can happen that we expect to read a large chunk of data, and reserve
34-
// a large chunk of the pool accordingly, but the read() call only filled
35-
// a portion of it. If a concurrently executing read() then uses the same pool,
36-
// the "reserved" portion cannot be used, so we allow it to be re-used as a
37-
// new pool later.
38-
const poolFragments = [];
39-
40-
function allocNewPool(poolSize) {
41-
if (poolFragments.length > 0)
42-
pool = poolFragments.pop();
43-
else
44-
pool = Buffer.allocUnsafe(poolSize);
45-
pool.used = 0;
46-
}
47-
48-
function roundUpToMultipleOf8(n) {
49-
return (n + 7) & ~7; // Align to 8 byte boundary.
50-
}
51-
5231
function _construct(callback) {
5332
const stream = this;
5433
if (typeof stream.fd === 'number') {
@@ -188,70 +167,51 @@ ReadStream.prototype.open = openReadFs;
188167
ReadStream.prototype._construct = _construct;
189168

190169
ReadStream.prototype._read = function(n) {
191-
if (!pool || pool.length - pool.used < kMinPoolSpace) {
192-
// Discard the old pool.
193-
allocNewPool(this.readableHighWaterMark);
194-
}
170+
n = this.pos !== undefined ?
171+
MathMin(this.end - this.pos + 1, n) :
172+
MathMin(this.end - this.bytesRead + 1, n);
195173

196-
// Grab another reference to the pool in the case that while we're
197-
// in the thread pool another read() finishes up the pool, and
198-
// allocates a new one.
199-
const thisPool = pool;
200-
let toRead = MathMin(pool.length - pool.used, n);
201-
const start = pool.used;
202-
203-
if (this.pos !== undefined)
204-
toRead = MathMin(this.end - this.pos + 1, toRead);
205-
else
206-
toRead = MathMin(this.end - this.bytesRead + 1, toRead);
174+
if (n <= 0) {
175+
this.push(null);
176+
return;
177+
}
207178

208-
// Already read everything we were supposed to read!
209-
// treat as EOF.
210-
if (toRead <= 0)
211-
return this.push(null);
179+
const buf = Buffer.allocUnsafeSlow(n);
212180

213-
// the actual read.
214181
this[kIsPerformingIO] = true;
215182
this[kFs]
216-
.read(this.fd, pool, pool.used, toRead, this.pos, (er, bytesRead) => {
183+
.read(this.fd, buf, 0, n, this.pos, (er, bytesRead, buf) => {
217184
this[kIsPerformingIO] = false;
185+
218186
// Tell ._destroy() that it's safe to close the fd now.
219-
if (this.destroyed) return this.emit(kIoDone, er);
187+
if (this.destroyed) {
188+
this.emit(kIoDone, er);
189+
return;
190+
}
220191

221192
if (er) {
222193
errorOrDestroy(this, er);
223-
} else {
224-
let b = null;
225-
// Now that we know how much data we have actually read, re-wind the
226-
// 'used' field if we can, and otherwise allow the remainder of our
227-
// reservation to be used as a new pool later.
228-
if (start + toRead === thisPool.used && thisPool === pool) {
229-
const newUsed = thisPool.used + bytesRead - toRead;
230-
thisPool.used = roundUpToMultipleOf8(newUsed);
231-
} else {
232-
// Round down to the next lowest multiple of 8 to ensure the new pool
233-
// fragment start and end positions are aligned to an 8 byte boundary.
234-
const alignedEnd = (start + toRead) & ~7;
235-
const alignedStart = roundUpToMultipleOf8(start + bytesRead);
236-
if (alignedEnd - alignedStart >= kMinPoolSpace) {
237-
poolFragments.push(thisPool.slice(alignedStart, alignedEnd));
238-
}
239-
}
240-
241-
if (bytesRead > 0) {
242-
this.bytesRead += bytesRead;
243-
b = thisPool.slice(start, start + bytesRead);
194+
} else if (bytesRead > 0) {
195+
this.bytesRead += bytesRead;
196+
197+
if (bytesRead !== buf.length) {
198+
// Slow path. Shrink to fit.
199+
// Copy instead of slice so that we don't retain
200+
// large backing buffer for small reads.
201+
const dst = Buffer.allocUnsafeSlow(bytesRead);
202+
buf.copy(dst, 0, 0, bytesRead);
203+
buf = dst;
244204
}
245205

246-
this.push(b);
206+
this.push(buf);
207+
} else {
208+
this.push(null);
247209
}
248210
});
249211

250-
// Move the pool positions, and internal position for reading.
251-
if (this.pos !== undefined)
252-
this.pos += toRead;
253-
254-
pool.used = roundUpToMultipleOf8(pool.used + toRead);
212+
if (this.pos !== undefined) {
213+
this.pos += n;
214+
}
255215
};
256216

257217
ReadStream.prototype._destroy = function(err, cb) {

test/known_issues/test-crypto-authenticated-stream.js test/parallel/test-crypto-authenticated-stream.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
'use strict';
33
// Refs: https://github.com/nodejs/node/issues/31733
44
const common = require('../common');
5+
if (!common.hasCrypto)
6+
common.skip('missing crypto');
7+
58
const assert = require('assert');
69
const crypto = require('crypto');
710
const fs = require('fs');
@@ -121,7 +124,6 @@ function test(config) {
121124

122125
tmpdir.refresh();
123126

124-
// OK
125127
test({
126128
cipher: 'aes-128-ccm',
127129
aad: Buffer.alloc(1),
@@ -131,7 +133,6 @@ test({
131133
plaintextLength: 32768,
132134
});
133135

134-
// Fails the fstream test.
135136
test({
136137
cipher: 'aes-128-ccm',
137138
aad: Buffer.alloc(1),

0 commit comments

Comments
 (0)