stream: cleanup async handling

Cleanup async stream method handling.

PR-URL: https://github.com/nodejs/node/pull/39329
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
Robert Nagy committed 2021-07-11 19:20:01 +02:00
1 parent 7a7ba82234
commit f2f6872b5c
4 files changed
+63 -113

No files matched your search

+36 -94
View File
@@ -76,12 +76,16 @@ function destroy(err, cb) {
function _destroy(self, err, cb) {
let called = false;
const result = self._destroy(err || null, (err) => {
function onDestroy(err) {
if (called) {
return;
}
called = true;
const r = self._readableState;
const w = self._writableState;
called = true;
checkError(err, w, r);
if (w) {
@@ -100,64 +104,24 @@ function _destroy(self, err, cb) {
} else {
process.nextTick(emitCloseNT, self);
}
});
if (result !== undefined && result !== null) {
try {
}
try {
const result = self._destroy(err || null, onDestroy);
if (result != null) {
const then = result.then;
if (typeof then === 'function') {
then.call(
result,
function() {
if (called)
return;
const r = self._readableState;
const w = self._writableState;
if (w) {
w.closed = true;
}
if (r) {
r.closed = true;
}
if (typeof cb === 'function') {
process.nextTick(cb);
}
process.nextTick(emitCloseNT, self);
process.nextTick(onDestroy, null);
},
function(err) {
const r = self._readableState;
const w = self._writableState;
err.stack; // eslint-disable-line no-unused-expressions
called = true;
if (w && !w.errored) {
w.errored = err;
}
if (r && !r.errored) {
r.errored = err;
}
if (w) {
w.closed = true;
}
if (r) {
r.closed = true;
}
if (typeof cb === 'function') {
process.nextTick(cb, err);
}
process.nextTick(emitErrorCloseNT, self, err);
process.nextTick(onDestroy, err);
});
}
} catch (err) {
process.nextTick(emitErrorNT, self, err);
}
} catch (err) {
onDestroy(err);
}
}
@@ -291,13 +255,19 @@ function construct(stream, cb) {
}
function constructNT(stream) {
const r = stream._readableState;
const w = stream._writableState;
// With duplex streams we use the writable side for state.
const s = w || r;
let called = false;
const result = stream._construct((err) => {
function onConstruct(err) {
if (called) {
errorOrDestroy(stream, err ?? new ERR_MULTIPLE_CALLBACK());
return;
}
called = true;
const r = stream._readableState;
const w = stream._writableState;
const s = w || r;
if (r) {
r.constructed = true;
}
@@ -305,12 +275,6 @@ function constructNT(stream) {
w.constructed = true;
}
if (called) {
err = new ERR_MULTIPLE_CALLBACK();
} else {
called = true;
}
if (s.destroyed) {
stream.emit(kDestroy, err);
} else if (err) {
@@ -318,47 +282,25 @@ function constructNT(stream) {
} else {
process.nextTick(emitConstructNT, stream);
}
});
if (result !== undefined && result !== null) {
try {
}
try {
const result = stream._construct(onConstruct);
if (result != null) {
const then = result.then;
if (typeof then === 'function') {
then.call(
result,
function() {
// If the callback was invoked, do nothing further.
if (called)
return;
if (r) {
r.constructed = true;
}
if (w) {
w.constructed = true;
}
if (s.destroyed) {
process.nextTick(() => stream.emit(kDestroy));
} else {
process.nextTick(emitConstructNT, stream);
}
process.nextTick(onConstruct, null);
},
function(err) {
if (r) {
r.constructed = true;
}
if (w) {
w.constructed = true;
}
called = true;
if (s.destroyed) {
process.nextTick(() => stream.emit(kDestroy, err));
} else {
process.nextTick(errorOrDestroy, stream, err);
}
process.nextTick(onConstruct, err);
});
}
} catch (err) {
process.nextTick(emitErrorNT, stream, err);
}
} catch (err) {
onConstruct(err);
}
}
+2
View File
@@ -483,8 +483,10 @@ Readable.prototype.read = function(n) {
// If the length is currently zero, then we *need* a readable event.
if (state.length === 0)
state.needReadable = true;
// Call internal read method
this._read(state.highWaterMark);
state.sync = false;
// If _read pushed data synchronously, then `reading` will be false,
// and we need to re-evaluate how much data we can return to the user.
+22 -19
View File
@@ -660,9 +660,15 @@ function needFinish(state) {
}
function callFinal(stream, state) {
state.sync = true;
state.pendingcb++;
const result = stream._final((err) => {
let called = false;
function onFinish(err) {
if (called) {
errorOrDestroy(stream, err ?? ERR_MULTIPLE_CALLBACK());
return;
}
called = true;
state.pendingcb--;
if (err) {
const onfinishCallbacks = state[kOnFinished].splice(0);
@@ -679,33 +685,30 @@ function callFinal(stream, state) {
state.pendingcb++;
process.nextTick(finish, stream, state);
}
});
if (result !== undefined && result !== null) {
try {
}
state.sync = true;
state.pendingcb++;
try {
const result = stream._final(onFinish);
if (result != null) {
const then = result.then;
if (typeof then === 'function') {
then.call(
result,
function() {
if (state.prefinished || !needFinish(state))
return;
state.prefinish = true;
process.nextTick(() => stream.emit('prefinish'));
state.pendingcb++;
process.nextTick(finish, stream, state);
process.nextTick(onFinish, null);
},
function(err) {
const onfinishCallbacks = state[kOnFinished].splice(0);
for (let i = 0; i < onfinishCallbacks.length; i++) {
process.nextTick(onfinishCallbacks[i], err);
}
process.nextTick(errorOrDestroy, stream, err, state.sync);
process.nextTick(onFinish, err);
});
}
} catch (err) {
process.nextTick(errorOrDestroy, stream, err, state.sync);
}
} catch (err) {
onFinish(stream, state, err);
}
state.sync = false;
}
@@ -98,6 +98,9 @@ const assert = require('assert');
const foo = new Foo();
foo.write('test', common.mustCall());
foo.on('error', common.expectsError({
code: 'ERR_MULTIPLE_CALLBACK'
}));
}
{