feat(select): poll/select and fd_set

This commit is contained in:
2026-09-05 21:15:07 -04:00
parent 5af4197ee9
commit ddcf8f39be
7 changed files with 671 additions and 0 deletions
+32
View File
@@ -0,0 +1,32 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <poll.h>
#include "../internal/syscall.h"
/*
* poll: plain POSIX poll over SYS_ppoll with a NULL sigmask (Linux has no
* separate poll syscall for >1024 fds; ppoll is the generic form). The
* millisecond timeout is converted to the struct timespec the kernel
* expects; a negative timeout means "block indefinitely" and stays NULL.
*
* The (nfds_t, int) parameter pair is the fixed POSIX signature, so the
* easily-swappable-parameters warning does not apply.
*/
int
poll(struct pollfd *fds, nfds_t nfds, // NOLINT(bugprone-easily-swappable-parameters)
int timeout)
{
struct timespec ts;
struct timespec *tsp = NULL;
if (timeout >= 0)
{
ts.tv_sec = timeout / 1000;
ts.tv_nsec = (long)(timeout % 1000) * 1000000L;
tsp = &ts;
}
return syscall_ret(__syscall5(SYS_ppoll, (long)fds, (long)nfds, (long)tsp, 0L, 0L));
}
+24
View File
@@ -0,0 +1,24 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <poll.h>
#include "../internal/syscall.h"
#if VLIBC_LEVEL_GE(2)
/*
* ppoll: Linux-specific poll over SYS_ppoll with a struct timespec timeout
* and an optional signal mask (the kernel takes the mask and its size as
* separate trailing arguments; the size is only announced — as the 8-byte
* kernel sigset size — when a mask is actually given).
*/
int
ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout, const sigset_t *sigmask)
{
return syscall_ret(__syscall5(SYS_ppoll, (long)fds, (long)nfds, (long)timeout, (long)sigmask,
sigmask != NULL ? (long)sizeof(sigset_t) : 0L));
}
#endif /* VLIBC_LEVEL_GE(2) */
+27
View File
@@ -0,0 +1,27 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <sys/select.h>
#include "../internal/syscall.h"
/*
* pselect: plain POSIX pselect over SYS_pselect6. Its 6th kernel argument is
* a pointer to a { sigset_t *, size_t } pair (the kernel reads two words);
* NULL means "leave the signal mask alone", so the pair is only supplied —
* and only the 8-byte kernel sigset size announced — when sigmask is given.
* The timeout passes through as a struct timespec (NULL = indefinite).
*/
int
pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
const struct timespec *timeout, const sigset_t *sigmask)
{
long data[2];
data[0] = (long)sigmask;
data[1] = (long)sizeof(sigset_t);
return syscall_ret(__syscall6(SYS_pselect6, (long)nfds, (long)readfds, (long)writefds,
(long)exceptfds, (long)timeout,
sigmask != NULL ? (long)data : 0L));
}
+38
View File
@@ -0,0 +1,38 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <sys/select.h>
#include <errno.h>
#include "../internal/syscall.h"
/*
* select: plain POSIX select over SYS_pselect6. The kernel wants a struct
* timespec, so the struct timeval timeout is converted here (NULL passes
* through as "block indefinitely") and the sigset pair stays NULL: select
* never changes the signal mask. select() and pselect() share this syscall
* on Linux; the 6th argument of pselect6 is a { sigset_t *, size_t } pair
* pointer that is NULL when the mask is left alone.
*/
int
select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
{
struct timespec ts;
struct timespec *tsp = NULL;
if (timeout != NULL)
{
if (timeout->tv_sec < 0 || timeout->tv_usec < 0 || timeout->tv_usec >= 1000000L)
{
errno = EINVAL;
return -1;
}
ts.tv_sec = timeout->tv_sec;
ts.tv_nsec = (long)timeout->tv_usec * 1000;
tsp = &ts;
}
return syscall_ret(__syscall6(SYS_pselect6, (long)nfds, (long)readfds, (long)writefds,
(long)exceptfds, (long)tsp, 0L));
}