diff --git a/docs/src/udp.rst b/docs/src/udp.rst index 2ca0736b..786a28b0 100644 --- a/docs/src/udp.rst +++ b/docs/src/udp.rst @@ -43,8 +43,7 @@ Data types */ UV_UDP_REUSEADDR = 4 /* - * Indicates that the message was received by recvmmsg and that it's not at - * the beginning of the buffer allocated by alloc_cb - so the buffer provided + * Indicates that the message was received by recvmmsg, so the buffer provided * must not be freed by the recv_cb callback. */ UV_UDP_MMSG_CHUNK = 8 @@ -72,9 +71,13 @@ Data types The callee is responsible for freeing the buffer, libuv does not reuse it. The buffer may be a null buffer (where `buf->base` == NULL and `buf->len` == 0) - on error. Don't free the buffer when the UV_UDP_MMSG_CHUNK flag is set. - The final callback receives the whole buffer (containing the first chunk) - with the UV_UDP_MMSG_CHUNK flag cleared. + on error. + + When using :man:`recvmmsg(2)`, chunks will have the `UV_UDP_MMSG_CHUNK` flag set, + those must not be freed. There will be a final callback with `nread` set to 0, + `addr` set to NULL and the buffer pointing at the initially allocated data with + the `UV_UDP_MMSG_CHUNK` flag cleared. This is a good chance for the callee to + free the provided buffer. .. note:: The receive callback will be called with `nread` == 0 and `addr` == NULL when there is @@ -373,6 +376,10 @@ API :returns: 0 on success, or an error code < 0 on failure. + .. 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`. + .. c:function:: int uv_udp_recv_stop(uv_udp_t* handle) Stop listening for incoming datagrams. diff --git a/include/uv.h b/include/uv.h index defc0ac4..0745fe0d 100644 --- a/include/uv.h +++ b/include/uv.h @@ -607,8 +607,7 @@ enum uv_udp_flags { */ UV_UDP_REUSEADDR = 4, /* - * Indicates that the message was received by recvmmsg and that it's not at - * the beginning of the buffer allocated by alloc_cb - so the buffer provided + * Indicates that the message was received by recvmmsg, so the buffer provided * must not be freed by the recv_cb callback. */ UV_UDP_MMSG_CHUNK = 8 diff --git a/src/unix/udp.c b/src/unix/udp.c index eb4b8f4c..f2fcae17 100644 --- a/src/unix/udp.c +++ b/src/unix/udp.c @@ -214,14 +214,11 @@ static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf) { else handle->recv_cb(handle, UV__ERR(errno), buf, NULL, 0); } else { - /* count to zero, so the buffer base comes last */ - for (k = nread; k > 0 && handle->recv_cb != NULL;) { - k--; - flags = 0; + /* pass each chunk to the application */ + for (k = 0; k < (size_t) nread && handle->recv_cb != NULL; k++) { + flags = UV_UDP_MMSG_CHUNK; if (msgs[k].msg_hdr.msg_flags & MSG_TRUNC) flags |= UV_UDP_PARTIAL; - if (k != 0) - flags |= UV_UDP_MMSG_CHUNK; chunk_buf = uv_buf_init(iov[k].iov_base, iov[k].iov_len); handle->recv_cb(handle, @@ -230,6 +227,10 @@ static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf) { msgs[k].msg_hdr.msg_name, flags); } + + /* one last callback so the original buffer is freed */ + if (handle->recv_cb != NULL) + handle->recv_cb(handle, 0, buf, NULL, 0); } return nread; }