Skip to content
This repository
Browse code

unix: fix loop starvation under high network load

uv__read() and uv__udp_recvmsg() read incoming data in a loop. If data comes
in at high speeds, the kernel receive buffer never drains and said functions
never terminate, stalling the event loop indefinitely. Limit the number of
consecutive reads to 32 to stop that from happening.

The number 32 was chosen at random. Empirically, it seems to maintain a high
throughput while still making the event loop move forward at a reasonable pace.
  • Loading branch information...
commit 738b31eb3aff440ae75ff9f32ba61086a948c3f4 1 parent be9d1ce
Ben Noordhuis bnoordhuis authored

Showing 2 changed files with 16 additions and 3 deletions. Show diff stats Hide diff stats

  1. +9 2 src/unix/stream.c
  2. +7 1 src/unix/udp.c
11 src/unix/stream.c
@@ -561,12 +561,19 @@ static void uv__read(uv_stream_t* stream) {
561 561 struct msghdr msg;
562 562 struct cmsghdr* cmsg;
563 563 char cmsg_space[64];
  564 + int count;
  565 +
  566 + /* Prevent loop starvation when the data comes in as fast as (or faster than)
  567 + * we can read it. XXX Need to rearm fd if we switch to edge-triggered I/O.
  568 + */
  569 + count = 32;
564 570
565 571 /* XXX: Maybe instead of having UV_STREAM_READING we just test if
566 572 * tcp->read_cb is NULL or not?
567 573 */
568   - while ((stream->read_cb || stream->read2_cb) &&
569   - stream->flags & UV_STREAM_READING) {
  574 + while ((stream->read_cb || stream->read2_cb)
  575 + && (stream->flags & UV_STREAM_READING)
  576 + && (count-- > 0)) {
570 577 assert(stream->alloc_cb);
571 578 buf = stream->alloc_cb((uv_handle_t*)stream, 64 * 1024);
572 579
8 src/unix/udp.c
@@ -196,6 +196,7 @@ static void uv__udp_recvmsg(uv_loop_t* loop, uv__io_t* w, int revents) {
196 196 ssize_t nread;
197 197 uv_buf_t buf;
198 198 int flags;
  199 + int count;
199 200
200 201 handle = container_of(w, uv_udp_t, read_watcher);
201 202 assert(handle->type == UV_UDP);
@@ -204,8 +205,12 @@ static void uv__udp_recvmsg(uv_loop_t* loop, uv__io_t* w, int revents) {
204 205 assert(handle->recv_cb != NULL);
205 206 assert(handle->alloc_cb != NULL);
206 207
  208 + /* Prevent loop starvation when the data comes in as fast as (or faster than)
  209 + * we can read it. XXX Need to rearm fd if we switch to edge-triggered I/O.
  210 + */
  211 + count = 32;
  212 +
207 213 do {
208   - /* FIXME: hoist alloc_cb out the loop but for now follow uv__read() */
209 214 buf = handle->alloc_cb((uv_handle_t*)handle, 64 * 1024);
210 215 assert(buf.len > 0);
211 216 assert(buf.base != NULL);
@@ -246,6 +251,7 @@ static void uv__udp_recvmsg(uv_loop_t* loop, uv__io_t* w, int revents) {
246 251 }
247 252 /* recv_cb callback may decide to pause or close the handle */
248 253 while (nread != -1
  254 + && count-- > 0
249 255 && handle->fd != -1
250 256 && handle->recv_cb != NULL);
251 257 }

0 comments on commit 738b31e

Please sign in to comment.
Something went wrong with that request. Please try again.