[libc-commits] [libc] be4e425 - [libc] Add select.h and the implementation of the select function for Linux.
Siva Chandra Reddy via libc-commits
libc-commits at lists.llvm.org
Fri Oct 21 20:17:57 PDT 2022
Author: Siva Chandra Reddy
Date: 2022-10-22T03:17:48Z
New Revision: be4e425758afb926c63eadcfa6bf054bc33c0e4c
URL: https://github.com/llvm/llvm-project/commit/be4e425758afb926c63eadcfa6bf054bc33c0e4c
DIFF: https://github.com/llvm/llvm-project/commit/be4e425758afb926c63eadcfa6bf054bc33c0e4c.diff
LOG: [libc] Add select.h and the implementation of the select function for Linux.
Reviewed By: michaelrj
Differential Revision: https://reviews.llvm.org/D136375
Added:
libc/include/llvm-libc-macros/sys-select-macros.h
libc/include/llvm-libc-types/fd_set.h
libc/include/sys/select.h.def
libc/src/sys/select/CMakeLists.txt
libc/src/sys/select/linux/CMakeLists.txt
libc/src/sys/select/linux/select.cpp
libc/src/sys/select/select.h
libc/test/src/sys/select/CMakeLists.txt
libc/test/src/sys/select/select_failure_test.cpp
libc/test/src/sys/select/select_ui_test.cpp
libc/test/src/sys/select/testdata/CMakeLists.txt
Modified:
libc/config/linux/api.td
libc/config/linux/x86_64/entrypoints.txt
libc/config/linux/x86_64/headers.txt
libc/include/CMakeLists.txt
libc/include/llvm-libc-macros/CMakeLists.txt
libc/include/llvm-libc-types/CMakeLists.txt
libc/spec/linux.td
libc/spec/posix.td
libc/spec/spec.td
libc/src/sys/CMakeLists.txt
libc/test/src/sys/CMakeLists.txt
Removed:
################################################################################
diff --git a/libc/config/linux/api.td b/libc/config/linux/api.td
index 2b5b033593330..8d2e64fb00fb0 100644
--- a/libc/config/linux/api.td
+++ b/libc/config/linux/api.td
@@ -274,6 +274,10 @@ def SysRandomAPI : PublicAPI<"sys/random.h"> {
let Types = ["size_t", "ssize_t"];
}
+def SysSelectAPI : PublicAPI<"sys/select.h"> {
+ let Types = ["fd_set", "sigset_t", "suseconds_t", "time_t", "struct timespec", "struct timeval"];
+}
+
def SysResourceAPI : PublicAPI<"sys/resource.h"> {
let Types = ["rlim_t", "struct rlimit"];
}
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index a05c451c9d1b7..2637faf9be01d 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -452,6 +452,9 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.unistd.execv
libc.src.unistd.fork
libc.src.unistd.__llvm_libc_syscall
+
+ # sys/select.h entrypoints
+ libc.src.sys.select.select
)
endif()
diff --git a/libc/config/linux/x86_64/headers.txt b/libc/config/linux/x86_64/headers.txt
index d968b053ef2fe..a44ea884b9028 100644
--- a/libc/config/linux/x86_64/headers.txt
+++ b/libc/config/linux/x86_64/headers.txt
@@ -25,6 +25,7 @@ set(TARGET_PUBLIC_HEADERS
libc.include.sys_prctl
libc.include.sys_random
libc.include.sys_resource
+ libc.include.sys_select
libc.include.sys_stat
libc.include.sys_syscall
libc.include.sys_time
diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index cd614ab81bfd0..cd00565aa13cb 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -301,6 +301,22 @@ add_gen_header(
.llvm-libc-types.struct_stat
)
+add_gen_header(
+ sys_select
+ DEF_FILE sys/select.h.def
+ GEN_HDR sys/select.h
+ DEPENDS
+ .llvm_libc_common_h
+ .llvm-libc-macros.sys_select_macros
+ .llvm-libc-types.fd_set
+ .llvm-libc-types.sigset_t
+ .llvm-libc-types.struct_timespec
+ .llvm-libc-types.struct_timeval
+ .llvm-libc-types.suseconds_t
+ .llvm-libc-types.time_t
+ .llvm-libc-types.ssize_t
+)
+
add_gen_header(
sys_sendfile
DEF_FILE sys/sendfile.h.def
diff --git a/libc/include/llvm-libc-macros/CMakeLists.txt b/libc/include/llvm-libc-macros/CMakeLists.txt
index 84453664325c3..7469145098e51 100644
--- a/libc/include/llvm-libc-macros/CMakeLists.txt
+++ b/libc/include/llvm-libc-macros/CMakeLists.txt
@@ -83,6 +83,12 @@ add_header(
.linux.sys_resource_macros
)
+add_header(
+ sys_select_macros
+ HDR
+ sys-select-macros.h
+)
+
add_header(
sys_time_macros
HDR
diff --git a/libc/include/llvm-libc-macros/sys-select-macros.h b/libc/include/llvm-libc-macros/sys-select-macros.h
new file mode 100644
index 0000000000000..5d6592c1c281c
--- /dev/null
+++ b/libc/include/llvm-libc-macros/sys-select-macros.h
@@ -0,0 +1,35 @@
+//===-- Macros defined in sys/select.h header file ------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __LLVM_LIBC_MACROS_SYS_SELECT_MACROS_H
+#define __LLVM_LIBC_MACROS_SYS_SELECT_MACROS_H
+
+#define FD_SETSIZE 1024
+#define __FD_SET_WORD_TYPE unsigned long
+#define __FD_SET_WORD_SIZE (sizeof(__FD_SET_WORD_TYPE) * 8)
+#define __FD_SET_ARRAYSIZE (FD_SETSIZE / __FD_SET_WORD_SIZE)
+
+#define FD_ZERO(set) \
+ do { \
+ unsigned i; \
+ for (i = 0; i < __FD_SET_ARRAYSIZE; ++i) \
+ (set)->__set[i] = 0; \
+ } while (0)
+
+#define __FD_WORD(fd) ((fd) / __FD_SET_WORD_SIZE)
+#define __FD_MASK(fd) \
+ ((__FD_SET_WORD_TYPE)1) << ((__FD_SET_WORD_TYPE)((fd) % __FD_SET_WORD_SIZE))
+
+#define FD_CLR(fd, set) (void)((set)->__set[__FD_WORD(fd)] &= ~__FD_MASK(fd))
+
+#define FD_SET(fd, set) (void)((set)->__set[__FD_WORD(fd)] |= __FD_MASK(fd))
+
+#define FD_ISSET(fd, set) \
+ (int)(((set)->__set[__FD_WORD(fd)] & __FD_MASK(fd)) != 0)
+
+#endif // __LLVM_LIBC_MACROS_SYS_SELECT_MACROS_H
diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt
index 40717f7549400..aa3fe46507787 100644
--- a/libc/include/llvm-libc-types/CMakeLists.txt
+++ b/libc/include/llvm-libc-types/CMakeLists.txt
@@ -27,6 +27,7 @@ add_header(div_t HDR div_t.h)
add_header(ldiv_t HDR ldiv_t.h)
add_header(lldiv_t HDR lldiv_t.h)
add_header(FILE HDR FILE.h)
+add_header(fd_set HDR fd_set.h DEPENDS libc.include.llvm-libc-macros.sys_select_macros)
add_header(fenv_t HDR fenv_t.h)
add_header(fexcept_t HDR fexcept_t.h)
add_header(float_t HDR float_t.h)
diff --git a/libc/include/llvm-libc-types/fd_set.h b/libc/include/llvm-libc-types/fd_set.h
new file mode 100644
index 0000000000000..54e3fc654c067
--- /dev/null
+++ b/libc/include/llvm-libc-types/fd_set.h
@@ -0,0 +1,18 @@
+//===-- Definition of fd_set type -----------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __LLVM_LIBC_TYPES_FD_SET_H__
+#define __LLVM_LIBC_TYPES_FD_SET_H__
+
+#include <llvm-libc-macros/sys-select-macros.h> // FD_SETSIZE
+
+typedef struct {
+ __FD_SET_WORD_TYPE __set[__FD_SET_ARRAYSIZE];
+} fd_set;
+
+#endif // __LLVM_LIBC_TYPES_FD_SET_H__
diff --git a/libc/include/sys/select.h.def b/libc/include/sys/select.h.def
new file mode 100644
index 0000000000000..4f3cebaecbb93
--- /dev/null
+++ b/libc/include/sys/select.h.def
@@ -0,0 +1,18 @@
+//===-- Linux sys/select.h ------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SYS_SELECT_H
+#define LLVM_LIBC_SYS_SELECT_H
+
+#include <__llvm-libc-common.h>
+
+#include <llvm-libc-macros/sys-select-macros.h>
+
+%%public_api()
+
+#endif // LLVM_LIBC_SYS_SELECT_H
diff --git a/libc/spec/linux.td b/libc/spec/linux.td
index d43bc88bbb558..7c4740ac7dbee 100644
--- a/libc/spec/linux.td
+++ b/libc/spec/linux.td
@@ -1,5 +1,3 @@
-def StructTimevalType : NamedType<"struct timeval">;
-
def Linux : StandardSpec<"Linux"> {
HeaderSpec Errno = HeaderSpec<
"errno.h",
diff --git a/libc/spec/posix.td b/libc/spec/posix.td
index 38ca165fb5f70..32e411372e4f4 100644
--- a/libc/spec/posix.td
+++ b/libc/spec/posix.td
@@ -70,6 +70,10 @@ def StackTPtr : PtrType<StackT>;
def RestrictedStackTPtr : RestrictedPtrType<StackT>;
def ConstRestrictedStackTPtr : ConstType<RestrictedStackTPtr>;
+def FdSet : NamedType<"fd_set">;
+def FdSetPtr : PtrType<FdSet>;
+def RestrictedFdSetPtr : RestrictedPtrType<FdSet>;
+
def POSIX : StandardSpec<"POSIX"> {
PtrType CharPtr = PtrType<CharType>;
RestrictedPtrType RestrictedCharPtr = RestrictedPtrType<CharType>;
@@ -1184,6 +1188,23 @@ def POSIX : StandardSpec<"POSIX"> {
]
>;
+ HeaderSpec SysSelect = HeaderSpec<
+ "sys/select.h",
+ [], // Macros
+ [FdSet, SigSetType, StructTimevalType, StructTimeSpec, SuSecondsT, TimeTType],
+ [], // Enumerations
+ [
+ FunctionSpec<
+ "select",
+ RetValSpec<IntType>,
+ [
+ ArgSpec<IntType>, ArgSpec<RestrictedFdSetPtr>, ArgSpec<RestrictedFdSetPtr>,
+ ArgSpec<RestrictedFdSetPtr>, ArgSpec<RestrictedStructTimevalPtr>
+ ]
+ >
+ ]
+ >;
+
let Headers = [
CType,
Dirent,
@@ -1197,6 +1218,7 @@ def POSIX : StandardSpec<"POSIX"> {
SysIOctl,
SysMMan,
SysResource,
+ SysSelect,
SysStat,
SysUtsName,
SysWait,
diff --git a/libc/spec/spec.td b/libc/spec/spec.td
index 1c39bbaa17b9f..017056d7d7738 100644
--- a/libc/spec/spec.td
+++ b/libc/spec/spec.td
@@ -121,6 +121,12 @@ def RestrictedPidTPtr : RestrictedPtrType<PidT>;
def StructRUsage : NamedType<"struct rusage">;
def StructRUsagePtr : PtrType<StructRUsage>;
+def StructTimevalType : NamedType<"struct timeval">;
+def StructTimevalPtr : PtrType<StructTimevalType>;
+def RestrictedStructTimevalPtr : RestrictedPtrType<StructTimevalType>;
+
+def SuSecondsT : NamedType<"suseconds_t">;
+
//added because __assert_fail needs it.
def UnsignedType : NamedType<"unsigned">;
diff --git a/libc/src/sys/CMakeLists.txt b/libc/src/sys/CMakeLists.txt
index bf6283df87acb..94f62123d2c40 100644
--- a/libc/src/sys/CMakeLists.txt
+++ b/libc/src/sys/CMakeLists.txt
@@ -1,6 +1,7 @@
add_subdirectory(mman)
add_subdirectory(random)
add_subdirectory(resource)
+add_subdirectory(select)
add_subdirectory(sendfile)
add_subdirectory(stat)
add_subdirectory(utsname)
diff --git a/libc/src/sys/select/CMakeLists.txt b/libc/src/sys/select/CMakeLists.txt
new file mode 100644
index 0000000000000..ce36c66b255b0
--- /dev/null
+++ b/libc/src/sys/select/CMakeLists.txt
@@ -0,0 +1,10 @@
+if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
+ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
+endif()
+
+add_entrypoint_object(
+ select
+ ALIAS
+ DEPENDS
+ .${LIBC_TARGET_OS}.select
+)
diff --git a/libc/src/sys/select/linux/CMakeLists.txt b/libc/src/sys/select/linux/CMakeLists.txt
new file mode 100644
index 0000000000000..9d4a3beaeccc2
--- /dev/null
+++ b/libc/src/sys/select/linux/CMakeLists.txt
@@ -0,0 +1,12 @@
+add_entrypoint_object(
+ select
+ SRCS
+ select.cpp
+ HDRS
+ ../select.h
+ DEPENDS
+ libc.include.sys_select
+ libc.include.sys_syscall
+ libc.src.__support.OSUtil.osutil
+ libc.src.errno.errno
+)
diff --git a/libc/src/sys/select/linux/select.cpp b/libc/src/sys/select/linux/select.cpp
new file mode 100644
index 0000000000000..ba81f9ba188ce
--- /dev/null
+++ b/libc/src/sys/select/linux/select.cpp
@@ -0,0 +1,65 @@
+//===-- Linux implementation of select ------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/sys/select/select.h"
+
+#include "src/__support/CPP/limits.h"
+#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
+#include "src/__support/common.h"
+
+#include <errno.h>
+#include <signal.h>
+#include <stddef.h> // For size_t
+#include <sys/select.h>
+#include <sys/syscall.h> // For syscall numbers.
+
+namespace __llvm_libc {
+
+struct pselect6_sigset_t {
+ sigset_t *ss;
+ size_t ss_len;
+};
+
+LLVM_LIBC_FUNCTION(int, select,
+ (int nfds, fd_set *__restrict read_set,
+ fd_set *__restrict write_set, fd_set *__restrict error_set,
+ struct timeval *__restrict timeout)) {
+ // Linux has a SYS_select syscall but it is not available on all
+ // architectures. So, we use the SYS_pselect6 syscall which is more
+ // widely available. However, SYS_pselect6 takes a struct timespec argument
+ // instead of a struct timeval argument. Also, it takes an additional
+ // argument which is a pointer to an object of a type defined above as
+ // "pselect6_sigset_t".
+ struct timespec ts {
+ 0, 0
+ };
+ if (timeout != nullptr) {
+ // In general, if the tv_sec and tv_usec in |timeout| are correctly set,
+ // then converting tv_usec to nanoseconds will not be a problem. However,
+ // if tv_usec in |timeout| is more than a second, it can lead to overflows.
+ // So, we detect such cases and adjust.
+ constexpr time_t TIME_MAX = cpp::numeric_limits<time_t>::max();
+ if ((TIME_MAX - timeout->tv_sec) < (timeout->tv_usec / 1000000)) {
+ ts.tv_sec = TIME_MAX;
+ ts.tv_nsec = 999999999;
+ } else {
+ ts.tv_sec = timeout->tv_sec + timeout->tv_usec / 1000000;
+ ts.tv_nsec = timeout->tv_usec * 1000;
+ }
+ }
+ pselect6_sigset_t pss{nullptr, sizeof(sigset_t)};
+ long ret = __llvm_libc::syscall_impl(SYS_pselect6, nfds, read_set, write_set,
+ error_set, &ts, &pss);
+ if (ret < 0) {
+ errno = -ret;
+ return -1;
+ }
+ return ret;
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/sys/select/select.h b/libc/src/sys/select/select.h
new file mode 100644
index 0000000000000..ca81c0bbd5d74
--- /dev/null
+++ b/libc/src/sys/select/select.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for select ------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SYS_SELECT_SELECT_H
+#define LLVM_LIBC_SRC_SYS_SELECT_SELECT_H
+
+#include <sys/select.h>
+
+namespace __llvm_libc {
+
+int select(int nfds, fd_set *__restrict read_set, fd_set *__restrict write_set,
+ fd_set *__restrict error_set, struct timeval *__restrict timeout);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_SYS_SELECT_SELECT_H
diff --git a/libc/test/src/sys/CMakeLists.txt b/libc/test/src/sys/CMakeLists.txt
index bf6283df87acb..94f62123d2c40 100644
--- a/libc/test/src/sys/CMakeLists.txt
+++ b/libc/test/src/sys/CMakeLists.txt
@@ -1,6 +1,7 @@
add_subdirectory(mman)
add_subdirectory(random)
add_subdirectory(resource)
+add_subdirectory(select)
add_subdirectory(sendfile)
add_subdirectory(stat)
add_subdirectory(utsname)
diff --git a/libc/test/src/sys/select/CMakeLists.txt b/libc/test/src/sys/select/CMakeLists.txt
new file mode 100644
index 0000000000000..9d45a447797b7
--- /dev/null
+++ b/libc/test/src/sys/select/CMakeLists.txt
@@ -0,0 +1,31 @@
+add_libc_testsuite(libc_sys_select_unittests)
+
+add_libc_unittest(
+ select_ui_test
+ NO_RUN_POSTBUILD
+ SUITE
+ libc_sys_select_unittests
+ SRCS
+ select_ui_test.cpp
+ DEPENDS
+ libc.include.errno
+ libc.include.unistd
+ libc.src.sys.select.select
+ libc.src.unistd.read
+)
+
+add_libc_unittest(
+ select_failure_test
+ SUITE
+ libc_sys_select_unittests
+ SRCS
+ select_failure_test.cpp
+ DEPENDS
+ libc.include.errno
+ libc.include.unistd
+ libc.src.sys.select.select
+ libc.src.unistd.read
+ libc.test.errno_setter_matcher
+)
+
+add_subdirectory(testdata)
diff --git a/libc/test/src/sys/select/select_failure_test.cpp b/libc/test/src/sys/select/select_failure_test.cpp
new file mode 100644
index 0000000000000..86863544a0ce3
--- /dev/null
+++ b/libc/test/src/sys/select/select_failure_test.cpp
@@ -0,0 +1,28 @@
+//===-- Failure unittests for select --------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/sys/select/select.h"
+#include "src/unistd/read.h"
+#include "test/ErrnoSetterMatcher.h"
+#include "utils/UnitTest/Test.h"
+
+#include <errno.h>
+#include <sys/select.h>
+#include <unistd.h>
+
+using __llvm_libc::testing::ErrnoSetterMatcher::Fails;
+
+TEST(LlvmLibcSelectTest, SelectInvalidFD) {
+ fd_set set;
+ FD_ZERO(&set);
+ struct timeval timeout {
+ 0, 0
+ };
+ ASSERT_THAT(__llvm_libc::select(-1, &set, nullptr, nullptr, &timeout),
+ Fails(EINVAL));
+}
diff --git a/libc/test/src/sys/select/select_ui_test.cpp b/libc/test/src/sys/select/select_ui_test.cpp
new file mode 100644
index 0000000000000..200b29173be78
--- /dev/null
+++ b/libc/test/src/sys/select/select_ui_test.cpp
@@ -0,0 +1,49 @@
+//===-- Interactive unittests for select ----------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/sys/select/select.h"
+#include "src/unistd/read.h"
+#include "utils/UnitTest/Test.h"
+
+#include <errno.h>
+#include <sys/select.h>
+#include <unistd.h>
+
+// This test is not be run automatically as part of the libc testsuite.
+// Instead, one has to run it manually and press a key on the keyboard
+// to make the test succeed.
+TEST(LlvmLibcSelectTest, ReadStdinAfterSelect) {
+ errno = 0;
+ constexpr int STDIN_FD = 0;
+ fd_set set;
+ FD_ZERO(&set);
+ FD_SET(STDIN_FD, &set);
+ struct timeval zero {
+ 0, 0
+ }; // No wait
+ struct timeval hr {
+ 3600, 0
+ }; // Wait for an hour.
+
+ // Zero timeout means we don't wait for input. So, select should return
+ // immediately.
+ int count = __llvm_libc::select(STDIN_FD + 1, &set, nullptr, nullptr, &zero);
+ // The set should indicate that stdin is NOT ready for reading.
+ ASSERT_EQ(0, FD_ISSET(STDIN_FD, &set));
+
+ FD_SET(STDIN_FD, &set);
+ // Wait for an hour and give the user a chance to hit a key.
+ count = __llvm_libc::select(STDIN_FD + 1, &set, nullptr, nullptr, &hr);
+ ASSERT_EQ(count, 1);
+ // The set should indicate that stdin is ready for reading.
+ ASSERT_EQ(1, FD_ISSET(STDIN_FD, &set));
+
+ // Verify that atleast one character can be read.
+ char c;
+ ASSERT_EQ(__llvm_libc::read(STDIN_FD, &c, 1), ssize_t(1));
+}
diff --git a/libc/test/src/sys/select/testdata/CMakeLists.txt b/libc/test/src/sys/select/testdata/CMakeLists.txt
new file mode 100644
index 0000000000000..e69de29bb2d1d
More information about the libc-commits
mailing list