Skip to content

Commit

Permalink
freebsd,linux: add recvmmsg() + sendmmsg() udp implementation
Browse files Browse the repository at this point in the history
This commits adds support for recvmmsg() and sendmmsg() extensions to
recvmsg() and sendmsg() that allows the caller to receive and send
multiple message from a socket using a single system call. This has
performance benefits for some applications.

Co-authored-by: Ondřej Surý <[email protected]>
Co-authored-by: Witold Kręcicki <[email protected]>

PR-URL: libuv#2532
Reviewed-By: Ben Noordhuis <[email protected]>
Reviewed-By: Saúl Ibarra Corretgé <[email protected]>
Reviewed-By: Santiago Gimeno <[email protected]>
  • Loading branch information
vavrusa authored and saghul committed Feb 20, 2020
1 parent 56598f3 commit 3d71366
Show file tree
Hide file tree
Showing 7 changed files with 271 additions and 24 deletions.
13 changes: 10 additions & 3 deletions docs/src/udp.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ Data types
* any traffic, in effect "stealing" the port from the previous listener.
*/
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
* must not be freed by the recv_cb callback.
*/
UV_UDP_MMSG_CHUNK = 8
};

.. c:type:: void (*uv_udp_send_cb)(uv_udp_send_t* req, int status)
Expand All @@ -62,12 +68,13 @@ Data types
* `buf`: :c:type:`uv_buf_t` with the received data.
* `addr`: ``struct sockaddr*`` containing the address of the sender.
Can be NULL. Valid for the duration of the callback only.
* `flags`: One or more or'ed UV_UDP_* constants. Right now only
``UV_UDP_PARTIAL`` is used.
* `flags`: One or more or'ed UV_UDP_* constants.

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

.. note::
The receive callback will be called with `nread` == 0 and `addr` == NULL when there is
Expand Down
8 changes: 7 additions & 1 deletion include/uv.h
Original file line number Diff line number Diff line change
Expand Up @@ -605,7 +605,13 @@ enum uv_udp_flags {
* (provided they all set the flag) but only the last one to bind will receive
* any traffic, in effect "stealing" the port from the previous listener.
*/
UV_UDP_REUSEADDR = 4
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
* must not be freed by the recv_cb callback.
*/
UV_UDP_MMSG_CHUNK = 8
};

typedef void (*uv_udp_send_cb)(uv_udp_send_t* req, int status);
Expand Down
25 changes: 25 additions & 0 deletions src/unix/freebsd.c
Original file line number Diff line number Diff line change
Expand Up @@ -288,3 +288,28 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
uv__free(cp_times);
return 0;
}


int uv__sendmmsg(int fd,
struct uv__mmsghdr* mmsg,
unsigned int vlen,
unsigned int flags) {
#if __FreeBSD__ >= 11
return sendmmsg(fd, mmsg, vlen, flags);
#else
return errno = ENOSYS, -1;
#endif
}


int uv__recvmmsg(int fd,
struct uv__mmsghdr* mmsg,
unsigned int vlen,
unsigned int flags,
struct timespec* timeout) {
#if __FreeBSD__ >= 11
return recvmmsg(fd, mmsg, vlen, flags, timeout);
#else
return errno = ENOSYS, -1;
#endif
}
24 changes: 24 additions & 0 deletions src/unix/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include <fcntl.h> /* O_CLOEXEC and O_NONBLOCK, if supported. */
#include <stdio.h>
#include <errno.h>
#include <sys/socket.h>

#if defined(__STRICT_ANSI__)
# define inline __inline
Expand Down Expand Up @@ -327,4 +328,27 @@ int uv__getsockpeername(const uv_handle_t* handle,
struct sockaddr* name,
int* namelen);

#if defined(__linux__) || \
defined(__FreeBSD__) || \
defined(__FreeBSD_kernel__)
#define HAVE_MMSG 1
struct uv__mmsghdr {
struct msghdr msg_hdr;
unsigned int msg_len;
};

int uv__recvmmsg(int fd,
struct uv__mmsghdr* mmsg,
unsigned int vlen,
unsigned int flags,
struct timespec* timeout);
int uv__sendmmsg(int fd,
struct uv__mmsghdr* mmsg,
unsigned int vlen,
unsigned int flags);
#else
#define HAVE_MMSG 0
#endif


#endif /* UV_UNIX_INTERNAL_H_ */
1 change: 1 addition & 0 deletions src/unix/linux-syscalls.c
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@
# endif
#endif /* __NR_getrandom */

struct uv__mmsghdr;

int uv__sendmmsg(int fd,
struct uv__mmsghdr* mmsg,
Expand Down
14 changes: 0 additions & 14 deletions src/unix/linux-syscalls.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,20 +61,6 @@ struct uv__statx {
uint64_t unused1[14];
};

struct uv__mmsghdr {
struct msghdr msg_hdr;
unsigned int msg_len;
};

int uv__recvmmsg(int fd,
struct uv__mmsghdr* mmsg,
unsigned int vlen,
unsigned int flags,
struct timespec* timeout);
int uv__sendmmsg(int fd,
struct uv__mmsghdr* mmsg,
unsigned int vlen,
unsigned int flags);
ssize_t uv__preadv(int fd, const struct iovec *iov, int iovcnt, int64_t offset);
ssize_t uv__pwritev(int fd, const struct iovec *iov, int iovcnt, int64_t offset);
int uv__dup3(int oldfd, int newfd, int flags);
Expand Down
Loading

0 comments on commit 3d71366

Please sign in to comment.