diff --git a/libc/calls/poll-nt.c b/libc/calls/poll-nt.c index 1f82dc9ae98..b63c25f9831 100644 --- a/libc/calls/poll-nt.c +++ b/libc/calls/poll-nt.c @@ -44,8 +44,6 @@ #include "libc/thread/posixthread.internal.h" #ifdef __x86_64__ -#define POLL_INTERVAL_MS 10 - // #define POLLERR_ 0x0001 // implied in events #define POLLHUP_ 0x0002 // implied in events diff --git a/libc/calls/poll.c b/libc/calls/poll.c index 6da322b2e7c..e547a41cf4a 100644 --- a/libc/calls/poll.c +++ b/libc/calls/poll.c @@ -58,7 +58,6 @@ * @return fds[𝑖].revents is always zero initializaed and then will * be populated with POLL{IN,OUT,PRI,HUP,ERR,NVAL} if something * was determined about the file descriptor - * @raise EINVAL if we exceeded the 64 socket limit on Windows * @raise ECANCELED if thread was cancelled in masked mode * @raise EINVAL if `nfds` exceeded `RLIMIT_NOFILE` * @raise ENOMEM on failure to allocate memory diff --git a/libc/calls/ppoll.c b/libc/calls/ppoll.c index 456dce16eda..322937e3190 100644 --- a/libc/calls/ppoll.c +++ b/libc/calls/ppoll.c @@ -35,80 +35,14 @@ #include "libc/sysv/consts/sig.h" #include "libc/sysv/errfuns.h" -/** - * Checks status on multiple file descriptors at once. - * - * This function is the same as saying: - * - * sigset_t old; - * sigprocmask(SIG_SETMASK, sigmask, &old); - * poll(fds, nfds, timeout); - * sigprocmask(SIG_SETMASK, old, 0); - * - * Except it happens atomically when the kernel supports doing that. On - * kernels such as XNU and NetBSD which don't, this wrapper will fall - * back to using the example above. If you need ironclad assurances of - * signal mask atomicity, then consider using pselect() which Cosmo Libc - * guarantees to be atomic on all supported platforms. - * - * Servers that need to handle an unbounded number of client connections - * should just create a separate thread for each client. poll(), ppoll() - * and select() aren't scalable i/o solutions on any platform. - * - * On Windows it's only possible to poll 64 file descriptors at a time; - * it's a limitation imposed by WSAPoll(). Cosmopolitan Libc's ppoll() - * polyfill can go higher in some cases; for example, It's possible to - * poll 64 sockets and 64 pipes/terminals at the same time. Furthermore, - * elements whose fd field is set to a negative number are ignored and - * will not count against this limit. - * - * One of the use cases for poll() is to quickly check if a number of - * file descriptors are valid. The canonical way to do this is to set - * events to 0 which prevents blocking and causes only the invalid, - * hangup, and error statuses to be checked. - * - * On XNU, the POLLHUP and POLLERR statuses aren't checked unless either - * POLLIN, POLLOUT, or POLLPRI are specified in the events field. Cosmo - * will however polyfill the checking of POLLNVAL on XNU with the events - * doesn't specify any of the above i/o events. - * - * When XNU and BSD OSes report POLLHUP, they will always set POLLIN too - * when POLLIN is requested, even in cases when there isn't unread data. - * - * @param fds[𝑖].fd should be a socket, input pipe, or conosle input - * and if it's a negative number then the entry is ignored, plus - * revents will be set to zero - * @param fds[𝑖].events flags can have POLLIN, POLLOUT, POLLPRI, - * POLLRDNORM, POLLWRNORM, POLLRDBAND, POLLWRBAND as well as - * POLLERR, POLLHUP, and POLLNVAL although the latter are - * always implied (assuming fd≥0) so they're ignored here - * @param timeout_ms if 0 means don't wait and negative waits forever - * @return number of `fds` whose revents field has been set to a nonzero - * number, 0 if the timeout elapsed without events, or -1 w/ errno - * @return fds[𝑖].revents is always zero initializaed and then will - * be populated with POLL{IN,OUT,PRI,HUP,ERR,NVAL} if something - * was determined about the file descriptor - * @param timeout if null will block indefinitely - * @param sigmask may be null in which case no mask change happens - * @raise EINVAL if we exceeded the 64 socket limit on Windows - * @raise ECANCELED if thread was cancelled in masked mode - * @raise EINVAL if `nfds` exceeded `RLIMIT_NOFILE` - * @raise ENOMEM on failure to allocate memory - * @raise EINVAL if `*timeout` is invalid - * @raise EINTR if signal was delivered - * @cancelationpoint - * @asyncsignalsafe - * @norestart - */ -int ppoll(struct pollfd *fds, size_t nfds, const struct timespec *timeout, - const sigset_t *sigmask) { +static int ppoll_impl(struct pollfd *fds, size_t nfds, + const struct timespec *timeout, const sigset_t *sigmask) { int e, fdcount; sigset_t oldmask; struct timespec ts, *tsp; - BEGIN_CANCELATION_POINT; // validate timeout - if (timeout && timeout->tv_nsec >= 1000000000) + if (timeout && timeout->tv_nsec >= 1000000000ull) return einval(); // The OpenBSD poll() man pages claims it'll ignore POLLERR, POLLHUP, @@ -192,6 +126,78 @@ int ppoll(struct pollfd *fds, size_t nfds, const struct timespec *timeout, } } + return fdcount; +} + +/** + * Checks status on multiple file descriptors at once. + * + * This function is the same as saying: + * + * sigset_t old; + * sigprocmask(SIG_SETMASK, sigmask, &old); + * poll(fds, nfds, timeout); + * sigprocmask(SIG_SETMASK, old, 0); + * + * Except it happens atomically when the kernel supports doing that. On + * kernels such as XNU and NetBSD which don't, this wrapper will fall + * back to using the example above. If you need ironclad assurances of + * signal mask atomicity, then consider using pselect() which Cosmo Libc + * guarantees to be atomic on all supported platforms. + * + * Servers that need to handle an unbounded number of client connections + * should just create a separate thread for each client. poll(), ppoll() + * and select() aren't scalable i/o solutions on any platform. + * + * On Windows it's only possible to poll 64 file descriptors at a time; + * it's a limitation imposed by WSAPoll(). Cosmopolitan Libc's ppoll() + * polyfill can go higher in some cases; for example, It's possible to + * poll 64 sockets and 64 pipes/terminals at the same time. Furthermore, + * elements whose fd field is set to a negative number are ignored and + * will not count against this limit. + * + * One of the use cases for poll() is to quickly check if a number of + * file descriptors are valid. The canonical way to do this is to set + * events to 0 which prevents blocking and causes only the invalid, + * hangup, and error statuses to be checked. + * + * On XNU, the POLLHUP and POLLERR statuses aren't checked unless either + * POLLIN, POLLOUT, or POLLPRI are specified in the events field. Cosmo + * will however polyfill the checking of POLLNVAL on XNU with the events + * doesn't specify any of the above i/o events. + * + * When XNU and BSD OSes report POLLHUP, they will always set POLLIN too + * when POLLIN is requested, even in cases when there isn't unread data. + * + * @param fds[𝑖].fd should be a socket, input pipe, or conosle input + * and if it's a negative number then the entry is ignored, plus + * revents will be set to zero + * @param fds[𝑖].events flags can have POLLIN, POLLOUT, POLLPRI, + * POLLRDNORM, POLLWRNORM, POLLRDBAND, POLLWRBAND as well as + * POLLERR, POLLHUP, and POLLNVAL although the latter are + * always implied (assuming fd≥0) so they're ignored here + * @param timeout_ms if 0 means don't wait and negative waits forever + * @return number of `fds` whose revents field has been set to a nonzero + * number, 0 if the timeout elapsed without events, or -1 w/ errno + * @return fds[𝑖].revents is always zero initializaed and then will + * be populated with POLL{IN,OUT,PRI,HUP,ERR,NVAL} if something + * was determined about the file descriptor + * @param timeout if null will block indefinitely + * @param sigmask may be null in which case no mask change happens + * @raise ECANCELED if thread was cancelled in masked mode + * @raise EINVAL if `nfds` exceeded `RLIMIT_NOFILE` + * @raise ENOMEM on failure to allocate memory + * @raise EINVAL if `*timeout` is invalid + * @raise EINTR if signal was delivered + * @cancelationpoint + * @asyncsignalsafe + * @norestart + */ +int ppoll(struct pollfd *fds, size_t nfds, const struct timespec *timeout, + const sigset_t *sigmask) { + int fdcount; + BEGIN_CANCELATION_POINT; + fdcount = ppoll_impl(fds, nfds, timeout, sigmask); END_CANCELATION_POINT; STRACE("ppoll(%s, %'zu, %s, %s) → %d% lm", DescribePollFds(fdcount, fds, nfds), nfds, diff --git a/libc/calls/sig.c b/libc/calls/sig.c index 9cb94f14131..7c82c31de1a 100644 --- a/libc/calls/sig.c +++ b/libc/calls/sig.c @@ -302,6 +302,15 @@ static textwindows int __sig_killer(struct PosixThread *pt, int sig, int sic) { return 0; } + // we can't preempt threads that masked sig or are blocked + if (atomic_load_explicit(&pt->tib->tib_sigmask, memory_order_acquire) & + (1ull << (sig - 1))) { + atomic_fetch_or_explicit(&pt->tib->tib_sigpending, 1ull << (sig - 1), + memory_order_relaxed); + __sig_cancel(pt, sig, flags); + return 0; + } + // if there's no handler then killing a thread kills the process if (rva == (intptr_t)SIG_DFL) { STRACE("terminating on %G due to no handler", sig); diff --git a/libc/calls/sigtimedwait-nt.c b/libc/calls/sigtimedwait-nt.c new file mode 100644 index 00000000000..5afb3d43806 --- /dev/null +++ b/libc/calls/sigtimedwait-nt.c @@ -0,0 +1,113 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2024 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/calls/sig.internal.h" +#include "libc/calls/struct/siginfo.h" +#include "libc/calls/struct/sigset.internal.h" +#include "libc/calls/struct/timespec.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" +#include "libc/intrin/atomic.h" +#include "libc/macros.h" +#include "libc/nt/runtime.h" +#include "libc/nt/synchronization.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/sicode.h" +#include "libc/sysv/consts/sig.h" +#include "libc/sysv/errfuns.h" +#include "libc/thread/posixthread.internal.h" + +textwindows static int sys_sigtimedwait_nt_check(sigset_t syncsigs, + siginfo_t *opt_info, + sigset_t waitmask) { + int sig; + if (_check_cancel() == -1) + return -1; + if ((sig = __sig_get(waitmask))) { + if ((1ull << (sig - 1)) & syncsigs) { + if (opt_info) { + memset(opt_info, 0, sizeof(*opt_info)); + opt_info->si_signo = sig; + opt_info->si_code = SI_TKILL; + opt_info->si_uid = sys_getuid_nt(); + } + return sig; + } + int handler_was_called = __sig_relay(sig, SI_TKILL, waitmask); + if (_check_cancel() == -1) + return -1; + if (handler_was_called) + return eintr(); + } + return 0; +} + +textwindows static int sys_sigtimedwait_nt_impl(sigset_t syncsigs, + siginfo_t *opt_info, + struct timespec deadline, + sigset_t waitmask, + intptr_t semaphore) { + for (;;) { + int sig; + if ((sig = sys_sigtimedwait_nt_check(syncsigs, opt_info, waitmask))) + return sig; + struct timespec now = sys_clock_gettime_monotonic_nt(); + if (timespec_cmp(now, deadline) >= 0) + return eagain(); + struct timespec remain = timespec_sub(deadline, now); + int64_t millis = timespec_tomillis(remain); + uint32_t waitms = MIN(millis, 0xffffffffu); + uint32_t wi = WaitForSingleObject(semaphore, waitms); + if (wi == -1u) + return __winerr(); + if (wi) + return eagain(); + } +} + +textwindows int sys_sigtimedwait_nt(const sigset_t *set, siginfo_t *opt_info, + const struct timespec *opt_timeout) { + int rc; + intptr_t sem; + struct PosixThread *pt; + struct timespec deadline; + sigset_t syncsigs, waitmask; + BLOCK_SIGNALS; + if (opt_timeout) { + deadline = timespec_add(sys_clock_gettime_monotonic_nt(), *opt_timeout); + } else { + deadline = timespec_max; + } + if ((sem = CreateSemaphore(0, 0, 1, 0))) { + syncsigs = *set & ~(1ull << (SIGTHR - 1)); // internal to pthreads + waitmask = ~syncsigs & _SigMask; + pt = _pthread_self(); + pt->pt_blkmask = waitmask; + pt->pt_semaphore = sem = CreateSemaphore(0, 0, 1, 0); + atomic_store_explicit(&pt->pt_blocker, PT_BLOCKER_SEM, + memory_order_release); + rc = sys_sigtimedwait_nt_impl(syncsigs, opt_info, deadline, waitmask, sem); + atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release); + CloseHandle(sem); + } else { + rc = __winerr(); + } + ALLOW_SIGNALS; + return rc; +} diff --git a/libc/calls/sigtimedwait.c b/libc/calls/sigtimedwait.c index 20579e199f6..65528d1e187 100644 --- a/libc/calls/sigtimedwait.c +++ b/libc/calls/sigtimedwait.c @@ -27,48 +27,62 @@ #include "libc/str/str.h" #include "libc/sysv/errfuns.h" +int sys_sigtimedwait_nt(const sigset_t *, siginfo_t *, const struct timespec *); + /** * Waits for signal synchronously, w/ timeout. * + * This function does not change the thread signal mask. Signals that + * aren't masked, which aren't in `set`, will be handled normally, in + * which case this function will raise `EINTR`. + * + * This function silently ignores attempts to synchronously wait for + * SIGTHR which is used internally by the POSIX threads implementation. + * * @param set is signals for which we'll be waiting - * @param info if not null shall receive info about signal - * @param timeout is relative deadline and null means wait forever + * @param opt_info if not null shall receive info about signal + * @param opt_timeout is relative deadline and null means wait forever * @return signal number on success, or -1 w/ errno * @raise EINTR if an asynchronous signal was delivered instead * @raise ECANCELED if thread was cancelled in masked mode * @raise EINVAL if nanoseconds parameter was out of range - * @raise EAGAIN if deadline expired - * @raise ENOSYS on Windows, XNU, OpenBSD, Metal + * @raise EAGAIN if timeout elapsed + * @raise ENOSYS on XNU, OpenBSD, and Metal * @raise EFAULT if invalid memory was supplied * @cancelationpoint + * @norestart */ -int sigtimedwait(const sigset_t *set, siginfo_t *info, - const struct timespec *timeout) { +int sigtimedwait(const sigset_t *set, siginfo_t *opt_info, + const struct timespec *opt_timeout) { int rc; char strsig[21]; struct timespec ts; union siginfo_meta si = {0}; BEGIN_CANCELATION_POINT; - if (IsLinux() || IsFreebsd() || IsNetbsd()) { - if (timeout) { + // validate timeout + if (opt_timeout && opt_timeout->tv_nsec >= 1000000000ull) { + rc = einval(); + } else if (IsLinux() || IsFreebsd() || IsNetbsd()) { + if (opt_timeout) { // 1. Linux needs its size parameter // 2. NetBSD modifies timeout argument - ts = *timeout; + ts = *opt_timeout; rc = sys_sigtimedwait(set, &si, &ts, 8); } else { rc = sys_sigtimedwait(set, &si, 0, 8); } - if (rc != -1 && info) { - __siginfo2cosmo(info, &si); - } + if (rc != -1 && opt_info) + __siginfo2cosmo(opt_info, &si); + } else if (IsWindows()) { + rc = sys_sigtimedwait_nt(set, opt_info, opt_timeout); } else { rc = enosys(); } END_CANCELATION_POINT; STRACE("sigtimedwait(%s, [%s], %s) → %s% m", DescribeSigset(0, set), - DescribeSiginfo(rc, info), DescribeTimespec(0, timeout), + DescribeSiginfo(rc, opt_info), DescribeTimespec(0, opt_timeout), strsignal_r(rc, strsig)); return rc; } diff --git a/libc/calls/sigwait.c b/libc/calls/sigwait.c index 77dc014fa4c..a76b0dfd09c 100644 --- a/libc/calls/sigwait.c +++ b/libc/calls/sigwait.c @@ -18,10 +18,26 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/sigtimedwait.h" -int sigwait(const sigset_t *mask, int *sig) { - siginfo_t si; - if (sigtimedwait(mask, &si, 0) < 0) +/** + * Waits for signal synchronously. + * + * See sigtimedwait() for further details. + * + * @param set is signals for which we'll be waiting + * @param out_sig shall receive signal number + * @return 0 on success, or -1 w/ errno + * @raise EINTR if an asynchronous signal was delivered instead + * @raise ECANCELED if thread was cancelled in masked mode + * @raise ENOSYS on OpenBSD, XNU, and Metal + * @see sigtimedwait() + * @cancelationpoint + * @norestart + */ +int sigwait(const sigset_t *mask, int *out_sig) { + int sig; + if ((sig = sigtimedwait(mask, 0, 0)) == -1) return -1; - *sig = si.si_signo; + if (out_sig) + *out_sig = sig; return 0; } diff --git a/libc/calls/sigwaitinfo.c b/libc/calls/sigwaitinfo.c index 368ae177727..ef5876698d9 100644 --- a/libc/calls/sigwaitinfo.c +++ b/libc/calls/sigwaitinfo.c @@ -21,14 +21,17 @@ /** * Waits for signal synchronously. * + * See sigtimedwait() for further details. + * * @param set is signals for which we'll be waiting * @param info if not null shall receive info about signal * @return signal number on success, or -1 w/ errno * @raise EINTR if an asynchronous signal was delivered instead * @raise ECANCELED if thread was cancelled in masked mode - * @raise ENOSYS on OpenBSD, XNU, and Windows + * @raise ENOSYS on OpenBSD, XNU, and Metal * @see sigtimedwait() * @cancelationpoint + * @norestart */ int sigwaitinfo(const sigset_t *mask, siginfo_t *si) { return sigtimedwait(mask, si, 0); diff --git a/libc/calls/syscall_support-nt.internal.h b/libc/calls/syscall_support-nt.internal.h index a002ef9e367..7b0ced2d325 100644 --- a/libc/calls/syscall_support-nt.internal.h +++ b/libc/calls/syscall_support-nt.internal.h @@ -4,6 +4,8 @@ #include "libc/nt/struct/overlapped.h" COSMOPOLITAN_C_START_ +#define POLL_INTERVAL_MS 10 + bool isdirectory_nt(const char *); bool isregularfile_nt(const char *); bool issymlink_nt(const char *); diff --git a/libc/sock/accept-nt.c b/libc/sock/accept-nt.c index 5490c3560c2..1fef186d89d 100644 --- a/libc/sock/accept-nt.c +++ b/libc/sock/accept-nt.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" #include "libc/calls/struct/sigset.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/errno.h" #include "libc/nt/errors.h" #include "libc/nt/struct/pollfd.h" @@ -33,8 +34,6 @@ #include "libc/sysv/errfuns.h" #ifdef __x86_64__ -#define POLL_INTERVAL_MS 10 - __msabi extern typeof(__sys_ioctlsocket_nt) *const __imp_ioctlsocket; textwindows static int sys_accept_nt_impl(struct Fd *f, diff --git a/libc/sock/connect-nt.c b/libc/sock/connect-nt.c index 4bbc190593b..a79196d7a06 100644 --- a/libc/sock/connect-nt.c +++ b/libc/sock/connect-nt.c @@ -19,6 +19,7 @@ #include "libc/calls/internal.h" #include "libc/calls/struct/sigset.h" #include "libc/calls/struct/sigset.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/errno.h" #include "libc/macros.h" #include "libc/nt/errors.h" @@ -42,8 +43,6 @@ #define CONNECTING 1 #define CONNECTED 2 -#define POLL_INTERVAL_MS 10 - __msabi extern typeof(__sys_getsockopt_nt) *const __imp_getsockopt; __msabi extern typeof(__sys_ioctlsocket_nt) *const __imp_ioctlsocket; __msabi extern typeof(__sys_select_nt) *const __imp_select; diff --git a/libc/stdio/fleaks.c b/libc/stdio/fleaks.c index 462c6ddb723..e4f0bfd9061 100644 --- a/libc/stdio/fleaks.c +++ b/libc/stdio/fleaks.c @@ -33,7 +33,7 @@ void CheckForFileLeaks(void) { char *pe = msg + 256; bool gotsome = false; if (IsQemuUser()) - usleep(1); // weird qemu mt flake + usleep(10000); // weird qemu mt flake for (int fd = 3; fd < MIN_CLANDESTINE_FD; ++fd) { if (fcntl(fd, F_GETFL) != -1) { if (!gotsome) { diff --git a/test/libc/calls/commandv_test.c b/test/libc/calls/commandv_test.c index f2681cd4cde..043b3164e11 100644 --- a/test/libc/calls/commandv_test.c +++ b/test/libc/calls/commandv_test.c @@ -100,8 +100,6 @@ TEST(commandv, test_DirPaths_wontConsiderDirectoriesExecutable2) { } TEST(commandv, test_nonExecutableFile_willEacces) { - if (IsWindows()) - return; // TODO: fixme setenv("PATH", "foo", true); EXPECT_SYS(0, 0, mkdir("foo", 0755)); EXPECT_SYS(0, 0, touch("foo/bar", 0400)); diff --git a/test/libc/calls/getrandom_test.c b/test/libc/calls/getrandom_test.c index 245fc967cec..42fb33c6538 100644 --- a/test/libc/calls/getrandom_test.c +++ b/test/libc/calls/getrandom_test.c @@ -59,11 +59,9 @@ void *TortureWorker(void *arg) { ASSERT_SYS(0, 0, sigprocmask(SIG_SETMASK, &ss, 0)); ready = true; while (!done) { - if (!IsWindows()) - pthread_kill(parent, SIGUSR1); + pthread_kill(parent, SIGUSR1); usleep(1); - if (!IsWindows()) - pthread_kill(parent, SIGUSR2); + pthread_kill(parent, SIGUSR2); usleep(1); } return 0; diff --git a/test/libc/calls/sigtimedwait_test.c b/test/libc/calls/sigtimedwait_test.c index 9103485c542..270e9e69e76 100644 --- a/test/libc/calls/sigtimedwait_test.c +++ b/test/libc/calls/sigtimedwait_test.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/sigtimedwait.h" +#include "libc/atomic.h" #include "libc/calls/calls.h" #include "libc/calls/struct/siginfo.h" #include "libc/calls/struct/siginfo.internal.h" @@ -28,22 +29,17 @@ #include "libc/sysv/consts/sicode.h" #include "libc/sysv/consts/sig.h" #include "libc/testlib/testlib.h" +#include "libc/thread/thread.h" void SetUp(void) { if (IsXnu()) exit(0); if (IsMetal()) exit(0); - if (IsWindows()) - exit(0); if (IsOpenbsd()) exit(0); } -TEST(sigtimedwait, nullSet_efault) { - ASSERT_SYS(EFAULT, -1, sigtimedwait(0, 0, 0)); -} - TEST(sigtimedwait, emptySet_timesOut) { sigset_t ss = {0}; struct timespec ts = {0, 0}; @@ -56,24 +52,28 @@ TEST(sigtimedwait, badTimestamp_einval) { ASSERT_SYS(EINVAL, -1, sigtimedwait(&ss, 0, &ts)); } -TEST(sigtimedwait, test) { - int pid, ws; +atomic_bool g_ready; + +void *worker(void *arg) { + sigset_t ss; siginfo_t info; - sigset_t ss, oldss; - struct timespec ts = {1, 0}; - sigemptyset(&ss); - sigaddset(&ss, SIGUSR1); - ASSERT_SYS(0, 0, sigprocmask(SIG_BLOCK, &ss, &oldss)); - ASSERT_NE(-1, (pid = fork())); - if (!pid) { - ASSERT_SYS(0, SIGUSR1, sigtimedwait(&ss, &info, &ts)); - ASSERT_EQ(SIGUSR1, info.si_signo); - ASSERT_EQ(SI_USER, info.si_code); - ASSERT_EQ(getuid(), info.si_uid); - _Exit(0); - } - ASSERT_SYS(0, 0, kill(pid, SIGUSR1)); - ASSERT_SYS(0, pid, wait(&ws)); - ASSERT_EQ(0, ws); - ASSERT_SYS(0, 0, sigprocmask(SIG_SETMASK, &oldss, 0)); + ASSERT_EQ(0, sigemptyset(&ss)); + ASSERT_EQ(0, sigaddset(&ss, SIGUSR1)); + ASSERT_SYS(0, 0, sigprocmask(SIG_BLOCK, &ss, 0)); + g_ready = true; + ASSERT_SYS(0, SIGUSR1, sigtimedwait(&ss, &info, 0)); + ASSERT_EQ(SIGUSR1, info.si_signo); + ASSERT_EQ(SI_TKILL, info.si_code); + ASSERT_EQ(getuid(), info.si_uid); + return 0; +} + +TEST(sigtimedwait, test) { + pthread_t th; + ASSERT_EQ(0, pthread_create(&th, 0, worker, 0)); + for (;;) + if (g_ready) + break; + ASSERT_EQ(0, pthread_kill(th, SIGUSR1)); + ASSERT_EQ(0, pthread_join(th, 0)); } diff --git a/test/libc/thread/pthread_cancel_test.c b/test/libc/thread/pthread_cancel_test.c index 790f74ad5bb..56f9fa5d550 100644 --- a/test/libc/thread/pthread_cancel_test.c +++ b/test/libc/thread/pthread_cancel_test.c @@ -103,8 +103,6 @@ TEST(pthread_cancel, synchronous) { TEST(pthread_cancel, synchronous_deferred) { void *rc; pthread_t th; - if (!IsWindows()) - return; ASSERT_SYS(0, 0, pipe(pfds)); ASSERT_EQ(0, pthread_create(&th, 0, Worker, 0)); while (!ready) diff --git a/test/libc/thread/pthread_kill_test.c b/test/libc/thread/pthread_kill_test.c index 25a8f0b3c38..f6494b13742 100644 --- a/test/libc/thread/pthread_kill_test.c +++ b/test/libc/thread/pthread_kill_test.c @@ -193,8 +193,6 @@ void *SocketAcceptWorker(void *arg) { } TEST(pthread_kill, canInterruptSocketAcceptOperation) { - if (IsWindows()) - return; // TODO(jart): BAH pthread_t t; struct sigaction oldsa; struct sigaction sa = {.sa_handler = OnSig};