diff --git a/libc/README.md b/libc/README.md index 184c406e2..274ff4bf1 100644 --- a/libc/README.md +++ b/libc/README.md @@ -1,50 +1,46 @@ -SYNOPSIS +# Cosmopolitan Standard Library - Cosmopolitan Standard Library. +This directory defines static archives defining functions, like +`printf()`, `mmap()`, win32, etc. Please note that the Cosmopolitan +build configuration doesn't link any C/C++ library dependencies +by default, so you still have the flexibility to choose the one +provided by your system. If you'd prefer Cosmopolitan, just add +`$(LIBC)` and `$(CRT)` to your linker arguments. -OVERVIEW +Your library is compromised of many bite-sized static archives. +We use the checkdeps tool to guarantee that the contents of the +archives are organized in a logical way that's easy to use with +or without our makefile infrastructure, since there's no cyclic +dependencies. - This directory defines static archives defining functions, like - printf(), mmap(), win32, etc. Please note that the Cosmopolitan - build configuration doesn't link any C/C++ library dependencies - by default, so you still have the flexibility to choose the one - provided by your system. If you'd prefer Cosmopolitan, just add - $(LIBC) and $(CRT) to your linker arguments. +The Cosmopolitan Library exports only the most stable canonical +system calls for all supported operating systems, regardless of +which platform is used for compilation. We polyfill many of the +APIs, e.g. `read()`, `write()` so they work consistently everywhere +while other apis, e.g. `CreateWindowEx()`, might only work on one +platform, in which case they become no-op functions on others. - Your library is compromised of many bite-sized static archives. - We use the checkdeps tool to guarantee that the contents of the - archives are organized in a logical way that's easy to use with - or without our makefile infrastructure, since there's no cyclic - dependencies. +Cosmopolitan polyfill wrappers will usually use the dollar sign +naming convention, so they may be bypassed when necessary. This +same convention is used when multiple implementations of string +library and other performance-critical function are provided to +allow Cosmopolitan to go fast on both old and newer computers. - The Cosmopolitan Library exports only the most stable canonical - system calls for all supported operating systems, regardless of - which platform is used for compilation. We polyfill many of the - APIs, e.g. read(), write() so they work consistently everywhere - while other apis, e.g. CreateWindowEx(), might only work on one - platform, in which case they become no-op functions on others. +We take an approach to configuration that relies heavily on the +compiler's dead code elimination pass (`libc/dce.h`). Most of the +code is written so that, for example, folks not wanting support +for OpenBSD can flip a bit in `SUPPORT_VECTOR` and that code will +be omitted from the build. The same is true for builds that are +tuned using `-march=native` which effectively asks the library to +not include runtime support hooks for x86 processors older than +what you use. - Cosmopolitan polyfill wrappers will usually use the dollar sign - naming convention, so they may be bypassed when necessary. This - same convention is used when multiple implementations of string - library and other performance-critical function are provided to - allow Cosmopolitan to go fast on both old and newer computers. - - We take an approach to configuration that relies heavily on the - compiler's dead code elimination pass (libc/dce.h). Most of the - code is written so that, for example, folks not wanting support - for OpenBSD can flip a bit in SUPPORT_VECTOR and that code will - be omitted from the build. The same is true for builds that are - tuned using -march=native which effectively asks the library to - not include runtime support hooks for x86 processors older than - what you use. - - Please note that, unlike Cygwin or MinGW, Cosmopolitan does not - achieve broad support by bolting on a POSIX emulation layer. We - do nothing more than (in most cases) stateless API translations - that get you 90% of the way there in a fast lightweight manner. - We therefore can't address some of the subtle differences, such - as the nuances of absolute paths on Windows. Our approach could - be compared to something more along the lines of, "the Russians - just used a pencil to write in space", versus spending millions - researching a pen like NASA. +Please note that, unlike Cygwin or MinGW, Cosmopolitan does not +achieve broad support by bolting on a POSIX emulation layer. We +do nothing more than (in most cases) stateless API translations +that get you 90% of the way there in a fast lightweight manner. +We therefore can't address some of the subtle differences, such +as the nuances of absolute paths on Windows. Our approach could +be compared to something more along the lines of, "the Russians +just used a pencil to write in space", versus spending millions +researching a pen like NASA. diff --git a/libc/intrin/pthread.h b/libc/intrin/pthread.h index 53737dc87..258564574 100644 --- a/libc/intrin/pthread.h +++ b/libc/intrin/pthread.h @@ -121,7 +121,7 @@ int pthread_rwlock_unlock(pthread_rwlock_t *); #define pthread_mutex_unlock(mutex) \ ((mutex)->attr == PTHREAD_MUTEX_NORMAL \ ? (atomic_store_explicit(&(mutex)->lock, 0, memory_order_relaxed), \ - (IsLinux() && \ + ((IsLinux() || IsOpenbsd()) && \ atomic_load_explicit(&(mutex)->waits, memory_order_relaxed) && \ _pthread_mutex_wake(mutex)), \ 0) \ diff --git a/libc/intrin/pthread_mutex_lock.c b/libc/intrin/pthread_mutex_lock.c index 0df55a822..1becaa669 100644 --- a/libc/intrin/pthread_mutex_lock.c +++ b/libc/intrin/pthread_mutex_lock.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/asmflag.h" #include "libc/bits/atomic.h" #include "libc/calls/calls.h" #include "libc/dce.h" @@ -24,16 +25,41 @@ #include "libc/intrin/spinlock.h" #include "libc/linux/futex.h" #include "libc/nexgen32e/threaded.h" +#include "libc/sysv/consts/futex.h" +#include "libc/sysv/consts/nr.h" + +static inline int FutexWait(void *addr, int expect, struct timespec *timeout) { + int ax; + bool cf; + asm volatile(CFLAG_ASM("mov\t%6,%%r10\n\t" + "clc\n\t" + "syscall") + : CFLAG_CONSTRAINT(cf), "=a"(ax) + : "1"(__NR_futex), "D"(addr), "S"(FUTEX_WAIT), "d"(expect), + "g"(timeout) + : "rcx", "r10", "r11", "memory"); + if (cf) ax = -ax; + return ax; +} static int pthread_mutex_lock_spin(pthread_mutex_t *mutex, int tries) { volatile int i; + struct timespec ts; if (tries < 7) { for (i = 0; i != 1 << tries; i++) { } tries++; - } else if (IsLinux()) { + } else if (IsLinux() || IsOpenbsd()) { atomic_fetch_add(&mutex->waits, 1); - LinuxFutexWait(&mutex->lock, 1, 0); + if (tries < 28) { + ts.tv_sec = 0; + ts.tv_nsec = 4 << tries; + tries++; + } else { + ts.tv_sec = 1; + ts.tv_nsec = 0; + } + FutexWait(&mutex->lock, 1, &ts); atomic_fetch_sub(&mutex->waits, 1); } else { sched_yield(); diff --git a/libc/intrin/pthread_mutex_unlock.c b/libc/intrin/pthread_mutex_unlock.c index 36fc36f94..726cf72ab 100644 --- a/libc/intrin/pthread_mutex_unlock.c +++ b/libc/intrin/pthread_mutex_unlock.c @@ -40,7 +40,7 @@ int(pthread_mutex_unlock)(pthread_mutex_t *mutex) { // fallthrough case PTHREAD_MUTEX_NORMAL: atomic_store_explicit(&mutex->lock, 0, memory_order_relaxed); - if (IsLinux() && + if ((IsLinux() || IsOpenbsd()) && atomic_load_explicit(&mutex->waits, memory_order_relaxed)) { _pthread_mutex_wake(mutex); } diff --git a/libc/intrin/pthread_mutex_wake.c b/libc/intrin/pthread_mutex_wake.c index e5866d79c..aea4699ef 100644 --- a/libc/intrin/pthread_mutex_wake.c +++ b/libc/intrin/pthread_mutex_wake.c @@ -16,11 +16,23 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/atomic.h" -#include "libc/dce.h" +#include "libc/bits/asmflag.h" #include "libc/intrin/pthread.h" -#include "libc/linux/futex.h" +#include "libc/sysv/consts/futex.h" +#include "libc/sysv/consts/nr.h" + +static inline int FutexWake(void *addr, int count) { + int ax; + bool cf; + asm volatile(CFLAG_ASM("clc\n\t" + "syscall") + : CFLAG_CONSTRAINT(cf), "=a"(ax) + : "1"(__NR_futex), "D"(addr), "S"(FUTEX_WAKE), "d"(count) + : "rcx", "r11", "memory"); + if (cf) ax = -ax; + return ax; +} int _pthread_mutex_wake(pthread_mutex_t *mutex) { - return LinuxFutexWake(&mutex->lock, 1); + return FutexWake(&mutex->lock, 1); } diff --git a/libc/runtime/printargs.greg.c b/libc/runtime/printargs.greg.c index 807a8297c..4f3fb22d1 100644 --- a/libc/runtime/printargs.greg.c +++ b/libc/runtime/printargs.greg.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/strace.internal.h" +#include "libc/calls/struct/rlimit.h" #include "libc/calls/struct/sigset.h" #include "libc/calls/struct/termios.h" #include "libc/calls/struct/utsname.h" @@ -49,6 +50,7 @@ #include "libc/sysv/consts/auxv.h" #include "libc/sysv/consts/f.h" #include "libc/sysv/consts/poll.h" +#include "libc/sysv/consts/rlim.h" #include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/termios.h" #include "tool/decode/lib/idname.h" @@ -140,6 +142,7 @@ textstartup void __printargs(const char *prologue) { unsigned i, n; int e, x, flags; uintptr_t *auxp; + struct rlimit rlim; struct utsname uts; struct termios termios; struct AuxiliaryValue *auxinfo; @@ -276,6 +279,17 @@ textstartup void __printargs(const char *prologue) { PRINT(" error: sigprocmask() failed %m"); } + PRINT(""); + PRINT("RESOURCE LIMITS"); + for (i = 0; i < RLIM_NLIMITS; ++i) { + if (!getrlimit(i, &rlim)) { + if (rlim.rlim_cur == RLIM_INFINITY) rlim.rlim_cur = -1; + if (rlim.rlim_max == RLIM_INFINITY) rlim.rlim_max = -1; + PRINT(" ☼ %-20s %,16ld %,16ld", DescribeRlimitName(i), rlim.rlim_cur, + rlim.rlim_max); + } + } + PRINT(""); PRINT("ARGUMENTS (%p)", __argv); if (*__argv) { diff --git a/libc/testlib/testmain.c b/libc/testlib/testmain.c index 1e5b8f65e..ecfb75277 100644 --- a/libc/testlib/testmain.c +++ b/libc/testlib/testmain.c @@ -103,8 +103,9 @@ static void EmptySignalMask(void) { } static void FixIrregularFds(void) { - int i, fd; - struct pollfd pfds[64]; + int i, fd, maxfds; + struct rlimit rlim; + struct pollfd *pfds; for (i = 0; i < 3; ++i) { if (fcntl(i, F_GETFL) == -1) { errno = 0; @@ -115,7 +116,16 @@ static void FixIrregularFds(void) { } } } - for (i = 0; i < ARRAYLEN(pfds); ++i) { + if (IsWindows()) { + maxfds = 64; + } else { + maxfds = 256; + if (!getrlimit(RLIMIT_NOFILE, &rlim)) { + maxfds = MIN(maxfds, (uint64_t)rlim.rlim_cur); + } + } + pfds = malloc(maxfds * sizeof(struct pollfd)); + for (i = 0; i < maxfds; ++i) { pfds[i].fd = i + 3; pfds[i].events = POLLIN; } @@ -123,12 +133,13 @@ static void FixIrregularFds(void) { // TODO(jart): Fix Blinkenlights poll() / close() return; } - if (poll(pfds, ARRAYLEN(pfds), 0) != -1) { - for (i = 0; i < ARRAYLEN(pfds); ++i) { + if (poll(pfds, maxfds, 0) != -1) { + for (i = 0; i < maxfds; ++i) { if (pfds[i].revents & POLLNVAL) continue; CHECK_EQ(0, close(pfds[i].fd)); } } + free(pfds); } static void SetLimit(int resource, uint64_t soft, uint64_t hard) { diff --git a/third_party/chibicc/as.c b/third_party/chibicc/as.c index 079ba23d3..bb36992bc 100644 --- a/third_party/chibicc/as.c +++ b/third_party/chibicc/as.c @@ -2328,6 +2328,14 @@ static dontinline void OpBsu(struct As *a, struct Slice opname, int op) { OpBsuImpl(a, opname, op); } +static dontinline void OpXadd(struct As *a) { + int reg, modrm, disp; + reg = GetRegisterReg(a); + ConsumeComma(a); + modrm = ParseModrm(a, &disp); + EmitRexOpModrm(a, 0x0FC0, reg, modrm, disp, 1); +} + static dontinline int OpF6Impl(struct As *a, struct Slice s, int reg) { int modrm, imm, disp; modrm = ParseModrm(a, &disp); @@ -3109,6 +3117,7 @@ static void OnUcomiss(struct As *a, struct Slice s) { OpSse(a, 0x0F2E); } static void OnUd2(struct As *a, struct Slice s) { EmitVarword(a, 0x0F0B); } static void OnUnpckhpd(struct As *a, struct Slice s) { OpSse(a, 0x660F15); } static void OnUnpcklpd(struct As *a, struct Slice s) { OpSse(a, 0x660F14); } +static void OnXadd(struct As *a, struct Slice s) { OpXadd(a); } static void OnXor(struct As *a, struct Slice s) { OpAlu(a, s, 6); } static void OnXorpd(struct As *a, struct Slice s) { OpSse(a, 0x660F57); } static void OnXorps(struct As *a, struct Slice s) { OpSse(a, 0x0F57); } @@ -3662,6 +3671,7 @@ static const struct Directive8 { {"unpckhpd", OnUnpckhpd}, // {"unpcklpd", OnUnpcklpd}, // {"wait", OnFwait}, // + {"xadd", OnXadd}, // {"xchg", OnXchg}, // {"xor", OnXor}, // {"xorb", OnXor}, // diff --git a/third_party/chibicc/test/atomic_test.c b/third_party/chibicc/test/atomic_test.c index 537b2367c..8f78dcb55 100644 --- a/third_party/chibicc/test/atomic_test.c +++ b/third_party/chibicc/test/atomic_test.c @@ -37,5 +37,18 @@ main() { ASSERT(4, x); ASSERT(4, lock); + // CAS success #3 + x = 4; + ASSERT(1, __atomic_compare_exchange_n(&lock, &x, 31337, 0, __ATOMIC_SEQ_CST, + __ATOMIC_SEQ_CST)); + ASSERT(4, x); + ASSERT(31337, lock); + + // xadd + ASSERT(31337, __atomic_fetch_add(&lock, 31337, __ATOMIC_SEQ_CST)); + ASSERT(62674, lock); + ASSERT(62674, __atomic_fetch_sub(&lock, 31337, __ATOMIC_SEQ_CST)); + ASSERT(31337, lock); + // }