| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent f63143f commit ec2666e
4 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -51,6 +51,8 @@ const { | |||
| 51 | 51 | } = require('internal/streams/iter/utils'); | |
| 52 | 52 | ||
| 53 | 53 | const { | |
| 54 | + drainableProtocol, | ||
| 55 | + kSyncWriteAcceptedOnFalse, | ||
| 54 | 56 | kValidatedTransform, | |
| 55 | 57 | } = require('internal/streams/iter/types'); | |
| 56 | 58 | ||
@@ -828,13 +830,33 @@ async function pipeTo(source, ...args) { | |||
| 828 | 830 | const hasWriteSync = typeof writer.writeSync === 'function'; | |
| 829 | 831 | const hasWritevSync = typeof writer.writevSync === 'function'; | |
| 830 | 832 | const hasEndSync = typeof writer.endSync === 'function'; | |
| 833 | + const syncFalseCanBeAccepted = writer[kSyncWriteAcceptedOnFalse] === true; | ||
| 834 | + | ||
| 835 | + function syncFalseWasAccepted() { | ||
| 836 | + return syncFalseCanBeAccepted && writer.desiredSize === 0; | ||
| 837 | + } | ||
| 838 | + | ||
| 839 | + function waitForSyncBackpressure() { | ||
| 840 | + const ondrain = writer[drainableProtocol]; | ||
| 841 | + return ondrain?.call(writer); | ||
| 842 | + } | ||
| 843 | + | ||
| 844 | + async function writeBatchAfterAcceptedBackpressure(batch, startIndex) { | ||
| 845 | + await waitForSyncBackpressure(); | ||
| 846 | + await writeBatchAsyncFallback(batch, startIndex); | ||
| 847 | + } | ||
| 848 | + | ||
| 831 | 849 | // Async fallback for writeBatch when sync write fails partway through. | |
| 832 | 850 | // Continues writing from batch[startIndex] using async write(). | |
| 833 | 851 | async function writeBatchAsyncFallback(batch, startIndex) { | |
| 834 | 852 | for (let i = startIndex; i < batch.length; i++) { | |
| 835 | 853 | const chunk = batch[i]; | |
| 836 | 854 | if (hasWriteSync && writer.writeSync(chunk)) { | |
| 837 | 855 | // Sync retry succeeded | |
| 856 | + } else if (syncFalseWasAccepted()) { | ||
| 857 | + totalBytes += TypedArrayPrototypeGetByteLength(chunk); | ||
| 858 | + await waitForSyncBackpressure(); | ||
| 859 | + continue; | ||
| 838 | 860 | } else { | |
| 839 | 861 | const result = writer.write( | |
| 840 | 862 | chunk, signal ? { __proto__: null, signal } : undefined); | |
@@ -852,6 +874,12 @@ async function pipeTo(source, ...args) { | |||
| 852 | 874 | function writeBatch(batch) { | |
| 853 | 875 | if (hasWritev && batch.length > 1) { | |
| 854 | 876 | if (!hasWritevSync || !writer.writevSync(batch)) { | |
| 877 | + if (hasWritevSync && syncFalseWasAccepted()) { | ||
| 878 | + for (let i = 0; i < batch.length; i++) { | ||
| 879 | + totalBytes += TypedArrayPrototypeGetByteLength(batch[i]); | ||
| 880 | + } | ||
| 881 | + return waitForSyncBackpressure(); | ||
| 882 | + } | ||
| 855 | 883 | const opts = signal ? { __proto__: null, signal } : undefined; | |
| 856 | 884 | return PromisePrototypeThen(writer.writev(batch, opts), () => { | |
| 857 | 885 | for (let i = 0; i < batch.length; i++) { | |
@@ -867,6 +895,10 @@ async function pipeTo(source, ...args) { | |||
| 867 | 895 | for (let i = 0; i < batch.length; i++) { | |
| 868 | 896 | const chunk = batch[i]; | |
| 869 | 897 | if (!hasWriteSync || !writer.writeSync(chunk)) { | |
| 898 | + if (hasWriteSync && syncFalseWasAccepted()) { | ||
| 899 | + totalBytes += TypedArrayPrototypeGetByteLength(chunk); | ||
| 900 | + return writeBatchAfterAcceptedBackpressure(batch, i + 1); | ||
| 901 | + } | ||
| 870 | 902 | // Sync path failed at index i - fall back to async for the rest. | |
| 871 | 903 | // Count bytes for chunks already written synchronously (0..i-1). | |
| 872 | 904 | return writeBatchAsyncFallback(batch, i); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -32,6 +32,7 @@ const { | |||
| 32 | 32 | ||
| 33 | 33 | const { | |
| 34 | 34 | drainableProtocol, | |
| 35 | + kSyncWriteAcceptedOnFalse, | ||
| 35 | 36 | } = require('internal/streams/iter/types'); | |
| 36 | 37 | ||
| 37 | 38 | const { | |
@@ -560,6 +561,10 @@ class PushWriter { | |||
| 560 | 561 | return this.#queue.desiredSize; | |
| 561 | 562 | } | |
| 562 | 563 | ||
| 564 | + get [kSyncWriteAcceptedOnFalse]() { | ||
| 565 | + return this.#queue.backpressurePolicy === 'block'; | ||
| 566 | + } | ||
| 567 | + | ||
| 563 | 568 | write(chunk, options) { | |
| 564 | 569 | if (!options?.signal && this.#queue.canWriteSync()) { | |
| 565 | 570 | const bytes = toUint8Array(chunk); | |
@@ -586,7 +591,8 @@ class PushWriter { | |||
| 586 | 591 | writeSync(chunk) { | |
| 587 | 592 | const bytes = toUint8Array(chunk); | |
| 588 | 593 | const result = this.#queue.writeSync([bytes]); | |
| 589 | - if (!result && this.#queue.backpressurePolicy === 'block') { | ||
| 594 | + if (!result && this.#queue.backpressurePolicy === 'block' && | ||
| 595 | + this.#queue.desiredSize === 0) { | ||
| 590 | 596 | // Block policy: force-enqueue and return false as backpressure signal. | |
| 591 | 597 | // Data IS accepted; false tells caller to slow down. | |
| 592 | 598 | this.#queue.forceEnqueue([bytes]); | |
@@ -601,7 +607,8 @@ class PushWriter { | |||
| 601 | 607 | } | |
| 602 | 608 | const bytes = convertChunks(chunks); | |
| 603 | 609 | const result = this.#queue.writeSync(bytes); | |
| 604 | - if (!result && this.#queue.backpressurePolicy === 'block') { | ||
| 610 | + if (!result && this.#queue.backpressurePolicy === 'block' && | ||
| 611 | + this.#queue.desiredSize === 0) { | ||
| 605 | 612 | this.#queue.forceEnqueue(bytes); | |
| 606 | 613 | return false; | |
| 607 | 614 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -64,9 +64,12 @@ const kValidatedTransform = Symbol('kValidatedTransform'); | |||
| 64 | 64 | */ | |
| 65 | 65 | const kValidatedSource = Symbol('kValidatedSource'); | |
| 66 | 66 | ||
| 67 | + const kSyncWriteAcceptedOnFalse = Symbol('kSyncWriteAcceptedOnFalse'); | ||
| 68 | + | ||
| 67 | 69 | module.exports = { | |
| 68 | 70 | broadcastProtocol, | |
| 69 | 71 | drainableProtocol, | |
| 72 | + kSyncWriteAcceptedOnFalse, | ||
| 70 | 73 | kValidatedSource, | |
| 71 | 74 | kValidatedTransform, | |
| 72 | 75 | shareProtocol, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -5,7 +5,8 @@ | |||
| 5 | 5 | ||
| 6 | 6 | const common = require('../common'); | |
| 7 | 7 | const assert = require('assert'); | |
| 8 | - const { pipeTo, pipeToSync } = require('stream/iter'); | ||
| 8 | + const { setImmediate: setImmediatePromise } = require('timers/promises'); | ||
| 9 | + const { pipeTo, pipeToSync, push, text } = require('stream/iter'); | ||
| 9 | 10 | ||
| 10 | 11 | // Multi-chunk batch with writevSync (sync success path) | |
| 11 | 12 | async function testWritevSyncSuccess() { | |
@@ -104,6 +105,35 @@ async function testWriteSyncAlwaysFails() { | |||
| 104 | 105 | assert.strictEqual(total, 2); | |
| 105 | 106 | } | |
| 106 | 107 | ||
| 108 | + // PushWriter block mode accepts sync writes even when returning false for | ||
| 109 | + // backpressure. pipeTo must wait for drain, not retry the same write. | ||
| 110 | + async function assertPushWriterBlockPipeTo(source, expected, expectedTotal) { | ||
| 111 | + const { writer, readable } = push({ | ||
| 112 | + highWaterMark: 1, | ||
| 113 | + backpressure: 'block', | ||
| 114 | + }); | ||
| 115 | + | ||
| 116 | + const pipe = pipeTo(source, writer); | ||
| 117 | + await setImmediatePromise(); | ||
| 118 | + const data = await text(readable); | ||
| 119 | + const total = await pipe; | ||
| 120 | + | ||
| 121 | + assert.strictEqual(data, expected); | ||
| 122 | + assert.strictEqual(total, expectedTotal); | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + async function testPushWriterBlockSyncFalseAccepted() { | ||
| 126 | + await assertPushWriterBlockPipeTo((async function*() { | ||
| 127 | + yield [new Uint8Array([97])]; | ||
| 128 | + yield [new Uint8Array([98])]; | ||
| 129 | + })(), 'ab', 2); | ||
| 130 | + | ||
| 131 | + await assertPushWriterBlockPipeTo((async function*() { | ||
| 132 | + yield [new Uint8Array([97, 98])]; | ||
| 133 | + yield [new Uint8Array([99]), new Uint8Array([100])]; | ||
| 134 | + })(), 'abcd', 4); | ||
| 135 | + } | ||
| 136 | + | ||
| 107 | 137 | // pipeToSync with writevSync | |
| 108 | 138 | async function testPipeToSyncWritev() { | |
| 109 | 139 | const batches = []; | |
@@ -142,6 +172,7 @@ Promise.all([ | |||
| 142 | 172 | testWritevSyncFails(), | |
| 143 | 173 | testWriteSyncFailsMidBatch(), | |
| 144 | 174 | testWriteSyncAlwaysFails(), | |
| 175 | + testPushWriterBlockSyncFalseAccepted(), | ||
| 145 | 176 | testPipeToSyncWritev(), | |
| 146 | 177 | testPipeToSyncWriteFallback(), | |
| 147 | 178 | ]).then(common.mustCall()); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments