Skip to content

Commit e3283c7

Browse files
jasnellcjihrig
authored andcommitted
http2: allocate on every chunk send
Previously, we were using a shared stack allocated buffer to hold the serialized outbound data but that runs into issues if the outgoing stream does not write or copy immediately. Instead, allocate a buffer each time. Slight additional overhead here, but necessary. Later on, once we've analyzed this more, we might be able to switch to a stack allocated ring or slab buffer but that's a bit more complicated than what we strictly need right now. PR-URL: #16669 Reviewed-By: Anatoli Papirovski <[email protected]> Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Khaidi Chu <[email protected]> Reviewed-By: Matteo Collina <[email protected]>
1 parent dfe5684 commit e3283c7

File tree

5 files changed

+42
-34
lines changed

5 files changed

+42
-34
lines changed

src/node_http2.cc

+19-18
Original file line numberDiff line numberDiff line change
@@ -853,32 +853,33 @@ int Http2Session::DoWrite(WriteWrap* req_wrap,
853853
return 0;
854854
}
855855

856-
void Http2Session::AllocateSend(uv_buf_t* buf) {
857-
buf->base = stream_alloc();
858-
buf->len = kAllocBufferSize;
856+
WriteWrap* Http2Session::AllocateSend() {
857+
HandleScope scope(env()->isolate());
858+
auto AfterWrite = [](WriteWrap* req, int status) {
859+
req->Dispose();
860+
};
861+
Local<Object> obj =
862+
env()->write_wrap_constructor_function()
863+
->NewInstance(env()->context()).ToLocalChecked();
864+
// Base the amount allocated on the remote peers max frame size
865+
uint32_t size =
866+
nghttp2_session_get_remote_settings(
867+
session(),
868+
NGHTTP2_SETTINGS_MAX_FRAME_SIZE);
869+
// Max frame size + 9 bytes for the header
870+
return WriteWrap::New(env(), obj, this, AfterWrite, size + 9);
859871
}
860872

861-
void Http2Session::Send(uv_buf_t* buf, size_t length) {
873+
void Http2Session::Send(WriteWrap* req, char* buf, size_t length) {
862874
DEBUG_HTTP2("Http2Session: Attempting to send data\n");
863875
if (stream_ == nullptr || !stream_->IsAlive() || stream_->IsClosing()) {
864876
return;
865877
}
866-
HandleScope scope(env()->isolate());
867-
auto AfterWrite = [](WriteWrap* req_wrap, int status) {
868-
req_wrap->Dispose();
869-
};
870-
Local<Object> req_wrap_obj =
871-
env()->write_wrap_constructor_function()
872-
->NewInstance(env()->context()).ToLocalChecked();
873-
WriteWrap* write_req = WriteWrap::New(env(),
874-
req_wrap_obj,
875-
this,
876-
AfterWrite);
877878

878879
chunks_sent_since_last_write_++;
879-
uv_buf_t actual = uv_buf_init(buf->base, length);
880-
if (stream_->DoWrite(write_req, &actual, 1, nullptr)) {
881-
write_req->Dispose();
880+
uv_buf_t actual = uv_buf_init(buf, length);
881+
if (stream_->DoWrite(req, &actual, 1, nullptr)) {
882+
req->Dispose();
882883
}
883884
}
884885

src/node_http2.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,6 @@ class Http2Session : public AsyncWrap,
416416
nghttp2_headers_category cat,
417417
uint8_t flags) override;
418418
void OnStreamClose(int32_t id, uint32_t code) override;
419-
void Send(uv_buf_t* bufs, size_t total) override;
420419
void OnDataChunk(Nghttp2Stream* stream, uv_buf_t* chunk) override;
421420
void OnSettings(bool ack) override;
422421
void OnPriority(int32_t stream,
@@ -430,7 +429,9 @@ class Http2Session : public AsyncWrap,
430429
void OnFrameError(int32_t id, uint8_t type, int error_code) override;
431430
void OnTrailers(Nghttp2Stream* stream,
432431
const SubmitTrailers& submit_trailers) override;
433-
void AllocateSend(uv_buf_t* buf) override;
432+
433+
void Send(WriteWrap* req, char* buf, size_t length) override;
434+
WriteWrap* AllocateSend();
434435

435436
int DoWrite(WriteWrap* w, uv_buf_t* bufs, size_t count,
436437
uv_stream_t* send_handle) override;

src/node_http2_core-inl.h

+15-8
Original file line numberDiff line numberDiff line change
@@ -490,17 +490,22 @@ inline void Nghttp2Session::SendPendingData() {
490490
if (IsDestroying())
491491
return;
492492

493-
uv_buf_t dest;
494-
AllocateSend(&dest);
493+
WriteWrap* req = nullptr;
494+
char* dest = nullptr;
495+
size_t destRemaining = 0;
495496
size_t destLength = 0; // amount of data stored in dest
496-
size_t destRemaining = dest.len; // amount space remaining in dest
497497
size_t destOffset = 0; // current write offset of dest
498498

499499
const uint8_t* src; // pointer to the serialized data
500500
ssize_t srcLength = 0; // length of serialized data chunk
501501

502502
// While srcLength is greater than zero
503503
while ((srcLength = nghttp2_session_mem_send(session_, &src)) > 0) {
504+
if (req == nullptr) {
505+
req = AllocateSend();
506+
destRemaining = req->self_size();
507+
dest = req->Extra();
508+
}
504509
DEBUG_HTTP2("Nghttp2Session %s: nghttp2 has %d bytes to send\n",
505510
TypeName(), srcLength);
506511
size_t srcRemaining = srcLength;
@@ -512,18 +517,20 @@ inline void Nghttp2Session::SendPendingData() {
512517
while (srcRemaining > destRemaining) {
513518
DEBUG_HTTP2("Nghttp2Session %s: pushing %d bytes to the socket\n",
514519
TypeName(), destLength + destRemaining);
515-
memcpy(dest.base + destOffset, src + srcOffset, destRemaining);
520+
memcpy(dest + destOffset, src + srcOffset, destRemaining);
516521
destLength += destRemaining;
517-
Send(&dest, destLength);
522+
Send(req, dest, destLength);
518523
destOffset = 0;
519524
destLength = 0;
520525
srcRemaining -= destRemaining;
521526
srcOffset += destRemaining;
522-
destRemaining = dest.len;
527+
req = AllocateSend();
528+
destRemaining = req->self_size();
529+
dest = req->Extra();
523530
}
524531

525532
if (srcRemaining > 0) {
526-
memcpy(dest.base + destOffset, src + srcOffset, srcRemaining);
533+
memcpy(dest + destOffset, src + srcOffset, srcRemaining);
527534
destLength += srcRemaining;
528535
destOffset += srcRemaining;
529536
destRemaining -= srcRemaining;
@@ -535,7 +542,7 @@ inline void Nghttp2Session::SendPendingData() {
535542
if (destLength > 0) {
536543
DEBUG_HTTP2("Nghttp2Session %s: pushing %d bytes to the socket\n",
537544
TypeName(), destLength);
538-
Send(&dest, destLength);
545+
Send(req, dest, destLength);
539546
}
540547
}
541548

src/node_http2_core.h

+5-4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
55

6+
#include "stream_base.h"
67
#include "util-inl.h"
78
#include "uv.h"
89
#include "nghttp2/nghttp2.h"
@@ -153,7 +154,6 @@ class Nghttp2Session {
153154
// Removes a stream instance from this session
154155
inline void RemoveStream(int32_t id);
155156

156-
virtual void Send(uv_buf_t* buf, size_t length) {}
157157
virtual void OnHeaders(
158158
Nghttp2Stream* stream,
159159
std::queue<nghttp2_header>* headers,
@@ -176,7 +176,10 @@ class Nghttp2Session {
176176
int error_code) {}
177177
virtual ssize_t GetPadding(size_t frameLength,
178178
size_t maxFrameLength) { return 0; }
179-
virtual void AllocateSend(uv_buf_t* buf) = 0;
179+
180+
inline void SendPendingData();
181+
virtual void Send(WriteWrap* req, char* buf, size_t length) = 0;
182+
virtual WriteWrap* AllocateSend() = 0;
180183

181184
virtual bool HasGetPaddingCallback() { return false; }
182185

@@ -199,8 +202,6 @@ class Nghttp2Session {
199202
virtual void OnTrailers(Nghttp2Stream* stream,
200203
const SubmitTrailers& submit_trailers) {}
201204

202-
inline void SendPendingData();
203-
204205
virtual uv_loop_t* event_loop() const = 0;
205206

206207
virtual void Close();

test/parallel/parallel.status

-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,5 @@ test-npm-install: PASS,FLAKY
2020
[$system==solaris] # Also applies to SmartOS
2121

2222
[$system==freebsd]
23-
test-http2-compat-serverrequest-pipe: PASS,FLAKY
24-
test-http2-pipe: PASS,FLAKY
2523

2624
[$system==aix]

0 commit comments

Comments
 (0)