@@ -763,6 +763,7 @@ jsg::Promise<void> WritableStreamInternalController::write(
763
763
}
764
764
765
765
auto prp = js.newPromiseAndResolver <void >();
766
+ increaseCurrentWriteBufferSize (js, byteLength);
766
767
queue.push_back (WriteEvent {
767
768
.outputLock = IoContext::current ().waitForOutputLocksIfNecessaryIoOwn (),
768
769
.event = Write {
@@ -780,6 +781,45 @@ jsg::Promise<void> WritableStreamInternalController::write(
780
781
KJ_UNREACHABLE;
781
782
}
782
783
784
+ void WritableStreamInternalController::increaseCurrentWriteBufferSize (
785
+ jsg::Lock& js,
786
+ uint64_t amount) {
787
+ currentWriteBufferSize += amount;
788
+ KJ_IF_MAYBE (highWaterMark, maybeHighWaterMark) {
789
+ updateBackpressure (js, (*highWaterMark) - currentWriteBufferSize <= 0 );
790
+ }
791
+ }
792
+
793
+ void WritableStreamInternalController::decreaseCurrentWriteBufferSize (
794
+ jsg::Lock& js,
795
+ uint64_t amount) {
796
+ currentWriteBufferSize -= amount;
797
+ KJ_IF_MAYBE (highWaterMark, maybeHighWaterMark) {
798
+ updateBackpressure (js, (*highWaterMark) - currentWriteBufferSize <= 0 );
799
+ }
800
+ }
801
+
802
+ void WritableStreamInternalController::updateBackpressure (jsg::Lock& js, bool backpressure) {
803
+ KJ_IF_MAYBE (writerLock, writeState.tryGet <WriterLocked>()) {
804
+ if (backpressure) {
805
+ // Per the spec, when backpressure is updated and is true, we replace the existing
806
+ // ready promise on the writer with a new pending promise, regardless of whether
807
+ // the existing one is resolved or not.
808
+ auto prp = js.newPromiseAndResolver <void >();
809
+ prp.promise .markAsHandled ();
810
+ writerLock->setReadyFulfiller (prp);
811
+ return ;
812
+ }
813
+
814
+ // When backpressure is updated and is false, we resolve the ready promise on the writer
815
+ maybeResolvePromise (writerLock->getReadyFulfiller ());
816
+ }
817
+ }
818
+
819
+ void WritableStreamInternalController::setHighWaterMark (uint64_t highWaterMark) {
820
+ maybeHighWaterMark = highWaterMark;
821
+ }
822
+
783
823
jsg::Promise<void > WritableStreamInternalController::close (
784
824
jsg::Lock& js,
785
825
bool markAsHandled) {
@@ -1010,7 +1050,12 @@ kj::Maybe<int> WritableStreamInternalController::getDesiredSize() {
1010
1050
KJ_SWITCH_ONEOF (state) {
1011
1051
KJ_CASE_ONEOF (closed, StreamStates::Closed) { return uint (0 ); }
1012
1052
KJ_CASE_ONEOF (errored, StreamStates::Errored) { return nullptr ; }
1013
- KJ_CASE_ONEOF (writable, Writable) { return 1 ; }
1053
+ KJ_CASE_ONEOF (writable, Writable) {
1054
+ KJ_IF_MAYBE (highWaterMark, maybeHighWaterMark) {
1055
+ return (*highWaterMark) - currentWriteBufferSize;
1056
+ }
1057
+ return 1 ;
1058
+ }
1014
1059
}
1015
1060
1016
1061
KJ_UNREACHABLE;
@@ -1024,24 +1069,27 @@ bool WritableStreamInternalController::lockWriter(jsg::Lock& js, Writer& writer)
1024
1069
auto closedPrp = js.newPromiseAndResolver <void >();
1025
1070
closedPrp.promise .markAsHandled ();
1026
1071
1027
- auto readyPromise = js.resolvedPromise ();
1072
+ auto readyPrp = js.newPromiseAndResolver <void >();
1073
+ readyPrp.promise .markAsHandled ();
1028
1074
1029
- auto lock = WriterLocked (writer, kj::mv (closedPrp.resolver ));
1075
+ auto lock = WriterLocked (writer, kj::mv (closedPrp.resolver ), kj::mv (readyPrp. resolver ) );
1030
1076
1031
1077
KJ_SWITCH_ONEOF (state) {
1032
1078
KJ_CASE_ONEOF (closed, StreamStates::Closed) {
1033
1079
maybeResolvePromise (lock.getClosedFulfiller ());
1080
+ maybeResolvePromise (lock.getReadyFulfiller ());
1034
1081
}
1035
1082
KJ_CASE_ONEOF (errored, StreamStates::Errored) {
1036
1083
maybeRejectPromise<void >(lock.getClosedFulfiller (), errored.getHandle (js));
1084
+ maybeRejectPromise<void >(lock.getReadyFulfiller (), errored.getHandle (js));
1037
1085
}
1038
1086
KJ_CASE_ONEOF (writable, Writable) {
1039
- // Nothing to do.
1087
+ maybeResolvePromise (lock. getReadyFulfiller ());
1040
1088
}
1041
1089
}
1042
1090
1043
1091
writeState = kj::mv (lock);
1044
- writer.attach (*this , kj::mv (closedPrp.promise ), kj::mv (readyPromise ));
1092
+ writer.attach (*this , kj::mv (closedPrp.promise ), kj::mv (readyPrp. promise ));
1045
1093
return true ;
1046
1094
}
1047
1095
@@ -1076,6 +1124,7 @@ void WritableStreamInternalController::doClose() {
1076
1124
state.init <StreamStates::Closed>();
1077
1125
KJ_IF_MAYBE (locked, writeState.tryGet <WriterLocked>()) {
1078
1126
maybeResolvePromise (locked->getClosedFulfiller ());
1127
+ maybeResolvePromise (locked->getReadyFulfiller ());
1079
1128
writeState.init <Locked>();
1080
1129
} else KJ_IF_MAYBE (locked, writeState.tryGet <PipeLocked>()) {
1081
1130
writeState.init <Unlocked>();
@@ -1087,6 +1136,7 @@ void WritableStreamInternalController::doError(jsg::Lock& js, v8::Local<v8::Valu
1087
1136
state.init <StreamStates::Errored>(js.v8Ref (reason));
1088
1137
KJ_IF_MAYBE (locked, writeState.tryGet <WriterLocked>()) {
1089
1138
maybeRejectPromise<void >(locked->getClosedFulfiller (), reason);
1139
+ maybeResolvePromise (locked->getReadyFulfiller ());
1090
1140
writeState.init <Locked>();
1091
1141
} else KJ_IF_MAYBE (locked, writeState.tryGet <PipeLocked>()) {
1092
1142
writeState.init <Unlocked>();
@@ -1197,6 +1247,8 @@ jsg::Promise<void> WritableStreamInternalController::writeLoopAfterFrontOutputLo
1197
1247
auto & writable = state.get <Writable>();
1198
1248
auto check = makeChecker (request);
1199
1249
1250
+ auto amountToWrite = request.bytes .size ();
1251
+
1200
1252
auto promise = writable->write (request.bytes .begin (), request.bytes .size ())
1201
1253
.attach (kj::mv (request.ownBytes ));
1202
1254
@@ -1210,18 +1262,20 @@ jsg::Promise<void> WritableStreamInternalController::writeLoopAfterFrontOutputLo
1210
1262
// That's a larger refactor, though.
1211
1263
return ioContext.awaitIoLegacy (kj::mv (promise)).then (js,
1212
1264
ioContext.addFunctor (
1213
- [this , check, maybeAbort](jsg::Lock& js) -> jsg::Promise<void > {
1265
+ [this , check, maybeAbort, amountToWrite ](jsg::Lock& js) -> jsg::Promise<void > {
1214
1266
auto & request = check ();
1215
1267
maybeResolvePromise (request.promise );
1268
+ decreaseCurrentWriteBufferSize (js, amountToWrite);
1216
1269
queue.pop_front ();
1217
1270
maybeAbort (js, request);
1218
1271
return writeLoop (js, IoContext::current ());
1219
1272
}), ioContext.addFunctor (
1220
- [this , check, maybeAbort](jsg::Lock& js, jsg::Value reason)
1273
+ [this , check, maybeAbort, amountToWrite ](jsg::Lock& js, jsg::Value reason)
1221
1274
-> jsg::Promise<void > {
1222
1275
auto handle = reason.getHandle (js);
1223
1276
auto & request = check ();
1224
1277
auto & writable = state.get <Writable>();
1278
+ decreaseCurrentWriteBufferSize (js, amountToWrite);
1225
1279
maybeRejectPromise<void >(request.promise , handle);
1226
1280
queue.pop_front ();
1227
1281
if (!maybeAbort (js, request)) {
0 commit comments