diff --git a/build/run b/build/run index c7fc0c292..079bc9991 100755 --- a/build/run +++ b/build/run @@ -4,5 +4,5 @@ UNAMES=$(uname -s) if [ x"$UNAMES" = x"Darwin" ] && [ x"$UNAMEM" = x"arm64" ]; then exec ape "$@" else - exec "$@" + exec rusage "$@" fi diff --git a/examples/aba.c b/examples/aba.c new file mode 100644 index 000000000..b38b26028 --- /dev/null +++ b/examples/aba.c @@ -0,0 +1,125 @@ +#if 0 +/*─────────────────────────────────────────────────────────────────╗ +│ To the extent possible under law, Justine Tunney has waived │ +│ all copyright and related or neighboring rights to this file, │ +│ as it is written in the following disclaimers: │ +│ • http://unlicense.org/ │ +│ • http://creativecommons.org/publicdomain/zero/1.0/ │ +╚─────────────────────────────────────────────────────────────────*/ +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +// lockless push / pop tutorial +// +// this file demonstrates how to create a singly linked list that can be +// pushed and popped across multiple threads, using only atomics. atomic +// operations (rather than using a mutex) make push/pop go faster and it +// ensures asynchronous signal safety too. therefore it will be safe for +// use in a variety of contexts, such as signal handlers. + +#define THREADS 128 +#define ITERATIONS 10000 + +// adjust mask based on alignment of list struct +// +// - 0x00fffffffffffff0 may be used if List* is always 16-byte aligned. +// We know that's the case here, because we call malloc() to create +// every List* object, and malloc() results are always max aligned. +// +// - 0x00fffffffffffff8 may be used if List* is always 8-byte aligned. +// This might be the case if you're pushing and popping stuff that was +// allocated from an array, to avoid malloc() calls. This has one +// fewer byte of safeguards against the ABA problem though. +// +// - 0x00fffffffffff000 may be used if List* is always page aligned. +// This is a good choice if you use mmap() to allocate each List* +// element, since it offers maximum protection against ABA. +// +// - only the highest byte of a 64-bit pointer is safe to use on our +// supported platforms. on most x86 and arm systems, it's possible to +// use the top sixteen bits. however that's not the case on more +// recent high end x86-64 systems that have pml5t. +// +#define MASQUE 0x00fffffffffffff0 + +#define PTR(x) ((uintptr_t)(x) & MASQUE) +#define TAG(x) ROL((uintptr_t)(x) & ~MASQUE, 8) +#define ABA(p, t) ((uintptr_t)(p) | (ROR((uintptr_t)(t), 8) & ~MASQUE)) +#define ROL(x, n) (((x) << (n)) | ((x) >> (64 - (n)))) +#define ROR(x, n) (((x) >> (n)) | ((x) << (64 - (n)))) + +struct List { + struct List* next; + int count; +}; + +atomic_uintptr_t list; + +void push(struct List* elem) { + uintptr_t tip; + assert(!TAG(elem)); + for (tip = atomic_load_explicit(&list, memory_order_relaxed);;) { + elem->next = (struct List*)PTR(tip); + if (atomic_compare_exchange_weak_explicit( + &list, &tip, ABA(elem, TAG(tip) + 1), memory_order_release, + memory_order_relaxed)) + break; + pthread_pause_np(); + } +} + +struct List* pop(void) { + uintptr_t tip; + struct List* elem; + tip = atomic_load_explicit(&list, memory_order_relaxed); + while ((elem = (struct List*)PTR(tip))) { + if (atomic_compare_exchange_weak_explicit( + &list, &tip, ABA(elem->next, TAG(tip) + 1), memory_order_acquire, + memory_order_relaxed)) + break; + pthread_pause_np(); + } + return elem; +} + +void* tester(void* arg) { + struct List* elem; + for (int i = 0; i < ITERATIONS; ++i) { + while (!(elem = pop())) { + elem = malloc(sizeof(*elem)); + elem->count = 0; + push(elem); + } + elem->count++; + push(elem); + } + return 0; +} + +int main() { + printf("testing aba problem..."); + fflush(stdout); + pthread_t th[THREADS]; + for (int i = 0; i < THREADS; ++i) + pthread_create(&th[i], 0, tester, 0); + for (int i = 0; i < THREADS; ++i) + pthread_join(th[i], 0); + int sum = 0; + struct List* elem; + while ((elem = pop())) { + printf(" %d", elem->count); + sum += elem->count; + free(elem); + } + printf("\n"); + assert(sum == ITERATIONS * THREADS); + printf("you are the dancing queen\n"); + CheckForMemoryLeaks(); +} diff --git a/examples/romanize.c b/examples/romanize.c index 6bf885b1a..a31b1ab1f 100644 --- a/examples/romanize.c +++ b/examples/romanize.c @@ -17,6 +17,7 @@ #include #include #include +#include "libc/ctype.h" /** * @fileoverview Roman Transliteration, e.g. diff --git a/libc/calls/finddebugbinary.c b/libc/calls/finddebugbinary.c index e52e464eb..04dbaea05 100644 --- a/libc/calls/finddebugbinary.c +++ b/libc/calls/finddebugbinary.c @@ -27,6 +27,7 @@ #include "libc/elf/tinyelf.internal.h" #include "libc/errno.h" #include "libc/intrin/directmap.h" +#include "libc/intrin/promises.h" #include "libc/nt/memory.h" #include "libc/nt/runtime.h" #include "libc/runtime/runtime.h" @@ -37,7 +38,6 @@ #include "libc/sysv/consts/prot.h" static struct { - atomic_uint once; const char *res; char buf[PATH_MAX]; } g_comdbg; @@ -69,35 +69,26 @@ static int GetElfMachine(void) { } static bool IsMyDebugBinary(const char *path) { + void *addr; int64_t size; uintptr_t value; bool res = false; int fd, e = errno; - struct DirectMap dm; - BLOCK_CANCELATION; if ((fd = open(path, O_RDONLY | O_CLOEXEC, 0)) != -1) { // sanity test that this .com.dbg file (1) is an elf image, and (2) // contains the same number of bytes of code as our .com executable // which is currently running in memory. if ((size = lseek(fd, 0, SEEK_END)) != -1 && - (dm = sys_mmap((void *)0x12345000000, size, PROT_READ, MAP_SHARED, fd, - 0)) - .addr != MAP_FAILED) { - if (READ32LE((char *)dm.addr) == READ32LE("\177ELF") && - ((Elf64_Ehdr *)dm.addr)->e_machine == GetElfMachine() && - GetElfSymbolValue(dm.addr, "_etext", &value)) { + (addr = mmap(0, size, PROT_READ, MAP_SHARED, fd, 0)) != MAP_FAILED) { + if (READ32LE((char *)addr) == READ32LE("\177ELF") && + ((Elf64_Ehdr *)addr)->e_machine == GetElfMachine() && + GetElfSymbolValue(addr, "_etext", &value)) { res = !_etext || value == (uintptr_t)_etext; } - if (!IsWindows()) { - sys_munmap(dm.addr, size); - } else { - CloseHandle(dm.maphandle); - UnmapViewOfFile(dm.addr); - } + munmap(addr, size); } close(fd); } - ALLOW_CANCELATION; errno = e; return res; } @@ -106,7 +97,7 @@ static void FindDebugBinaryInit(void) { const char *comdbg; if (issetugid()) return; - if ((comdbg = getenv("COMDBG")) && IsMyDebugBinary(comdbg)) { + if ((comdbg = getenv("COMDBG"))) { g_comdbg.res = comdbg; return; } @@ -125,9 +116,18 @@ static void FindDebugBinaryInit(void) { /** * Returns path of binary with the debug information, or null. * - * @return path to debug binary, or NULL + * You can specify the COMDBG environment variable, with the path of the + * debug binary, in case the automatic heuristics fail. What we look for + * is GetProgramExecutableName() with ".dbg", ".com.dbg", etc. appended. + * + * @return path to debug binary, or NULL if we couldn't find it + * @asyncsignalsafe */ const char *FindDebugBinary(void) { - cosmo_once(&g_comdbg.once, FindDebugBinaryInit); return g_comdbg.res; } + +// pay startup cost to make this signal safe from the user's perspective +__attribute__((__constructor__(10))) static void FindDebugBinaryCtor(void) { + FindDebugBinaryInit(); +} diff --git a/libc/calls/pledge.c b/libc/calls/pledge.c index 88f1b236f..812844502 100644 --- a/libc/calls/pledge.c +++ b/libc/calls/pledge.c @@ -232,6 +232,21 @@ * option might not be a good idea if you're pledging `exec` because * subprocesses can't inherit the `SIGSYS` handler this installs. * + * If you experience crashes during startup when execve'ing a cosmo + * binary that's had permissions like rpath pledged away, then try doing + * this before calling execve. This prevents special startup checks. + * + * putenv("COMDBG=program.dbg"); + * + * If having pledge() security is mission critical, then add this code + * to the start of your main() function to ensure your program fails + * with an error if it isn't available. + * + * if (pledge(0, 0)) { + * fprintf(stderr, "error: OS doesn't support pledge() security\n"); + * exit(1); + * } + * * @return 0 on success, or -1 w/ errno * @raise ENOSYS if `pledge(0, 0)` was used and security is not possible * @raise EINVAL if `execpromises` on Linux isn't a subset of `promises` diff --git a/libc/calls/unveil.c b/libc/calls/unveil.c index c4bcbb559..971c7b2b0 100644 --- a/libc/calls/unveil.c +++ b/libc/calls/unveil.c @@ -405,6 +405,15 @@ int sys_unveil_linux(const char *path, const char *permissions) { * - `c` allows `path` to be created and removed, corresponding to * the pledge promise "cpath". * + * If having unveil() security is mission critical, then add this code + * to the start of your main() function to ensure your program fails + * with an error if it isn't available. + * + * if (unveil("", 0) >= 0) { + * fprintf(stderr, "error: OS doesn't support unveil() security\n"); + * exit(1); + * } + * * @return 0 on success, or -1 w/ errno; note: if `unveil("",0)` is used * to perform a feature check, then on Linux a value greater than 0 * shall be returned which is the supported Landlock ABI version diff --git a/libc/dlopen/stubs.c b/libc/dlopen/stubs.c index 57c3f0724..9a94e891b 100644 --- a/libc/dlopen/stubs.c +++ b/libc/dlopen/stubs.c @@ -27,7 +27,7 @@ * * @return null always */ -void *dlopen(const char *, int) { +void *dlopen(const char *, int) { return 0; } diff --git a/libc/integral/c.inc b/libc/integral/c.inc index 9a253e41e..0f29ff5f0 100644 --- a/libc/integral/c.inc +++ b/libc/integral/c.inc @@ -531,10 +531,6 @@ typedef struct { #pragma GCC diagnostic ignored "-Wold-style-definition" /* orwellian bullsh */ #endif -#if !defined(__cplusplus) && defined(__GNUC__) && __GNUC__ >= 14 -#pragma GCC diagnostic ignored "-Wimplicit-function-declaration" -#endif - #ifdef __x86_64__ #define DebugBreak() __asm__("int3") #elif defined(__aarch64__) diff --git a/libc/intrin/atomic.c b/libc/intrin/atomic.c deleted file mode 100644 index f46f74f49..000000000 --- a/libc/intrin/atomic.c +++ /dev/null @@ -1,24 +0,0 @@ -/*-*- 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/intrin/atomic.h" - -bool dog(_Atomic(long) *p, long *e, long w) { - return atomic_compare_exchange_weak_explicit(p, e, w, memory_order_acq_rel, - memory_order_relaxed); -} diff --git a/libc/intrin/maps.h b/libc/intrin/maps.h index 5fc9b721b..c0b0a911d 100644 --- a/libc/intrin/maps.h +++ b/libc/intrin/maps.h @@ -29,7 +29,7 @@ struct Map { struct Maps { struct Tree *maps; _Atomic(uint64_t) lock; - _Atomic(struct Map *) freed; + _Atomic(uintptr_t) freed; size_t count; size_t pages; _Atomic(char *) pick; diff --git a/libc/intrin/mmap.c b/libc/intrin/mmap.c index f10287369..8be86f963 100644 --- a/libc/intrin/mmap.c +++ b/libc/intrin/mmap.c @@ -50,6 +50,13 @@ #define PGUP(x) (((x) + pagesz - 1) & -pagesz) +#define MASQUE 0x00fffffffffffff8 +#define PTR(x) ((uintptr_t)(x) & MASQUE) +#define TAG(x) ROL((uintptr_t)(x) & ~MASQUE, 8) +#define ABA(p, t) ((uintptr_t)(p) | (ROR((uintptr_t)(t), 8) & ~MASQUE)) +#define ROL(x, n) (((x) << (n)) | ((x) >> (64 - (n)))) +#define ROR(x, n) (((x) >> (n)) | ((x) << (64 - (n)))) + #if !MMDEBUG #define ASSERT(x) (void)0 #else @@ -227,14 +234,17 @@ StartOver: } void __maps_free(struct Map *map) { + uintptr_t tip; + ASSERT(!TAG(map)); map->size = 0; map->addr = MAP_FAILED; - map->freed = atomic_load_explicit(&__maps.freed, memory_order_relaxed); - for (;;) { - if (atomic_compare_exchange_weak_explicit(&__maps.freed, &map->freed, map, - memory_order_release, - memory_order_relaxed)) + for (tip = atomic_load_explicit(&__maps.freed, memory_order_relaxed);;) { + map->freed = (struct Map *)PTR(tip); + if (atomic_compare_exchange_weak_explicit( + &__maps.freed, &tip, ABA(map, TAG(tip) + 1), memory_order_release, + memory_order_relaxed)) break; + pthread_pause_np(); } } @@ -297,12 +307,13 @@ void __maps_insert(struct Map *map) { struct Map *__maps_alloc(void) { struct Map *map; - map = atomic_load_explicit(&__maps.freed, memory_order_relaxed); - while (map) { - if (atomic_compare_exchange_weak_explicit(&__maps.freed, &map, map->freed, - memory_order_acquire, - memory_order_relaxed)) + uintptr_t tip = atomic_load_explicit(&__maps.freed, memory_order_relaxed); + while ((map = (struct Map *)PTR(tip))) { + if (atomic_compare_exchange_weak_explicit( + &__maps.freed, &tip, ABA(map->freed, TAG(tip) + 1), + memory_order_acquire, memory_order_relaxed)) return map; + pthread_pause_np(); } int gransz = __gransize; struct DirectMap sys = sys_mmap(0, gransz, PROT_READ | PROT_WRITE, diff --git a/libc/intrin/wsarecv.c b/libc/intrin/wsarecv.c index e4fe65f11..62c489e0f 100644 --- a/libc/intrin/wsarecv.c +++ b/libc/intrin/wsarecv.c @@ -59,8 +59,8 @@ textwindows int WSARecv( } if (UNLIKELY(__strace > 0) && strace_enabled(0) > 0) { kprintf(STRACE_PROLOGUE "WSARecv(%lu, [", s); - DescribeIovNt(inout_lpBuffers, dwBufferCount, - rc != -1 ? NumberOfBytesRecvd : 0); + _DescribeIovNt(inout_lpBuffers, dwBufferCount, + rc != -1 ? NumberOfBytesRecvd : 0); kprintf("], %u, [%'u], %p, %s, %p) → %d% lm\n", dwBufferCount, NumberOfBytesRecvd, inout_lpFlags, DescribeNtOverlapped(opt_inout_lpOverlapped), diff --git a/libc/mem/levenshtein.c b/libc/mem/levenshtein.c index 198ddf200..f1e1cc131 100644 --- a/libc/mem/levenshtein.c +++ b/libc/mem/levenshtein.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/mem/alg.h" #include "libc/mem/mem.h" +#include "libc/str/str.h" #define MIN3(a, b, c) \ ((a) < (b) ? ((a) < (c) ? (a) : (c)) : ((b) < (c) ? (b) : (c))) diff --git a/libc/nt/struct/iovec.h b/libc/nt/struct/iovec.h index b29f4bd8b..e2898da96 100644 --- a/libc/nt/struct/iovec.h +++ b/libc/nt/struct/iovec.h @@ -7,7 +7,7 @@ struct NtIovec { char *buf; }; -void DescribeIovNt(const struct NtIovec *, uint32_t, ssize_t); +void _DescribeIovNt(const struct NtIovec *, uint32_t, ssize_t); COSMOPOLITAN_C_END_ #endif /* COSMOPOLITAN_LIBC_NT_STRUCT_IOVEC_H_ */ diff --git a/libc/nt/struct/securityattributes.h b/libc/nt/struct/securityattributes.h index e481ede22..05145944c 100644 --- a/libc/nt/struct/securityattributes.h +++ b/libc/nt/struct/securityattributes.h @@ -9,9 +9,9 @@ struct NtSecurityAttributes { bool32 bInheritHandle; }; -const char *DescribeNtSecurityAttributes(char[32], - const struct NtSecurityAttributes *); +const char *_DescribeNtSecurityAttributes(char[32], + const struct NtSecurityAttributes *); #define DescribeNtSecurityAttributes(x) \ - DescribeNtSecurityAttributes(alloca(32), x) + _DescribeNtSecurityAttributes(alloca(32), x) #endif /* COSMOPOLITAN_LIBC_NT_STRUCT_SECURITYATTRIBUTES_H_ */ diff --git a/libc/runtime/runtime.h b/libc/runtime/runtime.h index 9b26bbbf1..452125bcb 100644 --- a/libc/runtime/runtime.h +++ b/libc/runtime/runtime.h @@ -119,7 +119,7 @@ void __morph_begin(void) libcesque; void __morph_end(void) libcesque; void __jit_begin(void) libcesque; void __jit_end(void) libcesque; -void __clear_cache(void *, void *) libcesque; +void __clear_cache(void *, void *); /* portability */ bool32 IsGenuineBlink(void) libcesque; bool32 IsCygwin(void) libcesque; diff --git a/libc/str/BUILD.mk b/libc/str/BUILD.mk index ab0193593..d7a655dea 100644 --- a/libc/str/BUILD.mk +++ b/libc/str/BUILD.mk @@ -44,9 +44,6 @@ $(LIBC_STR_A).pkg: \ $(LIBC_STR_A_OBJS) \ $(foreach x,$(LIBC_STR_A_DIRECTDEPS),$($(x)_A).pkg) -o/$(MODE)/libc/str/wow.o: private \ - CC = gcc - o/$(MODE)/libc/str/wmemset.o \ o/$(MODE)/libc/str/memset16.o \ o/$(MODE)/libc/str/dosdatetimetounix.o: private \ diff --git a/net/turfwar/turfwar.c b/net/turfwar/turfwar.c index 77efea1d3..f2cf5ee3f 100644 --- a/net/turfwar/turfwar.c +++ b/net/turfwar/turfwar.c @@ -17,63 +17,58 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" +#include "libc/atomic.h" #include "libc/calls/calls.h" #include "libc/calls/pledge.h" #include "libc/calls/struct/iovec.h" #include "libc/calls/struct/rusage.h" #include "libc/calls/struct/sigaction.h" +#include "libc/calls/struct/siginfo.h" #include "libc/calls/struct/sigset.h" #include "libc/calls/struct/stat.h" #include "libc/calls/struct/sysinfo.h" #include "libc/calls/struct/timespec.h" -#include "libc/calls/struct/timeval.h" +#include "libc/calls/struct/ucontext.internal.h" +#include "libc/calls/ucontext.h" #include "libc/ctype.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/fmt/conv.h" #include "libc/fmt/itoa.h" #include "libc/intrin/atomic.h" -#include "libc/intrin/bsr.h" -#include "libc/intrin/hilbert.h" +#include "libc/intrin/iscall.h" #include "libc/intrin/kprintf.h" -#include "libc/intrin/strace.h" -#include "libc/log/check.h" #include "libc/log/log.h" #include "libc/macros.h" #include "libc/mem/gc.h" #include "libc/mem/mem.h" #include "libc/mem/sortedints.internal.h" -#include "libc/nexgen32e/crc32.h" +#include "libc/nexgen32e/stackframe.h" #include "libc/paths.h" -#include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" -#include "libc/runtime/stack.h" #include "libc/runtime/sysconf.h" -#include "libc/serialize.h" #include "libc/sock/sock.h" -#include "libc/sock/struct/pollfd.h" #include "libc/sock/struct/sockaddr.h" #include "libc/stdio/append.h" -#include "libc/stdio/rand.h" -#include "libc/stdio/stdio.h" #include "libc/str/slice.h" #include "libc/str/str.h" #include "libc/sysv/consts/af.h" #include "libc/sysv/consts/clock.h" +#include "libc/sysv/consts/f.h" #include "libc/sysv/consts/o.h" -#include "libc/sysv/consts/poll.h" #include "libc/sysv/consts/prot.h" #include "libc/sysv/consts/rusage.h" +#include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/so.h" #include "libc/sysv/consts/sock.h" #include "libc/sysv/consts/sol.h" #include "libc/sysv/consts/tcp.h" +#include "libc/sysv/consts/timer.h" #include "libc/thread/thread.h" #include "libc/thread/thread2.h" #include "libc/time.h" #include "libc/x/x.h" -#include "libc/x/xasprintf.h" #include "libc/zip.h" #include "net/http/escape.h" #include "net/http/http.h" @@ -81,16 +76,8 @@ #include "net/http/tokenbucket.h" #include "net/http/url.h" #include "third_party/getopt/getopt.internal.h" -#include "third_party/nsync/counter.h" -#include "third_party/nsync/cv.h" -#include "third_party/nsync/mu.h" -#include "third_party/nsync/note.h" -#include "third_party/nsync/time.h" #include "third_party/sqlite3/sqlite3.h" -#include "third_party/stb/stb_image_write.h" -#include "third_party/zlib/zconf.h" #include "third_party/zlib/zlib.h" -#include "tool/net/lfuncs.h" /** * @fileoverview production webserver for turfwar online game @@ -98,8 +85,6 @@ #define PORT 8080 // default server listening port #define CPUS 64 // number of cpus to actually use -#define XN 64 // plot width in pixels -#define YN 64 // plot height in pixels #define WORKERS 500 // size of http client thread pool #define SUPERVISE_MS 1000 // how often to stat() asset files #define KEEPALIVE_MS 60000 // max time to keep idle conn open @@ -109,7 +94,6 @@ #define SCORE_W_UPDATE_MS 70000 // how often to regenerate /score/week #define SCORE_M_UPDATE_MS 100000 // how often to regenerate /score/month #define SCORE_UPDATE_MS 210000 // how often to regenerate /score -#define PLOTS_UPDATE_MS 999000 // how often to regenerate /plot/xxx #define ACCEPT_DEADLINE_MS 100 // how long accept() can take to find worker #define CLAIM_DEADLINE_MS 100 // how long /claim may block if queue is full #define CONCERN_LOAD .75 // avoid keepalive, upon this connection load @@ -124,7 +108,7 @@ #define MSG_BUF 512 // small response lookaside #define INBUF_SIZE 65536 -#define OUTBUF_SIZE 8192 +#define OUTBUF_SIZE 65536 #define TB_BYTES (1u << TB_CIDR) #define TB_WORDS (TB_BYTES / 8) @@ -235,9 +219,10 @@ struct Data { }; struct Asset { + atomic_bool ready; int cash; char *path; - nsync_mu lock; + pthread_rwlock_t lock; const char *type; struct Data data; struct Data gzip; @@ -256,18 +241,23 @@ struct Blackhole { // cli flags bool g_integrity; bool g_daemonize; +int g_crash_fd; int g_port = PORT; int g_workers = WORKERS; int g_keepalive = KEEPALIVE_MS; struct SortedInts g_whitelisted; +thread_local char last_message[INBUF_SIZE]; +sig_atomic_t is_shutting_down; + +// threads +pthread_t g_listener; +pthread_t scorer, recenter, claimer, replenisher; +pthread_t scorer_hour, scorer_day, scorer_week, scorer_month; // lifecycle vars -pthread_t g_listener; -nsync_time g_started; -nsync_counter g_ready; +struct timespec g_started; atomic_int g_connections; -nsync_note g_shutdown[3]; -int g_hilbert[YN * XN][2]; +atomic_int g_worker_threads; // whitebox metrics atomic_long g_banned; @@ -308,25 +298,23 @@ union TokenBucket { // http worker objects struct Worker { pthread_t th; + atomic_bool dead; atomic_int msgcount; - atomic_int shutdown; atomic_int connected; struct timespec startread; + char *msgbuf; + char *inbuf; + char *outbuf; + struct HttpMessage *msg; + struct Client *client; } *g_worker; // recentworker wakeup struct Recent { - nsync_mu mu; - nsync_cv cv; + pthread_mutex_t mu; + pthread_cond_t cv; } g_recent; -// global date header -struct Nowish { - nsync_mu lock; - struct timespec ts; - struct tm tm; -} g_nowish; - // static assets struct Assets { struct Asset index; @@ -339,16 +327,15 @@ struct Assets { struct Asset score_month; struct Asset recent; struct Asset favicon; - struct Asset plot[256]; } g_asset; // queues ListenWorker() to HttpWorker() struct Clients { int pos; int count; - nsync_mu mu; - nsync_cv non_full; - nsync_cv non_empty; + pthread_mutex_t mu; + pthread_cond_t non_full; + pthread_cond_t non_empty; struct Client { int sock; uint32_t size; @@ -360,9 +347,9 @@ struct Clients { struct Claims { int pos; int count; - nsync_mu mu; - nsync_cv non_full; - nsync_cv non_empty; + pthread_mutex_t mu; + pthread_cond_t non_full; + pthread_cond_t non_empty; struct Claim { uint32_t ip; int64_t created; @@ -391,25 +378,25 @@ struct timespec WaitFor(int millis) { bool CheckMem(const char *file, int line, void *ptr) { if (ptr) return true; - kprintf("%s:%d: %P: out of memory: %s\n", file, line, strerror(errno)); + kprintf("%s:%d: %H: out of memory: %s\n", file, line, strerror(errno)); return false; } bool CheckSys(const char *file, int line, long rc) { if (rc != -1) return true; - kprintf("%s:%d: %P: %s\n", file, line, strerror(errno)); + kprintf("%s:%d: %H: %s\n", file, line, strerror(errno)); return false; } bool CheckSql(const char *file, int line, int rc) { if (rc == SQLITE_OK) return true; - kprintf("%s:%d: %P: %s\n", file, line, sqlite3_errstr(rc)); + kprintf("%s:%d: %H: %s\n", file, line, sqlite3_errstr(rc)); return false; } bool CheckDb(const char *file, int line, int rc, sqlite3 *db) { if (rc == SQLITE_OK) return true; - kprintf("%s:%d: %P: %s: %s\n", file, line, sqlite3_errstr(rc), + kprintf("%s:%d: %H: %s: %s\n", file, line, sqlite3_errstr(rc), sqlite3_errmsg(db)); return false; } @@ -501,51 +488,187 @@ bool IsValidNick(const char *s, size_t n) { return true; } +struct Clock { + atomic_uint roll; + atomic_ulong time; + atomic_ulong date; +}; + +static struct Clock g_clck[2]; +static pthread_t g_time_thread; + +static void set_clck(struct Clock *clck, long time, long date) { + unsigned long roll; + roll = atomic_fetch_add_explicit(&clck->roll, 1, memory_order_relaxed); + time &= 0xffffffffffff; + date &= 0xffffffffffff; + time |= roll << 48; + date |= roll << 48; + atomic_store_explicit(&clck->time, time, memory_order_relaxed); + atomic_store_explicit(&clck->date, date, memory_order_relaxed); +} + +static void get_clck(struct Clock *clck, long *out_time, long *out_date) { + long time, date; + do { + time = atomic_load_explicit(&clck->time, memory_order_relaxed); + date = atomic_load_explicit(&clck->date, memory_order_relaxed); + } while ((time >> 48) != (date >> 48)); + *out_date = date & 0xffffffffffff; + *out_time = time & 0xffffffffffff; +} + +static long encode_date(const struct tm *tm) { + long date; + date = tm->tm_year; + date <<= 4; + date |= tm->tm_isdst == 1; + date <<= 1; + date |= tm->tm_mon; + date <<= 5; + date |= tm->tm_mday; + date <<= 3; + date |= tm->tm_wday; + date <<= 5; + date |= tm->tm_hour; + date <<= 6; + date |= tm->tm_min; + date <<= 6; + date |= tm->tm_sec; + return date; +} + +static void decode_date(long date, struct tm *tm) { + tm->tm_sec = date & 63; + date >>= 6; + tm->tm_min = date & 63; + date >>= 6; + tm->tm_hour = date & 31; + date >>= 5; + tm->tm_wday = date & 7; + date >>= 3; + tm->tm_mday = date & 31; + date >>= 5; + tm->tm_mon = date & 15; + date >>= 4; + tm->tm_isdst = date & 1; + date >>= 1; + tm->tm_year = date; + tm->tm_gmtoff = 0; // unsupported + tm->tm_zone = 0; // unsupported + tm->tm_yday = 0; // unsupported +} + +static void update_time() { + struct tm tm; + struct timespec ts; + clock_gettime(0, &ts); + gmtime_r(&ts.tv_sec, &tm); + set_clck(&g_clck[0], ts.tv_sec, encode_date(&tm)); + localtime_r(&ts.tv_sec, &tm); + set_clck(&g_clck[1], ts.tv_sec, encode_date(&tm)); +} + +static void *time_worker(void *arg) { + sigset_t ss; + sigemptyset(&ss); + sigaddset(&ss, SIGHUP); + sigaddset(&ss, SIGINT); + sigaddset(&ss, SIGQUIT); + sigaddset(&ss, SIGTERM); + sigaddset(&ss, SIGUSR1); + sigaddset(&ss, SIGALRM); + pthread_sigmask(SIG_SETMASK, &ss, 0); + pthread_setname_np(pthread_self(), "localtime"); + for (;;) { + sleep(10); + update_time(); + } + return nullptr; +} + +void time_init() { + update_time(); + if (pthread_create(&g_time_thread, 0, time_worker, 0)) + __builtin_trap(); +} + +void time_destroy() { + pthread_cancel(g_time_thread); + if (pthread_join(g_time_thread, 0)) + __builtin_trap(); +} + +static const char kMonDays[2][12] = { + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, + {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, +}; + +static void time_lockless(struct Clock *clck, long now, struct tm *tm) { + long time, date, since; + get_clck(clck, &time, &date); + decode_date(date, tm); + since = now - time; + since = since < 60 ? since : 60; + for (; since > 0; --since) { + if (++tm->tm_sec >= 60) { + tm->tm_sec = 0; + if (++tm->tm_min >= 60) { + tm->tm_min = 0; + if (++tm->tm_hour >= 24) { + tm->tm_hour = 0; + if (++tm->tm_mday >= 7) + tm->tm_mday = 0; + if (++tm->tm_mday > kMonDays[!!tm->tm_isdst][tm->tm_mon]) { + tm->tm_mday = 1; + if (++tm->tm_mon >= 12) { + tm->tm_mon = 0; + ++tm->tm_year; + } + } + } + } + } + } +} + +void gmtime_lockless(long now, struct tm *tm) { + time_lockless(&g_clck[0], now, tm); +} + +void localtime_lockless(long now, struct tm *tm) { + time_lockless(&g_clck[1], now, tm); +} + // turn unix timestamp into string the easy way char *FormatUnixHttpDateTime(char *s, int64_t t) { struct tm tm; - gmtime_r(&t, &tm); + gmtime_lockless(t, &tm); FormatHttpDateTime(s, &tm); return s; } -// gmtime_r() does a shocking amount of compute -// so we try to handle that globally right here -void UpdateNow(void) { - int64_t secs; - struct tm tm; - g_nowish.ts = timespec_real(); - secs = g_nowish.ts.tv_sec; - gmtime_r(&secs, &tm); - //!//!//!//!//!//!//!//!//!//!//!//!//!/ - nsync_mu_lock(&g_nowish.lock); - g_nowish.tm = tm; - nsync_mu_unlock(&g_nowish.lock); - //!//!//!//!//!//!//!//!//!//!//!//!//!/ -} - // the standard strftime() function is dismally slow // this function is non-generalized for just http so // it needs 25 cycles rather than 709 cycles so cool char *FormatDate(char *p) { - //////////////////////////////////////// - nsync_mu_rlock(&g_nowish.lock); - p = FormatHttpDateTime(p, &g_nowish.tm); - nsync_mu_runlock(&g_nowish.lock); - //////////////////////////////////////// - return p; + return FormatUnixHttpDateTime(p, timespec_real().tv_sec); } -bool AddClient(struct Clients *q, const struct Client *v, nsync_time dead) { +void unlock_mutex(void *arg) { + pthread_mutex_t *lock = arg; + pthread_mutex_unlock(lock); +} + +bool AddClient(struct Clients *q, const struct Client *v, + struct timespec dead) { bool wake = false; bool added = false; - nsync_mu_lock(&q->mu); - while (q->count == ARRAYLEN(q->data)) { - if (nsync_cv_wait_with_deadline(&q->non_full, &q->mu, dead, - g_shutdown[0])) { - break; // must be ETIMEDOUT or ECANCELED - } - } + pthread_mutex_lock(&q->mu); + pthread_cleanup_push(unlock_mutex, &q->mu); + while (q->count == ARRAYLEN(q->data)) + if (pthread_cond_timedwait(&q->non_full, &q->mu, &dead)) + break; // must be ETIMEDOUT if (q->count != ARRAYLEN(q->data)) { int i = q->pos + q->count; if (ARRAYLEN(q->data) <= i) @@ -556,52 +679,44 @@ bool AddClient(struct Clients *q, const struct Client *v, nsync_time dead) { q->count++; added = true; } - nsync_mu_unlock(&q->mu); - if (wake) { - nsync_cv_broadcast(&q->non_empty); - } + pthread_cleanup_pop(true); + if (wake) + pthread_cond_broadcast(&q->non_empty); return added; } int GetClient(struct Clients *q, struct Client *out) { int got = 0; int len = 1; - nsync_mu_lock(&q->mu); - while (!q->count) { - if (nsync_cv_wait_with_deadline(&q->non_empty, &q->mu, - nsync_time_no_deadline, g_shutdown[1])) { - break; // must be ECANCELED - } - } + pthread_mutex_lock(&q->mu); + pthread_cleanup_push(unlock_mutex, &q->mu); + while (!q->count) + pthread_cond_timedwait(&q->non_empty, &q->mu, 0); while (got < len && q->count) { memcpy(out + got, q->data + q->pos, sizeof(*out)); - if (q->count == ARRAYLEN(q->data)) { - nsync_cv_broadcast(&q->non_full); - } + if (q->count == ARRAYLEN(q->data)) + pthread_cond_broadcast(&q->non_full); ++got; q->pos++; q->count--; - if (q->pos == ARRAYLEN(q->data)) { + if (q->pos == ARRAYLEN(q->data)) q->pos = 0; - } } - nsync_mu_unlock(&q->mu); + pthread_cleanup_pop(true); return got; } // inserts ip:name claim into blocking message queue // may be interrupted by absolute deadline // may be cancelled by server shutdown -bool AddClaim(struct Claims *q, const struct Claim *v, nsync_time dead) { +bool AddClaim(struct Claims *q, const struct Claim *v, struct timespec dead) { bool wake = false; bool added = false; - nsync_mu_lock(&q->mu); - while (q->count == ARRAYLEN(q->data)) { - if (nsync_cv_wait_with_deadline(&q->non_full, &q->mu, dead, - g_shutdown[1])) { + pthread_mutex_lock(&q->mu); + pthread_cleanup_push(unlock_mutex, &q->mu); + while (q->count == ARRAYLEN(q->data)) + if (pthread_cond_timedwait(&q->non_full, &q->mu, &dead)) break; // must be ETIMEDOUT or ECANCELED - } - } if (q->count != ARRAYLEN(q->data)) { int i = q->pos + q->count; if (ARRAYLEN(q->data) <= i) @@ -612,10 +727,9 @@ bool AddClaim(struct Claims *q, const struct Claim *v, nsync_time dead) { q->count++; added = true; } - nsync_mu_unlock(&q->mu); - if (wake) { - nsync_cv_broadcast(&q->non_empty); - } + pthread_cleanup_pop(true); + if (wake) + pthread_cond_broadcast(&q->non_empty); return added; } @@ -623,26 +737,25 @@ bool AddClaim(struct Claims *q, const struct Claim *v, nsync_time dead) { // has no deadline or cancellation; enqueued must be processed int GetClaims(struct Claims *q, struct Claim *out, int len) { int got = 0; - nsync_mu_lock(&q->mu); - while (!q->count) { - if (nsync_cv_wait_with_deadline(&q->non_empty, &q->mu, - nsync_time_no_deadline, g_shutdown[2])) { + pthread_mutex_lock(&q->mu); + pthread_cleanup_push(unlock_mutex, &q->mu); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, 0); + while (!q->count) + if (pthread_cond_timedwait(&q->non_empty, &q->mu, 0)) break; // must be ECANCELED - } - } + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0); while (got < len && q->count) { memcpy(out + got, q->data + q->pos, sizeof(*out)); if (q->count == ARRAYLEN(q->data)) { - nsync_cv_broadcast(&q->non_full); + pthread_cond_broadcast(&q->non_full); } ++got; q->pos++; q->count--; - if (q->pos == ARRAYLEN(q->data)) { + if (q->pos == ARRAYLEN(q->data)) q->pos = 0; - } } - nsync_mu_unlock(&q->mu); + pthread_cleanup_pop(true); return got; } @@ -694,14 +807,14 @@ void FreeSafeBuffer(void *p) { void BlockSignals(void) { sigset_t mask; sigfillset(&mask); - sigprocmask(SIG_SETMASK, &mask, 0); -} - -// main thread uses sigusr1 to deliver io cancellations -void AllowSigusr1(void) { - sigset_t mask; - sigfillset(&mask); - sigdelset(&mask, SIGUSR1); + sigdelset(&mask, SIGABRT); + sigdelset(&mask, SIGTRAP); + sigdelset(&mask, SIGFPE); + sigdelset(&mask, SIGBUS); + sigdelset(&mask, SIGSEGV); + sigdelset(&mask, SIGILL); + sigdelset(&mask, SIGXCPU); + sigdelset(&mask, SIGXFSZ); sigprocmask(SIG_SETMASK, &mask, 0); } @@ -731,6 +844,7 @@ void ServeStatusz(int client, char *outbuf) { p = Statusz(p, "now", now.tv_sec); p = Statusz(p, "messages", g_messages); p = Statusz(p, "connections", g_connections); + p = Statusz(p, "worker_threads", g_worker_threads); p = Statusz(p, "banned", g_banned); p = Statusz(p, "workers", g_workers); p = Statusz(p, "accepts", g_accepts); @@ -791,9 +905,8 @@ void *ListenWorker(void *arg) { struct Client client; struct timeval timeo = {g_keepalive / 1000, g_keepalive % 1000}; struct sockaddr_in addr = {.sin_family = AF_INET, .sin_port = htons(g_port)}; - AllowSigusr1(); pthread_setname_np(pthread_self(), "Listener"); - CHECK_NE(-1, (server = socket(AF_INET, SOCK_STREAM, 0))); + npassert((server = socket(AF_INET, SOCK_STREAM, 0)) != -1); setsockopt(server, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo)); setsockopt(server, SOL_SOCKET, SO_SNDTIMEO, &timeo, sizeof(timeo)); setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); @@ -802,8 +915,8 @@ void *ListenWorker(void *arg) { setsockopt(server, SOL_TCP, TCP_CORK, &no, sizeof(no)); setsockopt(server, SOL_TCP, TCP_NODELAY, &yes, sizeof(yes)); bind(server, (struct sockaddr *)&addr, sizeof(addr)); - CHECK_NE(-1, listen(server, 1)); - while (!nsync_note_is_notified(g_shutdown[0])) { + npassert(!listen(server, 1)); + for (;;) { client.size = sizeof(client.addr); client.sock = accept(server, (struct sockaddr *)&client.addr, &client.size); if (client.sock == -1) { @@ -815,6 +928,7 @@ void *ListenWorker(void *arg) { if (!AddClient(&g_clients, &client, WaitFor(ACCEPT_DEADLINE_MS))) { ++g_rejected; LOG("503 Accept Queue Full\n"); + fcntl(client.sock, F_SETFL, fcntl(client.sock, F_GETFL) | O_NONBLOCK); Write(client.sock, "HTTP/1.1 503 Accept Queue Full\r\n" "Content-Type: text/plain\r\n" "Connection: close\r\n" @@ -823,9 +937,18 @@ void *ListenWorker(void *arg) { close(client.sock); } } - close(server); - nsync_note_notify(g_shutdown[1]); - return 0; +} + +void OnHttpWorkerCancel(void *arg) { + struct Worker *w = arg; + if (w->client->sock != -1) + close(w->client->sock); + FreeSafeBuffer(w->outbuf); + FreeSafeBuffer(w->inbuf); + DestroyHttpMessage(w->msg); + free(w->msgbuf); + --g_worker_threads; + w->dead = true; } // make thousands of http client handler threads @@ -833,14 +956,26 @@ void *ListenWorker(void *arg) { // hangup on any browser clients that lag for more than a few seconds void *HttpWorker(void *arg) { struct Client client; + client.sock = -1; int id = (intptr_t)arg; - char *msgbuf = gc(xmalloc(MSG_BUF)); + char *msgbuf = malloc(MSG_BUF); char *inbuf = NewSafeBuffer(INBUF_SIZE); char *outbuf = NewSafeBuffer(OUTBUF_SIZE); - struct HttpMessage *msg = gc(xcalloc(1, sizeof(struct HttpMessage))); + struct HttpMessage msg[1]; + InitHttpMessage(msg, kHttpRequest); - BlockSignals(); - pthread_setname_np(pthread_self(), gc(xasprintf("HTTP%d", id))); + g_worker[id].msgbuf = msgbuf; + g_worker[id].inbuf = inbuf; + g_worker[id].outbuf = outbuf; + g_worker[id].msg = msg; + g_worker[id].client = &client; + pthread_cleanup_push(OnHttpWorkerCancel, g_worker + id); + + char name[32]; + sprintf(name, "HTTP%d", id); + pthread_setname_np(pthread_self(), name); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, 0); + ++g_worker_threads; // connection loop while (GetClient(&g_clients, &client)) { @@ -867,12 +1002,14 @@ void *HttpWorker(void *arg) { bool comp, ipv6; // wait for http message - // this may be cancelled by sigusr1 - AllowSigusr1(); - DestroyHttpMessage(msg); - InitHttpMessage(msg, kHttpRequest); + ResetHttpMessage(msg, kHttpRequest); g_worker[id].startread = timespec_real(); - if ((got = read(client.sock, inbuf, INBUF_SIZE)) <= 0) { + got = read(client.sock, inbuf, INBUF_SIZE - 1); + if (got >= 0) { + memcpy(last_message, inbuf, got); + last_message[got] = 0; + } + if (got <= 0) { ++g_readfails; break; } @@ -919,9 +1056,6 @@ void *HttpWorker(void *arg) { ksnprintf(ipbuf, sizeof(ipbuf), "%hhu.%hhu.%hhu.%hhu", ip >> 24, ip >> 16, ip >> 8, ip); - if (UrlStartsWith("/plot/") && (_rand64() % 256)) { - goto SkipSecurity; - } if (!ipv6 && !ContainsInt(&g_whitelisted, ip) && (tok = AcquireToken(g_tok.b, ip, TB_CIDR)) < 32) { if (tok > 4) { @@ -938,7 +1072,6 @@ void *HttpWorker(void *arg) { ++g_ratelimits; break; } - SkipSecurity: // we don't support http/1.0 and http/0.9 right now if (msg->version != 11) { @@ -989,18 +1122,15 @@ void *HttpWorker(void *arg) { a = &g_asset.score; } else if (UrlStartsWith("/recent")) { a = &g_asset.recent; - } else if (UrlStartsWith("/plot/")) { - int i, block = 0; - for (i = msg->uri.a + 6; i < msg->uri.b && isdigit(inbuf[i]); ++i) { - block *= 10; - block += inbuf[i] - '0'; - block &= 255; - } - a = g_asset.plot + block; } else { a = 0; } + // wait for server initialization + while (a) + if (a->ready) + break; + // assert serving if (a) { struct iovec iov[2]; @@ -1008,7 +1138,7 @@ void *HttpWorker(void *arg) { comp = a->gzip.n < a->data.n && HeaderHas(msg, inbuf, kHttpAcceptEncoding, "gzip", 4); //////////////////////////////////////// - nsync_mu_rlock(&a->lock); + pthread_rwlock_rdlock(&a->lock); if (HasHeader(kHttpIfModifiedSince) && a->mtim.tv_sec <= ParseHttpDateTime(HeaderData(kHttpIfModifiedSince), @@ -1055,7 +1185,7 @@ void *HttpWorker(void *arg) { outmsglen = iov[0].iov_len + iov[1].iov_len; sent = writev(client.sock, iov, 2); } - nsync_mu_runlock(&a->lock); + pthread_rwlock_unlock(&a->lock); //////////////////////////////////////// } else if (UrlStartsWith("/ip")) { @@ -1102,7 +1232,7 @@ void *HttpWorker(void *arg) { ++g_claimrequests; if (ipv6) goto Ipv6Warning; - struct Claim v = {.ip = ip, .created = g_nowish.ts.tv_sec}; + struct Claim v = {.ip = ip, .created = timespec_real().tv_sec}; if (GetNick(inbuf, msg, &v)) { if (AddClaim(&g_claims, &v, timespec_add(timespec_real(), @@ -1238,25 +1368,22 @@ void *HttpWorker(void *arg) { // amount, then since we sent the content length and checked // that the client didn't attach a payload, we are so synced // thus we can safely process more messages - } while (got == inmsglen && // - sent == outmsglen && // - !HasHeader(kHttpContentLength) && // - !HasHeader(kHttpTransferEncoding) && // - !HeaderEqualCase(kHttpConnection, "close") && // - (msg->method == kHttpGet || // - msg->method == kHttpHead) && // - 1. / g_workers * g_connections < CONCERN_LOAD && // - !nsync_note_is_notified(g_shutdown[1])); + } while (got == inmsglen && // + sent == outmsglen && // + !HasHeader(kHttpContentLength) && // + !HasHeader(kHttpTransferEncoding) && // + !HeaderEqualCase(kHttpConnection, "close") && // + (msg->method == kHttpGet || // + msg->method == kHttpHead) && // + 1. / g_workers * g_connections < CONCERN_LOAD); DestroyHttpMessage(msg); close(client.sock); + client.sock = -1; g_worker[id].connected = false; --g_connections; } - LOG("HttpWorker #%d exiting", id); - g_worker[id].shutdown = true; - FreeSafeBuffer(outbuf); - FreeSafeBuffer(inbuf); + pthread_cleanup_pop(true); return 0; } @@ -1282,8 +1409,8 @@ struct Data Gzip(struct Data data) { deflateEnd(&zs); return (struct Data){0}; } - CHECK_EQ(Z_STREAM_END, deflate(&zs, Z_FINISH)); - CHECK_EQ(Z_OK, deflateEnd(&zs)); + npassert(Z_STREAM_END == deflate(&zs, Z_FINISH)); + npassert(Z_OK == deflateEnd(&zs)); res.n = sizeof(kGzipHeader) + zs.total_out + sizeof(footer); if (!(p = res.p = malloc(res.n))) { free(tmp); @@ -1300,14 +1427,16 @@ struct Data Gzip(struct Data data) { struct Asset LoadAsset(const char *path, const char *type, int cash) { struct stat st; struct Asset a = {0}; - CHECK_EQ(0, stat(path, &st)); - CHECK_NOTNULL((a.data.p = xslurp(path, &a.data.n))); + pthread_rwlock_init(&a.lock, 0); + npassert(!stat(path, &st)); + npassert((a.data.p = xslurp(path, &a.data.n))); a.type = type; a.cash = cash; - CHECK_NOTNULL((a.path = strdup(path))); + unassert((a.path = strdup(path))); a.mtim = st.st_mtim; - CHECK_NOTNULL((a.gzip = Gzip(a.data)).p); + unassert((a.gzip = Gzip(a.data)).p); FormatUnixHttpDateTime(a.lastmodified, a.mtim.tv_sec); + a.ready = true; return a; } @@ -1331,15 +1460,16 @@ bool ReloadAsset(struct Asset *a) { goto OnError; CHECK_MEM((gzip = Gzip(data)).p); //!//!//!//!//!//!//!//!//!//!//!//!//!/ - nsync_mu_lock(&a->lock); + pthread_rwlock_wrlock(&a->lock); f[0] = a->data.p; f[1] = a->gzip.p; a->data = data; a->gzip = gzip; a->mtim = st.st_mtim; memcpy(a->lastmodified, lastmodified, 32); - nsync_mu_unlock(&a->lock); + pthread_rwlock_unlock(&a->lock); //!//!//!//!//!//!//!//!//!//!//!//!//!/ + a->ready = true; free(f[0]); free(f[1]); } @@ -1353,34 +1483,14 @@ OnError: } void FreeAsset(struct Asset *a) { + pthread_rwlock_destroy(&a->lock); free(a->path); free(a->data.p); free(a->gzip.p); } -void IgnoreSignal(int sig) { - // so worker i/o routines may eintr safely -} - -// asynchronous handler of sigint, sigterm, and sighup signals -// this handler is always invoked from within the main thread, -// because our helper and worker threads always block signals. void OnCtrlC(int sig) { - if (!nsync_note_is_notified(g_shutdown[0])) { - LOG("Received %s shutting down...\n", strsignal(sig)); - nsync_note_notify(g_shutdown[0]); - } else { - // there's no way to deliver signals to workers atomically, unless - // we pay the cost of ppoll() which isn't necessary in this design - // so if a user smashes that ctrl-c then we tkill the workers more - LOG("Received %s again so sending another volley...\n", strsignal(sig)); - for (int i = 0; i < g_workers; ++i) { - pthread_kill(g_listener, SIGUSR1); - if (!g_worker[i].shutdown) { - pthread_kill(g_worker[i].th, SIGUSR1); - } - } - } + is_shutting_down = 1; } // parses cli arguments @@ -1432,9 +1542,10 @@ void Update(struct Asset *a, bool gen(struct Asset *, long, long), long x, long y) { void *f[2]; struct Asset t; + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0); if (gen(&t, x, y)) { //!//!//!//!//!//!//!//!//!//!//!//!//!/ - nsync_mu_lock(&a->lock); + pthread_rwlock_wrlock(&a->lock); f[0] = a->data.p; f[1] = a->gzip.p; a->data = t.data; @@ -1443,11 +1554,13 @@ void Update(struct Asset *a, bool gen(struct Asset *, long, long), long x, a->type = t.type; a->cash = t.cash; memcpy(a->lastmodified, t.lastmodified, 32); - nsync_mu_unlock(&a->lock); + pthread_rwlock_unlock(&a->lock); //!//!//!//!//!//!//!//!//!//!//!//!//!/ + a->ready = true; free(f[0]); free(f[1]); } + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, 0); } // generator function for the big board @@ -1528,182 +1641,74 @@ OnError: return false; } -// generator function for the big board -bool GeneratePlot(struct Asset *out, long block, long cash) { - _Static_assert(IS2POW(XN * YN), "area must be 2-power"); - _Static_assert(XN == YN, "hilbert algorithm needs square"); - int rc, out_len; - sqlite3 *db = 0; - struct Asset a = {0}; - unsigned char *rgba; - sqlite3_stmt *stmt = 0; - unsigned x, y, i, ip, area, mask, clump; - DEBUG("GeneratePlot %ld\n", block); - a.type = "image/png"; - a.cash = cash; - a.mtim = timespec_real(); - FormatUnixHttpDateTime(a.lastmodified, a.mtim.tv_sec); - CHECK_MEM((rgba = calloc(4, YN * XN))); - for (y = 0; y < YN; ++y) { - for (x = 0; x < XN; ++x) { - rgba[y * XN * 4 + x * 4 + 0] = 255; - rgba[y * XN * 4 + x * 4 + 1] = 255; - rgba[y * XN * 4 + x * 4 + 2] = 255; - } - } - CHECK_SQL(DbOpen("db.sqlite3", &db)); - CHECK_DB(DbPrepare(db, &stmt, - "SELECT ip\n" - " FROM land\n" - "WHERE ip >= ?1\n" - " AND ip <= ?2")); - CHECK_DB(sqlite3_bind_int64(stmt, 1, block << 24 | 0x000000)); - CHECK_DB(sqlite3_bind_int64(stmt, 2, block << 24 | 0xffffff)); - CHECK_SQL(sqlite3_exec(db, "BEGIN TRANSACTION", 0, 0, 0)); - area = XN * YN; - mask = area - 1; - clump = 32 - bsr(area) - 8; - while ((rc = DbStep(stmt)) != SQLITE_DONE) { - if (rc != SQLITE_ROW) - CHECK_DB(rc); - ip = sqlite3_column_int64(stmt, 0); - i = (ip >> clump) & mask; - y = g_hilbert[i][0]; - x = g_hilbert[i][1]; - if (rgba[y * XN * 4 + x * 4 + 3] < 255) { - ++rgba[y * XN * 4 + x * 4 + 3]; - } - } - CHECK_SQL(sqlite3_exec(db, "END TRANSACTION", 0, 0, 0)); - CHECK_DB(sqlite3_finalize(stmt)); - CHECK_SQL(sqlite3_close(db)); - a.data.p = (char *)stbi_write_png_to_mem(rgba, XN * 4, XN, YN, 4, &out_len); - a.data.n = out_len; - a.gzip = Gzip(a.data); - free(rgba); - *out = a; - return true; -OnError: - sqlite3_finalize(stmt); - sqlite3_close(db); - free(a.data.p); - free(rgba); - return false; -} - // single thread for regenerating the user scores json void *ScoreWorker(void *arg) { - BlockSignals(); pthread_setname_np(pthread_self(), "ScoreAll"); - LOG("%P Score started\n"); - long wait = SCORE_UPDATE_MS; - Update(&g_asset.score, GenerateScore, -1, MS2CASH(wait)); - nsync_counter_add(g_ready, -1); // #1 - do { - Update(&g_asset.score, GenerateScore, -1, MS2CASH(wait)); - } while (!nsync_note_wait(g_shutdown[1], WaitFor(wait))); - LOG("Score exiting\n"); - return 0; + for (;;) { + LOG("%H regenerating score...\n"); + Update(&g_asset.score, GenerateScore, -1, MS2CASH(SCORE_UPDATE_MS)); + usleep(SCORE_UPDATE_MS * 1000); + } } // single thread for regenerating the user scores json void *ScoreHourWorker(void *arg) { - BlockSignals(); pthread_setname_np(pthread_self(), "ScoreHour"); - LOG("%P ScoreHour started\n"); - long secs = 60L * 60; - long wait = SCORE_H_UPDATE_MS; - Update(&g_asset.score_hour, GenerateScore, secs, MS2CASH(wait)); - nsync_counter_add(g_ready, -1); // #2 - do { - Update(&g_asset.score_hour, GenerateScore, secs, MS2CASH(wait)); - } while (!nsync_note_wait(g_shutdown[1], WaitFor(wait))); - LOG("ScoreHour exiting\n"); - return 0; + for (;;) { + LOG("%H regenerating hour score...\n"); + Update(&g_asset.score_hour, GenerateScore, 60L * 60, + MS2CASH(SCORE_H_UPDATE_MS)); + usleep(SCORE_H_UPDATE_MS * 1000); + } } // single thread for regenerating the user scores json void *ScoreDayWorker(void *arg) { - BlockSignals(); pthread_setname_np(pthread_self(), "ScoreDay"); - LOG("%P ScoreDay started\n"); - long secs = 60L * 60 * 24; - long wait = SCORE_D_UPDATE_MS; - Update(&g_asset.score_day, GenerateScore, secs, MS2CASH(wait)); - nsync_counter_add(g_ready, -1); // #3 - do { - Update(&g_asset.score_day, GenerateScore, secs, MS2CASH(wait)); - } while (!nsync_note_wait(g_shutdown[1], WaitFor(wait))); - LOG("ScoreDay exiting\n"); - return 0; + for (;;) { + LOG("%H regenerating day score...\n"); + Update(&g_asset.score_day, GenerateScore, 60L * 60 * 24, + MS2CASH(SCORE_D_UPDATE_MS)); + usleep(SCORE_D_UPDATE_MS * 1000); + } } // single thread for regenerating the user scores json void *ScoreWeekWorker(void *arg) { - BlockSignals(); pthread_setname_np(pthread_self(), "ScoreWeek"); - LOG("%P ScoreWeek started\n"); - long secs = 60L * 60 * 24 * 7; - long wait = SCORE_W_UPDATE_MS; - Update(&g_asset.score_week, GenerateScore, secs, MS2CASH(wait)); - nsync_counter_add(g_ready, -1); // #4 - do { - Update(&g_asset.score_week, GenerateScore, secs, MS2CASH(wait)); - } while (!nsync_note_wait(g_shutdown[1], WaitFor(wait))); - LOG("ScoreWeek exiting\n"); - return 0; + for (;;) { + LOG("%H regenerating week score...\n"); + Update(&g_asset.score_week, GenerateScore, 60L * 60 * 24 * 7, + MS2CASH(SCORE_W_UPDATE_MS)); + usleep(SCORE_W_UPDATE_MS * 1000); + } } // single thread for regenerating the user scores json void *ScoreMonthWorker(void *arg) { - BlockSignals(); pthread_setname_np(pthread_self(), "ScoreMonth"); - LOG("%P ScoreMonth started\n"); - long secs = 60L * 60 * 24 * 30; - long wait = SCORE_M_UPDATE_MS; - Update(&g_asset.score_month, GenerateScore, secs, MS2CASH(wait)); - nsync_counter_add(g_ready, -1); // #5 - do { - Update(&g_asset.score_month, GenerateScore, secs, MS2CASH(wait)); - } while (!nsync_note_wait(g_shutdown[1], WaitFor(wait))); - LOG("ScoreMonth exiting\n"); - return 0; -} - -// single thread for regenerating /8 cell background image charts -void *PlotWorker(void *arg) { - long i, wait; - BlockSignals(); - pthread_setname_np(pthread_self(), "Plotter"); - LOG("%P Plotter started\n"); - wait = PLOTS_UPDATE_MS; - for (i = 0; i < 256; ++i) { - Update(g_asset.plot + i, GeneratePlot, i, MS2CASH(wait)); + for (;;) { + LOG("%H regenerating month score...\n"); + Update(&g_asset.score_month, GenerateScore, 60L * 60 * 24 * 30, + MS2CASH(SCORE_M_UPDATE_MS)); + usleep(SCORE_M_UPDATE_MS * 1000); } - nsync_counter_add(g_ready, -1); // #6 - do { - for (i = 0; i < 256; ++i) { - Update(g_asset.plot + i, GeneratePlot, i, MS2CASH(wait)); - } - } while (!nsync_note_wait(g_shutdown[1], WaitFor(wait))); - LOG("Plotter exiting\n"); - return 0; } // thread for realtime json generation of recent successful claims void *RecentWorker(void *arg) { + int rc; bool once; void *f[2]; - int rc, err; sqlite3 *db; char *sb = 0; size_t sblen = 0; + const char *text; sqlite3_stmt *stmt; struct Asset *a, t; - bool warmedup = false; - BlockSignals(); + sleep(2); pthread_setname_np(pthread_self(), "RecentWorker"); - LOG("%P RecentWorker started\n"); + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0); StartOver: db = 0; stmt = 0; @@ -1715,7 +1720,7 @@ StartOver: "WHERE created NOT NULL\n" "ORDER BY created DESC\n" "LIMIT 50")); - do { + for (;;) { // regenerate json t.mtim = timespec_real(); FormatUnixHttpDateTime(t.lastmodified, t.mtim.tv_sec); @@ -1727,13 +1732,14 @@ StartOver: for (once = false; (rc = DbStep(stmt)) != SQLITE_DONE; once = true) { if (rc != SQLITE_ROW) CHECK_SQL(rc); - if (once) - CHECK_SYS(appends(&t.data.p, ",\n")); - CHECK_SYS( - appendf(&t.data.p, "[%ld,\"%s\",%ld]", sqlite3_column_int64(stmt, 0), - EscapeJsStringLiteral( - &sb, &sblen, (void *)sqlite3_column_text(stmt, 1), -1, 0), - sqlite3_column_int64(stmt, 2))); + if ((text = (const char *)sqlite3_column_text(stmt, 1))) { + if (once) + CHECK_SYS(appends(&t.data.p, ",\n")); + CHECK_SYS(appendf(&t.data.p, "[%ld,\"%s\",%ld]", + sqlite3_column_int64(stmt, 0), + EscapeJsStringLiteral(&sb, &sblen, text, -1, 0), + sqlite3_column_int64(stmt, 2))); + } } CHECK_SQL(sqlite3_reset(stmt)); CHECK_SQL(sqlite3_exec(db, "END TRANSACTION", 0, 0, 0)); @@ -1743,7 +1749,7 @@ StartOver: // deploy json a = &g_asset.recent; //!//!//!//!//!//!//!//!//!//!//!//!//!/ - nsync_mu_lock(&a->lock); + pthread_rwlock_wrlock(&a->lock); f[0] = a->data.p; f[1] = a->gzip.p; a->data = t.data; @@ -1752,25 +1758,22 @@ StartOver: a->type = "application/json"; a->cash = 0; memcpy(a->lastmodified, t.lastmodified, 32); - nsync_mu_unlock(&a->lock); + pthread_rwlock_unlock(&a->lock); //!//!//!//!//!//!//!//!//!//!//!//!//!/ + a->ready = true; bzero(&t, sizeof(t)); free(f[0]); free(f[1]); - // handle startup condition - if (!warmedup) { - nsync_counter_add(g_ready, -1); // #7 - warmedup = true; - } // wait for wakeup or cancel - nsync_mu_lock(&g_recent.mu); - err = nsync_cv_wait_with_deadline(&g_recent.cv, &g_recent.mu, - nsync_time_no_deadline, g_shutdown[1]); - nsync_mu_unlock(&g_recent.mu); - } while (err != ECANCELED); + pthread_mutex_lock(&g_recent.mu); + pthread_cleanup_push(unlock_mutex, &g_recent.mu); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, 0); + pthread_cond_timedwait(&g_recent.cv, &g_recent.mu, 0); + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0); + pthread_cleanup_pop(true); + } CHECK_DB(sqlite3_finalize(stmt)); CHECK_SQL(sqlite3_close(db)); - LOG("RecentWorker exiting\n"); free(sb); return 0; OnError: @@ -1788,11 +1791,9 @@ void *ClaimWorker(void *arg) { int i, n, rc; long processed; sqlite3_stmt *stmt; - bool warmedup = false; - struct Claim *v = gc(xcalloc(BATCH_MAX, sizeof(struct Claim))); - BlockSignals(); + struct Claim *v = gc(calloc(BATCH_MAX, sizeof(struct Claim))); + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0); pthread_setname_np(pthread_self(), "ClaimWorker"); - LOG("%P ClaimWorker started\n"); StartOver: db = 0; stmt = 0; @@ -1805,10 +1806,6 @@ StartOver: " WHERE nick != ?2\n" " OR created IS NULL\n" " OR ?3 - created > 3600")); - if (!warmedup) { - nsync_counter_add(g_ready, -1); // #8 - warmedup = true; - } while ((n = GetClaims(&g_claims, v, BATCH_MAX))) { processed = 0; CHECK_SQL(sqlite3_exec(db, "BEGIN TRANSACTION", 0, 0, 0)); @@ -1824,13 +1821,12 @@ StartOver: atomic_fetch_add(&g_claimsprocessed, processed); DEBUG("Committed %d claims\n", n); // wake up RecentWorker() - nsync_mu_lock(&g_recent.mu); - nsync_cv_signal(&g_recent.cv); - nsync_mu_unlock(&g_recent.mu); + pthread_mutex_lock(&g_recent.mu); + pthread_cond_signal(&g_recent.cv); + pthread_mutex_unlock(&g_recent.mu); } CHECK_DB(sqlite3_finalize(stmt)); CHECK_SQL(sqlite3_close(db)); - LOG("ClaimWorker exiting\n"); return 0; OnError: sqlite3_finalize(stmt); @@ -1838,40 +1834,36 @@ OnError: goto StartOver; } -// single thread for computing HTTP Date header -void *NowWorker(void *arg) { - BlockSignals(); - pthread_setname_np(pthread_self(), "NowWorker"); - LOG("%P NowWorker started\n"); - UpdateNow(); - nsync_counter_add(g_ready, -1); // #9 - for (struct timespec ts = {timespec_real().tv_sec};; ++ts.tv_sec) { - if (!nsync_note_wait(g_shutdown[1], ts)) { - UpdateNow(); - } else { - break; - } - } - LOG("NowWorker exiting\n"); - return 0; -} - // worker for refilling token buckets void *ReplenishWorker(void *arg) { - BlockSignals(); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, 0); pthread_setname_np(pthread_self(), "Replenisher"); - LOG("%P Replenisher started\n"); - UpdateNow(); for (struct timespec ts = timespec_real();; ts = timespec_add(ts, timespec_frommillis(TB_INTERVAL))) { - if (!nsync_note_wait(g_shutdown[1], ts)) { - ReplenishTokens(g_tok.w, TB_WORDS); - } else { - break; - } + clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &ts, 0); + ReplenishTokens(g_tok.w, TB_WORDS); } - LOG("Replenisher exiting\n"); - return 0; +} + +void SpawnWorker(intptr_t i) { + sigset_t thmask; + pthread_attr_t attr; + sigfillset(&thmask); + sigdelset(&thmask, SIGABRT); + sigdelset(&thmask, SIGTRAP); + sigdelset(&thmask, SIGFPE); + sigdelset(&thmask, SIGBUS); + sigdelset(&thmask, SIGSEGV); + sigdelset(&thmask, SIGILL); + sigdelset(&thmask, SIGXCPU); + sigdelset(&thmask, SIGXFSZ); + pthread_attr_init(&attr); + pthread_attr_setsigmask_np(&attr, &thmask); + pthread_attr_setstacksize(&attr, 128 * 1024); + pthread_attr_setguardsize(&attr, sysconf(_SC_PAGESIZE)); + pthread_attr_setsigaltstacksize_np(&attr, sysconf(_SC_MINSIGSTKSZ) + 32768); + pthread_create(&g_worker[i].th, &attr, HttpWorker, (void *)i); + pthread_attr_destroy(&attr); } // we're permissive in allowing http connection keepalive until the @@ -1882,7 +1874,7 @@ void Meltdown(void) { int i, marks; struct timespec now; ++g_meltdowns; - LOG("Panicking because %d out of %d workers is connected\n", g_connections, + LOG("%H panicking because %d out of %d workers is connected\n", g_connections, g_workers); now = timespec_real(); for (marks = i = 0; i < g_workers; ++i) { @@ -1890,7 +1882,9 @@ void Meltdown(void) { (g_worker[i].msgcount > PANIC_MSGS || timespec_cmp(timespec_sub(now, g_worker[i].startread), timespec_frommillis(MELTALIVE_MS)) >= 0)) { - pthread_kill(g_worker[i].th, SIGUSR1); + pthread_cancel(g_worker[i].th); + pthread_join(g_worker[i].th, 0); + SpawnWorker(i); ++marks; } } @@ -1899,18 +1893,29 @@ void Meltdown(void) { // main thread worker void *Supervisor(void *arg) { - for (;;) { - if (!nsync_note_wait(g_shutdown[0], WaitFor(SUPERVISE_MS))) { - if (g_workers > 1 && 1. / g_workers * g_connections > PANIC_LOAD) { - Meltdown(); + while (!is_shutting_down) { + + // check for updates to web assets on disk + ReloadAsset(&g_asset.index); + ReloadAsset(&g_asset.about); + ReloadAsset(&g_asset.user); + ReloadAsset(&g_asset.favicon); + + // check if server is about to explode + if (g_workers > 1 && 1. / g_workers * g_connections > PANIC_LOAD) + Meltdown(); + + // spawn replacements for crashed workers + for (int i = 0; i < g_workers; ++i) { + if (g_worker[i].dead) { + pthread_join(g_worker[i].th, 0); + SpawnWorker(i); } - ReloadAsset(&g_asset.index); - ReloadAsset(&g_asset.about); - ReloadAsset(&g_asset.user); - ReloadAsset(&g_asset.favicon); - } else { - break; } + + // wait a little bit + if (!is_shutting_down) + usleep(SUPERVISE_MS * 1000); } return 0; } @@ -1919,9 +1924,9 @@ void CheckDatabase(void) { sqlite3 *db; if (g_integrity) { CHECK_SQL(DbOpen("db.sqlite3", &db)); - LOG("Checking database integrity...\n"); + LOG("%H Checking database integrity...\n"); CHECK_SQL(sqlite3_exec(db, "PRAGMA integrity_check", 0, 0, 0)); - LOG("Vacuuming database...\n"); + LOG("%H Vacuuming database...\n"); CHECK_SQL(sqlite3_exec(db, "VACUUM", 0, 0, 0)); CHECK_SQL(sqlite3_close(db)); } @@ -1930,8 +1935,159 @@ OnError: exit(1); } +char *hexcpy(char *p, unsigned long x) { + int k = x ? (__builtin_clzl(x) ^ 63) + 1 : 1; + k = (k + 3) & -4; + while (k > 0) + *p++ = "0123456789abcdef"[(x >> (k -= 4)) & 15]; + *p = '\0'; + return p; +} + +char *describe_backtrace(char *p, size_t len, const struct StackFrame *sf) { + char *pe = p + len; + bool gotsome = false; + + // show address of each function + while (sf) { + if (kisdangerous(sf)) { + if (p + 1 + 9 + 1 < pe) { + if (gotsome) + *p++ = ' '; + p = stpcpy(p, "DANGEROUS"); + if (p + 16 + 1 < pe) { + *p++ = ' '; + p = hexcpy(p, (long)sf); + } + } + break; + } + if (p + 16 + 1 < pe) { + unsigned char *ip = (unsigned char *)sf->addr; +#ifdef __x86_64__ + // x86 advances the progrem counter before an instruction + // begins executing. return addresses in backtraces shall + // point to code after the call, which means addr2line is + // going to print unrelated code unless we fixup the addr + if (!kisdangerous(ip)) + ip -= __is_call(ip); +#endif + if (gotsome) + *p++ = ' '; + else + gotsome = true; + p = hexcpy(p, (long)ip); + } else { + break; + } + sf = sf->next; + } + + // terminate string + if (p < pe) + *p = '\0'; + return p; +} + +// abashed the devil stood +// and felt how awful goodness is +char *describe_crash(char *buf, size_t len, int sig, siginfo_t *si, void *arg) { + char *p = buf; + + // check minimum length + if (len < 64) + return p; + + // describe crash + char signame[21]; + p = stpcpy(p, strsignal_r(sig, signame)); + if (si && // + (sig == SIGFPE || // + sig == SIGILL || // + sig == SIGBUS || // + sig == SIGSEGV || // + sig == SIGTRAP)) { + p = stpcpy(p, " at "); + p = hexcpy(p, (long)si->si_addr); + } + + // get stack frame daisy chain + struct StackFrame pc; + struct StackFrame *sf; + ucontext_t *ctx; + if ((ctx = (ucontext_t *)arg)) { + pc.addr = ctx->uc_mcontext.PC; + pc.next = (struct StackFrame *)ctx->uc_mcontext.BP; + sf = &pc; + } else { + sf = (struct StackFrame *)__builtin_frame_address(0); + } + + // describe backtrace + p = stpcpy(p, " bt "); + p = describe_backtrace(p, len - (p - buf), sf); + + return p; +} + +void on_crash_signal(int sig, siginfo_t *si, void *arg) { + char *p; + char message[512]; + write(2, "crash!\n", 7); + p = describe_crash(message, sizeof(message), sig, si, arg); + write(g_crash_fd, "crash: ", 7); + write(g_crash_fd, message, p - message); + write(g_crash_fd, "\n", 1); + write(g_crash_fd, last_message, strlen(last_message)); + write(g_crash_fd, "\n", 1); + pthread_exit(PTHREAD_CANCELED); +} + +void make_server_crash_resistant(void) { + const char *path = "crash.log"; + if ((g_crash_fd = open(path, O_CREAT | O_WRONLY | O_APPEND, 0644)) == -1) { + fprintf(stderr, "%s: %s\n", path, strerror(errno)); + exit(1); + } + + struct sigaction sa; + sa.sa_flags = SA_SIGINFO; + sigemptyset(&sa.sa_mask); + + sa.sa_sigaction = on_crash_signal; + sigaddset(&sa.sa_mask, SIGABRT); + sigaddset(&sa.sa_mask, SIGTRAP); + sigaddset(&sa.sa_mask, SIGFPE); + sigaddset(&sa.sa_mask, SIGBUS); + sigaddset(&sa.sa_mask, SIGSEGV); + sigaddset(&sa.sa_mask, SIGILL); + sigaddset(&sa.sa_mask, SIGXCPU); + sigaddset(&sa.sa_mask, SIGXFSZ); + + sigaction(SIGABRT, &sa, 0); + sigaction(SIGTRAP, &sa, 0); + sigaction(SIGFPE, &sa, 0); + sigaction(SIGILL, &sa, 0); + sigaction(SIGXCPU, &sa, 0); + sigaction(SIGXFSZ, &sa, 0); + + sa.sa_flags |= SA_ONSTACK; + sigaction(SIGBUS, &sa, 0); + sigaction(SIGSEGV, &sa, 0); +} + int main(int argc, char *argv[]) { - // ShowCrashReports(); + make_server_crash_resistant(); + + if (pledge(0, 0)) { + fprintf(stderr, "%s: this OS doesn't support pledge() security\n", argv[0]); + exit(1); + } + + if (unveil("", 0) < 2) { + fprintf(stderr, "%s: need OpenBSD or Landlock LSM v3+\n", argv[0]); + exit(1); + } if (IsLinux()) { Write(2, "Enabling TCP_FASTOPEN for server sockets...\n"); @@ -1951,7 +2107,7 @@ int main(int argc, char *argv[]) { __| | | __| | \\ \\ \\ / _` | __|\n\ | | | | __|\\ \\ \\ / ( | |\n\ \\__|\\__,_|_| _| \\_/\\_/ \\__,_|_|\n"); - CHECK_EQ(0, chdir("/opt/turfwar")); + npassert(!chdir("/opt/turfwar")); putenv("TMPDIR=/opt/turfwar/tmp"); if ((g_blackhole.fd = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) { @@ -1979,13 +2135,6 @@ int main(int argc, char *argv[]) { npassert(2 == open("turfwar.log", O_CREAT | O_WRONLY | O_APPEND, 0644)); } - LOG("Generating Hilbert Curve...\n"); - for (int i = 0; i < YN * XN; ++i) { - axdx_t h = unhilbert(XN, i); - g_hilbert[i][0] = h.ax; - g_hilbert[i][1] = h.dx; - } - // library init sqlite3_initialize(); CheckDatabase(); @@ -1996,9 +2145,6 @@ int main(int argc, char *argv[]) { // server lifecycle locks g_started = timespec_real(); - for (int i = 0; i < ARRAYLEN(g_shutdown); ++i) { - g_shutdown[i] = nsync_note_new(0, nsync_time_no_deadline); - } // load static assets into memory and pre-zip them g_asset.index = LoadAsset("index.html", "text/html; charset=utf-8", 900); @@ -2008,11 +2154,11 @@ int main(int argc, char *argv[]) { // sandbox ourselves __pledge_mode = PLEDGE_PENALTY_RETURN_EPERM; - CHECK_EQ(0, unveil("/opt/turfwar", "rwc")); - CHECK_EQ(0, unveil(0, 0)); + npassert(!unveil("/opt/turfwar", "rwc")); + npassert(!unveil(0, 0)); if (!IsOpenbsd()) { // TODO(jart): why isn't pledge working on openbsd? - CHECK_EQ(0, pledge("stdio flock rpath wpath cpath inet", 0)); + npassert(!pledge("stdio flock rpath wpath cpath inet", 0)); } // shutdown signals @@ -2023,83 +2169,89 @@ int main(int argc, char *argv[]) { sigaction(SIGHUP, &sa, 0); sigaction(SIGINT, &sa, 0); sigaction(SIGTERM, &sa, 0); - sa.sa_handler = IgnoreSignal; - sigaction(SIGUSR1, &sa, 0); - // make 9 helper threads - g_ready = nsync_counter_new(10); - pthread_t scorer, recenter, claimer, nower, replenisher, plotter; - pthread_t scorer_hour, scorer_day, scorer_week, scorer_month; - CHECK_EQ(0, pthread_create(&scorer, 0, ScoreWorker, 0)); - CHECK_EQ(0, pthread_create(&scorer_hour, 0, ScoreHourWorker, 0)); - CHECK_EQ(0, pthread_create(&scorer_day, 0, ScoreDayWorker, 0)); - CHECK_EQ(0, pthread_create(&scorer_week, 0, ScoreWeekWorker, 0)); - CHECK_EQ(0, pthread_create(&scorer_month, 0, ScoreMonthWorker, 0)); - CHECK_EQ(0, pthread_create(&replenisher, 0, ReplenishWorker, 0)); - CHECK_EQ(0, pthread_create(&recenter, 0, RecentWorker, 0)); - CHECK_EQ(0, pthread_create(&claimer, 0, ClaimWorker, 0)); - CHECK_EQ(0, pthread_create(&plotter, 0, PlotWorker, 0)); - CHECK_EQ(0, pthread_create(&nower, 0, NowWorker, 0)); + time_init(); - // wait for helper threads to warm up creating assets - if (nsync_counter_add(g_ready, -1)) { // #10 - nsync_counter_wait(g_ready, nsync_time_no_deadline); - } + sigset_t thmask; + sigfillset(&thmask); + sigdelset(&thmask, SIGABRT); + sigdelset(&thmask, SIGTRAP); + sigdelset(&thmask, SIGFPE); + sigdelset(&thmask, SIGBUS); + sigdelset(&thmask, SIGSEGV); + sigdelset(&thmask, SIGILL); + sigdelset(&thmask, SIGXCPU); + sigdelset(&thmask, SIGXFSZ); - // create one thread to listen - CHECK_EQ(0, pthread_create(&g_listener, 0, ListenWorker, 0)); - - // create lots of http workers to serve those assets - LOG("Online\n"); - g_worker = xcalloc(g_workers, sizeof(*g_worker)); - for (intptr_t i = 0; i < g_workers; ++i) { - CHECK_EQ(0, pthread_create(&g_worker[i].th, 0, HttpWorker, (void *)i)); - } + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setsigmask_np(&attr, &thmask); + pthread_attr_setstacksize(&attr, 128 * 1024); + pthread_attr_setguardsize(&attr, sysconf(_SC_PAGESIZE)); + pthread_attr_setsigaltstacksize_np(&attr, sysconf(_SC_MINSIGSTKSZ) + 32768); + npassert(!pthread_create(&scorer, &attr, ScoreWorker, 0)); + npassert(!pthread_create(&scorer_hour, &attr, ScoreHourWorker, 0)); + npassert(!pthread_create(&scorer_day, &attr, ScoreDayWorker, 0)); + npassert(!pthread_create(&scorer_week, &attr, ScoreWeekWorker, 0)); + npassert(!pthread_create(&scorer_month, &attr, ScoreMonthWorker, 0)); + npassert(!pthread_create(&replenisher, &attr, ReplenishWorker, 0)); + npassert(!pthread_create(&recenter, &attr, RecentWorker, 0)); + npassert(!pthread_create(&claimer, &attr, ClaimWorker, 0)); + npassert(!pthread_create(&g_listener, &attr, ListenWorker, 0)); + unassert((g_worker = calloc(g_workers, sizeof(*g_worker)))); + for (intptr_t i = 0; i < g_workers; ++i) + npassert(!pthread_create(&g_worker[i].th, &attr, HttpWorker, (void *)i)); + pthread_attr_destroy(&attr); // time to serve - LOG("Ready\n"); + LOG("%H ready\n"); Supervisor(0); - // cancel listen() so we stop accepting new clients - LOG("Interrupting listen...\n"); - pthread_kill(g_listener, SIGUSR1); - pthread_join(g_listener, 0); + // cancel listen() + LOG("%H interrupting services...\n"); + pthread_cancel(scorer); + pthread_cancel(recenter); + pthread_cancel(g_listener); + pthread_cancel(scorer_day); + pthread_cancel(scorer_hour); + pthread_cancel(scorer_week); + pthread_cancel(scorer_month); + pthread_cancel(replenisher); + + LOG("%H joining services...\n"); + unassert(!pthread_join(scorer, 0)); + unassert(!pthread_join(recenter, 0)); + unassert(!pthread_join(g_listener, 0)); + unassert(!pthread_join(scorer_day, 0)); + unassert(!pthread_join(scorer_hour, 0)); + unassert(!pthread_join(scorer_week, 0)); + unassert(!pthread_join(scorer_month, 0)); + unassert(!pthread_join(replenisher, 0)); // cancel read() so that keepalive clients finish faster - LOG("Interrupting workers...\n"); - for (int i = 0; i < g_workers; ++i) { - pthread_kill(g_worker[i].th, SIGUSR1); - } + LOG("%H interrupting workers...\n"); + for (int i = 0; i < g_workers; ++i) + if (!g_worker[i].dead) + pthread_cancel(g_worker[i].th); // wait for producers to finish - LOG("Waiting for workers to finish...\n"); - for (int i = 0; i < g_workers; ++i) { - CHECK_EQ(0, pthread_join(g_worker[i].th, 0)); - } - LOG("Waiting for helpers to finish...\n"); - CHECK_EQ(0, pthread_join(nower, 0)); - CHECK_EQ(0, pthread_join(scorer, 0)); - CHECK_EQ(0, pthread_join(plotter, 0)); - CHECK_EQ(0, pthread_join(recenter, 0)); - CHECK_EQ(0, pthread_join(scorer_day, 0)); - CHECK_EQ(0, pthread_join(scorer_hour, 0)); - CHECK_EQ(0, pthread_join(scorer_week, 0)); - CHECK_EQ(0, pthread_join(scorer_month, 0)); - CHECK_EQ(0, pthread_join(replenisher, 0)); + LOG("%H joining workers...\n"); + for (int i = 0; i < g_workers; ++i) + unassert(!pthread_join(g_worker[i].th, 0)); // now that all workers have terminated, the claims queue must be // empty, therefore, it is now safe to send a cancellation to the // claims worker thread which waits forever for new claims. - CHECK_EQ(0, g_claims.count); - LOG("waiting for claims worker...\n"); - nsync_note_notify(g_shutdown[2]); - CHECK_EQ(0, pthread_join(claimer, 0)); + unassert(!g_claims.count); + pthread_cancel(claimer); + LOG("%H waiting for claims worker...\n"); + unassert(!pthread_join(claimer, 0)); // perform some sanity checks - CHECK_EQ(g_claimsprocessed, g_claimsenqueued); + unassert(g_claimsprocessed == g_claimsenqueued); // free memory - LOG("Freeing memory...\n"); + LOG("%H freeing memory...\n"); FreeAsset(&g_asset.user); FreeAsset(&g_asset.about); FreeAsset(&g_asset.index); @@ -2110,13 +2262,11 @@ int main(int argc, char *argv[]) { FreeAsset(&g_asset.score_month); FreeAsset(&g_asset.recent); FreeAsset(&g_asset.favicon); - for (int i = 0; i < ARRAYLEN(g_shutdown); ++i) { - nsync_note_free(g_shutdown[i]); - } - nsync_counter_free(g_ready); free(g_worker); free(g_tok.b); - LOG("Goodbye\n"); + time_destroy(); + + LOG("%H goodbye\n"); // CheckForMemoryLeaks(); } diff --git a/test/libc/calls/pledge_test.c b/test/libc/calls/pledge_test.c index d53886841..71d600834 100644 --- a/test/libc/calls/pledge_test.c +++ b/test/libc/calls/pledge_test.c @@ -109,12 +109,15 @@ TEST(pledge, execpromises_notok) { int ws, pid; ASSERT_NE(-1, (pid = fork())); if (!pid) { + putenv("COMDBG=REDACTED"); __pledge_mode = PLEDGE_PENALTY_RETURN_EPERM; ASSERT_SYS(0, 0, pledge("stdio rpath exec", "stdio")); execl("sock.elf", "sock.elf", 0); _Exit(127); } EXPECT_NE(-1, wait(&ws)); + EXPECT_FALSE(WIFSIGNALED(ws)); + EXPECT_EQ(0, WTERMSIG(ws)); EXPECT_TRUE(WIFEXITED(ws)); EXPECT_EQ(129, WEXITSTATUS(ws)); } @@ -532,6 +535,7 @@ TEST(pledge, execpromises_ok) { int ws, pid; ASSERT_NE(-1, (pid = fork())); if (!pid) { + putenv("COMDBG=REDACTED"); ASSERT_SYS(0, 0, pledge("stdio exec", "stdio")); execl("life.elf", "life.elf", 0); _Exit(127); @@ -547,6 +551,7 @@ TEST(pledge, execpromises_notok1) { int ws, pid; ASSERT_NE(-1, (pid = fork())); if (!pid) { + putenv("COMDBG=REDACTED"); ASSERT_SYS(0, 0, pledge("stdio exec", "stdio")); execl("sock.elf", "sock.elf", 0); _Exit(127); @@ -562,6 +567,7 @@ TEST(pledge, execpromises_reducesAtExecOnLinux) { int ws, pid; ASSERT_NE(-1, (pid = fork())); if (!pid) { + putenv("COMDBG=REDACTED"); ASSERT_SYS(0, 0, pledge("stdio inet tty exec", "stdio tty")); execl("sock.elf", "sock.elf", 0); _Exit(127); @@ -619,6 +625,7 @@ TEST(pledge_openbsd, execpromises_notok) { int ws, pid; ASSERT_NE(-1, (pid = fork())); if (!pid) { + putenv("COMDBG=REDACTED"); ASSERT_SYS(0, 0, pledge("stdio exec", "stdio")); execl("sock.elf", "sock.elf", 0); _Exit(127); diff --git a/third_party/dlmalloc/threaded.inc b/third_party/dlmalloc/threaded.inc index f6664b653..e8768dbc3 100644 --- a/third_party/dlmalloc/threaded.inc +++ b/third_party/dlmalloc/threaded.inc @@ -25,6 +25,7 @@ #include "libc/thread/thread.h" #include "libc/thread/threads.h" #include "libc/errno.h" +#include "libc/calls/struct/cpuset.h" #include "third_party/dlmalloc/dlmalloc.h" #if !FOOTERS || !MSPACES diff --git a/third_party/lua/lua.main.c b/third_party/lua/lua.main.c index 0672809be..9a0cee129 100644 --- a/third_party/lua/lua.main.c +++ b/third_party/lua/lua.main.c @@ -50,6 +50,7 @@ #include "third_party/lua/lrepl.h" #include "third_party/lua/lualib.h" #include "third_party/lua/lunix.h" +#include "libc/cosmo.h" #include "libc/mem/leaks.h" __static_yoink("lua_notice"); diff --git a/third_party/nsync/common.c b/third_party/nsync/common.c index d5427a3be..31f7519e4 100644 --- a/third_party/nsync/common.c +++ b/third_party/nsync/common.c @@ -15,6 +15,7 @@ │ See the License for the specific language governing permissions and │ │ limitations under the License. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/atomic.h" #include "libc/calls/calls.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" @@ -136,14 +137,45 @@ waiter *nsync_dll_waiter_samecond_ (struct Dll *e) { /* -------------------------------- */ -static _Atomic(waiter *) free_waiters; +#define MASQUE 0x00fffffffffffff8 +#define PTR(x) ((uintptr_t)(x) & MASQUE) +#define TAG(x) ROL((uintptr_t)(x) & ~MASQUE, 8) +#define ABA(p, t) ((uintptr_t)(p) | (ROR((uintptr_t)(t), 8) & ~MASQUE)) +#define ROL(x, n) (((x) << (n)) | ((x) >> (64 - (n)))) +#define ROR(x, n) (((x) >> (n)) | ((x) << (64 - (n)))) + +static atomic_uintptr_t free_waiters; static void free_waiters_push (waiter *w) { - int backoff = 0; - w->next_free = atomic_load_explicit (&free_waiters, memory_order_relaxed); - while (!atomic_compare_exchange_weak_explicit (&free_waiters, &w->next_free, w, - memory_order_acq_rel, memory_order_relaxed)) - backoff = pthread_delay_np (free_waiters, backoff); + uintptr_t tip; + ASSERT (!TAG(w)); + tip = atomic_load_explicit (&free_waiters, memory_order_relaxed); + for (;;) { + w->next_free = (waiter *) PTR (tip); + if (atomic_compare_exchange_weak_explicit (&free_waiters, + &tip, + ABA (w, TAG (tip) + 1), + memory_order_release, + memory_order_relaxed)) + break; + pthread_pause_np (); + } +} + +static waiter *free_waiters_pop (void) { + waiter *w; + uintptr_t tip; + tip = atomic_load_explicit (&free_waiters, memory_order_relaxed); + while ((w = (waiter *) PTR (tip))) { + if (atomic_compare_exchange_weak_explicit (&free_waiters, + &tip, + ABA (w->next_free, TAG (tip) + 1), + memory_order_acquire, + memory_order_relaxed)) + break; + pthread_pause_np (); + } + return w; } static void free_waiters_populate (void) { @@ -172,30 +204,12 @@ static void free_waiters_populate (void) { } w->nw.sem = &w->sem; dll_init (&w->nw.q); - NSYNC_ATOMIC_UINT32_STORE_ (&w->nw.waiting, 0); w->nw.flags = NSYNC_WAITER_FLAG_MUCV; - ATM_STORE (&w->remove_count, 0); dll_init (&w->same_condition); - w->flags = 0; free_waiters_push (w); } } -static waiter *free_waiters_pop (void) { - waiter *w; - int backoff = 0; - for (;;) { - if ((w = atomic_load_explicit (&free_waiters, memory_order_relaxed))) { - if (atomic_compare_exchange_weak_explicit (&free_waiters, &w, w->next_free, - memory_order_acq_rel, memory_order_relaxed)) - return w; - backoff = pthread_delay_np (free_waiters, backoff); - } else { - free_waiters_populate (); - } - } -} - /* -------------------------------- */ #define waiter_for_thread __get_tls()->tib_nsync @@ -221,7 +235,8 @@ waiter *nsync_waiter_new_ (void) { tw = waiter_for_thread; w = tw; if (w == NULL || (w->flags & (WAITER_RESERVED|WAITER_IN_USE)) != WAITER_RESERVED) { - w = free_waiters_pop (); + while (!(w = free_waiters_pop ())) + free_waiters_populate (); if (tw == NULL) { w->flags |= WAITER_RESERVED; waiter_for_thread = w; diff --git a/third_party/nsync/mem/nsync_cv.c b/third_party/nsync/mem/nsync_cv.c index 8e363f77c..a85fb67d2 100644 --- a/third_party/nsync/mem/nsync_cv.c +++ b/third_party/nsync/mem/nsync_cv.c @@ -233,7 +233,9 @@ static int nsync_cv_wait_with_deadline_impl_ (struct nsync_cv_wait_with_deadline /* Requeue on *pmu using existing waiter struct; current thread is the designated waker. */ nsync_mu_lock_slow_ (c->cv_mu, c->w, MU_DESIG_WAKER, c->w->l_type); + nsync_waiter_free_ (c->w); } else { + nsync_waiter_free_ (c->w); /* Traditional case: We've woken from the cv, and need to reacquire *pmu. */ if (c->is_reader_mu) { nsync_mu_rlock (c->cv_mu); @@ -241,7 +243,6 @@ static int nsync_cv_wait_with_deadline_impl_ (struct nsync_cv_wait_with_deadline (*c->lock) (c->pmu); } } - nsync_waiter_free_ (c->w); IGNORE_RACES_END (); return (outcome); } diff --git a/third_party/nsync/mu_semaphore_sem.c b/third_party/nsync/mu_semaphore_sem.c index 9b25ae7a6..b821dd08c 100644 --- a/third_party/nsync/mu_semaphore_sem.c +++ b/third_party/nsync/mu_semaphore_sem.c @@ -52,11 +52,11 @@ static nsync_semaphore *sem_big_enough_for_sem = (nsync_semaphore *) (uintptr_t) (sizeof (struct sem) <= sizeof (*sem_big_enough_for_sem))); static void sems_push (struct sem *f) { - int backoff = 0; f->next = atomic_load_explicit (&g_sems, memory_order_relaxed); while (!atomic_compare_exchange_weak_explicit (&g_sems, &f->next, f, - memory_order_acq_rel, memory_order_relaxed)) - backoff = pthread_delay_np (&g_sems, backoff); + memory_order_acq_rel, + memory_order_relaxed)) + pthread_pause_np (); } static bool nsync_mu_semaphore_sem_create (struct sem *f) { diff --git a/tool/cosmocc/README.md b/tool/cosmocc/README.md index 5edf76573..532cdcf83 100644 --- a/tool/cosmocc/README.md +++ b/tool/cosmocc/README.md @@ -417,7 +417,7 @@ statements instead, so that Cosmopolitan Libc's system constants will work as expected. Our modifications to GNU GCC are published under the ISC license at . The binaries you see here were first published at - which + which is regularly updated. ## Legal diff --git a/tool/cosmocc/bin/cosmocc b/tool/cosmocc/bin/cosmocc index 1cbb13ff1..b07dc1be5 100755 --- a/tool/cosmocc/bin/cosmocc +++ b/tool/cosmocc/bin/cosmocc @@ -75,6 +75,15 @@ elif [ ! -d "$TMPDIR" ]; then fi fi +CLANG=0 +CC_X86_64="$BIN/x86_64-linux-cosmo-gcc" +CC_AARCH64="$BIN/aarch64-linux-cosmo-gcc" +CXX_X86_64="$BIN/x86_64-linux-cosmo-g++" +CXX_AARCH64="$BIN/aarch64-linux-cosmo-g++" +FPORTCOSMO="-fportcosmo" +TARGET_X86_64= +TARGET_AARCH64= + X= OPT= ARGS= @@ -93,6 +102,7 @@ FLAGS_AARCH64= INPUT_FILE_COUNT=0 DEPENDENCY_OUTPUT= NEED_DEPENDENCY_OUTPUT= + for x; do if [ x"$x" != x"${x#* }" ]; then fatal_error "arguments containing spaces unsupported: $x" @@ -185,6 +195,16 @@ EOF elif [ x"$x" = x"-moptlinux" ]; then MODE=optlinux continue + elif [ x"$x" = x"-mclang" ]; then + CLANG=1 + CC_X86_64="$BIN/cosmo-clang" + CC_AARCH64="$BIN/cosmo-clang" + CXX_X86_64="$BIN/cosmo-clang++" + CXX_AARCH64="$BIN/cosmo-clang++" + TARGET_X86_64="--target=x86_64" + TARGET_AARCH64="--target=aarch64" + FPORTCOSMO= + continue elif [ x"$x" = x"-m64" ]; then continue elif [ x"$x" = x"-fomit-frame-pointer" ]; then @@ -298,7 +318,7 @@ fi PLATFORM="-D__COSMOPOLITAN__ -D__COSMOCC__ -D__FATCOSMOCC__" PREDEF="-include libc/integral/normalize.inc" CPPFLAGS="-fno-pie -nostdinc -isystem $BIN/../include" -CFLAGS="-fportcosmo -fno-dwarf2-cfi-asm -fno-unwind-tables -fno-asynchronous-unwind-tables -fno-semantic-interposition" +CFLAGS="$FPORTCOSMO -fno-dwarf2-cfi-asm -fno-unwind-tables -fno-asynchronous-unwind-tables -fno-semantic-interposition" LDFLAGS="-static -nostdlib -no-pie -fuse-ld=bfd -Wl,-z,noexecstack -Wl,-z,norelro -Wl,--gc-sections" PRECIOUS="-fno-omit-frame-pointer" @@ -307,7 +327,9 @@ if [ x"$OPT" != x"-Os" ] && [ x"$MODE" != x"tiny" ] && [ x"$MODE" != x"optlinux" CFLAGS="$CFLAGS -fno-optimize-sibling-calls -mno-omit-leaf-frame-pointer" fi if [ x"$OPT" != x"-O3" ] && [ x"$MODE" != x"optlinux" ]; then - CFLAGS="$CFLAGS -fno-schedule-insns2" + if [ $CLANG -eq 0 ]; then + CFLAGS="$CFLAGS -fno-schedule-insns2" + fi fi if [ x"$X" = x"c" ] || [ x"$X" = x"c-header" ]; then @@ -320,11 +342,9 @@ else CPLUSPLUS=0 fi -CC_X86_64="$BIN/x86_64-linux-cosmo-gcc" -CC_AARCH64="$BIN/aarch64-linux-cosmo-gcc" if [ $CPLUSPLUS -eq 1 ]; then - CC_X86_64="$BIN/x86_64-linux-cosmo-g++" - CC_AARCH64="$BIN/aarch64-linux-cosmo-g++" + CC_X86_64=$CXX_X86_64 + CC_AARCH64=$CXX_AARCH64 if [ $INTENT != cpp ]; then CFLAGS="$CFLAGS -fno-rtti -fno-exceptions -fuse-cxa-atexit" fi @@ -461,6 +481,7 @@ build_object() { ( set -- \ "$CC_X86_64" \ + $TARGET_X86_64 \ -o"$OUTPUT_X86_64" \ $PLATFORM \ $PREDEF \ @@ -482,6 +503,7 @@ build_object() { ( set -- \ "$CC_AARCH64" \ + $TARGET_AARCH64 \ -o"$OUTPUT_AARCH64" \ $PLATFORM \ $PREDEF \ @@ -588,6 +610,7 @@ TEMP_FILES="${TEMP_FILES} $out2" ( set -- \ "$CC_X86_64" \ + $TARGET_X86_64 \ -o"$OUTPUT_X86_64"\ $CRT_X86_64 \ $LDFLAGS_X86_64 \ @@ -605,6 +628,7 @@ pid1=$! ( set -- \ "$CC_AARCH64" \ + $TARGET_AARCH64 \ -o"$OUTPUT_AARCH64"\ $CRT_AARCH64 \ $LDFLAGS_AARCH64 \ diff --git a/tool/cosmocc/package.sh b/tool/cosmocc/package.sh index 17635f8de..207b3ca7a 100755 --- a/tool/cosmocc/package.sh +++ b/tool/cosmocc/package.sh @@ -182,12 +182,17 @@ fetch() { OLD=$PWD cd "$OUTDIR/" if [ ! -x bin/x86_64-linux-cosmo-gcc ]; then - fetch https://github.com/ahgamut/superconfigure/releases/download/z0.0.52/aarch64-gcc.zip - unzip aarch64-gcc.zip + fetch https://github.com/ahgamut/superconfigure/releases/download/z0.0.53/aarch64-gcc.zip & + fetch https://github.com/ahgamut/superconfigure/releases/download/z0.0.53/x86_64-gcc.zip & + fetch https://github.com/ahgamut/superconfigure/releases/download/z0.0.53/llvm.zip & + wait + unzip aarch64-gcc.zip & + unzip x86_64-gcc.zip & + unzip llvm.zip bin/clang-18 & + wait rm -f aarch64-gcc.zip - fetch https://github.com/ahgamut/superconfigure/releases/download/z0.0.52/x86_64-gcc.zip - unzip x86_64-gcc.zip rm -f x86_64-gcc.zip + mv bin/clang-18 bin/cosmo-clang fi rm -f bin/*-cpp rm -f bin/*-gcc-* diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 1bfbc64d9..eeb2e3116 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -31,6 +31,7 @@ #include "libc/calls/struct/termios.h" #include "libc/calls/struct/timespec.h" #include "libc/calls/termios.h" +#include "libc/cosmo.h" #include "libc/ctype.h" #include "libc/dce.h" #include "libc/dos.h" diff --git a/tool/viz/malloc_scalability.c b/tool/viz/malloc_scalability.c index 434be2123..cf48d345b 100644 --- a/tool/viz/malloc_scalability.c +++ b/tool/viz/malloc_scalability.c @@ -19,6 +19,7 @@ #include "libc/calls/struct/timespec.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" #include "libc/thread/thread.h" #define ALLOCATIONS 1000