Skip to content

Commit 0b327bd

Browse files
addaleaxcodebytere
authored andcommitted
src: enable StreamPipe for generic StreamBases
Originally landed in the nodejs/quic repo and used there for file sending. Original review: ``` PR-URL: nodejs/quic#150 Reviewed-By: James M Snell <[email protected]> ``` PR-URL: #31869 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Daniel Bevenius <[email protected]>
1 parent c2ffef8 commit 0b327bd

File tree

2 files changed

+47
-16
lines changed

2 files changed

+47
-16
lines changed

src/stream_pipe.cc

+43-13
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ StreamPipe::StreamPipe(StreamBase* source,
2525
source->PushStreamListener(&readable_listener_);
2626
sink->PushStreamListener(&writable_listener_);
2727

28-
CHECK(sink->HasWantsWrite());
28+
uses_wants_write_ = sink->HasWantsWrite();
2929

3030
// Set up links between this object and the source/sink objects.
3131
// In particular, this makes sure that they are garbage collected as a group,
@@ -66,7 +66,8 @@ void StreamPipe::Unpipe() {
6666
is_closed_ = true;
6767
is_reading_ = false;
6868
source()->RemoveStreamListener(&readable_listener_);
69-
sink()->RemoveStreamListener(&writable_listener_);
69+
if (pending_writes_ == 0)
70+
sink()->RemoveStreamListener(&writable_listener_);
7071

7172
// Delay the JS-facing part with SetImmediate, because this might be from
7273
// inside the garbage collector, so we can’t run JS here.
@@ -123,13 +124,16 @@ void StreamPipe::ReadableListener::OnStreamRead(ssize_t nread,
123124
// EOF or error; stop reading and pass the error to the previous listener
124125
// (which might end up in JS).
125126
pipe->is_eof_ = true;
127+
// Cache `sink()` here because the previous listener might do things
128+
// that eventually lead to an `Unpipe()` call.
129+
StreamBase* sink = pipe->sink();
126130
stream()->ReadStop();
127131
CHECK_NOT_NULL(previous_listener_);
128132
previous_listener_->OnStreamRead(nread, uv_buf_init(nullptr, 0));
129133
// If we’re not writing, close now. Otherwise, we’ll do that in
130134
// `OnStreamAfterWrite()`.
131-
if (!pipe->is_writing_) {
132-
pipe->ShutdownWritable();
135+
if (pipe->pending_writes_ == 0) {
136+
sink->Shutdown();
133137
pipe->Unpipe();
134138
}
135139
return;
@@ -139,32 +143,40 @@ void StreamPipe::ReadableListener::OnStreamRead(ssize_t nread,
139143
}
140144

141145
void StreamPipe::ProcessData(size_t nread, AllocatedBuffer&& buf) {
146+
CHECK(uses_wants_write_ || pending_writes_ == 0);
142147
uv_buf_t buffer = uv_buf_init(buf.data(), nread);
143148
StreamWriteResult res = sink()->Write(&buffer, 1);
149+
pending_writes_++;
144150
if (!res.async) {
145151
writable_listener_.OnStreamAfterWrite(nullptr, res.err);
146152
} else {
147-
is_writing_ = true;
148153
is_reading_ = false;
149154
res.wrap->SetAllocatedStorage(std::move(buf));
150155
if (source() != nullptr)
151156
source()->ReadStop();
152157
}
153158
}
154159

155-
void StreamPipe::ShutdownWritable() {
156-
sink()->Shutdown();
157-
}
158-
159160
void StreamPipe::WritableListener::OnStreamAfterWrite(WriteWrap* w,
160161
int status) {
161162
StreamPipe* pipe = ContainerOf(&StreamPipe::writable_listener_, this);
162-
pipe->is_writing_ = false;
163+
pipe->pending_writes_--;
164+
if (pipe->is_closed_) {
165+
if (pipe->pending_writes_ == 0) {
166+
Environment* env = pipe->env();
167+
HandleScope handle_scope(env->isolate());
168+
Context::Scope context_scope(env->context());
169+
pipe->MakeCallback(env->oncomplete_string(), 0, nullptr).ToLocalChecked();
170+
stream()->RemoveStreamListener(this);
171+
}
172+
return;
173+
}
174+
163175
if (pipe->is_eof_) {
164176
HandleScope handle_scope(pipe->env()->isolate());
165177
InternalCallbackScope callback_scope(pipe,
166178
InternalCallbackScope::kSkipTaskQueues);
167-
pipe->ShutdownWritable();
179+
pipe->sink()->Shutdown();
168180
pipe->Unpipe();
169181
return;
170182
}
@@ -176,6 +188,10 @@ void StreamPipe::WritableListener::OnStreamAfterWrite(WriteWrap* w,
176188
prev->OnStreamAfterWrite(w, status);
177189
return;
178190
}
191+
192+
if (!pipe->uses_wants_write_) {
193+
OnStreamWantsWrite(65536);
194+
}
179195
}
180196

181197
void StreamPipe::WritableListener::OnStreamAfterShutdown(ShutdownWrap* w,
@@ -199,6 +215,7 @@ void StreamPipe::WritableListener::OnStreamDestroy() {
199215
StreamPipe* pipe = ContainerOf(&StreamPipe::writable_listener_, this);
200216
pipe->sink_destroyed_ = true;
201217
pipe->is_eof_ = true;
218+
pipe->pending_writes_ = 0;
202219
pipe->Unpipe();
203220
}
204221

@@ -239,8 +256,7 @@ void StreamPipe::Start(const FunctionCallbackInfo<Value>& args) {
239256
StreamPipe* pipe;
240257
ASSIGN_OR_RETURN_UNWRAP(&pipe, args.Holder());
241258
pipe->is_closed_ = false;
242-
if (pipe->wanted_data_ > 0)
243-
pipe->writable_listener_.OnStreamWantsWrite(pipe->wanted_data_);
259+
pipe->writable_listener_.OnStreamWantsWrite(65536);
244260
}
245261

246262
void StreamPipe::Unpipe(const FunctionCallbackInfo<Value>& args) {
@@ -249,6 +265,18 @@ void StreamPipe::Unpipe(const FunctionCallbackInfo<Value>& args) {
249265
pipe->Unpipe();
250266
}
251267

268+
void StreamPipe::IsClosed(const FunctionCallbackInfo<Value>& args) {
269+
StreamPipe* pipe;
270+
ASSIGN_OR_RETURN_UNWRAP(&pipe, args.Holder());
271+
args.GetReturnValue().Set(pipe->is_closed_);
272+
}
273+
274+
void StreamPipe::PendingWrites(const FunctionCallbackInfo<Value>& args) {
275+
StreamPipe* pipe;
276+
ASSIGN_OR_RETURN_UNWRAP(&pipe, args.Holder());
277+
args.GetReturnValue().Set(pipe->pending_writes_);
278+
}
279+
252280
namespace {
253281

254282
void InitializeStreamPipe(Local<Object> target,
@@ -263,6 +291,8 @@ void InitializeStreamPipe(Local<Object> target,
263291
FIXED_ONE_BYTE_STRING(env->isolate(), "StreamPipe");
264292
env->SetProtoMethod(pipe, "unpipe", StreamPipe::Unpipe);
265293
env->SetProtoMethod(pipe, "start", StreamPipe::Start);
294+
env->SetProtoMethod(pipe, "isClosed", StreamPipe::IsClosed);
295+
env->SetProtoMethod(pipe, "pendingWrites", StreamPipe::PendingWrites);
266296
pipe->Inherit(AsyncWrap::GetConstructorTemplate(env));
267297
pipe->SetClassName(stream_pipe_string);
268298
pipe->InstanceTemplate()->SetInternalFieldCount(1);

src/stream_pipe.h

+4-3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ class StreamPipe : public AsyncWrap {
1717
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
1818
static void Start(const v8::FunctionCallbackInfo<v8::Value>& args);
1919
static void Unpipe(const v8::FunctionCallbackInfo<v8::Value>& args);
20+
static void IsClosed(const v8::FunctionCallbackInfo<v8::Value>& args);
21+
static void PendingWrites(const v8::FunctionCallbackInfo<v8::Value>& args);
2022

2123
SET_NO_MEMORY_INFO()
2224
SET_MEMORY_INFO_NAME(StreamPipe)
@@ -26,14 +28,13 @@ class StreamPipe : public AsyncWrap {
2628
inline StreamBase* source();
2729
inline StreamBase* sink();
2830

29-
inline void ShutdownWritable();
30-
31+
int pending_writes_ = 0;
3132
bool is_reading_ = false;
32-
bool is_writing_ = false;
3333
bool is_eof_ = false;
3434
bool is_closed_ = true;
3535
bool sink_destroyed_ = false;
3636
bool source_destroyed_ = false;
37+
bool uses_wants_write_ = false;
3738

3839
// Set a default value so that when we’re coming from Start(), we know
3940
// that we don’t want to read just yet.

0 commit comments

Comments
 (0)