diff --git a/docs/src/udp.rst b/docs/src/udp.rst index 193a86b9e..e4126fc4c 100644 --- a/docs/src/udp.rst +++ b/docs/src/udp.rst @@ -76,7 +76,8 @@ Data types */ UV_UDP_REUSEPORT = 64, /* - * Indicates that recvmmsg should be used, if available. + * Indicates that recvmmsg should be used, if available. The uv_alloc_cb + * for this handle should create buffers that are multiples of 64 KiB. */ UV_UDP_RECVMMSG = 256 }; @@ -168,7 +169,8 @@ API The remaining bits can be used to set one of these flags: * `UV_UDP_RECVMMSG`: if set, and the platform supports it, :man:`recvmmsg(2)` will - be used. + be used. The :c:type:`uv_alloc_cb` for this handle should create + buffers that are multiples of 64 KiB. .. versionadded:: 1.7.0 .. versionchanged:: 1.37.0 added the `UV_UDP_RECVMMSG` flag. @@ -481,8 +483,8 @@ API `suggested_size` in `alloc_cb` for udp_recv is always set to the size of 1 max size dgram. .. versionchanged:: 1.35.0 added support for :man:`recvmmsg(2)` on supported platforms). - The use of this feature requires a buffer larger than - 2 * 64KB to be passed to `alloc_cb`. + The :c:type:`uv_alloc_cb` for this handle should create + buffers that are multiples of 64 KiB. .. versionchanged:: 1.37.0 :man:`recvmmsg(2)` support is no longer enabled implicitly, it must be explicitly requested by passing the `UV_UDP_RECVMMSG` flag to :c:func:`uv_udp_init_ex`. diff --git a/include/uv.h b/include/uv.h index da0634555..d435a8de3 100644 --- a/include/uv.h +++ b/include/uv.h @@ -705,7 +705,8 @@ enum uv_udp_flags { */ UV_UDP_REUSEPORT = 64, /* - * Indicates that recvmmsg should be used, if available. + * Indicates that recvmmsg should be used, if available. The uv_alloc_cb + * for this handle should create buffers that are multiples of 64 KiB. */ UV_UDP_RECVMMSG = 256 }; diff --git a/src/unix/udp.c b/src/unix/udp.c index c520128bc..c497f1af2 100644 --- a/src/unix/udp.c +++ b/src/unix/udp.c @@ -217,6 +217,8 @@ static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf, int flag) { /* prepare structures for recvmmsg */ chunks = buf->len / UV__UDP_DGRAM_MAXSIZE; + if (chunks == 0) + return UV_EINVAL; if (chunks > ARRAY_SIZE(iov)) chunks = ARRAY_SIZE(iov); for (k = 0; k < chunks; ++k) { @@ -316,8 +318,11 @@ static void uv__udp_recvmsg(uv_udp_t* handle, int flag) { if (uv_udp_using_recvmmsg(handle)) { nread = uv__udp_recvmmsg(handle, &buf, flag); - if (nread > 0) - count -= nread; + if (nread <= 0) { + handle->recv_cb(handle, nread, &buf, NULL, 0); + return; + } + count -= nread; continue; } diff --git a/test/test-list.h b/test/test-list.h index 4e8ce1aba..b417f2b87 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -181,6 +181,7 @@ TEST_DECLARE (udp_recvmsg_unreachable_error) TEST_DECLARE (udp_recvmsg_unreachable_error6) TEST_DECLARE (udp_send_pollerr_no_recv) TEST_DECLARE (udp_mmsg) +TEST_DECLARE (udp_mmsg_small_buf) TEST_DECLARE (udp_multicast_join) TEST_DECLARE (udp_multicast_join6) TEST_DECLARE (udp_multicast_ttl) @@ -826,6 +827,7 @@ TASK_LIST_START TEST_ENTRY (udp_options6) TEST_ENTRY (udp_no_autobind) TEST_ENTRY (udp_mmsg) + TEST_ENTRY (udp_mmsg_small_buf) TEST_ENTRY (udp_multicast_interface) TEST_ENTRY (udp_multicast_interface6) TEST_ENTRY (udp_multicast_join) diff --git a/test/test-udp-mmsg.c b/test/test-udp-mmsg.c index 73213c43d..108ffea6b 100644 --- a/test/test-udp-mmsg.c +++ b/test/test-udp-mmsg.c @@ -106,6 +106,31 @@ static void recv_cb(uv_udp_t* handle, } +static void small_alloc_cb(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + CHECK_HANDLE(handle); + buf->len = 64; + buf->base = malloc(buf->len); + ASSERT_NOT_NULL(buf->base); + alloc_cb_called++; +} + + +static void small_recv_cb(uv_udp_t* handle, + ssize_t nread, + const uv_buf_t* rcvbuf, + const struct sockaddr* addr, + unsigned flags) { + CHECK_HANDLE(handle); + ASSERT_EQ(UV_EINVAL, nread); + uv_close((uv_handle_t*) &recver, close_cb); + uv_close((uv_handle_t*) &sender, close_cb); + free(rcvbuf->base); + recv_cb_called++; +} + + TEST_IMPL(udp_mmsg) { struct sockaddr_in addr; uv_buf_t buf; @@ -148,3 +173,31 @@ TEST_IMPL(udp_mmsg) { MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } + + +TEST_IMPL(udp_mmsg_small_buf) { + struct sockaddr_in addr; + uv_loop_t* loop; + uv_buf_t buf; + + loop = uv_default_loop(); + ASSERT_OK(uv_udp_init_ex(loop, &recver, AF_UNSPEC | UV_UDP_RECVMMSG)); + if (uv_udp_using_recvmmsg(&recver)) { + ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + ASSERT_OK(uv_udp_bind(&recver, (const struct sockaddr*) &addr, 0)); + ASSERT_OK(uv_udp_recv_start(&recver, small_alloc_cb, small_recv_cb)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_udp_init(loop, &sender)); + buf = uv_buf_init("PING", 4); + ASSERT_EQ(4, uv_udp_try_send(&sender, &buf, 1, (const struct sockaddr*) &addr)); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_EQ(1, recv_cb_called); + ASSERT_EQ(2, close_cb_called); + } else { + uv_close((uv_handle_t*) &recver, close_cb); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_EQ(1, close_cb_called); + } + MAKE_VALGRIND_HAPPY(loop); + return 0; +}