Skip to content
This repository was archived by the owner on Aug 11, 2020. It is now read-only.

Commit 27cfb37

Browse files
committed
src: enable StreamPipe for generic StreamBases
PR-URL: #150 Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 4965428 commit 27cfb37

File tree

2 files changed

+47
-16
lines changed

2 files changed

+47
-16
lines changed

src/stream_pipe.cc

Lines changed: 43 additions & 13 deletions
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.
@@ -124,13 +125,16 @@ void StreamPipe::ReadableListener::OnStreamRead(ssize_t nread,
124125
// EOF or error; stop reading and pass the error to the previous listener
125126
// (which might end up in JS).
126127
pipe->is_eof_ = true;
128+
// Cache `sink()` here because the previous listener might do things
129+
// that eventually lead to an `Unpipe()` call.
130+
StreamBase* sink = pipe->sink();
127131
stream()->ReadStop();
128132
CHECK_NOT_NULL(previous_listener_);
129133
previous_listener_->OnStreamRead(nread, uv_buf_init(nullptr, 0));
130134
// If we’re not writing, close now. Otherwise, we’ll do that in
131135
// `OnStreamAfterWrite()`.
132-
if (!pipe->is_writing_) {
133-
pipe->ShutdownWritable();
136+
if (pipe->pending_writes_ == 0) {
137+
sink->Shutdown();
134138
pipe->Unpipe();
135139
}
136140
return;
@@ -140,30 +144,38 @@ void StreamPipe::ReadableListener::OnStreamRead(ssize_t nread,
140144
}
141145

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

156-
void StreamPipe::ShutdownWritable() {
157-
sink()->Shutdown();
158-
}
159-
160161
void StreamPipe::WritableListener::OnStreamAfterWrite(WriteWrap* w,
161162
int status) {
162163
StreamPipe* pipe = ContainerOf(&StreamPipe::writable_listener_, this);
163-
pipe->is_writing_ = false;
164+
pipe->pending_writes_--;
165+
if (pipe->is_closed_) {
166+
if (pipe->pending_writes_ == 0) {
167+
Environment* env = pipe->env();
168+
HandleScope handle_scope(env->isolate());
169+
Context::Scope context_scope(env->context());
170+
pipe->MakeCallback(env->oncomplete_string(), 0, nullptr).ToLocalChecked();
171+
stream()->RemoveStreamListener(this);
172+
}
173+
return;
174+
}
175+
164176
if (pipe->is_eof_) {
165177
AsyncScope async_scope(pipe);
166-
pipe->ShutdownWritable();
178+
pipe->sink()->Shutdown();
167179
pipe->Unpipe();
168180
return;
169181
}
@@ -175,6 +187,10 @@ void StreamPipe::WritableListener::OnStreamAfterWrite(WriteWrap* w,
175187
prev->OnStreamAfterWrite(w, status);
176188
return;
177189
}
190+
191+
if (!pipe->uses_wants_write_) {
192+
OnStreamWantsWrite(65536);
193+
}
178194
}
179195

180196
void StreamPipe::WritableListener::OnStreamAfterShutdown(ShutdownWrap* w,
@@ -198,6 +214,7 @@ void StreamPipe::WritableListener::OnStreamDestroy() {
198214
StreamPipe* pipe = ContainerOf(&StreamPipe::writable_listener_, this);
199215
pipe->sink_destroyed_ = true;
200216
pipe->is_eof_ = true;
217+
pipe->pending_writes_ = 0;
201218
pipe->Unpipe();
202219
}
203220

@@ -236,8 +253,7 @@ void StreamPipe::Start(const FunctionCallbackInfo<Value>& args) {
236253
StreamPipe* pipe;
237254
ASSIGN_OR_RETURN_UNWRAP(&pipe, args.Holder());
238255
pipe->is_closed_ = false;
239-
if (pipe->wanted_data_ > 0)
240-
pipe->writable_listener_.OnStreamWantsWrite(pipe->wanted_data_);
256+
pipe->writable_listener_.OnStreamWantsWrite(65536);
241257
}
242258

243259
void StreamPipe::Unpipe(const FunctionCallbackInfo<Value>& args) {
@@ -246,6 +262,18 @@ void StreamPipe::Unpipe(const FunctionCallbackInfo<Value>& args) {
246262
pipe->Unpipe();
247263
}
248264

265+
void StreamPipe::IsClosed(const FunctionCallbackInfo<Value>& args) {
266+
StreamPipe* pipe;
267+
ASSIGN_OR_RETURN_UNWRAP(&pipe, args.Holder());
268+
args.GetReturnValue().Set(pipe->is_closed_);
269+
}
270+
271+
void StreamPipe::PendingWrites(const FunctionCallbackInfo<Value>& args) {
272+
StreamPipe* pipe;
273+
ASSIGN_OR_RETURN_UNWRAP(&pipe, args.Holder());
274+
args.GetReturnValue().Set(pipe->pending_writes_);
275+
}
276+
249277
namespace {
250278

251279
void InitializeStreamPipe(Local<Object> target,
@@ -260,6 +288,8 @@ void InitializeStreamPipe(Local<Object> target,
260288
FIXED_ONE_BYTE_STRING(env->isolate(), "StreamPipe");
261289
env->SetProtoMethod(pipe, "unpipe", StreamPipe::Unpipe);
262290
env->SetProtoMethod(pipe, "start", StreamPipe::Start);
291+
env->SetProtoMethod(pipe, "isClosed", StreamPipe::IsClosed);
292+
env->SetProtoMethod(pipe, "pendingWrites", StreamPipe::PendingWrites);
263293
pipe->Inherit(AsyncWrap::GetConstructorTemplate(env));
264294
pipe->SetClassName(stream_pipe_string);
265295
pipe->InstanceTemplate()->SetInternalFieldCount(1);

src/stream_pipe.h

Lines changed: 4 additions & 3 deletions
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)