diff --git a/include/uv.h b/include/uv.h index 78232b05..6a385583 100644 --- a/include/uv.h +++ b/include/uv.h @@ -342,6 +342,12 @@ UV_EXTERN int uv_backend_timeout(const uv_loop_t*); * * `suggested_size` is a hint. Returning a buffer that is smaller is perfectly * okay as long as `buf.len > 0`. + * + * If you return a buffer with `buf.len == 0`, libuv skips the read and calls + * your read or recv callback with nread=UV_ENOBUFS. + * + * Note that returning a zero-length buffer does not stop the handle, call + * uv_read_stop() or uv_udp_recv_stop() for that. */ typedef uv_buf_t (*uv_alloc_cb)(uv_handle_t* handle, size_t suggested_size); diff --git a/src/unix/stream.c b/src/unix/stream.c index 4b449dce..6d1144f6 100644 --- a/src/unix/stream.c +++ b/src/unix/stream.c @@ -977,11 +977,16 @@ static void uv__read(uv_stream_t* stream) { while ((stream->read_cb || stream->read2_cb) && (stream->flags & UV_STREAM_READING) && (count-- > 0)) { - assert(stream->alloc_cb); - buf = stream->alloc_cb((uv_handle_t*)stream, 64 * 1024); + assert(stream->alloc_cb != NULL); - assert(buf.len > 0); - assert(buf.base); + buf = stream->alloc_cb((uv_handle_t*)stream, 64 * 1024); + if (buf.len == 0) { + /* User indicates it can't or won't handle the read. */ + uv__stream_read_cb(stream, UV_ENOBUFS, buf, UV_UNKNOWN_HANDLE); + return; + } + + assert(buf.base != NULL); assert(uv__stream_fd(stream) >= 0); if (stream->read_cb) { diff --git a/src/unix/udp.c b/src/unix/udp.c index 72a4acf9..8c3fc9cd 100644 --- a/src/unix/udp.c +++ b/src/unix/udp.c @@ -205,8 +205,11 @@ static void uv__udp_recvmsg(uv_loop_t* loop, h.msg_name = &peer; do { - buf = handle->alloc_cb((uv_handle_t*)handle, 64 * 1024); - assert(buf.len > 0); + buf = handle->alloc_cb((uv_handle_t*) handle, 64 * 1024); + if (buf.len == 0) { + handle->recv_cb(handle, UV_ENOBUFS, buf, NULL, 0); + return; + } assert(buf.base != NULL); h.msg_namelen = sizeof(peer); diff --git a/src/win/pipe.c b/src/win/pipe.c index 171c077f..03b786ce 100644 --- a/src/win/pipe.c +++ b/src/win/pipe.c @@ -1429,7 +1429,15 @@ void uv_process_pipe_read_req(uv_loop_t* loop, uv_pipe_t* handle, } buf = handle->alloc_cb((uv_handle_t*) handle, avail); - assert(buf.len > 0); + if (buf.len == 0) { + if (handle->read2_cb) { + handle->read2_cb(handle, UV_ENOBUFS, buf, UV_UNKNOWN_HANDLE); + } else if (handle->read_cb) { + handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, buf); + } + break; + } + assert(buf.base != NULL); if (ReadFile(handle->handle, buf.base, diff --git a/src/win/tcp.c b/src/win/tcp.c index 281b6fe0..0c7a521b 100644 --- a/src/win/tcp.c +++ b/src/win/tcp.c @@ -434,7 +434,11 @@ static void uv_tcp_queue_read(uv_loop_t* loop, uv_tcp_t* handle) { if (loop->active_tcp_streams < uv_active_tcp_streams_threshold) { handle->flags &= ~UV_HANDLE_ZERO_READ; handle->read_buffer = handle->alloc_cb((uv_handle_t*) handle, 65536); - assert(handle->read_buffer.len > 0); + if (handle->read_buffer.len == 0) { + handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, buf); + return; + } + assert(handle->read_buffer.base != NULL); buf = handle->read_buffer; } else { handle->flags |= UV_HANDLE_ZERO_READ; @@ -943,7 +947,12 @@ void uv_process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle, /* Do nonblocking reads until the buffer is empty */ while (handle->flags & UV_HANDLE_READING) { buf = handle->alloc_cb((uv_handle_t*) handle, 65536); - assert(buf.len > 0); + if (buf.len == 0) { + handle->read_cb(handle, UV_ENOBUFS, buf); + break; + } + assert(buf.base != NULL); + flags = 0; if (WSARecv(handle->socket, (WSABUF*)&buf, diff --git a/src/win/tty.c b/src/win/tty.c index ee615295..e6077a99 100644 --- a/src/win/tty.c +++ b/src/win/tty.c @@ -348,8 +348,11 @@ static void uv_tty_queue_read_line(uv_loop_t* loop, uv_tty_t* handle) { memset(&req->overlapped, 0, sizeof(req->overlapped)); handle->read_line_buffer = handle->alloc_cb((uv_handle_t*) handle, 8192); + if (handle->read_line_buffer.len == 0) { + handle->read_cb(handle, UV_ENOBUFS, handle->read_line_buffer); + return; + } assert(handle->read_line_buffer.base != NULL); - assert(handle->read_line_buffer.len > 0); /* Duplicate the console handle, so if we want to cancel the read, we can */ /* just close this handle duplicate. */ @@ -682,6 +685,11 @@ void uv_process_tty_read_raw_req(uv_loop_t* loop, uv_tty_t* handle, /* Allocate a buffer if needed */ if (buf_used == 0) { buf = handle->alloc_cb((uv_handle_t*) handle, 1024); + if (buf.len == 0) { + handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, buf); + goto out; + } + assert(buf.base != NULL); } buf.base[buf_used++] = handle->last_key[handle->last_key_offset++]; diff --git a/src/win/udp.c b/src/win/udp.c index 07bd5298..ba21b545 100644 --- a/src/win/udp.c +++ b/src/win/udp.c @@ -272,7 +272,11 @@ static void uv_udp_queue_recv(uv_loop_t* loop, uv_udp_t* handle) { handle->flags &= ~UV_HANDLE_ZERO_READ; handle->recv_buffer = handle->alloc_cb((uv_handle_t*) handle, 65536); - assert(handle->recv_buffer.len > 0); + if (handle->recv_buffer.len == 0) { + handle->recv_cb(handle, UV_ENOBUFS, handle->recv_buffer, NULL, 0); + return; + } + assert(handle->recv_buffer.base != NULL); buf = handle->recv_buffer; memset(&handle->recv_from, 0, sizeof handle->recv_from); @@ -515,7 +519,11 @@ void uv_process_udp_recv_req(uv_loop_t* loop, uv_udp_t* handle, /* Do a nonblocking receive */ /* TODO: try to read multiple datagrams at once. FIONREAD maybe? */ buf = handle->alloc_cb((uv_handle_t*) handle, 65536); - assert(buf.len > 0); + if (buf.len == 0) { + handle->recv_cb(handle, UV_ENOBUFS, buf, NULL, 0); + goto done; + } + assert(buf.base != NULL); memset(&from, 0, sizeof from); from_len = sizeof from;