From 0d74673213d9a4949b10a0eb9306bad3f7c78ee3 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Thu, 19 Sep 2024 03:02:13 -0700 Subject: [PATCH] Introduce interprocess signaling on Windows This change gets rsync working without any warning or errors. On Windows we now create a bunch of C:\var\sig\x\y.pid shared memory files, so sigs can be delivered between processes. WinMain() creates this file when the process starts. If the program links signaling system calls then we make a thread at startup too, which allows asynchronous delivery each quantum and cancelation points can spot these signals potentially faster on wait See #1240 --- libc/calls/createpipename.c | 22 +------ libc/calls/sig.c | 32 ++++++++-- libc/calls/sig.internal.h | 8 ++- libc/calls/sigpending.c | 2 +- libc/fmt/internal.h | 1 + libc/intrin/itoa16.c | 36 +++++++++++ libc/intrin/sig.c | 76 ++++++++++++++++++++++++ libc/intrin/sigprocmask-nt.c | 3 +- libc/intrin/terminatethisprocess.c | 20 +++++++ libc/nt/files.h | 4 ++ libc/nt/kernel32/SetFilePointer.S | 18 ++++++ libc/nt/master.sh | 1 + libc/proc/fork-nt.c | 16 +++-- libc/proc/kill-nt.c | 26 ++++++-- libc/runtime/winmain.greg.c | 13 ++-- test/libc/calls/sigsuspend_test.c | 4 -- test/libc/proc/fork_test.c | 2 - test/libc/proc/handkill_test.c | 4 -- test/libc/proc/posix_spawn_test.c | 2 - test/libc/proc/system_test.c | 5 +- test/libc/stdio/popen_test.c | 2 - test/posix/interprocess_signaling_test.c | 67 +++++++++++++++++++++ 22 files changed, 302 insertions(+), 62 deletions(-) create mode 100644 libc/intrin/itoa16.c create mode 100644 libc/nt/kernel32/SetFilePointer.S create mode 100644 test/posix/interprocess_signaling_test.c diff --git a/libc/calls/createpipename.c b/libc/calls/createpipename.c index a5d518522..f8eed1531 100644 --- a/libc/calls/createpipename.c +++ b/libc/calls/createpipename.c @@ -17,26 +17,10 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/atomic.h" +#include "libc/fmt/internal.h" #include "libc/intrin/atomic.h" #include "libc/runtime/internal.h" -static textwindows char16_t *itoa16(char16_t p[21], uint64_t x) { - char t; - size_t a, b, i = 0; - do { - p[i++] = x % 10 + '0'; - x = x / 10; - } while (x > 0); - if (i) { - for (a = 0, b = i - 1; a < b; ++a, --b) { - t = p[a]; - p[a] = p[b]; - p[b] = t; - } - } - return p + i; -} - // This function is called very early by WinMain(). textwindows char16_t *__create_pipe_name(char16_t *a) { char16_t *p = a; @@ -44,9 +28,9 @@ textwindows char16_t *__create_pipe_name(char16_t *a) { static atomic_uint x; while (*q) *p++ = *q++; - p = itoa16(p, __pid); + p = __itoa16(p, __pid); *p++ = '-'; - p = itoa16(p, atomic_fetch_add(&x, 1)); + p = __itoa16(p, atomic_fetch_add(&x, 1)); *p = 0; return a; } diff --git a/libc/calls/sig.c b/libc/calls/sig.c index fc2275e8d..3347e4114 100644 --- a/libc/calls/sig.c +++ b/libc/calls/sig.c @@ -32,12 +32,13 @@ #include "libc/intrin/bsf.h" #include "libc/intrin/describebacktrace.h" #include "libc/intrin/dll.h" -#include "libc/intrin/kprintf.h" +#include "libc/intrin/maps.h" #include "libc/intrin/strace.h" #include "libc/intrin/weaken.h" #include "libc/nt/console.h" #include "libc/nt/enum/context.h" #include "libc/nt/enum/exceptionhandleractions.h" +#include "libc/nt/enum/processcreationflags.h" #include "libc/nt/enum/signal.h" #include "libc/nt/enum/status.h" #include "libc/nt/events.h" @@ -46,6 +47,7 @@ #include "libc/nt/struct/ntexceptionpointers.h" #include "libc/nt/synchronization.h" #include "libc/nt/thread.h" +#include "libc/runtime/internal.h" #include "libc/runtime/symbols.internal.h" #include "libc/str/str.h" #include "libc/sysv/consts/sa.h" @@ -58,6 +60,8 @@ * @fileoverview Cosmopolitan Signals for Windows. */ +#define STKSZ 65536 + struct SignalFrame { unsigned rva; unsigned flags; @@ -80,7 +84,7 @@ textwindows bool __sig_ignored(int sig) { textwindows void __sig_delete(int sig) { struct Dll *e; - atomic_fetch_and_explicit(&__sig.pending, ~(1ull << (sig - 1)), + atomic_fetch_and_explicit(__sig.process, ~(1ull << (sig - 1)), memory_order_relaxed); _pthread_lock(); for (e = dll_last(_pthread_list); e; e = dll_prev(_pthread_list, e)) @@ -108,7 +112,7 @@ static textwindows int __sig_getter(atomic_ulong *sigs, sigset_t masked) { textwindows int __sig_get(sigset_t masked) { int sig; if (!(sig = __sig_getter(&__get_tls()->tib_sigpending, masked))) - sig = __sig_getter(&__sig.pending, masked); + sig = __sig_getter(__sig.process, masked); return sig; } @@ -179,6 +183,7 @@ textwindows int __sig_raise(volatile int sig, int sic) { unsigned rva, flags; struct PosixThread *pt = _pthread_self(); if (__sig_start(pt, sig, &rva, &flags)) { + if (flags & SA_RESETHAND) { STRACE("resetting %G handler", sig); __sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL; @@ -410,7 +415,7 @@ textwindows void __sig_generate(int sig, int sic) { STRACE("terminating on %G due to no handler", sig); __sig_terminate(sig); } - if (atomic_load_explicit(&__sig.pending, memory_order_acquire) & + if (atomic_load_explicit(__sig.process, memory_order_acquire) & (1ull << (sig - 1))) { return; } @@ -448,7 +453,7 @@ textwindows void __sig_generate(int sig, int sic) { __sig_killer(mark, sig, sic); _pthread_unref(mark); } else { - atomic_fetch_or_explicit(&__sig.pending, 1ull << (sig - 1), + atomic_fetch_or_explicit(__sig.process, 1ull << (sig - 1), memory_order_relaxed); } ALLOW_SIGNALS; @@ -611,11 +616,28 @@ textwindows int __sig_check(void) { } } +// delivers signals from other processes asynchronously +textwindows dontinstrument static uint32_t __sig_worker(void *arg) { + struct CosmoTib tls; + __bootstrap_tls(&tls, __builtin_frame_address(0)); + char *sp = __builtin_frame_address(0); + __maps_track((char *)(((uintptr_t)sp + __pagesize - 1) & -__pagesize) - STKSZ, + STKSZ); + for (;;) { + int sig; + if ((sig = __sig_getter(__sig.process, 0))) + __sig_generate(sig, SI_KERNEL); + Sleep(1); + } + return 0; +} + __attribute__((__constructor__(10))) textstartup void __sig_init(void) { if (!IsWindows()) return; AddVectoredExceptionHandler(true, (void *)__sig_crash); SetConsoleCtrlHandler((void *)__sig_console, true); + CreateThread(0, STKSZ, __sig_worker, 0, kNtStackSizeParamIsAReservation, 0); } #endif /* __x86_64__ */ diff --git a/libc/calls/sig.internal.h b/libc/calls/sig.internal.h index 48566f303..db6e711b4 100644 --- a/libc/calls/sig.internal.h +++ b/libc/calls/sig.internal.h @@ -1,5 +1,6 @@ #ifndef COSMOPOLITAN_LIBC_CALLS_SIGNALS_INTERNAL_H_ #define COSMOPOLITAN_LIBC_CALLS_SIGNALS_INTERNAL_H_ +#include "libc/atomic.h" #include "libc/calls/struct/sigset.h" #include "libc/thread/posixthread.internal.h" @@ -9,8 +10,8 @@ COSMOPOLITAN_C_START_ struct Signals { - _Atomic(uint64_t) pending; - _Atomic(uint64_t) count; + atomic_ulong *process; + atomic_ulong count; }; extern struct Signals __sig; @@ -27,5 +28,8 @@ void __sig_delete(int); void __sig_generate(int, int); void __sig_init(void); +char16_t *__sig_process_path(char16_t *, uint32_t); +atomic_ulong *__sig_map_process(int); + COSMOPOLITAN_C_END_ #endif /* COSMOPOLITAN_LIBC_CALLS_SIGNALS_INTERNAL_H_ */ diff --git a/libc/calls/sigpending.c b/libc/calls/sigpending.c index e11e194c0..0fceafa98 100644 --- a/libc/calls/sigpending.c +++ b/libc/calls/sigpending.c @@ -53,7 +53,7 @@ int sigpending(sigset_t *pending) { } rc = 0; } else if (IsWindows()) { - *pending = atomic_load_explicit(&__sig.pending, memory_order_acquire) | + *pending = atomic_load_explicit(__sig.process, memory_order_acquire) | atomic_load_explicit(&__get_tls()->tib_sigpending, memory_order_acquire); rc = 0; diff --git a/libc/fmt/internal.h b/libc/fmt/internal.h index 8203bf153..135acfbdb 100644 --- a/libc/fmt/internal.h +++ b/libc/fmt/internal.h @@ -48,5 +48,6 @@ int __vcscanf(int (*)(void *), int (*)(int, void *), void *, const char *, va_list); int __fmt(void *, void *, const char *, va_list, int *); +char16_t *__itoa16(char16_t[21], uint64_t); #endif /* COSMOPOLITAN_LIBC_FMT_STRTOL_H_ */ diff --git a/libc/intrin/itoa16.c b/libc/intrin/itoa16.c new file mode 100644 index 000000000..f89116e5d --- /dev/null +++ b/libc/intrin/itoa16.c @@ -0,0 +1,36 @@ +/*-*- 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/fmt/internal.h" + +textwindows char16_t *__itoa16(char16_t p[21], uint64_t x) { + char t; + size_t a, b, i = 0; + do { + p[i++] = x % 10 + '0'; + x = x / 10; + } while (x > 0); + if (i) { + for (a = 0, b = i - 1; a < b; ++a, --b) { + t = p[a]; + p[a] = p[b]; + p[b] = t; + } + } + return p + i; +} diff --git a/libc/intrin/sig.c b/libc/intrin/sig.c index 8679b811d..1fa65fc27 100644 --- a/libc/intrin/sig.c +++ b/libc/intrin/sig.c @@ -17,11 +17,25 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/sysv/consts/sig.h" +#include "libc/atomic.h" #include "libc/calls/sig.internal.h" #include "libc/calls/struct/sigset.internal.h" #include "libc/dce.h" +#include "libc/fmt/internal.h" #include "libc/intrin/atomic.h" #include "libc/intrin/weaken.h" +#include "libc/limits.h" +#include "libc/nt/createfile.h" +#include "libc/nt/enum/accessmask.h" +#include "libc/nt/enum/creationdisposition.h" +#include "libc/nt/enum/fileflagandattributes.h" +#include "libc/nt/enum/filemapflags.h" +#include "libc/nt/enum/filemovemethod.h" +#include "libc/nt/enum/filesharemode.h" +#include "libc/nt/enum/pageflags.h" +#include "libc/nt/files.h" +#include "libc/nt/memory.h" +#include "libc/nt/runtime.h" #include "libc/thread/tls.h" struct Signals __sig; @@ -52,3 +66,65 @@ void __sig_unblock(sigset_t m) { sys_sigprocmask(SIG_SETMASK, &m, 0); } } + +#ifdef __x86_64__ + +textwindows char16_t *__sig_process_path(char16_t *path, uint32_t pid) { + char16_t *p = path; + *p++ = 'C'; + *p++ = ':'; + *p++ = '\\'; + *p++ = 'v'; + *p++ = 'a'; + *p++ = 'r'; + *p = 0; + CreateDirectory(path, 0); + *p++ = '\\'; + *p++ = 's'; + *p++ = 'i'; + *p++ = 'g'; + *p = 0; + CreateDirectory(path, 0); + *p++ = '\\'; + p = __itoa16(p, (pid & 0x000fff00) >> 8); + *p = 0; + CreateDirectory(path, 0); + *p++ = '\\'; + p = __itoa16(p, pid); + *p++ = '.'; + *p++ = 'p'; + *p++ = 'i'; + *p++ = 'd'; + *p = 0; + return path; +} + +textwindows static atomic_ulong *__sig_map_process_impl(int pid) { + char16_t path[128]; + intptr_t hand = CreateFile(__sig_process_path(path, pid), + kNtGenericRead | kNtGenericWrite, + kNtFileShareRead | kNtFileShareWrite, 0, + kNtOpenAlways, kNtFileAttributeNormal, 0); + if (hand == -1) + return 0; + SetFilePointer(hand, 8, 0, kNtFileBegin); + SetEndOfFile(hand); + intptr_t map = CreateFileMapping(hand, 0, kNtPageReadwrite, 0, 8, 0); + if (!map) { + CloseHandle(hand); + return 0; + } + atomic_ulong *sigs = MapViewOfFileEx(map, kNtFileMapWrite, 0, 0, 8, 0); + CloseHandle(map); + CloseHandle(hand); + return sigs; +} + +textwindows atomic_ulong *__sig_map_process(int pid) { + int e = errno; + atomic_ulong *res = __sig_map_process_impl(pid); + errno = e; + return res; +} + +#endif /* __x86_64__ */ diff --git a/libc/intrin/sigprocmask-nt.c b/libc/intrin/sigprocmask-nt.c index 0d31b61af..7281938c8 100644 --- a/libc/intrin/sigprocmask-nt.c +++ b/libc/intrin/sigprocmask-nt.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" +#include "libc/atomic.h" #include "libc/calls/sig.internal.h" #include "libc/calls/struct/sigset.h" #include "libc/intrin/atomic.h" @@ -34,7 +35,7 @@ textwindows int __sig_mask(int how, const sigset_t *neu, sigset_t *old) { } // get address of sigset to modify - _Atomic(uint64_t) *mask = &__get_tls()->tib_sigmask; + atomic_ulong *mask = &__get_tls()->tib_sigmask; // handle read-only case sigset_t oldmask; diff --git a/libc/intrin/terminatethisprocess.c b/libc/intrin/terminatethisprocess.c index cff6d6e79..b8d81b441 100644 --- a/libc/intrin/terminatethisprocess.c +++ b/libc/intrin/terminatethisprocess.c @@ -16,8 +16,16 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/atomic.h" +#include "libc/calls/sig.internal.h" +#include "libc/intrin/kprintf.h" +#include "libc/limits.h" +#include "libc/nt/files.h" +#include "libc/nt/memory.h" #include "libc/nt/runtime.h" #include "libc/nt/thunk/msabi.h" +#include "libc/runtime/internal.h" +#ifdef __x86_64__ __msabi extern typeof(TerminateProcess) *const __imp_TerminateProcess; @@ -25,8 +33,20 @@ __msabi extern typeof(TerminateProcess) *const __imp_TerminateProcess; * Terminates the calling process and all of its threads. */ textwindows dontinstrument void TerminateThisProcess(uint32_t dwWaitStatus) { + + // delete sig file + char16_t path[128]; + atomic_ulong *real; + atomic_ulong fake = 0; + real = __sig.process; + __sig.process = &fake; + UnmapViewOfFile(real); + DeleteFile(__sig_process_path(path, __pid)); + // "When a process terminates itself, TerminateProcess stops execution // of the calling thread and does not return." -Quoth MSDN __imp_TerminateProcess(-1, dwWaitStatus); __builtin_unreachable(); } + +#endif /* __x86_64__ */ diff --git a/libc/nt/files.h b/libc/nt/files.h index fcbc294cb..6959a0d13 100644 --- a/libc/nt/files.h +++ b/libc/nt/files.h @@ -225,6 +225,10 @@ bool32 GetVolumeInformationByHandle(int64_t hFile, char16_t *opt_out_lpFileSystemNameBuffer, uint32_t nFileSystemNameSize); +uint32_t SetFilePointer(intptr_t hFile, int32_t lDistanceToMove, + long *opt_inout_lpDistanceToMoveHigh, + uint32_t dwMoveMethod); + #if ShouldUseMsabiAttribute() #include "libc/nt/thunk/files.inc" #endif /* ShouldUseMsabiAttribute() */ diff --git a/libc/nt/kernel32/SetFilePointer.S b/libc/nt/kernel32/SetFilePointer.S new file mode 100644 index 000000000..2e14b9e3e --- /dev/null +++ b/libc/nt/kernel32/SetFilePointer.S @@ -0,0 +1,18 @@ +#include "libc/nt/codegen.h" +.imp kernel32,__imp_SetFilePointer,SetFilePointer + + .text.windows + .ftrace1 +SetFilePointer: + .ftrace2 +#ifdef __x86_64__ + push %rbp + mov %rsp,%rbp + mov __imp_SetFilePointer(%rip),%rax + jmp __sysv2nt +#elif defined(__aarch64__) + mov x0,#0 + ret +#endif + .endfn SetFilePointer,globl + .previous diff --git a/libc/nt/master.sh b/libc/nt/master.sh index c79782d21..5a4fbf402 100755 --- a/libc/nt/master.sh +++ b/libc/nt/master.sh @@ -259,6 +259,7 @@ imp 'SetEvent' SetEvent kernel32 1 imp 'SetFileAttributes' SetFileAttributesW kernel32 2 imp 'SetFileCompletionNotificationModes' SetFileCompletionNotificationModes kernel32 2 imp 'SetFileInformationByHandle' SetFileInformationByHandle kernel32 4 +imp 'SetFilePointer' SetFilePointer kernel32 4 imp 'SetFileTime' SetFileTime kernel32 4 imp 'SetFileValidData' SetFileValidData kernel32 2 imp 'SetHandleCount' SetHandleCount kernel32 1 diff --git a/libc/proc/fork-nt.c b/libc/proc/fork-nt.c index 996aff0fa..721b2cb63 100644 --- a/libc/proc/fork-nt.c +++ b/libc/proc/fork-nt.c @@ -196,9 +196,13 @@ textwindows void WinMainForked(void) { int64_t reader; int64_t savetsc; uint32_t varlen; + atomic_ulong *sigproc; char16_t fvar[21 + 1 + 21 + 1]; struct Fds *fds = __veil("r", &g_fds); + // save signal pointer + sigproc = __sig.process; + // check to see if the process was actually forked // this variable should have the pipe handle numba varlen = GetEnvironmentVariable(u"_FORK", fvar, ARRAYLEN(fvar)); @@ -295,12 +299,13 @@ textwindows void WinMainForked(void) { fds->p[1].handle = GetStdHandle(kNtStdOutputHandle); fds->p[2].handle = GetStdHandle(kNtStdErrorHandle); + // restore signal pointer + __sig.process = sigproc; + // restore the crash reporting stuff #if SYSDEBUG RemoveVectoredExceptionHandler(oncrash); #endif - if (_weaken(__sig_init)) - _weaken(__sig_init)(); // jump back into function below longjmp(jb, 1); @@ -460,15 +465,16 @@ textwindows int sys_fork_nt(uint32_t dwCreationFlags) { __morph_tls(); __tls_enabled = true; // the child's pending signals is initially empty - atomic_store_explicit(&__sig.pending, 0, memory_order_relaxed); + atomic_store_explicit(__sig.process, 0, memory_order_relaxed); atomic_store_explicit(&tib->tib_sigpending, 0, memory_order_relaxed); // re-apply code morphing for function tracing - if (ftrace_stackdigs) { + if (ftrace_stackdigs) _weaken(__hook)(_weaken(ftrace_hook), _weaken(GetSymbolTable)()); - } // reset core runtime services __proc_wipe(); WipeKeystrokes(); + if (_weaken(__sig_init)) + _weaken(__sig_init)(); if (_weaken(__itimer_wipe)) _weaken(__itimer_wipe)(); // notify pthread join diff --git a/libc/proc/kill-nt.c b/libc/proc/kill-nt.c index 45f9b740d..306f7fb2b 100644 --- a/libc/proc/kill-nt.c +++ b/libc/proc/kill-nt.c @@ -16,16 +16,21 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/atomic.h" #include "libc/calls/calls.h" +#include "libc/calls/sig.internal.h" #include "libc/calls/struct/sigset.internal.h" #include "libc/calls/syscall-nt.internal.h" #include "libc/errno.h" +#include "libc/intrin/atomic.h" #include "libc/intrin/dll.h" +#include "libc/intrin/kprintf.h" #include "libc/intrin/strace.h" #include "libc/nt/console.h" #include "libc/nt/enum/ctrlevent.h" #include "libc/nt/enum/processaccess.h" #include "libc/nt/errors.h" +#include "libc/nt/memory.h" #include "libc/nt/process.h" #include "libc/nt/runtime.h" #include "libc/proc/proc.internal.h" @@ -36,21 +41,18 @@ textwindows int sys_kill_nt(int pid, int sig) { // validate api usage - if (!(0 <= sig && sig <= 64)) { + if (!(0 <= sig && sig <= 64)) return einval(); - } // XXX: NT doesn't really have process groups. For instance the // CreateProcess() flag for starting a process group actually // just does an "ignore ctrl-c" internally. - if (pid < -1) { + if (pid < -1) pid = -pid; - } // no support for kill all yet - if (pid == -1) { + if (pid == -1) return einval(); - } // just call raise() if we're targeting self if (pid <= 0 || pid == getpid()) { @@ -70,6 +72,18 @@ textwindows int sys_kill_nt(int pid, int sig) { } } + // attempt to signal via /var/sig shared memory file + if (pid > 0 && sig != 9) { + atomic_ulong *sigproc; + if ((sigproc = __sig_map_process(pid))) { + if (sig > 0) + atomic_fetch_or_explicit(sigproc, 1ull << (sig - 1), + memory_order_release); + UnmapViewOfFile(sigproc); + return 0; + } + } + // find existing handle we own for process int64_t handle, closeme = 0; if (!(handle = __proc_handle(pid))) { diff --git a/libc/runtime/winmain.greg.c b/libc/runtime/winmain.greg.c index 5bf331879..fdaac83cb 100644 --- a/libc/runtime/winmain.greg.c +++ b/libc/runtime/winmain.greg.c @@ -17,11 +17,11 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" +#include "libc/atomic.h" #include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" #include "libc/calls/syscall_support-nt.internal.h" #include "libc/intrin/dll.h" -#include "libc/intrin/kprintf.h" #include "libc/intrin/maps.h" #include "libc/intrin/nomultics.h" #include "libc/intrin/weaken.h" @@ -294,7 +294,6 @@ static abi wontreturn void WinInit(const char16_t *cmdline) { ARRAYLEN(wa->envp) - 1); __imp_FreeEnvironmentStringsW(env16); __envp = &wa->envp[0]; - // handover control to cosmopolitan runtime __stack_call(count, wa->argv, wa->envp, wa->auxv, cosmo, (uintptr_t)(stackaddr + (stacksize - sizeof(struct WinArgs)))); @@ -302,6 +301,7 @@ static abi wontreturn void WinInit(const char16_t *cmdline) { abi int64_t WinMain(int64_t hInstance, int64_t hPrevInstance, const char *lpCmdLine, int64_t nCmdShow) { + static atomic_ulong fake_process_signals; const char16_t *cmdline; extern char os asm("__hostos"); os = _HOSTWINDOWS; // madness https://news.ycombinator.com/item?id=21019722 @@ -317,19 +317,20 @@ abi int64_t WinMain(int64_t hInstance, int64_t hPrevInstance, __gransize = si.dwAllocationGranularity; __umask = 077; __pid = __imp_GetCurrentProcessId(); + if (!(__sig.process = __sig_map_process(__pid))) + __sig.process = &fake_process_signals; + atomic_store_explicit(__sig.process, 0, memory_order_release); cmdline = __imp_GetCommandLineW(); #if SYSDEBUG // sloppy flag-only check for early initialization if (StrStr(cmdline, u"--strace")) ++__strace; #endif - if (_weaken(WinSockInit)) { + if (_weaken(WinSockInit)) _weaken(WinSockInit)(); - } DeduplicateStdioHandles(); - if (_weaken(WinMainForked)) { + if (_weaken(WinMainForked)) _weaken(WinMainForked)(); - } WinInit(cmdline); } diff --git a/test/libc/calls/sigsuspend_test.c b/test/libc/calls/sigsuspend_test.c index 457c3ba23..8386005ac 100644 --- a/test/libc/calls/sigsuspend_test.c +++ b/test/libc/calls/sigsuspend_test.c @@ -76,10 +76,6 @@ TEST(sigsuspend, testSignalQueuingSelf) { } TEST(sigsuspend, testSignalQueuingIpc) { - if (IsWindows()) { - // xxx: probably need a signal server to do this kind of signalling - return; - } int pid, ws; sigset_t neu, old, bits; struct sigaction oldusr1, oldusr2; diff --git a/test/libc/proc/fork_test.c b/test/libc/proc/fork_test.c index 958fbbb2b..1bb7d61ee 100644 --- a/test/libc/proc/fork_test.c +++ b/test/libc/proc/fork_test.c @@ -103,8 +103,6 @@ static void OnSigusr2(int sig) { } TEST(fork, childToChild) { - if (IsWindows()) - return; // :'( sigset_t mask, oldmask; int ws, parent, child1, child2; gotsigusr1 = false; diff --git a/test/libc/proc/handkill_test.c b/test/libc/proc/handkill_test.c index 07284b4ee..a669eeb75 100644 --- a/test/libc/proc/handkill_test.c +++ b/test/libc/proc/handkill_test.c @@ -125,8 +125,6 @@ TEST(handkill, thread2thread_async) { } TEST(handkill, process_async) { - if (IsWindows()) - return; SPAWN(fork); shm->ready = true; while (!shm->got_signal) @@ -142,8 +140,6 @@ TEST(handkill, process_async) { } TEST(handkill, process_pause) { - if (IsWindows()) - return; SPAWN(fork); shm->ready = true; pause(); diff --git a/test/libc/proc/posix_spawn_test.c b/test/libc/proc/posix_spawn_test.c index 044c24dcf..2ddf868aa 100644 --- a/test/libc/proc/posix_spawn_test.c +++ b/test/libc/proc/posix_spawn_test.c @@ -262,8 +262,6 @@ void EmptySigHandler(int sig) { } TEST(posix_spawn, etxtbsy) { - if (IsWindows()) - return; // can't deliver signals between processes if (IsXnu()) return; // they don't appear impacted by this race condition if (IsNetbsd()) diff --git a/test/libc/proc/system_test.c b/test/libc/proc/system_test.c index b80873320..a387b25bb 100644 --- a/test/libc/proc/system_test.c +++ b/test/libc/proc/system_test.c @@ -166,13 +166,12 @@ TEST(system, notequals) { } TEST(system, usleep) { - ASSERT_EQ(0, GETEXITSTATUS(system("usleep & kill $!"))); + ASSERT_EQ(0, GETEXITSTATUS(system("usleep & kill $!; wait"))); } TEST(system, kill) { int ws = system("kill -TERM $$; usleep"); - if (!IsWindows()) - ASSERT_EQ(SIGTERM, WTERMSIG(ws)); + ASSERT_EQ(SIGTERM, WTERMSIG(ws)); } TEST(system, exitStatusPreservedAfterSemiColon) { diff --git a/test/libc/stdio/popen_test.c b/test/libc/stdio/popen_test.c index 648e885ac..10e53cd87 100644 --- a/test/libc/stdio/popen_test.c +++ b/test/libc/stdio/popen_test.c @@ -120,8 +120,6 @@ void OnSig(int sig) { } TEST(popen, complicated) { - if (IsWindows()) - return; // windows treats sigusr1 as terminate char cmd[64]; signal(SIGUSR1, OnSig); sprintf(cmd, "read a ; test \"x$a\" = xhello && kill -USR1 %d", getpid()); diff --git a/test/posix/interprocess_signaling_test.c b/test/posix/interprocess_signaling_test.c new file mode 100644 index 000000000..d6372492e --- /dev/null +++ b/test/posix/interprocess_signaling_test.c @@ -0,0 +1,67 @@ +// 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 +#include +#include +#include + +atomic_int *got; + +void onsig(int sig) { + *got = sig; +} + +int main(int argc, char *argv[]) { + + // create process shared memory + got = mmap(0, 4, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (got == MAP_FAILED) + return 5; + + // listen for signal + if (signal(SIGUSR1, onsig)) + return 6; + + // block signals + sigset_t full; + if (sigfillset(&full)) + return 7; + if (sigprocmask(SIG_BLOCK, &full, 0)) + return 8; + + // create child process + int pid; + if (!(pid = fork())) { + sigset_t empty; + sigemptyset(&empty); + sigsuspend(&empty); + *got |= 128; + _exit(0); + } + + // send signal + if (kill(pid, SIGUSR1)) + return 9; + + // wait for child to die + int ws; + if (wait(&ws) != pid) + return 10; + if (ws) + return 11; + if (*got != (128 | SIGUSR1)) + return 12; +}