Skip to content

Commit 319beaf

Browse files
jasnellMylesBorins
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 <apapirovski@mac.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Khaidi Chu <i@2333.moe> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
1 parent 18652b6 commit 319beaf

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
@@ -843,32 +843,33 @@ int Http2Session::DoWrite(WriteWrap* req_wrap,
843843
return 0;
844844
}
845845

846-
void Http2Session::AllocateSend(uv_buf_t* buf) {
847-
buf->base = stream_alloc();
848-
buf->len = kAllocBufferSize;
846+
WriteWrap* Http2Session::AllocateSend() {
847+
HandleScope scope(env()->isolate());
848+
auto AfterWrite = [](WriteWrap* req, int status) {
849+
req->Dispose();
850+
};
851+
Local<Object> obj =
852+
env()->write_wrap_constructor_function()
853+
->NewInstance(env()->context()).ToLocalChecked();
854+
// Base the amount allocated on the remote peers max frame size
855+
uint32_t size =
856+
nghttp2_session_get_remote_settings(
857+
session(),
858+
NGHTTP2_SETTINGS_MAX_FRAME_SIZE);
859+
// Max frame size + 9 bytes for the header
860+
return WriteWrap::New(env(), obj, this, AfterWrite, size + 9);
849861
}
850862

851-
void Http2Session::Send(uv_buf_t* buf, size_t length) {
863+
void Http2Session::Send(WriteWrap* req, char* buf, size_t length) {
852864
DEBUG_HTTP2("Http2Session: Attempting to send data\n");
853865
if (stream_ == nullptr || !stream_->IsAlive() || stream_->IsClosing()) {
854866
return;
855867
}
856-
HandleScope scope(env()->isolate());
857-
auto AfterWrite = [](WriteWrap* req_wrap, int status) {
858-
req_wrap->Dispose();
859-
};
860-
Local<Object> req_wrap_obj =
861-
env()->write_wrap_constructor_function()
862-
->NewInstance(env()->context()).ToLocalChecked();
863-
WriteWrap* write_req = WriteWrap::New(env(),
864-
req_wrap_obj,
865-
this,
866-
AfterWrite);
867868

868869
chunks_sent_since_last_write_++;
869-
uv_buf_t actual = uv_buf_init(buf->base, length);
870-
if (stream_->DoWrite(write_req, &actual, 1, nullptr)) {
871-
write_req->Dispose();
870+
uv_buf_t actual = uv_buf_init(buf, length);
871+
if (stream_->DoWrite(req, &actual, 1, nullptr)) {
872+
req->Dispose();
872873
}
873874
}
874875

src/node_http2.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,6 @@ class Http2Session : public AsyncWrap,
430430
nghttp2_headers_category cat,
431431
uint8_t flags) override;
432432
void OnStreamClose(int32_t id, uint32_t code) override;
433-
void Send(uv_buf_t* bufs, size_t total) override;
434433
void OnDataChunk(Nghttp2Stream* stream, uv_buf_t* chunk) override;
435434
void OnSettings(bool ack) override;
436435
void OnPriority(int32_t stream,
@@ -444,7 +443,9 @@ class Http2Session : public AsyncWrap,
444443
void OnFrameError(int32_t id, uint8_t type, int error_code) override;
445444
void OnTrailers(Nghttp2Stream* stream,
446445
const SubmitTrailers& submit_trailers) override;
447-
void AllocateSend(uv_buf_t* buf) override;
446+
447+
void Send(WriteWrap* req, char* buf, size_t length) override;
448+
WriteWrap* AllocateSend();
448449

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

src/node_http2_core-inl.h

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

491-
uv_buf_t dest;
492-
AllocateSend(&dest);
491+
WriteWrap* req = nullptr;
492+
char* dest = nullptr;
493+
size_t destRemaining = 0;
493494
size_t destLength = 0; // amount of data stored in dest
494-
size_t destRemaining = dest.len; // amount space remaining in dest
495495
size_t destOffset = 0; // current write offset of dest
496496

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

500500
// While srcLength is greater than zero
501501
while ((srcLength = nghttp2_session_mem_send(session_, &src)) > 0) {
502+
if (req == nullptr) {
503+
req = AllocateSend();
504+
destRemaining = req->self_size();
505+
dest = req->Extra();
506+
}
502507
DEBUG_HTTP2("Nghttp2Session %s: nghttp2 has %d bytes to send\n",
503508
TypeName(), srcLength);
504509
size_t srcRemaining = srcLength;
@@ -510,18 +515,20 @@ inline void Nghttp2Session::SendPendingData() {
510515
while (srcRemaining > destRemaining) {
511516
DEBUG_HTTP2("Nghttp2Session %s: pushing %d bytes to the socket\n",
512517
TypeName(), destLength + destRemaining);
513-
memcpy(dest.base + destOffset, src + srcOffset, destRemaining);
518+
memcpy(dest + destOffset, src + srcOffset, destRemaining);
514519
destLength += destRemaining;
515-
Send(&dest, destLength);
520+
Send(req, dest, destLength);
516521
destOffset = 0;
517522
destLength = 0;
518523
srcRemaining -= destRemaining;
519524
srcOffset += destRemaining;
520-
destRemaining = dest.len;
525+
req = AllocateSend();
526+
destRemaining = req->self_size();
527+
dest = req->Extra();
521528
}
522529

523530
if (srcRemaining > 0) {
524-
memcpy(dest.base + destOffset, src + srcOffset, srcRemaining);
531+
memcpy(dest + destOffset, src + srcOffset, srcRemaining);
525532
destLength += srcRemaining;
526533
destOffset += srcRemaining;
527534
destRemaining -= srcRemaining;
@@ -533,7 +540,7 @@ inline void Nghttp2Session::SendPendingData() {
533540
if (destLength > 0) {
534541
DEBUG_HTTP2("Nghttp2Session %s: pushing %d bytes to the socket\n",
535542
TypeName(), destLength);
536-
Send(&dest, destLength);
543+
Send(req, dest, destLength);
537544
}
538545
}
539546

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
@@ -18,7 +18,5 @@ test-npm-install: PASS,FLAKY
1818
[$system==solaris] # Also applies to SmartOS
1919

2020
[$system==freebsd]
21-
test-http2-compat-serverrequest-pipe: PASS,FLAKY
22-
test-http2-pipe: PASS,FLAKY
2321

2422
[$system==aix]

0 commit comments

Comments
 (0)