http2: make sessions garbage-collectible

Use weak handles to track the lifetime of an `Http2Session` instance.

Refs: https://github.com/nodejs/http2/issues/140
PR-URL: https://github.com/nodejs/node/pull/16461
Reviewed-By: Anatoli Papirovski <apapirovski@mac.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com>
This commit is contained in:
Anna Henningsen 2017-10-24 23:41:48 +02:00
parent 3ddd151611
commit c8a00fdf62
No known key found for this signature in database
GPG Key ID: 9C63F3A6CD2AD8F9
4 changed files with 25 additions and 12 deletions

View File

@ -75,7 +75,7 @@ Http2Session::Http2Session(Environment* env,
nghttp2_session_type type) nghttp2_session_type type)
: AsyncWrap(env, wrap, AsyncWrap::PROVIDER_HTTP2SESSION), : AsyncWrap(env, wrap, AsyncWrap::PROVIDER_HTTP2SESSION),
StreamBase(env) { StreamBase(env) {
Wrap(object(), this); MakeWeak<Http2Session>(this);
Http2Options opts(env); Http2Options opts(env);
@ -102,11 +102,16 @@ Http2Session::Http2Session(Environment* env,
} }
Http2Session::~Http2Session() { Http2Session::~Http2Session() {
CHECK_EQ(false, persistent().IsEmpty()); CHECK(persistent().IsEmpty());
ClearWrap(object()); Close();
persistent().Reset(); }
CHECK_EQ(true, persistent().IsEmpty());
void Http2Session::Close() {
if (!object().IsEmpty())
ClearWrap(object());
persistent().Reset();
this->Nghttp2Session::Close();
// Stop the loop // Stop the loop
CHECK_EQ(uv_prepare_stop(prep_), 0); CHECK_EQ(uv_prepare_stop(prep_), 0);
auto prep_close = [](uv_handle_t* handle) { auto prep_close = [](uv_handle_t* handle) {
@ -407,7 +412,7 @@ void Http2Session::Destroy(const FunctionCallbackInfo<Value>& args) {
if (!skipUnconsume) if (!skipUnconsume)
session->Unconsume(); session->Unconsume();
session->Free(); session->Close();
} }
void Http2Session::Destroying(const FunctionCallbackInfo<Value>& args) { void Http2Session::Destroying(const FunctionCallbackInfo<Value>& args) {

View File

@ -474,6 +474,8 @@ class Http2Session : public AsyncWrap,
return stream_buf_; return stream_buf_;
} }
void Close() override;
private: private:
StreamBase* stream_; StreamBase* stream_;
StreamResource::Callback<StreamResource::AllocCb> prev_alloc_cb_; StreamResource::Callback<StreamResource::AllocCb> prev_alloc_cb_;

View File

@ -587,16 +587,18 @@ inline void Nghttp2Session::MarkDestroying() {
destroying_ = true; destroying_ = true;
} }
inline int Nghttp2Session::Free() { inline Nghttp2Session::~Nghttp2Session() {
#if defined(DEBUG) && DEBUG Close();
CHECK(session_ != nullptr); }
#endif
inline void Nghttp2Session::Close() {
if (IsClosed())
return;
DEBUG_HTTP2("Nghttp2Session %s: freeing session\n", TypeName()); DEBUG_HTTP2("Nghttp2Session %s: freeing session\n", TypeName());
nghttp2_session_terminate_session(session_, NGHTTP2_NO_ERROR); nghttp2_session_terminate_session(session_, NGHTTP2_NO_ERROR);
nghttp2_session_del(session_); nghttp2_session_del(session_);
session_ = nullptr; session_ = nullptr;
DEBUG_HTTP2("Nghttp2Session %s: session freed\n", TypeName()); DEBUG_HTTP2("Nghttp2Session %s: session freed\n", TypeName());
return 1;
} }
// Write data received from the socket to the underlying nghttp2_session. // Write data received from the socket to the underlying nghttp2_session.

View File

@ -97,7 +97,7 @@ class Nghttp2Session {
nghttp2_mem* mem = nullptr); nghttp2_mem* mem = nullptr);
// Frees this session instance // Frees this session instance
inline int Free(); inline ~Nghttp2Session();
inline void MarkDestroying(); inline void MarkDestroying();
bool IsDestroying() { bool IsDestroying() {
return destroying_; return destroying_;
@ -140,6 +140,8 @@ class Nghttp2Session {
// Returns the nghttp2 library session // Returns the nghttp2 library session
inline nghttp2_session* session() const { return session_; } inline nghttp2_session* session() const { return session_; }
inline bool IsClosed() const { return session_ == nullptr; }
nghttp2_session_type type() const { nghttp2_session_type type() const {
return session_type_; return session_type_;
} }
@ -201,6 +203,8 @@ class Nghttp2Session {
virtual uv_loop_t* event_loop() const = 0; virtual uv_loop_t* event_loop() const = 0;
virtual void Close();
private: private:
inline void HandleHeadersFrame(const nghttp2_frame* frame); inline void HandleHeadersFrame(const nghttp2_frame* frame);
inline void HandlePriorityFrame(const nghttp2_frame* frame); inline void HandlePriorityFrame(const nghttp2_frame* frame);