diff --git a/libc/calls/calls.mk b/libc/calls/calls.mk index fe59edd02..8c7316407 100644 --- a/libc/calls/calls.mk +++ b/libc/calls/calls.mk @@ -69,6 +69,8 @@ $(LIBC_CALLS_A).pkg: \ # we're on a stack owned by win32 without tls o/$(MODE)/libc/calls/foist.o \ o/$(MODE)/libc/calls/__sig2.o \ +o/$(MODE)/libc/calls/sigchld-nt.o \ +o/$(MODE)/libc/calls/sigwinch-nt.o \ o/$(MODE)/libc/calls/onntconsoleevent.o \ o/$(MODE)/libc/calls/wincrash.o \ o/$(MODE)/libc/calls/ntcontext2linux.o: private \ diff --git a/libc/calls/execve-nt.greg.c b/libc/calls/execve-nt.greg.c index 0d6b871b0..95bd1bfae 100644 --- a/libc/calls/execve-nt.greg.c +++ b/libc/calls/execve-nt.greg.c @@ -19,6 +19,7 @@ #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/ntspawn.h" +#include "libc/calls/struct/sigaction.internal.h" #include "libc/calls/syscall-nt.internal.h" #include "libc/dce.h" #include "libc/fmt/itoa.h" @@ -166,6 +167,8 @@ keywords int sys_execve_nt(const char *program, char *const argv[], // kill siblings sys_execve_killer(); + PurgeThread(*_weaken(__sigchld_thread)); + PurgeThread(*_weaken(__sigwinch_thread)); // close win32 handles for memory mappings // unlike fork calling execve destroys all memory diff --git a/libc/calls/interrupts-nt.c b/libc/calls/interrupts-nt.c index e12df20ee..ade78806d 100644 --- a/libc/calls/interrupts-nt.c +++ b/libc/calls/interrupts-nt.c @@ -16,19 +16,12 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" -#include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" -#include "libc/calls/state.internal.h" -#include "libc/calls/struct/fd.internal.h" -#include "libc/calls/struct/sigaction.h" -#include "libc/calls/struct/sigaction.internal.h" #include "libc/calls/syscall_support-nt.internal.h" -#include "libc/dce.h" #include "libc/errno.h" -#include "libc/intrin/strace.internal.h" #include "libc/intrin/weaken.h" +#include "libc/nt/struct/windowplacement.h" #include "libc/sysv/errfuns.h" #include "libc/thread/thread.h" #include "libc/thread/tls.h" @@ -51,14 +44,6 @@ textwindows int _check_interrupts(int sigops) { if (__tls_enabled) { __get_tls()->tib_flags = flags; } - if (_weaken(_check_sigwinch)) { - _weaken(_check_sigwinch)(); - } - if (!__tls_enabled || !(__get_tls()->tib_flags & TIB_FLAG_TIME_CRITICAL)) { - if (!(sigops & kSigOpNochld) && _weaken(_check_sigchld)) { - _weaken(_check_sigchld)(); - } - } if (_weaken(__sig_check) && _weaken(__sig_check)(sigops)) { return eintr(); } diff --git a/libc/calls/ntspawn.c b/libc/calls/ntspawn.c index a7ddee742..6c4390a53 100644 --- a/libc/calls/ntspawn.c +++ b/libc/calls/ntspawn.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/ntspawn.h" #include "libc/assert.h" +#include "libc/calls/struct/sigaction.internal.h" #include "libc/calls/syscall_support-nt.internal.h" #include "libc/errno.h" #include "libc/intrin/pushpop.internal.h" @@ -83,6 +84,7 @@ textwindows int ntspawn( char16_t prog16[PATH_MAX]; rc = -1; block = NULL; + _init_sigchld(); if (__mkntpath(prog, prog16) == -1) return -1; // we can't call malloc() because we're higher in the topological order // we can't call kmalloc() because fork() calls this when kmalloc is locked diff --git a/libc/calls/onntconsoleevent.c b/libc/calls/onntconsoleevent.c index 934f62326..992102b6b 100644 --- a/libc/calls/onntconsoleevent.c +++ b/libc/calls/onntconsoleevent.c @@ -34,18 +34,6 @@ __msabi extern typeof(GetStdHandle) *const __imp_GetStdHandle; __msabi extern typeof(WriteFile) *const __imp_WriteFile; -static textwindows unsigned long StrLen(const char *s) { - unsigned long n = 0; - while (*s++) ++n; - return n; -} - -static textwindows void Log(const char *s) { -#ifndef NDEBUG - __imp_WriteFile(__imp_GetStdHandle(kNtStdErrorHandle), s, StrLen(s), 0, 0); -#endif -} - static textwindows int GetSig(uint32_t dwCtrlType) { switch (dwCtrlType) { case kNtCtrlCEvent: @@ -61,11 +49,10 @@ static textwindows int GetSig(uint32_t dwCtrlType) { } } -__msabi textwindows bool32 __onntconsoleevent(uint32_t dwCtrlType) { +textwindows bool32 __sig_notify(int sig, int sic) { // we're on a stack that's owned by win32. to make matters worse, // win32 spawns a totally new thread just to invoke this handler. - int sig = GetSig(dwCtrlType); if (__sighandrvas[sig] == (uintptr_t)SIG_IGN) { return true; } @@ -73,7 +60,7 @@ __msabi textwindows bool32 __onntconsoleevent(uint32_t dwCtrlType) { // if we don't have tls, then we can't hijack a safe stack from a // thread so just try our luck punting the signal to the next i/o if (!__tls_enabled) { - __sig_add(0, sig, SI_KERNEL); + __sig_add(0, sig, sic); return true; } @@ -92,7 +79,7 @@ __msabi textwindows bool32 __onntconsoleevent(uint32_t dwCtrlType) { if (pt->tib->tib_sigmask & (1ull << (sig - 1))) continue; // masked if (pt->flags & PT_BLOCKED) { pthread_spin_unlock(&_pthread_lock); - __sig_add(0, sig, SI_KERNEL); + __sig_add(0, sig, sic); return true; } } @@ -111,8 +98,8 @@ __msabi textwindows bool32 __onntconsoleevent(uint32_t dwCtrlType) { if (tid <= 0) continue; // -1 means spawning, 0 means terminated if (pt->tib->tib_sigmask & (1ull << (sig - 1))) continue; // masked pthread_spin_unlock(&_pthread_lock); - if (_pthread_signal(pt, sig, SI_KERNEL) == -1) { - __sig_add(0, sig, SI_KERNEL); + if (_pthread_signal(pt, sig, sic) == -1) { + __sig_add(0, sig, sic); } return true; } @@ -121,4 +108,8 @@ __msabi textwindows bool32 __onntconsoleevent(uint32_t dwCtrlType) { return true; } +__msabi textwindows bool32 __onntconsoleevent(uint32_t dwCtrlType) { + return __sig_notify(GetSig(dwCtrlType), SI_KERNEL); +} + #endif /* __x86_64__ */ diff --git a/libc/calls/sig.internal.h b/libc/calls/sig.internal.h index 77b4f948e..77c017988 100644 --- a/libc/calls/sig.internal.h +++ b/libc/calls/sig.internal.h @@ -47,6 +47,7 @@ void __sig_pending(sigset_t *); int __sig_is_applicable(struct Signal *); bool __sig_deliver(int, int, int, ucontext_t *); int __sig_tramp(struct Delivery *); +bool32 __sig_notify(int, int); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/calls/sigaction.c b/libc/calls/sigaction.c index e65f27068..f83af9577 100644 --- a/libc/calls/sigaction.c +++ b/libc/calls/sigaction.c @@ -54,7 +54,6 @@ __static_yoink("strsignal"); // for kprintf() #if SupportsWindows() __static_yoink("_init_onntconsoleevent"); -__static_yoink("_check_sigwinch"); __static_yoink("_init_wincrash"); #endif @@ -506,6 +505,9 @@ int sigaction(int sig, const struct sigaction *act, struct sigaction *oldact) { once = true; } } + if (IsWindows() && !rc && sig == SIGWINCH) { + _init_sigwinch(); // lazy b/c sigwinch is otherwise ignored + } } STRACE("sigaction(%G, %s, [%s]) → %d% m", sig, DescribeSigaction(0, act), DescribeSigaction(rc, oldact), rc); diff --git a/libc/calls/sigchld-nt.c b/libc/calls/sigchld-nt.c index 92a16195f..0e7dc36db 100644 --- a/libc/calls/sigchld-nt.c +++ b/libc/calls/sigchld-nt.c @@ -16,54 +16,65 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" -#include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" #include "libc/calls/state.internal.h" -#include "libc/calls/struct/siginfo.h" #include "libc/calls/syscall_support-nt.internal.h" -#include "libc/dce.h" +#include "libc/cosmo.h" #include "libc/nt/enum/wait.h" #include "libc/nt/runtime.h" #include "libc/nt/synchronization.h" +#include "libc/nt/thread.h" #include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sicode.h" #include "libc/sysv/consts/sig.h" - +#include "libc/thread/tls.h" +#include "libc/thread/tls2.internal.h" #ifdef __x86_64__ -static textwindows bool CheckForExitedChildProcess(void) { - int pids[64]; - uint32_t i, n; - int64_t handles[64]; - if (!(n = __sample_pids(pids, handles, true))) return false; - i = WaitForMultipleObjects(n, handles, false, 0); - if (i == kNtWaitFailed) return false; - if (i == kNtWaitTimeout) return false; - if ((__sighandrvas[SIGCHLD] >= kSigactionMinRva) && - (__sighandflags[SIGCHLD] & SA_NOCLDWAIT)) { - CloseHandle(handles[i]); - __releasefd(pids[i]); - } else { - g_fds.p[pids[i]].zombie = true; +intptr_t __sigchld_thread; +static atomic_uint __sigchld_once; +static struct CosmoTib __sigchld_tls; + +static textwindows bool __sigchld_check(void) { + bool should_signal = false; + for (;;) { + int pids[64]; + int64_t handles[64]; + uint32_t n = __sample_pids(pids, handles, true); + if (!n) return should_signal; + uint32_t i = WaitForMultipleObjects(n, handles, false, 0); + if (i == kNtWaitFailed) return should_signal; + if (i == kNtWaitTimeout) return should_signal; + i &= ~kNtWaitAbandoned; + if ((__sighandrvas[SIGCHLD] >= kSigactionMinRva) && + (__sighandflags[SIGCHLD] & SA_NOCLDWAIT)) { + CloseHandle(handles[i]); + __releasefd(pids[i]); + } else { + g_fds.p[pids[i]].zombie = true; + } + should_signal = true; } - return true; } -/** - * Checks to see if SIGCHLD should be raised on Windows. - * @return true if a signal was raised - * @note yoinked by fork-nt.c - */ -textwindows void _check_sigchld(void) { - bool should_signal; - __fds_lock(); - should_signal = CheckForExitedChildProcess(); - __fds_unlock(); - if (should_signal) { - __sig_add(0, SIGCHLD, CLD_EXITED); +static textwindows uint32_t __sigchld_worker(void *arg) { + __set_tls_win32(&__sigchld_tls); + for (;;) { + if (__sigchld_check()) { + __sig_notify(SIGCHLD, CLD_EXITED); + } + SleepEx(100, false); } + return 0; +} + +static textwindows void __sigchld_init(void) { + __sigchld_thread = CreateThread(0, 65536, __sigchld_worker, 0, 0, 0); +} + +void _init_sigchld(void) { + cosmo_once(&__sigchld_once, __sigchld_init); } #endif /* __x86_64__ */ diff --git a/libc/calls/sigwinch-nt.c b/libc/calls/sigwinch-nt.c index ac17c68d4..7aa08c0b7 100644 --- a/libc/calls/sigwinch-nt.c +++ b/libc/calls/sigwinch-nt.c @@ -20,18 +20,21 @@ #include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" #include "libc/calls/struct/fd.internal.h" -#include "libc/calls/struct/winsize.h" -#include "libc/calls/struct/winsize.internal.h" -#include "libc/dce.h" -#include "libc/intrin/atomic.h" +#include "libc/cosmo.h" #include "libc/nt/console.h" #include "libc/nt/struct/consolescreenbufferinfoex.h" +#include "libc/nt/synchronization.h" +#include "libc/nt/thread.h" #include "libc/sysv/consts/sicode.h" #include "libc/sysv/consts/sig.h" - +#include "libc/thread/tls.h" +#include "libc/thread/tls2.internal.h" #ifdef __x86_64__ -static atomic_uint __win_winsize; +intptr_t __sigwinch_thread; +static unsigned __sigwinch_size; +static atomic_uint __sigwinch_once; +static struct CosmoTib __sigwinch_tls; static textwindows unsigned __get_console_size(void) { for (int fd = 1; fd < 10; ++fd) { @@ -51,20 +54,27 @@ static textwindows unsigned __get_console_size(void) { return -1u; } -textwindows void _check_sigwinch(void) { - unsigned old = atomic_load_explicit(&__win_winsize, memory_order_acquire); - if (old == -1u) return; - unsigned neu = __get_console_size(); - old = atomic_exchange(&__win_winsize, neu); - if (neu != old) { - __sig_add(0, SIGWINCH, SI_KERNEL); +static textwindows uint32_t __sigwinch_worker(void *arg) { + __set_tls_win32(&__sigwinch_tls); + for (;;) { + unsigned old = __sigwinch_size; + unsigned neu = __get_console_size(); + if (neu != old) { + __sigwinch_size = neu; + __sig_notify(SIGWINCH, SI_KERNEL); + } + SleepEx(25, false); } + return 0; } -__attribute__((__constructor__)) static void sigwinch_init(void) { - if (!IsWindows()) return; - unsigned ws = __get_console_size(); - atomic_store_explicit(&__win_winsize, ws, memory_order_release); +static textwindows void __sigwinch_init(void) { + __sigwinch_size = __get_console_size(); + __sigwinch_thread = CreateThread(0, 65536, __sigwinch_worker, 0, 0, 0); +} + +textwindows void _init_sigwinch(void) { + cosmo_once(&__sigwinch_once, __sigwinch_init); } #endif /* __x86_64__ */ diff --git a/libc/calls/struct/sigaction.internal.h b/libc/calls/struct/sigaction.internal.h index 4cc16d597..d94fb3ada 100644 --- a/libc/calls/struct/sigaction.internal.h +++ b/libc/calls/struct/sigaction.internal.h @@ -65,7 +65,11 @@ const char *DescribeSigaction(char[256], int, const struct sigaction *); void _init_onntconsoleevent(void); void _init_wincrash(void); -void _check_sigwinch(void); +void _init_sigwinch(void); +void _init_sigchld(void); + +extern intptr_t __sigchld_thread; +extern intptr_t __sigwinch_thread; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/calls/syscall_support-nt.internal.h b/libc/calls/syscall_support-nt.internal.h index 6ccd33353..649b56c92 100644 --- a/libc/calls/syscall_support-nt.internal.h +++ b/libc/calls/syscall_support-nt.internal.h @@ -22,7 +22,6 @@ int64_t ntreturn(uint32_t); void *GetProcAddressModule(const char *, const char *); void WinMainForked(void); void _check_sigalrm(void); -void _check_sigchld(void); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/log/log.h b/libc/log/log.h index 06d079d82..0ebb51589 100644 --- a/libc/log/log.h +++ b/libc/log/log.h @@ -48,7 +48,6 @@ bool IsRunningUnderMake(void); char *GetSymbolByAddr(int64_t); void PrintGarbage(void); void PrintGarbageNumeric(FILE *); -void CheckForMemoryLeaks(void); void PrintWindowsMemory(const char *, size_t); #ifndef __STRICT_ANSI__ diff --git a/libc/runtime/fork-nt.c b/libc/runtime/fork-nt.c index 945257dad..8ec61223f 100644 --- a/libc/runtime/fork-nt.c +++ b/libc/runtime/fork-nt.c @@ -65,8 +65,6 @@ #ifdef __x86_64__ -__static_yoink("_check_sigchld"); - extern int64_t __wincrashearly; bool32 __onntconsoleevent(uint32_t); void sys_setitimer_nt_reset(void); diff --git a/libc/runtime/runtime.h b/libc/runtime/runtime.h index 8814ffd84..2f9b4110a 100644 --- a/libc/runtime/runtime.h +++ b/libc/runtime/runtime.h @@ -115,6 +115,8 @@ void __paginate(int, const char *); void _weakfree(void *); void *_mapanon(size_t) attributeallocsize((1)) mallocesque; void *_mapshared(size_t) attributeallocsize((1)) mallocesque; +void CheckForMemoryLeaks(void); +void CheckForFileLeaks(void); void __enable_threads(void); void __oom_hook(size_t); bool _isheap(void *); diff --git a/libc/runtime/stackuse.c b/libc/stdio/fleaks.c similarity index 51% rename from libc/runtime/stackuse.c rename to libc/stdio/fleaks.c index 7b85c024a..5e55aa772 100644 --- a/libc/runtime/stackuse.c +++ b/libc/stdio/fleaks.c @@ -1,7 +1,7 @@ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ Copyright 2023 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 │ @@ -17,81 +17,43 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/dce.h" #include "libc/errno.h" #include "libc/fmt/itoa.h" -#include "libc/intrin/atomic.h" -#include "libc/intrin/promises.internal.h" #include "libc/runtime/runtime.h" -#include "libc/runtime/stack.h" +#include "libc/stdio/stdio.h" #include "libc/str/str.h" -#include "libc/sysv/consts/o.h" +#include "libc/sysv/consts/f.h" -// TODO(jart): Delete? - -STATIC_STACK_ALIGN(GetStackSize()); - -static char stacklog[1024]; - -dontasan size_t GetStackUsage(char *s, size_t n) { - // RHEL5 MAP_GROWSDOWN seems to only grow to 68kb :'( - // So we count non-zero bytes down from the top - // First clear 64 bytes is considered the end - long *p; - size_t got; - p = (long *)(s + n); - got = 0; - for (;;) { - p -= 8; - if (p[0] | p[1] | p[2] | p[3] | p[4] | p[5] | p[6] | p[7]) { - ++got; - } else { - break; - } - } - return got * 8 * sizeof(long); -} - -static textexit void LogStackUse(void) { - bool quote; - char *p, *q; - int i, e, fd; - size_t n, usage; - if (!PLEDGED(STDIO) || !PLEDGED(WPATH) || !PLEDGED(CPATH)) return; - usage = GetStackUsage((char *)GetStackAddr(), GetStackSize()); - e = errno; - if ((fd = open(stacklog, O_APPEND | O_CREAT | O_WRONLY | O_CLOEXEC, 0644)) != - -1) { - p = FormatUint64(stacklog, usage); - for (i = 0; i < __argc; ++i) { - n = strlen(__argv[i]); - if ((q = memchr(__argv[i], '\n', n))) n = q - __argv[i]; - if (p - stacklog + 1 + 1 + n + 1 + 1 < sizeof(stacklog)) { - quote = !!memchr(__argv[i], ' ', n); +void CheckForFileLeaks(void) { + char msg[512]; + char *p = msg; + char *pe = msg + 256; + bool gotsome = false; + for (int fd = 3; fd < 200; ++fd) { + if (fcntl(fd, F_GETFL) != -1) { + if (!gotsome) { + p = stpcpy(p, program_invocation_short_name); + p = stpcpy(p, ": FILE DESCRIPTOR LEAKS:"); + gotsome = true; + } + if (p + 1 + 12 + 1 < pe) { *p++ = ' '; - if (quote) *p++ = '\''; - p = mempcpy(p, __argv[i], n); - if (quote) *p++ = '\''; + p = FormatInt32(p, fd); } else { break; } } + } + if (gotsome) { + char proc[64]; + char *p = proc; *p++ = '\n'; - write(fd, stacklog, p - stacklog); - close(fd); - } - errno = e; -} - -static textstartup void LogStackUseInit(void) { - if (!PLEDGED(WPATH)) return; - if (isdirectory("o/" MODE) && - getcwd(stacklog, sizeof(stacklog) - strlen("/o/" MODE "/stack.log"))) { - strcat(stacklog, "/o/" MODE "/stack.log"); - atexit(LogStackUse); + *p = 0; + write(2, msg, p - msg); + p = stpcpy(p, "ls -hal /proc/"); + p = FormatInt32(p, getpid()); + p = stpcpy(p, "/fd"); + system(proc); + exit(1); } } - -const void *const stack_usage_logging[] initarray = { - LogStackUseInit, -}; diff --git a/libc/testlib/testmain.c b/libc/testlib/testmain.c index 24cac360f..9618fed69 100644 --- a/libc/testlib/testmain.c +++ b/libc/testlib/testmain.c @@ -71,7 +71,6 @@ Flags:\n\ __static_yoink("__die"); __static_yoink("GetSymbolByAddr"); __static_yoink("testlib_quota_handlers"); -__static_yoink("stack_usage_logging"); static bool runbenchmarks_; diff --git a/tool/build/runitd.c b/tool/build/runitd.c index 96841a1a1..5348eca02 100644 --- a/tool/build/runitd.c +++ b/tool/build/runitd.c @@ -416,6 +416,8 @@ void PrintProgramOutput(struct Client *client) { void FreeClient(struct Client *client) { DEBUF("FreeClient"); + Close(&client->pipe[1]); + Close(&client->pipe[0]); if (client->pid) { kill(client->pid, SIGHUP); waitpid(client->pid, 0, 0); @@ -520,6 +522,8 @@ void *ClientWorker(void *arg) { // spawn the program int etxtbsy_tries = 0; RetryOnEtxtbsyRaceCondition: + Close(&client->pipe[1]); + Close(&client->pipe[0]); if (etxtbsy_tries++) { if (etxtbsy_tries == 24) { // ~30 seconds WARNF("%s failed to spawn on %s due because either (1) the ETXTBSY race " @@ -547,8 +551,6 @@ RetryOnEtxtbsyRaceCondition: posix_spawn_file_actions_adddup2(&spawnfila, client->pipe[1], 2); err = posix_spawn(&client->pid, exe, &spawnfila, &spawnattr, args, environ); if (err) { - Close(&client->pipe[1]); - Close(&client->pipe[0]); if (err == ETXTBSY) { goto RetryOnEtxtbsyRaceCondition; } @@ -782,8 +784,8 @@ int main(int argc, char *argv[]) { Serve(); free(g_psk); #if IsModeDbg() - void CheckForMemoryLeaks(void); CheckForMemoryLeaks(); + CheckForFileLeaks(); #endif pthread_exit(0); }