Regarding new uv__udp_recvmmsg in 1.35.0

36 views
Skip to first unread message

Iñaki Baz Castillo

unread,
Mar 12, 2020, 9:58:48 AM3/12/20
to li...@googlegroups.com
Hi,

libuv 1.35.0 uses uv__udp_recvmmsg() internally if the buffer given
within uv_udp_recv_cb has length >= 2 * UV__UDP_DGRAM_MAXSIZE. If so,
uv_udp_recv_cb is called with UV_UDP_MMSG_CHUNK flag enabled.

By looking at the code I see:

#if HAVE_MMSG
uv_once(&once, uv__udp_mmsg_init);
if (uv__recvmmsg_avail) {
/* Returned space for more than 1 datagram, use it to receive
* multiple datagrams. */
if (buf.len >= 2 * UV__UDP_DGRAM_MAXSIZE) {
nread = uv__udp_recvmmsg(handle, &buf);
if (nread > 0)
count -= nread;
continue;
}
}
#endif

I just want to be sure that the buffer and nread given in
uv_udp_recv_cb will just point to a **single** received UDP datagram
(as always). I assume this is true, otherwise it would be a super
breaking change since the application should do parsing to separate
different messages received into two separate UDP datagrams.

Can someone confirm that this is has no changed so there is nothing to
worry about?

Thanks.

--
Iñaki Baz Castillo
<i...@aliax.net>

Iñaki Baz Castillo

unread,
Mar 12, 2020, 3:04:33 PM3/12/20
to li...@googlegroups.com
> I just want to be sure that the buffer and nread given in
> uv_udp_recv_cb will just point to a **single** received UDP datagram
> (as always). I assume this is true, otherwise it would be a super
> breaking change since the application should do parsing to separate
> different messages received into two separate UDP datagrams.

Ok, by looking at the code in depth it seems that, indeed, nothing
changes and, no matter recvmmsg is used, a recv_cb() is called for
each received datagram:

In src/unix/udp.c:

--------------------------------
#if HAVE_MMSG
static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf) {
struct sockaddr_in6 peers[UV__MMSG_MAXWIDTH];
struct iovec iov[UV__MMSG_MAXWIDTH];
struct uv__mmsghdr msgs[UV__MMSG_MAXWIDTH];
ssize_t nread;
uv_buf_t chunk_buf;
size_t chunks;
int flags;
size_t k;

/* prepare structures for recvmmsg */
chunks = buf->len / UV__UDP_DGRAM_MAXSIZE;
if (chunks > ARRAY_SIZE(iov))
chunks = ARRAY_SIZE(iov);
for (k = 0; k < chunks; ++k) {
iov[k].iov_base = buf->base + k * UV__UDP_DGRAM_MAXSIZE;
iov[k].iov_len = UV__UDP_DGRAM_MAXSIZE;
msgs[k].msg_hdr.msg_iov = iov + k;
msgs[k].msg_hdr.msg_iovlen = 1;
msgs[k].msg_hdr.msg_name = peers + k;
msgs[k].msg_hdr.msg_namelen = sizeof(peers[0]);
}

do
nread = uv__recvmmsg(handle->io_watcher.fd, msgs, chunks, 0, NULL);
while (nread == -1 && errno == EINTR);

if (nread < 1) {
if (nread == 0 || errno == EAGAIN || errno == EWOULDBLOCK)
handle->recv_cb(handle, 0, buf, NULL, 0);
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;
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,
msgs[k].msg_len,
&chunk_buf,
msgs[k].msg_hdr.msg_name,
flags);
}
}
return nread;
}
#endif
-----------------------

Iñaki Baz Castillo

unread,
Mar 12, 2020, 3:18:15 PM3/12/20
to li...@googlegroups.com
Said that, the fact that udp_recv_cb is called in the *reverse* order
(so first received datagram is notified the last) makes me crazy.
Reply all
Reply to author
Forward
0 new messages