forked from OSchip/llvm-project
[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:
parent
21ef1cac13
commit
be4e425758
|
@ -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"];
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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__
|
|
@ -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
|
|
@ -1,5 +1,3 @@
|
|||
def StructTimevalType : NamedType<"struct timeval">;
|
||||
|
||||
def Linux : StandardSpec<"Linux"> {
|
||||
HeaderSpec Errno = HeaderSpec<
|
||||
"errno.h",
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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">;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
)
|
|
@ -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
|
||||
)
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
|
@ -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));
|
||||
}
|
|
@ -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));
|
||||
}
|
Loading…
Reference in New Issue