Skip to content

Commit 8e3a81e

Browse files
addaleaxdanielleadams
authored andcommitted
http2,tls: store WriteWrap using BaseObjectPtr
Create weak `WriteWrap` and `ShutdownWrap` objects, and when referencing them in C++ is necessary, use `BaseObjectPtr<>` instead of plain pointers to keep these objects from being garbage-collected. This solves issues that arise when the underlying `StreamBase` instance is weak, but the `WriteWrap` or `ShutdownWrap` instances are not; in that case, they would otherwise potentially stick around in memory after the stream that they originally belong to is long gone. It probably makes sense to use `BaseObjectptr<>` more extensively in `StreamBase` in the long run as well. PR-URL: #35488 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Matteo Collina <[email protected]>
1 parent 27cd99b commit 8e3a81e

7 files changed

+73
-32
lines changed

src/node_http2.cc

+14-12
Original file line numberDiff line numberDiff line change
@@ -1518,12 +1518,12 @@ void Http2Session::ClearOutgoing(int status) {
15181518
std::vector<NgHttp2StreamWrite> current_outgoing_buffers_;
15191519
current_outgoing_buffers_.swap(outgoing_buffers_);
15201520
for (const NgHttp2StreamWrite& wr : current_outgoing_buffers_) {
1521-
WriteWrap* wrap = wr.req_wrap;
1522-
if (wrap != nullptr) {
1521+
BaseObjectPtr<AsyncWrap> wrap = std::move(wr.req_wrap);
1522+
if (wrap) {
15231523
// TODO(addaleax): Pass `status` instead of 0, so that we actually error
15241524
// out with the error from the write to the underlying protocol,
15251525
// if one occurred.
1526-
wrap->Done(0);
1526+
WriteWrap::FromObject(wrap)->Done(0);
15271527
}
15281528
}
15291529
}
@@ -1806,7 +1806,7 @@ void Http2Session::OnStreamRead(ssize_t nread, const uv_buf_t& buf_) {
18061806

18071807
bool Http2Session::HasWritesOnSocketForStream(Http2Stream* stream) {
18081808
for (const NgHttp2StreamWrite& wr : outgoing_buffers_) {
1809-
if (wr.req_wrap != nullptr && wr.req_wrap->stream() == stream)
1809+
if (wr.req_wrap && WriteWrap::FromObject(wr.req_wrap)->stream() == stream)
18101810
return true;
18111811
}
18121812
return false;
@@ -1959,8 +1959,8 @@ void Http2Stream::Destroy() {
19591959
// we still have queued outbound writes.
19601960
while (!queue_.empty()) {
19611961
NgHttp2StreamWrite& head = queue_.front();
1962-
if (head.req_wrap != nullptr)
1963-
head.req_wrap->Done(UV_ECANCELED);
1962+
if (head.req_wrap)
1963+
WriteWrap::FromObject(head.req_wrap)->Done(UV_ECANCELED);
19641964
queue_.pop();
19651965
}
19661966

@@ -2189,7 +2189,8 @@ int Http2Stream::DoWrite(WriteWrap* req_wrap,
21892189
// Store the req_wrap on the last write info in the queue, so that it is
21902190
// only marked as finished once all buffers associated with it are finished.
21912191
queue_.emplace(NgHttp2StreamWrite {
2192-
i == nbufs - 1 ? req_wrap : nullptr,
2192+
BaseObjectPtr<AsyncWrap>(
2193+
i == nbufs - 1 ? req_wrap->GetAsyncWrap() : nullptr),
21932194
bufs[i]
21942195
});
21952196
IncrementAvailableOutboundLength(bufs[i].len);
@@ -2283,10 +2284,11 @@ ssize_t Http2Stream::Provider::Stream::OnRead(nghttp2_session* handle,
22832284
// find out when the HTTP2 stream wants to consume data, and because the
22842285
// StreamBase API allows empty input chunks.
22852286
while (!stream->queue_.empty() && stream->queue_.front().buf.len == 0) {
2286-
WriteWrap* finished = stream->queue_.front().req_wrap;
2287+
BaseObjectPtr<AsyncWrap> finished =
2288+
std::move(stream->queue_.front().req_wrap);
22872289
stream->queue_.pop();
2288-
if (finished != nullptr)
2289-
finished->Done(0);
2290+
if (finished)
2291+
WriteWrap::FromObject(finished)->Done(0);
22902292
}
22912293

22922294
if (!stream->queue_.empty()) {
@@ -2912,8 +2914,8 @@ void Http2Ping::DetachFromSession() {
29122914
}
29132915

29142916
void NgHttp2StreamWrite::MemoryInfo(MemoryTracker* tracker) const {
2915-
if (req_wrap != nullptr)
2916-
tracker->TrackField("req_wrap", req_wrap->GetAsyncWrap());
2917+
if (req_wrap)
2918+
tracker->TrackField("req_wrap", req_wrap);
29172919
tracker->TrackField("buf", buf);
29182920
}
29192921

src/node_http2.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -145,12 +145,12 @@ using Http2Headers = NgHeaders<Http2HeadersTraits>;
145145
using Http2RcBufferPointer = NgRcBufPointer<Http2RcBufferPointerTraits>;
146146

147147
struct NgHttp2StreamWrite : public MemoryRetainer {
148-
WriteWrap* req_wrap = nullptr;
148+
BaseObjectPtr<AsyncWrap> req_wrap;
149149
uv_buf_t buf;
150150

151151
inline explicit NgHttp2StreamWrite(uv_buf_t buf_) : buf(buf_) {}
152-
inline NgHttp2StreamWrite(WriteWrap* req, uv_buf_t buf_) :
153-
req_wrap(req), buf(buf_) {}
152+
inline NgHttp2StreamWrite(BaseObjectPtr<AsyncWrap> req_wrap, uv_buf_t buf_) :
153+
req_wrap(std::move(req_wrap)), buf(buf_) {}
154154

155155
void MemoryInfo(MemoryTracker* tracker) const override;
156156
SET_MEMORY_INFO_NAME(NgHttp2StreamWrite)

src/stream_base-inl.h

+22
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,28 @@ StreamBase* StreamBase::FromObject(v8::Local<v8::Object> obj) {
243243
StreamBase::kStreamBaseField));
244244
}
245245

246+
WriteWrap* WriteWrap::FromObject(v8::Local<v8::Object> req_wrap_obj) {
247+
return static_cast<WriteWrap*>(StreamReq::FromObject(req_wrap_obj));
248+
}
249+
250+
template <typename T, bool kIsWeak>
251+
WriteWrap* WriteWrap::FromObject(
252+
const BaseObjectPtrImpl<T, kIsWeak>& base_obj) {
253+
if (!base_obj) return nullptr;
254+
return FromObject(base_obj->object());
255+
}
256+
257+
ShutdownWrap* ShutdownWrap::FromObject(v8::Local<v8::Object> req_wrap_obj) {
258+
return static_cast<ShutdownWrap*>(StreamReq::FromObject(req_wrap_obj));
259+
}
260+
261+
template <typename T, bool kIsWeak>
262+
ShutdownWrap* ShutdownWrap::FromObject(
263+
const BaseObjectPtrImpl<T, kIsWeak>& base_obj) {
264+
if (!base_obj) return nullptr;
265+
return FromObject(base_obj->object());
266+
}
267+
246268
void WriteWrap::SetAllocatedStorage(AllocatedBuffer&& storage) {
247269
CHECK_NULL(storage_.data());
248270
storage_ = std::move(storage);

src/stream_base.cc

+6-2
Original file line numberDiff line numberDiff line change
@@ -621,12 +621,16 @@ StreamResource::~StreamResource() {
621621

622622
ShutdownWrap* StreamBase::CreateShutdownWrap(
623623
Local<Object> object) {
624-
return new SimpleShutdownWrap<AsyncWrap>(this, object);
624+
auto* wrap = new SimpleShutdownWrap<AsyncWrap>(this, object);
625+
wrap->MakeWeak();
626+
return wrap;
625627
}
626628

627629
WriteWrap* StreamBase::CreateWriteWrap(
628630
Local<Object> object) {
629-
return new SimpleWriteWrap<AsyncWrap>(this, object);
631+
auto* wrap = new SimpleWriteWrap<AsyncWrap>(this, object);
632+
wrap->MakeWeak();
633+
return wrap;
630634
}
631635

632636
} // namespace node

src/stream_base.h

+10
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ class ShutdownWrap : public StreamReq {
7777
StreamBase* stream,
7878
v8::Local<v8::Object> req_wrap_obj);
7979

80+
static inline ShutdownWrap* FromObject(v8::Local<v8::Object> req_wrap_obj);
81+
template <typename T, bool kIsWeak>
82+
static inline ShutdownWrap* FromObject(
83+
const BaseObjectPtrImpl<T, kIsWeak>& base_obj);
84+
8085
// Call stream()->EmitAfterShutdown() and dispose of this request wrap.
8186
void OnDone(int status) override;
8287
};
@@ -89,6 +94,11 @@ class WriteWrap : public StreamReq {
8994
StreamBase* stream,
9095
v8::Local<v8::Object> req_wrap_obj);
9196

97+
static inline WriteWrap* FromObject(v8::Local<v8::Object> req_wrap_obj);
98+
template <typename T, bool kIsWeak>
99+
static inline WriteWrap* FromObject(
100+
const BaseObjectPtrImpl<T, kIsWeak>& base_obj);
101+
92102
// Call stream()->EmitAfterWrite() and dispose of this request wrap.
93103
void OnDone(int status) override;
94104

src/tls_wrap.cc

+16-13
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,10 @@ bool TLSWrap::InvokeQueued(int status, const char* error_str) {
9595
if (!write_callback_scheduled_)
9696
return false;
9797

98-
if (current_write_ != nullptr) {
99-
WriteWrap* w = current_write_;
100-
current_write_ = nullptr;
98+
if (current_write_) {
99+
BaseObjectPtr<AsyncWrap> current_write = std::move(current_write_);
100+
current_write_.reset();
101+
WriteWrap* w = WriteWrap::FromObject(current_write);
101102
w->Done(status, error_str);
102103
}
103104

@@ -301,7 +302,7 @@ void TLSWrap::EncOut() {
301302
}
302303

303304
// Split-off queue
304-
if (established_ && current_write_ != nullptr) {
305+
if (established_ && current_write_) {
305306
Debug(this, "EncOut() setting write_callback_scheduled_");
306307
write_callback_scheduled_ = true;
307308
}
@@ -372,10 +373,12 @@ void TLSWrap::EncOut() {
372373

373374
void TLSWrap::OnStreamAfterWrite(WriteWrap* req_wrap, int status) {
374375
Debug(this, "OnStreamAfterWrite(status = %d)", status);
375-
if (current_empty_write_ != nullptr) {
376+
if (current_empty_write_) {
376377
Debug(this, "Had empty write");
377-
WriteWrap* finishing = current_empty_write_;
378-
current_empty_write_ = nullptr;
378+
BaseObjectPtr<AsyncWrap> current_empty_write =
379+
std::move(current_empty_write_);
380+
current_empty_write_.reset();
381+
WriteWrap* finishing = WriteWrap::FromObject(current_empty_write);
379382
finishing->Done(status);
380383
return;
381384
}
@@ -735,23 +738,23 @@ int TLSWrap::DoWrite(WriteWrap* w,
735738
ClearOut();
736739
if (BIO_pending(enc_out_) == 0) {
737740
Debug(this, "No pending encrypted output, writing to underlying stream");
738-
CHECK_NULL(current_empty_write_);
739-
current_empty_write_ = w;
741+
CHECK(!current_empty_write_);
742+
current_empty_write_.reset(w->GetAsyncWrap());
740743
StreamWriteResult res =
741744
underlying_stream()->Write(bufs, count, send_handle);
742745
if (!res.async) {
743746
BaseObjectPtr<TLSWrap> strong_ref{this};
744747
env()->SetImmediate([this, strong_ref](Environment* env) {
745-
OnStreamAfterWrite(current_empty_write_, 0);
748+
OnStreamAfterWrite(WriteWrap::FromObject(current_empty_write_), 0);
746749
});
747750
}
748751
return 0;
749752
}
750753
}
751754

752755
// Store the current write wrap
753-
CHECK_NULL(current_write_);
754-
current_write_ = w;
756+
CHECK(!current_write_);
757+
current_write_.reset(w->GetAsyncWrap());
755758

756759
// Write encrypted data to underlying stream and call Done().
757760
if (length == 0) {
@@ -804,7 +807,7 @@ int TLSWrap::DoWrite(WriteWrap* w,
804807
// If we stopped writing because of an error, it's fatal, discard the data.
805808
if (!arg.IsEmpty()) {
806809
Debug(this, "Got SSL error (%d), returning UV_EPROTO", err);
807-
current_write_ = nullptr;
810+
current_write_.reset();
808811
return UV_EPROTO;
809812
}
810813

src/tls_wrap.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -194,9 +194,9 @@ class TLSWrap : public AsyncWrap,
194194
// Waiting for ClearIn() to pass to SSL_write().
195195
AllocatedBuffer pending_cleartext_input_;
196196
size_t write_size_ = 0;
197-
WriteWrap* current_write_ = nullptr;
197+
BaseObjectPtr<AsyncWrap> current_write_;
198198
bool in_dowrite_ = false;
199-
WriteWrap* current_empty_write_ = nullptr;
199+
BaseObjectPtr<AsyncWrap> current_empty_write_;
200200
bool write_callback_scheduled_ = false;
201201
bool started_ = false;
202202
bool established_ = false;

0 commit comments

Comments
 (0)