Improve performance and remove fd leaks

This commit is contained in:
Justine Tunney 2023-09-10 11:52:03 -07:00
parent 26e254fb4d
commit 1965d7488e
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
17 changed files with 131 additions and 159 deletions

View file

@ -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 \

View file

@ -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

View file

@ -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();
}

View file

@ -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

View file

@ -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__ */

View file

@ -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) */

View file

@ -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);

View file

@ -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__ */

View file

@ -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__ */

View file

@ -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) */

View file

@ -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) */

View file

@ -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__

View file

@ -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);

View file

@ -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 *);

View file

@ -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,
};

View file

@ -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_;

View file

@ -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);
}