[libc-commits] [libc] 6ff34e9 - [libc] Implement recvmmsg (on linux) (#202328)
via libc-commits
libc-commits at lists.llvm.org
Thu Jun 11 06:22:20 PDT 2026
Author: Pavel Labath
Date: 2026-06-11T15:22:15+02:00
New Revision: 6ff34e926827050dfca1874b4fdf16f186f533f0
URL: https://github.com/llvm/llvm-project/commit/6ff34e926827050dfca1874b4fdf16f186f533f0
DIFF: https://github.com/llvm/llvm-project/commit/6ff34e926827050dfca1874b4fdf16f186f533f0.diff
LOG: [libc] Implement recvmmsg (on linux) (#202328)
The function (unlike sendmmsg) takes a timeout argument, so I make sure
to always call the version with a 64-bit time_t. I've also renamed the
sendmmsg_test.cpp unit test to sendrecvmmsg_test.cpp to test both
functions. I also updated the yaml config and docgen files to include
the new function and the struct_timespec type dependency.
Assisted by Gemini.
Added:
libc/src/__support/OSUtil/linux/syscall_wrappers/recvmmsg.h
libc/src/sys/socket/linux/recvmmsg.cpp
libc/src/sys/socket/recvmmsg.h
libc/test/src/sys/socket/linux/sendrecvmmsg_test.cpp
Modified:
libc/config/linux/aarch64/entrypoints.txt
libc/config/linux/riscv/entrypoints.txt
libc/config/linux/x86_64/entrypoints.txt
libc/include/sys/socket.yaml
libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
libc/src/sys/socket/CMakeLists.txt
libc/src/sys/socket/linux/CMakeLists.txt
libc/test/src/sys/socket/linux/CMakeLists.txt
libc/utils/docgen/sys/socket.yaml
Removed:
libc/test/src/sys/socket/linux/sendmmsg_test.cpp
################################################################################
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 854d8dd4a8db3..9eb6b61f57729 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -302,6 +302,7 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.sys.socket.socketpair
# TODO: These functions are not compatible with 64-bit musl. They need to
# be excluded if overlaying musl.
+ libc.src.sys.socket.recvmmsg
libc.src.sys.socket.recvmsg
libc.src.sys.socket.sendmmsg
libc.src.sys.socket.sendmsg
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 90738562f8441..189f748279421 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -302,6 +302,7 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.sys.socket.socketpair
# TODO: These functions are not compatible with 64-bit musl. They need to
# be excluded if overlaying musl.
+ libc.src.sys.socket.recvmmsg
libc.src.sys.socket.recvmsg
libc.src.sys.socket.sendmmsg
libc.src.sys.socket.sendmsg
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index c9e9fb98ce362..d70908f873cc6 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -328,6 +328,7 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.sys.socket.socketpair
# TODO: These functions are not compatible with 64-bit musl. They need to
# be excluded if overlaying musl.
+ libc.src.sys.socket.recvmmsg
libc.src.sys.socket.recvmsg
libc.src.sys.socket.sendmmsg
libc.src.sys.socket.sendmsg
diff --git a/libc/include/sys/socket.yaml b/libc/include/sys/socket.yaml
index c4f4b0fc58fce..18b8b9e750aa7 100644
--- a/libc/include/sys/socket.yaml
+++ b/libc/include/sys/socket.yaml
@@ -11,6 +11,7 @@ types:
- type_name: sa_family_t
- type_name: struct_cmsghdr
- type_name: struct_mmsghdr
+ - type_name: struct_timespec
- type_name: struct_msghdr
- type_name: struct_iovec
- type_name: struct_linger
@@ -105,6 +106,16 @@ functions:
- type: int
- type: struct sockaddr *__restrict
- type: socklen_t *__restrict
+ - name: recvmmsg
+ standards:
+ - linux
+ return_type: int
+ arguments:
+ - type: int
+ - type: struct mmsghdr *
+ - type: unsigned int
+ - type: int
+ - type: struct timespec *
- name: recvmsg
standards:
- posix
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
index 4fc8c4eb3b67a..97848b1848660 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
@@ -175,6 +175,20 @@ add_header_library(
libc.include.sys_syscall
)
+add_header_library(
+ recvmmsg
+ HDRS
+ recvmmsg.h
+ DEPENDS
+ libc.src.__support.OSUtil.osutil
+ libc.src.__support.common
+ libc.src.__support.error_or
+ libc.src.__support.macros.config
+ libc.hdr.types.struct_mmsghdr
+ libc.hdr.types.struct_timespec
+ libc.include.sys_syscall
+)
+
add_header_library(
recvmsg
HDRS
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/recvmmsg.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/recvmmsg.h
new file mode 100644
index 0000000000000..4faebc99f90b5
--- /dev/null
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/recvmmsg.h
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Syscall wrapper for recvmmsg.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_RECVMMSG_H
+#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_RECVMMSG_H
+
+#include "hdr/types/struct_mmsghdr.h"
+#include "hdr/types/struct_timespec.h"
+#include "src/__support/OSUtil/linux/syscall.h" // For syscall_checked
+#include "src/__support/macros/config.h"
+#include <sys/syscall.h> // For syscall numbers
+
+namespace LIBC_NAMESPACE_DECL {
+namespace linux_syscalls {
+
+LIBC_INLINE ErrorOr<int> recvmmsg(int sockfd, struct mmsghdr *msgvec,
+ unsigned int vlen, int flags,
+ struct timespec *timeout) {
+#ifdef SYS_recvmmsg_time64
+ return syscall_checked<int>(SYS_recvmmsg_time64, sockfd, msgvec, vlen, flags,
+ timeout);
+#else
+ static_assert(
+ sizeof(timespec::tv_nsec) == sizeof(long),
+ "This legacy syscall fallback is only safe on platforms where tv_nsec "
+ "matches the register size (long). It is unsafe on 32-bit platforms "
+ "with 64-bit tv_nsec.");
+ return syscall_checked<int>(SYS_recvmmsg, sockfd, msgvec, vlen, flags,
+ timeout);
+#endif
+}
+
+} // namespace linux_syscalls
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_RECVMMSG_H
diff --git a/libc/src/sys/socket/CMakeLists.txt b/libc/src/sys/socket/CMakeLists.txt
index 22b665392a4d6..45c0aee6f6f3b 100644
--- a/libc/src/sys/socket/CMakeLists.txt
+++ b/libc/src/sys/socket/CMakeLists.txt
@@ -128,6 +128,14 @@ add_entrypoint_object(
.${LIBC_TARGET_OS}.recvmsg
)
+add_entrypoint_object(
+ recvmmsg
+ ALIAS
+ DEPENDS
+ .${LIBC_TARGET_OS}.recvmmsg
+)
+
+
add_entrypoint_object(
shutdown
ALIAS
diff --git a/libc/src/sys/socket/linux/CMakeLists.txt b/libc/src/sys/socket/linux/CMakeLists.txt
index b8ff28f94dd2c..82a7554f80107 100644
--- a/libc/src/sys/socket/linux/CMakeLists.txt
+++ b/libc/src/sys/socket/linux/CMakeLists.txt
@@ -236,6 +236,20 @@ add_entrypoint_object(
libc.src.errno.errno
)
+add_entrypoint_object(
+ recvmmsg
+ SRCS
+ recvmmsg.cpp
+ HDRS
+ ../recvmmsg.h
+ DEPENDS
+ libc.hdr.types.struct_mmsghdr
+ libc.hdr.types.struct_timespec
+ libc.src.__support.common
+ libc.src.__support.OSUtil.linux.syscall_wrappers.recvmmsg
+ libc.src.errno.errno
+)
+
add_entrypoint_object(
recvmsg
SRCS
diff --git a/libc/src/sys/socket/linux/recvmmsg.cpp b/libc/src/sys/socket/linux/recvmmsg.cpp
new file mode 100644
index 0000000000000..509c521e921a2
--- /dev/null
+++ b/libc/src/sys/socket/linux/recvmmsg.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Linux implementation of recvmmsg.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/sys/socket/recvmmsg.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/recvmmsg.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, recvmmsg,
+ (int sockfd, struct mmsghdr *msgvec, unsigned int vlen,
+ int flags, struct timespec *timeout)) {
+ auto result = linux_syscalls::recvmmsg(sockfd, msgvec, vlen, flags, timeout);
+ if (!result.has_value()) {
+ libc_errno = result.error();
+ return -1;
+ }
+ return result.value();
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/sys/socket/recvmmsg.h b/libc/src/sys/socket/recvmmsg.h
new file mode 100644
index 0000000000000..7dc12b25a1d27
--- /dev/null
+++ b/libc/src/sys/socket/recvmmsg.h
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Declaration of the recvmmsg function.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SYS_SOCKET_RECVMMSG_H
+#define LLVM_LIBC_SRC_SYS_SOCKET_RECVMMSG_H
+
+#include "hdr/types/struct_mmsghdr.h"
+#include "hdr/types/struct_timespec.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int recvmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags,
+ struct timespec *timeout);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SYS_SOCKET_RECVMMSG_H
diff --git a/libc/test/src/sys/socket/linux/CMakeLists.txt b/libc/test/src/sys/socket/linux/CMakeLists.txt
index 5e39b90813cb9..b3ac8c1fbdd40 100644
--- a/libc/test/src/sys/socket/linux/CMakeLists.txt
+++ b/libc/test/src/sys/socket/linux/CMakeLists.txt
@@ -211,19 +211,21 @@ add_libc_unittest(
)
add_libc_unittest(
- sendmmsg_test
+ sendrecvmmsg_test
SUITE
libc_sys_socket_unittests
SRCS
- sendmmsg_test.cpp
+ sendrecvmmsg_test.cpp
DEPENDS
libc.hdr.sys_socket_macros
+ libc.hdr.types.struct_iovec
libc.hdr.types.struct_mmsghdr
+ libc.hdr.types.struct_timespec
libc.src.errno.errno
libc.src.string.strlen
libc.src.sys.socket.socketpair
libc.src.sys.socket.sendmmsg
- libc.src.sys.socket.recvmsg
+ libc.src.sys.socket.recvmmsg
libc.src.unistd.close
libc.src.__support.CPP.scope
libc.test.UnitTest.ErrnoCheckingTest
diff --git a/libc/test/src/sys/socket/linux/sendmmsg_test.cpp b/libc/test/src/sys/socket/linux/sendrecvmmsg_test.cpp
similarity index 57%
rename from libc/test/src/sys/socket/linux/sendmmsg_test.cpp
rename to libc/test/src/sys/socket/linux/sendrecvmmsg_test.cpp
index adbfe20205b8f..75f352802f124 100644
--- a/libc/test/src/sys/socket/linux/sendmmsg_test.cpp
+++ b/libc/test/src/sys/socket/linux/sendrecvmmsg_test.cpp
@@ -7,15 +7,17 @@
//===----------------------------------------------------------------------===//
///
/// \file
-/// Unit tests for sendmmsg.
+/// Unit tests for sendmmsg and recvmmsg.
///
//===----------------------------------------------------------------------===//
#include "hdr/sys_socket_macros.h"
+#include "hdr/types/struct_iovec.h"
#include "hdr/types/struct_mmsghdr.h"
+#include "hdr/types/struct_timespec.h"
#include "src/__support/CPP/scope.h"
#include "src/string/strlen.h"
-#include "src/sys/socket/recvmsg.h"
+#include "src/sys/socket/recvmmsg.h"
#include "src/sys/socket/sendmmsg.h"
#include "src/sys/socket/socketpair.h"
#include "src/unistd/close.h"
@@ -26,9 +28,9 @@
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
-using LlvmLibcSendMmsgTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+using LlvmLibcSendRecvMmsgTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
-TEST_F(LlvmLibcSendMmsgTest, SendMmsgSucceedsWithSocketPair) {
+TEST_F(LlvmLibcSendRecvMmsgTest, SendRecvMmsgSucceedsWithSocketPair) {
const char *const TEST_MESSAGES[] = {"message one", "message two"};
const size_t MESSAGES_COUNT = 2;
@@ -44,8 +46,7 @@ TEST_F(LlvmLibcSendMmsgTest, SendMmsgSucceedsWithSocketPair) {
struct iovec send_msg_vec[MESSAGES_COUNT] = {};
struct mmsghdr send_msg_hdr[MESSAGES_COUNT] = {};
for (size_t i = 0; i < MESSAGES_COUNT; ++i) {
- send_msg_vec[i].iov_base =
- reinterpret_cast<void *>(const_cast<char *>(TEST_MESSAGES[i]));
+ send_msg_vec[i].iov_base = const_cast<char *>(TEST_MESSAGES[i]);
send_msg_vec[i].iov_len = LIBC_NAMESPACE::strlen(TEST_MESSAGES[i]) + 1;
send_msg_hdr[i].msg_hdr.msg_iov = &send_msg_vec[i];
send_msg_hdr[i].msg_hdr.msg_iovlen = 1;
@@ -53,32 +54,46 @@ TEST_F(LlvmLibcSendMmsgTest, SendMmsgSucceedsWithSocketPair) {
ASSERT_THAT(
LIBC_NAMESPACE::sendmmsg(sockpair[0], send_msg_hdr, MESSAGES_COUNT, 0),
- Succeeds(static_cast<int>(MESSAGES_COUNT)));
+ Succeeds<int>(MESSAGES_COUNT));
for (size_t i = 0; i < MESSAGES_COUNT; ++i) {
ASSERT_EQ(static_cast<size_t>(send_msg_hdr[i].msg_len),
LIBC_NAMESPACE::strlen(TEST_MESSAGES[i]) + 1);
}
+ char recv_buffers[MESSAGES_COUNT][256] = {};
+ struct iovec recv_msg_vec[MESSAGES_COUNT] = {};
+ struct mmsghdr recv_msg_hdr[MESSAGES_COUNT] = {};
for (size_t i = 0; i < MESSAGES_COUNT; ++i) {
- char recv_buffer[256] = {};
- struct iovec recv_msg_vec;
- recv_msg_vec.iov_base = reinterpret_cast<void *>(recv_buffer);
- recv_msg_vec.iov_len = sizeof(recv_buffer);
+ recv_msg_vec[i].iov_base = recv_buffers[i];
+ recv_msg_vec[i].iov_len = sizeof(recv_buffers[i]);
+ recv_msg_hdr[i].msg_hdr.msg_iov = &recv_msg_vec[i];
+ recv_msg_hdr[i].msg_hdr.msg_iovlen = 1;
+ }
+
+ struct timespec invalid_timeout = {-1, 0};
+ ASSERT_THAT(LIBC_NAMESPACE::recvmmsg(sockpair[1], recv_msg_hdr,
+ MESSAGES_COUNT, 0, &invalid_timeout),
+ Fails<int>(EINVAL));
- struct msghdr recv_msg_hdr = {};
- recv_msg_hdr.msg_iov = &recv_msg_vec;
- recv_msg_hdr.msg_iovlen = 1;
+ ASSERT_THAT(LIBC_NAMESPACE::recvmmsg(sockpair[1], recv_msg_hdr,
+ MESSAGES_COUNT, 0, nullptr),
+ Succeeds<int>(MESSAGES_COUNT));
- ASSERT_THAT(LIBC_NAMESPACE::recvmsg(sockpair[1], &recv_msg_hdr, 0),
- Succeeds(static_cast<ssize_t>(
- LIBC_NAMESPACE::strlen(TEST_MESSAGES[i]) + 1)));
- ASSERT_STREQ(recv_buffer, TEST_MESSAGES[i]);
+ for (size_t i = 0; i < MESSAGES_COUNT; ++i) {
+ ASSERT_EQ(static_cast<size_t>(recv_msg_hdr[i].msg_len),
+ LIBC_NAMESPACE::strlen(TEST_MESSAGES[i]) + 1);
+ ASSERT_STREQ(recv_buffers[i], TEST_MESSAGES[i]);
}
}
-TEST_F(LlvmLibcSendMmsgTest, SendMmsgFails) {
+TEST_F(LlvmLibcSendRecvMmsgTest, SendMmsgFails) {
struct mmsghdr msg_hdrs = {};
-
ASSERT_THAT(LIBC_NAMESPACE::sendmmsg(-1, &msg_hdrs, 1, 0), Fails(EBADF, -1));
}
+
+TEST_F(LlvmLibcSendRecvMmsgTest, RecvmmsgFails) {
+ struct mmsghdr msg_hdrs = {};
+ ASSERT_THAT(LIBC_NAMESPACE::recvmmsg(-1, &msg_hdrs, 1, 0, nullptr),
+ Fails(EBADF, -1));
+}
diff --git a/libc/utils/docgen/sys/socket.yaml b/libc/utils/docgen/sys/socket.yaml
index 1c9af64a76331..e8a250453df4a 100644
--- a/libc/utils/docgen/sys/socket.yaml
+++ b/libc/utils/docgen/sys/socket.yaml
@@ -122,6 +122,8 @@ functions:
in-latest-posix: ''
recvfrom:
in-latest-posix: ''
+ recvmmsg:
+ c-definition: ''
recvmsg:
in-latest-posix: ''
send:
More information about the libc-commits
mailing list