• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===-- Linux implementation of select ------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "src/sys/select/select.h"
10 
11 #include "hdr/types/sigset_t.h"
12 #include "hdr/types/struct_timespec.h"
13 #include "src/__support/CPP/limits.h"
14 #include "src/__support/OSUtil/syscall.h" // For internal syscall function.
15 #include "src/__support/common.h"
16 #include "src/errno/libc_errno.h"
17 
18 #include <stddef.h>      // For size_t
19 #include <sys/syscall.h> // For syscall numbers.
20 
21 namespace LIBC_NAMESPACE {
22 
23 struct pselect6_sigset_t {
24   sigset_t *ss;
25   size_t ss_len;
26 };
27 
28 LLVM_LIBC_FUNCTION(int, select,
29                    (int nfds, fd_set *__restrict read_set,
30                     fd_set *__restrict write_set, fd_set *__restrict error_set,
31                     struct timeval *__restrict timeout)) {
32   // Linux has a SYS_select syscall but it is not available on all
33   // architectures. So, we use the SYS_pselect6 syscall which is more
34   // widely available. However, SYS_pselect6 takes a struct timespec argument
35   // instead of a struct timeval argument. Also, it takes an additional
36   // argument which is a pointer to an object of a type defined above as
37   // "pselect6_sigset_t".
38   struct timespec ts {
39     0, 0
40   };
41   if (timeout != nullptr) {
42     // In general, if the tv_sec and tv_usec in |timeout| are correctly set,
43     // then converting tv_usec to nanoseconds will not be a problem. However,
44     // if tv_usec in |timeout| is more than a second, it can lead to overflows.
45     // So, we detect such cases and adjust.
46     constexpr time_t TIME_MAX = cpp::numeric_limits<time_t>::max();
47     if ((TIME_MAX - timeout->tv_sec) < (timeout->tv_usec / 1000000)) {
48       ts.tv_sec = TIME_MAX;
49       ts.tv_nsec = 999999999;
50     } else {
51       ts.tv_sec = timeout->tv_sec + timeout->tv_usec / 1000000;
52       ts.tv_nsec = timeout->tv_usec * 1000;
53     }
54   }
55   pselect6_sigset_t pss{nullptr, sizeof(sigset_t)};
56 #if SYS_pselect6
57   int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_pselect6, nfds, read_set,
58                                               write_set, error_set, &ts, &pss);
59 #elif defined(SYS_pselect6_time64)
60   int ret = LIBC_NAMESPACE::syscall_impl<int>(
61       SYS_pselect6_time64, nfds, read_set, write_set, error_set, &ts, &pss);
62 #else
63 #error "SYS_pselect6 and SYS_pselect6_time64 syscalls not available."
64 #endif
65   if (ret < 0) {
66     libc_errno = -ret;
67     return -1;
68   }
69   return ret;
70 }
71 
72 } // namespace LIBC_NAMESPACE
73