udp: return recvmmsg-ed datagrams in order

When recvmmsg support was added it returned the datagrams in reverse
received order, which may impact some applications.

To restore the previous behavior, we call recv_cb one last time with
nread == 0 and addr == NULL so applications can free the buffer.

PR-URL: https://github.com/libuv/libuv/pull/2736
Reviewed-By: Santiago Gimeno <santiago.gimeno@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
This commit is contained in:
Saúl Ibarra Corretgé 2020-03-12 20:27:35 +01:00
parent 055e89f637
commit d9cd7d437d
3 changed files with 20 additions and 13 deletions

View File

@ -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.

View File

@ -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

View File

@ -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;
}