[libc] Add select.h and the implementation of the select function for Linux.

Reviewed By: michaelrj

Differential Revision: https://reviews.llvm.org/D136375
This commit is contained in:
Siva Chandra Reddy 2022-10-19 23:55:15 +00:00
parent 21ef1cac13
commit be4e425758
22 changed files with 348 additions and 2 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,3 @@
def StructTimevalType : NamedType<"struct timeval">;
def Linux : StandardSpec<"Linux"> {
HeaderSpec Errno = HeaderSpec<
"errno.h",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File