diff --git a/src/header/bits_timespec/cbindgen.toml b/src/header/bits_timespec/cbindgen.toml index f407cd32a0..b1e24dc3da 100644 --- a/src/header/bits_timespec/cbindgen.toml +++ b/src/header/bits_timespec/cbindgen.toml @@ -8,7 +8,7 @@ # - sched.h # - semaphore.h # - signal.h -# - sys/select.h (not currently imported, needed for pselect, no TODO) +# - sys/select.h # - sys/stat.h # - sys/time.h # - time.h (where it should be defined) diff --git a/src/header/sys_select/mod.rs b/src/header/sys_select/mod.rs index d79cb21cc6..e4fd546dc0 100644 --- a/src/header/sys_select/mod.rs +++ b/src/header/sys_select/mod.rs @@ -9,13 +9,15 @@ use cbitset::BitSet; use crate::{ fs::File, header::{ + bits_sigset_t::sigset_t, errno, sys_epoll::{ EPOLL_CLOEXEC, EPOLL_CTL_ADD, EPOLLERR, EPOLLIN, EPOLLOUT, epoll_create1, epoll_ctl, - epoll_data, epoll_event, epoll_wait, + epoll_data, epoll_event, epoll_pwait, }, + time::timespec, }, - platform::{self, types::c_int}, + platform::types::{c_int, suseconds_t}, }; pub use crate::header::bits_timeval::timeval; @@ -42,9 +44,10 @@ pub fn select_epoll( writefds: Option<&mut fd_set>, exceptfds: Option<&mut fd_set>, timeout: Option<&mut timeval>, + sigmask: *const sigset_t, ) -> c_int { if nfds < 0 || nfds > FD_SETSIZE as i32 { - platform::ERRNO.set(errno::EINVAL); + crate::platform::ERRNO.set(errno::EINVAL); return -1; }; @@ -55,7 +58,6 @@ pub fn select_epoll( } File::new(epfd) }; - let mut read_bitset: Option<&mut FdBitSet> = readfds.map(|fd_set| &mut fd_set.fds_bits); let mut write_bitset: Option<&mut FdBitSet> = writefds.map(|fd_set| &mut fd_set.fds_bits); let mut except_bitset: Option<&mut FdBitSet> = exceptfds.map(|fd_set| &mut fd_set.fds_bits); @@ -90,7 +92,7 @@ pub fn select_epoll( ..Default::default() // clippy lint, _pad field on redox but not linux }; if unsafe { epoll_ctl(*ep, EPOLL_CTL_ADD, fd, &raw mut event) } < 0 { - if platform::ERRNO.get() == errno::EPERM { + if crate::platform::ERRNO.get() == errno::EPERM { not_epoll += 1; } else { return -1; @@ -134,12 +136,14 @@ pub fn select_epoll( None => -1, } }; + let res = unsafe { - epoll_wait( + epoll_pwait( *ep, events.as_mut_ptr(), events.len() as c_int, epoll_timeout, + sigmask, ) }; if res < 0 { @@ -174,7 +178,7 @@ pub fn select_epoll( count } -/// See . +/// See . #[unsafe(no_mangle)] pub unsafe extern "C" fn select( nfds: c_int, @@ -205,7 +209,8 @@ pub unsafe extern "C" fn select( None } else { Some(unsafe { &mut *timeout }) - } + }, + core::ptr::null(), ), "select({}, {:p}, {:p}, {:p}, {:p})", nfds, @@ -215,3 +220,54 @@ pub unsafe extern "C" fn select( timeout ) } + +/// See . +#[unsafe(no_mangle)] +pub unsafe extern "C" fn pselect( + nfds: c_int, + readfds: *mut fd_set, + writefds: *mut fd_set, + exceptfds: *mut fd_set, + timeout: *const timespec, + sigmask: *const sigset_t, +) -> c_int { + let mut micro_timeout = if timeout.is_null() { + None + } else { + unsafe { + Some(timeval { + tv_sec: (*timeout).tv_sec, + tv_usec: ((*timeout).tv_nsec / 1000) as suseconds_t, + }) + } + }; + trace_expr!( + select_epoll( + nfds, + if readfds.is_null() { + None + } else { + Some(unsafe { &mut *readfds }) + }, + if writefds.is_null() { + None + } else { + Some(unsafe { &mut *writefds }) + }, + if exceptfds.is_null() { + None + } else { + Some(unsafe { &mut *exceptfds }) + }, + micro_timeout.as_mut(), + sigmask + ), + "pselect({}, {:p}, {:p}, {:p}, {:p}, {:p})", + nfds, + readfds, + writefds, + exceptfds, + timeout, + sigmask, + ) +} diff --git a/tests/Makefile b/tests/Makefile index 98fbc6a0bd..a47fd5534b 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -22,7 +22,7 @@ support_build: $(BUILD)/expected \ $(BUILD)/Makefile \ $(BUILD)/Makefile.tests.mk \ $(BUILD)/example_dir \ - $(BUILD)/select.c \ + $(BUILD)/sys_select/select.c \ $(BUILD)/stdlib/realpath.c \ $(BUILD)/stdio/fread.in \ $(BUILD)/stdio/fscanf_offby1.c \ diff --git a/tests/Makefile.tests.mk b/tests/Makefile.tests.mk index 15637742f6..5264e65a21 100644 --- a/tests/Makefile.tests.mk +++ b/tests/Makefile.tests.mk @@ -70,7 +70,6 @@ EXPECT_NAMES=\ locale/setlocale \ math \ regex \ - select \ setjmp \ sigaction \ sigaltstack \ @@ -138,6 +137,9 @@ EXPECT_NAMES=\ string/stpcpy \ string/stpncpy \ strings \ + sys_select/select \ + sys_select/select_timed \ + sys_select/pselect \ sys_shm/shm \ sys_socket/recv \ sys_socket/recvfrom \ diff --git a/tests/expected/sys_select/pselect.stdout b/tests/expected/sys_select/pselect.stdout new file mode 100644 index 0000000000..6c73680869 --- /dev/null +++ b/tests/expected/sys_select/pselect.stdout @@ -0,0 +1,2 @@ +write bar +read bar diff --git a/tests/expected/select.stdout b/tests/expected/sys_select/select.stdout similarity index 100% rename from tests/expected/select.stdout rename to tests/expected/sys_select/select.stdout diff --git a/tests/expected/sys_select/select_timed.stdout b/tests/expected/sys_select/select_timed.stdout new file mode 100644 index 0000000000..0e75f9e6b7 --- /dev/null +++ b/tests/expected/sys_select/select_timed.stdout @@ -0,0 +1,2 @@ +write foo +read foo diff --git a/tests/sys_select/pselect.c b/tests/sys_select/pselect.c new file mode 100644 index 0000000000..f3c7ecda45 --- /dev/null +++ b/tests/sys_select/pselect.c @@ -0,0 +1,93 @@ +#include +#include + +#include +#include +#include + +#include "test_helpers.h" + +volatile sig_atomic_t got_signal = 0; + +void handle_sigusr1(int sig) +{ + (void)sig; + got_signal = 1; +} + +int main(void) +{ + int status; + int pipe_fds[2]; + + status = pipe(pipe_fds); + ERROR_IF(pipe, status, == -1); + + int read_fd = pipe_fds[0]; + int write_fd = pipe_fds[1]; + + // test sigmask + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = handle_sigusr1; + status = sigaction(SIGUSR1, &sa, NULL); + ERROR_IF(sigaction, status, == -1); + + sigset_t block_mask, orig_mask; + sigemptyset(&block_mask); + sigaddset(&block_mask, SIGUSR1); // block SIGUSR1 + status = sigprocmask(SIG_BLOCK, &block_mask, &orig_mask); + ERROR_IF(sigprocmask, status, == -1); + + status = raise(SIGUSR1); + ERROR_IF(raise, status, == -1); + UNEXP_IF(raise, got_signal, != 0); // must not fired + + fd_set read_fds; + FD_ZERO(&read_fds); + FD_SET(read_fd, &read_fds); + + struct timespec timeout = { .tv_sec = 5, .tv_nsec = 0 }; + + int ready = pselect(read_fd + 1, &read_fds, NULL, NULL, &timeout, &orig_mask); + + UNEXP_IF(pselect, ready, != -1); // errno should be EINTR + UNEXP_IF(pselect_signal_handler, got_signal, != 1); // must been fired + + status = sigprocmask(SIG_SETMASK, &orig_mask, NULL); + ERROR_IF(sigprocmask, status, == -1); + + // test timespec + char *c = "bar"; + ssize_t written = write(write_fd, c, 4); + ERROR_IF(write, written, == -1); + UNEXP_IF(write, written, != 4); + + FD_ZERO(&read_fds); + FD_SET(read_fd, &read_fds); + + timeout.tv_sec = 999; + timeout.tv_nsec = 0; + + ready = pselect(read_fd + 1, &read_fds, NULL, NULL, &timeout, NULL); + ERROR_IF(pselect, ready, == -1); + UNEXP_IF(pselect, ready, != 1); + + int is_set = FD_ISSET(read_fd, &read_fds); + UNEXP_IF(FD_ISSET, is_set, == 0); + + char x[4]; + ssize_t amount = read(read_fd, x, 4); + ERROR_IF(read, amount, == -1); + UNEXP_IF(read, amount, != 4); + + status = strcmp(c, x); + printf("write %s\n", c); + printf("read %s\n", x); + UNEXP_IF(strcmp, status, != 0); + + close(read_fd); + close(write_fd); + + return 0; +} \ No newline at end of file diff --git a/tests/select.c b/tests/sys_select/select.c similarity index 96% rename from tests/select.c rename to tests/sys_select/select.c index dfe7161c2a..b8fd2ae1c7 100644 --- a/tests/select.c +++ b/tests/sys_select/select.c @@ -6,7 +6,7 @@ #include "test_helpers.h" int file_test(void) { - int fd = open("select.c", 0, 0); + int fd = open("sys_select/select.c", 0, 0); if (fd < 0) { perror("open"); return -1; diff --git a/tests/sys_select/select_timed.c b/tests/sys_select/select_timed.c new file mode 100644 index 0000000000..152d5ff4dc --- /dev/null +++ b/tests/sys_select/select_timed.c @@ -0,0 +1,62 @@ +#include + +#include +#include +#include + +#include "test_helpers.h" + +int main(void) +{ + int status; + int pipe_fds[2]; + + status = pipe(pipe_fds); + ERROR_IF(pipe, status, == -1); + + int read_fd = pipe_fds[0]; + int write_fd = pipe_fds[1]; + + fd_set read_fds; + FD_ZERO(&read_fds); + FD_SET(read_fd, &read_fds); + + struct timeval timeout = { .tv_sec = 0, .tv_usec = 10000 }; + + int ready = select(read_fd + 1, &read_fds, NULL, NULL, &timeout); + ERROR_IF(pselect, ready, == -1); + UNEXP_IF(pselect, ready, != 0); + + char *c = "foo"; + ssize_t written = write(write_fd, c, 4); + ERROR_IF(write, written, == -1); + UNEXP_IF(write, written, != 4); + + FD_ZERO(&read_fds); + FD_SET(read_fd, &read_fds); + + timeout.tv_sec = 5; + timeout.tv_usec = 0; + + ready = select(read_fd + 1, &read_fds, NULL, NULL, &timeout); + ERROR_IF(pselect, ready, == -1); + UNEXP_IF(pselect, ready, != 1); + + int is_set = FD_ISSET(read_fd, &read_fds); + UNEXP_IF(FD_ISSET, is_set, == 0); + + char x[4]; + ssize_t amount = read(read_fd, x, 4); + ERROR_IF(read, amount, == -1); + UNEXP_IF(read, amount, != 4); + + status = strcmp(c, x); + printf("write %s\n", c); + printf("read %s\n", x); + UNEXP_IF(strcmp, status, != 0); + + close(read_fd); + close(write_fd); + + return 0; +} \ No newline at end of file