From a80eea78fe67d076653dc2d93f4ce2dc083256da Mon Sep 17 00:00:00 2001 From: Gavin Hayes Date: Wed, 21 Sep 2022 22:43:07 -0400 Subject: [PATCH] Squashed commit of the following: commit d9e0429dec42bf786aa73a66c259c275be5a8de9 Author: Gavin Hayes Date: Wed Sep 21 22:41:59 2022 -0400 Revert "Don't relocate file descriptor memory" This reverts commit 3265324e00634e8e9d659ba705da5b0ce88e78ef. commit 22ce7f16eeb326408208fabee57eace63371352c Author: Gavin Hayes Date: Wed Sep 21 22:41:58 2022 -0400 Revert "Make some minor touchups for nightlies" This reverts commit e9272f03fbdadee8ced8670514ec5f987c47bbd2. commit 8ef0057111e955fa18f2cce77b36046fa96fdcd5 Author: Gavin Hayes Date: Wed Sep 21 22:41:57 2022 -0400 Revert "Tidy up the threading implementation" This reverts commit 155b378a3962e4d291f6af577eafa1f7267b6198. commit 7e99e6e08b14003df21d42bedf473219417d3610 Author: Gavin Hayes Date: Wed Sep 21 22:41:56 2022 -0400 Revert "Bare metal VGA: minor improvements (#608)" This reverts commit bae7367774c946f81f8d53b8c3f298dd007b974f. commit cd184ff7fe4f8539a3928e8f2b8703e699c199e4 Author: Gavin Hayes Date: Wed Sep 21 22:41:55 2022 -0400 Revert "Rewrite recursive mutex code" This reverts commit cfcf5918bcce92fbf66c0d6546223ee8097f88c6. commit 4aa26c44ee200ffbcb591b3145876095fa235bd3 Author: Gavin Hayes Date: Wed Sep 21 22:41:54 2022 -0400 Revert "Clean up the TLS code" This reverts commit 333768440c2d6e3b63d30b03eac3595702de2a3c. commit f8c989127c7b8821e2183858c321fcd54fc45ffe Author: Gavin Hayes Date: Wed Sep 21 22:41:52 2022 -0400 Revert "Remove stdio lock macros from amalgamation" This reverts commit cdb2284f0d2ed7887ba26ebf892d5252faf8c0f8. commit d10cd5c40f576a2dde8448338d2e9b3d88e160f8 Author: Gavin Hayes Date: Wed Sep 21 22:41:50 2022 -0400 Revert "Import nsync" This reverts commit 5dff63a31ced854822c24e3dbaac9bf50a0f4923. commit 2a49d50c29f518367e0a51cb7a0a2b4b0a1679f0 Author: Gavin Hayes Date: Wed Sep 21 22:41:49 2022 -0400 Revert "Fix the build" This reverts commit 3de35e196cfcf6163547db92b668837786687866. commit c258d09ee5a7f48cd3d0d71667aba6fa7ab0e9ee Author: Gavin Hayes Date: Wed Sep 21 22:41:49 2022 -0400 Revert "Use *NSYNC for POSIX threads locking APIs" This reverts commit b5cb71ab84e1978fdda72fa29679979c414f109a. commit 43bf84f09737bf87d79c7650dde52e030556056d Author: Gavin Hayes Date: Wed Sep 21 22:41:48 2022 -0400 Revert "Add *NSYNC mu_test" This reverts commit 9b89a7c51d804c70afebfa37172bf95eba57be83. commit 94a0e96d666f7cf2dd4af03b42516956908f4ad7 Author: Gavin Hayes Date: Wed Sep 21 22:41:47 2022 -0400 Revert "Don't pushpop in long mode without stack (#611)" This reverts commit 569c0319349df520bc9377c9ec36fc5ca21c6edd. commit 55f9f15c8659f6956ac4c9e0334b7884f5ff1c01 Author: Gavin Hayes Date: Wed Sep 21 22:41:46 2022 -0400 Revert "Update redbean doc to correct return values (#610)" This reverts commit 55ddf73484d06aa7572e81c94f64b1b8729c9300. commit 30865675f4c750d1fec64df9b7bf98e6132787a9 Author: Gavin Hayes Date: Wed Sep 21 22:41:45 2022 -0400 Revert "Left-align stub arguments to dd (fixes #374) (#598)" This reverts commit 0305194d9854556e2bbef1fdcfda6e76910b1baf. commit 01e8de146f8de3abb1fe8cf2c6e7054a68be411e Author: Gavin Hayes Date: Wed Sep 21 22:41:43 2022 -0400 Revert "Add Vista branch to GitHub workflows" This reverts commit c03359c637293171b7d62ba1c7efee56e82b7538. commit 66966f8ae2d020e7c3e8f1ed20eddd9b155f9ac4 Author: Gavin Hayes Date: Wed Sep 21 22:41:43 2022 -0400 Revert "[WIP] Get bare metal working outside of an emulator (#609)" This reverts commit 116bda997e4765eb2b8acbecb57fe367c87f8553. commit 9ab0952c35086ac6c3c1dc186ae9ff1f90d8d76c Author: Gavin Hayes Date: Wed Sep 21 22:41:41 2022 -0400 Revert "Bare metal VGA: implement "status report" escape codes (#613)" This reverts commit c4eb701d56af9104ddcf6fe94cc9295ab862694b. commit 86f6a61d16e2c896939f536119ba328c7220f1eb Author: Gavin Hayes Date: Wed Sep 21 22:41:38 2022 -0400 Revert "Fix the build" This reverts commit 790c66131794f6b1e1b86386601cff0198675c1a. commit 92b09acf939fdd3c10a81d5dd212f76351f05451 Author: Gavin Hayes Date: Wed Sep 21 22:41:37 2022 -0400 Revert "Fix bug with sendfile() on XNU" This reverts commit 827f25f054b577e6f1a90dcddfbc9ffd3f6c4f53. commit 3a5b536995cf8c48fffd371097aae9dfbeeebb44 Author: Gavin Hayes Date: Wed Sep 21 22:41:36 2022 -0400 Revert "Fix preadv() and pwritev()" This reverts commit 050062bcbb3d21899299e07ccf8acef92755bf3f. commit 854ec3631f54d087797a67c8e86b7d2655bc1317 Author: Gavin Hayes Date: Wed Sep 21 22:41:35 2022 -0400 Revert "Fix POSIX advisory locks on Windows" This reverts commit a5fa90a21f911e33c17f5b624f4e6ae6aa6aabb4. commit 5cc1a788126b5b2b54656e0f4244a56596b59978 Author: Gavin Hayes Date: Wed Sep 21 22:41:35 2022 -0400 Revert "Fix F_DUPFD_CLOEXEC on some systems" This reverts commit c64e7eaf7c73172948277a3cc002428ebb6f75c0. commit 0264d3b34dcf80b0d34d6f4ef100a0f8545047e0 Author: Gavin Hayes Date: Wed Sep 21 22:41:33 2022 -0400 Revert "Add README description for new vista branch" This reverts commit 30140812f05162b40e9a2c6d21af2ab250b30542. --- .github/workflows/build.yml | 2 - Makefile | 4 - README.md | 5 +- ape/ape.S | 83 +- ape/ape.lds | 13 +- ape/ape.mk | 2 - ape/loader.c | 30 +- ape/loader.h | 2 +- ape/macros.internal.h | 71 +- examples/examples.mk | 1 - examples/greenbean.c | 17 +- examples/hello4.c | 20 + examples/mu_test.c | 2054 ----------------- examples/thread.c | 2 +- examples/vga.c | 60 - examples/walk.c | 4 + libc/assert.h | 14 - libc/atomic.h | 1 - libc/calls/_timespec_real.c | 3 +- libc/calls/_timespec_sub.c | 14 +- libc/{intrin => calls}/arememoryintervalsok.c | 0 libc/calls/calls.mk | 14 + libc/calls/clock_gettime-mono.c | 2 +- libc/{intrin => calls}/directmap-metal.c | 0 libc/{intrin => calls}/directmap-nt.c | 0 libc/{intrin => calls}/directmap.c | 0 libc/calls/extend.internal.h | 10 - libc/calls/fcntl-nt.c | 85 +- libc/calls/fcntl-sysv.c | 8 +- libc/calls/fstatat64.S | 7 +- libc/calls/ftruncate64.S | 7 +- libc/calls/g_sighandrvas.c | 2 +- libc/calls/getloadavg-nt.c | 2 +- libc/calls/interrupts-nt.c | 2 +- libc/calls/lstat64.S | 7 +- libc/{intrin => calls}/memtrack.greg.c | 9 +- libc/{intrin => calls}/mman.greg.c | 5 +- libc/calls/{nanos.internal.h => nanos.h} | 0 libc/calls/preadv.c | 96 +- libc/calls/pwritev.c | 95 +- libc/calls/raise.c | 2 +- libc/calls/readv-metal.c | 13 - libc/calls/reservefd.c | 31 +- libc/calls/samplepids.c | 4 +- libc/calls/sched_setscheduler.c | 2 +- libc/calls/semaphore.internal.h | 26 - libc/calls/sig2.c | 1 - libc/calls/siglock.c | 4 +- libc/calls/stat64.S | 4 +- libc/calls/state.internal.h | 6 +- libc/calls/struct/fd.internal.h | 7 +- libc/calls/struct/timespec.h | 1 - libc/calls/symlinkat-nt.c | 2 +- libc/calls/unveil.c | 2 +- libc/dns/gethoststxt.c | 2 +- libc/dns/getresolvconf.c | 2 +- libc/fmt/conv.h | 3 +- libc/{intrin => fmt}/lengthuint64.c | 0 libc/integral/c.inc | 4 +- libc/integral/normalize.inc | 4 +- libc/intrin/asan.c | 41 +- libc/intrin/asan.internal.h | 2 - libc/intrin/bigword.internal.h | 16 + .../cmpxchg.c} | 45 +- libc/intrin/cmpxchg.h | 5 + libc/intrin/cxaatexit.internal.h | 4 +- libc/intrin/cxalock.c | 2 +- libc/intrin/describeflags.internal.h | 5 +- libc/intrin/describefutexop.c | 49 +- libc/intrin/describentoverlapped.c | 50 - libc/intrin/describentoverlapped.internal.h | 13 - libc/intrin/extend.c | 86 - libc/intrin/fds_lock.c | 2 +- libc/intrin/ftrace.c | 3 +- libc/intrin/futex.S | 7 +- libc/intrin/futex.internal.h | 12 + libc/intrin/{unlockfileex.c => futex_wait.c} | 51 +- ...describentlockfileflags.c => futex_wake.c} | 33 +- libc/intrin/g_fds.c | 36 +- libc/intrin/gettid.c | 23 +- libc/intrin/intrin.mk | 19 +- libc/intrin/kprintf.greg.c | 34 +- libc/intrin/leaky.internal.h | 16 - libc/intrin/lockcmpxchg.c | 47 + libc/intrin/lockcmpxchg.h | 5 + libc/intrin/lockfileex.c | 50 - .../_timespec_cmp.c => intrin/lockxadd.c} | 30 +- .../lockxchg.c} | 32 +- libc/intrin/mmi.init.S | 4 +- libc/intrin/mmi_lock.c | 4 +- libc/intrin/mmi_lock_obj.c | 2 +- libc/intrin/{nopl.internal.h => nopl.h} | 0 .../phtread_attr_setschedpolicy.c} | 2 +- .../pthread_cancel.c => intrin/pthread.c} | 8 +- libc/intrin/pthread.h | 205 ++ libc/{thread/thread2.h => intrin/pthread2.h} | 2 +- .../{thread => intrin}/pthread_attr_destroy.c | 2 +- .../pthread_attr_getdetachstate.c | 2 +- .../pthread_attr_getguardsize.c | 2 +- .../pthread_attr_getinheritsched.c | 2 +- .../pthread_attr_getschedparam.c | 2 +- .../pthread_attr_getschedpolicy.c | 2 +- .../pthread_attr_getscope.c | 2 +- .../pthread_attr_getstack.c | 2 +- .../pthread_attr_getstacksize.c | 2 +- libc/{thread => intrin}/pthread_attr_init.c | 2 +- .../pthread_attr_setdetachstate.c | 2 +- .../pthread_attr_setguardsize.c | 2 +- .../pthread_attr_setinheritsched.c | 2 +- .../pthread_attr_setschedparam.c | 2 +- .../pthread_attr_setscope.c | 2 +- .../pthread_attr_setstack.c | 2 +- .../pthread_attr_setstacksize.c | 2 +- .../pthread_barrier_destroy.c | 12 +- .../{thread => intrin}/pthread_barrier_init.c | 23 +- .../{thread => intrin}/pthread_barrier_wait.c | 27 +- .../pthread_barrierattr_destroy.c | 2 +- .../pthread_barrierattr_getpshared.c | 6 +- .../pthread_barrierattr_init.c | 4 +- .../pthread_barrierattr_setpshared.c | 7 +- libc/intrin/pthread_cond_broadcast.c | 96 + .../{thread => intrin}/pthread_cond_destroy.c | 8 +- libc/{thread => intrin}/pthread_cond_init.c | 4 +- .../pthread_condattr_destroy.c | 2 +- .../pthread_condattr_getpshared.c | 4 +- .../pthread_condattr_init.c | 4 +- .../pthread_condattr_setpshared.c | 7 +- .../pthread_mutex_destroy.c | 8 +- libc/intrin/pthread_mutex_init.c | 6 +- libc/intrin/pthread_mutex_lock.c | 119 +- libc/intrin/pthread_mutex_trylock.c | 72 +- libc/intrin/pthread_mutex_unlock.c | 66 +- .../pthread_mutexattr_destroy.c | 2 +- .../pthread_mutexattr_getpshared.c | 6 +- .../pthread_mutexattr_gettype.c | 4 +- .../pthread_mutexattr_init.c | 7 +- .../pthread_mutexattr_setpshared.c | 6 +- .../pthread_mutexattr_settype.c | 4 +- libc/intrin/pthread_once.c | 27 +- .../pthread_rwlock_destroy.c | 8 +- libc/{thread => intrin}/pthread_rwlock_init.c | 4 +- .../pthread_rwlock_rdlock.c | 36 +- .../pthread_rwlock_unlock.c} | 49 +- .../pthread_rwlock_wrlock.c | 36 +- .../pthread_rwlockattr_destroy.c | 2 +- .../pthread_rwlockattr_getpshared.c | 6 +- .../pthread_rwlockattr_init.c | 4 +- .../pthread_rwlockattr_setpshared.c | 7 +- .../{thread => intrin}/pthread_spin_destroy.c | 2 +- libc/{thread => intrin}/pthread_spin_init.c | 2 +- libc/{thread => intrin}/pthread_spin_lock.c | 2 +- .../{thread => intrin}/pthread_spin_trylock.c | 2 +- libc/{thread => intrin}/pthread_spin_unlock.c | 2 +- libc/intrin/pthread_yield.c | 2 +- libc/intrin/quick_exit.c | 6 +- libc/intrin/rand64.c | 4 +- libc/intrin/tlsisrequired.c | 4 +- libc/{thread => intrin}/wait0.c | 30 +- libc/{thread => intrin}/wait0.internal.h | 0 libc/intrin/wantcrashreports.c | 21 - libc/isystem/limits.h | 2 +- libc/isystem/linux/futex.h | 5 - libc/isystem/pthread.h | 4 +- libc/isystem/semaphore.h | 4 - libc/isystem/stdio.h | 2 +- libc/isystem/time.h | 1 - libc/log/backtrace2.c | 6 +- libc/log/backtrace3.c | 6 +- libc/log/internal.h | 1 - libc/log/leaks.c | 13 +- libc/log/libfatal.internal.h | 3 +- libc/log/oncrash.c | 2 +- libc/log/printgarbage.c | 6 +- libc/log/showcrashreports.c | 4 +- libc/log/vflogf.c | 2 +- libc/mem/gc.c | 20 +- libc/nexgen32e/bsr.h | 2 +- libc/nexgen32e/gc.S | 2 +- libc/nexgen32e/gclongjmp.S | 2 +- libc/{thread/tls2.h => nexgen32e/gettls.h} | 25 +- libc/nexgen32e/threaded.c | 2 +- libc/nexgen32e/threaded.h | 16 + libc/nt/codegen.sh | 2 +- libc/nt/kernel32/CreateSemaphoreW.s | 10 - libc/nt/kernel32/LockFileEx.s | 4 +- libc/nt/kernel32/SuspendThread.s | 13 - libc/nt/kernel32/UnlockFileEx.s | 4 +- libc/nt/master.sh | 8 +- libc/nt/synchronization.h | 4 - libc/{thread => runtime}/clone-linux.S | 0 libc/{thread => runtime}/clone-openbsd.S | 0 libc/{thread => runtime}/clone.c | 21 +- libc/{intrin/leaky.S => runtime/construct.S} | 35 +- libc/runtime/enable_threads.c | 2 +- libc/runtime/enable_tls.c | 31 +- libc/runtime/fork-nt.c | 8 +- libc/runtime/fork.c | 5 +- libc/runtime/ftracer.c | 33 +- libc/runtime/getsymboltable.c | 2 +- libc/runtime/hook.greg.c | 7 + libc/runtime/internal.h | 2 - libc/runtime/memtrack.internal.h | 4 +- libc/runtime/memtrack64.txt | 4 +- libc/runtime/mman.internal.h | 18 +- libc/runtime/mmap.c | 2 +- .../printmemoryintervals.c | 0 .../clone-nt.S => runtime/winthreadlaunch.S} | 0 libc/sock/sendfile.c | 30 +- libc/stdio/clearerr.c | 2 +- libc/stdio/dirstream.c | 4 +- libc/stdio/ecvt.c | 17 +- libc/stdio/fdopen.c | 4 +- libc/stdio/feof.c | 2 +- libc/stdio/ferror.c | 2 +- libc/stdio/fflush.c | 2 +- libc/stdio/fflush.internal.h | 4 +- libc/stdio/fflush_unlocked.c | 2 +- libc/stdio/fgetc.c | 2 +- libc/stdio/fgetln.c | 2 +- libc/stdio/fgets.c | 2 +- libc/stdio/fgetwc.c | 2 +- libc/stdio/fgetws.c | 2 +- libc/stdio/fileno.c | 2 +- libc/stdio/flockfile.c | 2 +- libc/stdio/flushlbf.c | 4 +- libc/stdio/fmemopen.c | 4 +- libc/stdio/fprintf.c | 2 +- libc/stdio/fputc.c | 2 +- libc/stdio/fputs.c | 2 +- libc/stdio/fputwc.c | 2 +- libc/stdio/fputws.c | 2 +- libc/stdio/fread.c | 2 +- libc/stdio/freopen.c | 2 +- libc/stdio/fseeko.c | 2 +- libc/stdio/ftello.c | 2 +- libc/stdio/fwrite.c | 2 +- libc/stdio/getdelim.c | 2 +- libc/stdio/iconv.c | 7 +- libc/stdio/{lock.internal.h => lock.h} | 8 +- libc/stdio/puts.c | 2 +- libc/stdio/readdir64.S | 7 +- libc/stdio/rewind.c | 2 +- libc/stdio/setvbuf.c | 2 +- libc/stdio/stderr-init.S | 4 +- libc/stdio/stdin-init.S | 4 +- libc/stdio/stdio.h | 5 +- libc/stdio/stdout-init.S | 4 +- libc/stdio/ungetc.c | 2 +- libc/stdio/ungetwc.c | 2 +- libc/stdio/vfprintf.c | 2 +- libc/str/langinfo.c | 5 +- libc/stubs/ld.S | 6 - libc/sysv/consts.sh | 12 +- libc/sysv/consts/FUTEX_PRIVATE_FLAG.s | 2 +- libc/sysv/consts/FUTEX_REQUEUE.s | 2 +- libc/sysv/consts/FUTEX_WAKE.s | 2 +- libc/sysv/consts/SOMAXCONN.s | 2 +- libc/sysv/consts/__NR_futex.s | 2 +- libc/sysv/consts/futex.h | 14 +- libc/sysv/consts/ss.h | 2 +- libc/sysv/errno_location.greg.c | 8 +- libc/sysv/strace.greg.c | 3 +- libc/sysv/syscall.S | 57 +- libc/testlib/globals.c | 5 +- libc/testlib/testmain.c | 6 +- libc/testlib/testrunner.c | 2 +- libc/thread/README.md | 5 - libc/thread/internal.h | 15 + libc/{intrin/pthread_keys.c => thread/key.c} | 5 +- libc/thread/mktls.c | 40 +- libc/thread/nsync_counter.c | 158 -- libc/thread/nsync_cv.c | 497 ---- libc/thread/nsync_debug.c | 292 --- libc/thread/nsync_mu_wait.c | 322 --- libc/thread/nsync_note.c | 307 --- libc/thread/nsync_once.c | 150 -- libc/thread/nsync_sem_wait.c | 85 - libc/thread/nsync_sem_wait_no_note.c | 33 - libc/thread/nsync_wait.c | 109 - libc/thread/posixthread.internal.h | 14 +- libc/thread/pthread_cond_timedwait.c | 51 +- libc/thread/pthread_cond_wait.c | 11 +- libc/thread/pthread_create.c | 41 +- libc/thread/pthread_detach.c | 2 +- libc/thread/pthread_equal.c | 2 +- libc/thread/pthread_exit.c | 5 +- libc/thread/pthread_getaffinity_np.c | 1 + libc/thread/pthread_getattr_np.c | 1 + libc/thread/pthread_getname_np.c | 1 + libc/thread/pthread_getschedparam.c | 2 +- libc/{intrin => thread}/pthread_getspecific.c | 4 +- libc/thread/pthread_getthreadid_np.c | 2 +- libc/thread/pthread_getunique_np.c | 1 + libc/thread/pthread_join.c | 2 +- libc/{intrin => thread}/pthread_key_create.c | 13 +- libc/{intrin => thread}/pthread_key_delete.c | 10 +- .../{intrin => thread}/pthread_key_destruct.c | 8 +- libc/thread/pthread_reschedule.c | 1 + libc/thread/pthread_self.c | 5 +- libc/thread/pthread_setaffinity_np.c | 1 + libc/thread/pthread_setname_np.c | 1 + libc/thread/pthread_setschedparam.c | 2 +- libc/thread/pthread_setschedparam_freebsd.c | 1 + libc/{intrin => thread}/pthread_setspecific.c | 3 +- libc/thread/pthread_sigmask.c | 1 + libc/thread/spawn.c | 15 +- libc/thread/thread.h | 204 +- libc/thread/thread.mk | 15 +- libc/thread/tls.h | 48 - .../{pthread_ungarbage.c => ungarbage.c} | 7 +- libc/thread/{pthread_zombies.c => zombie.c} | 2 +- libc/time/localtime.c | 6 +- libc/vga/readv-vga.c | 49 - libc/vga/tty.c | 172 +- libc/vga/vga-init.S | 1 - libc/vga/vga.internal.h | 16 +- libc/vga/writev-vga.c | 25 +- libc/zipos/free.c | 5 + libc/zipos/get.c | 2 +- libc/zipos/lock.c | 2 +- libc/zipos/open.c | 49 +- libc/zipos/zipos.h | 0 libc/zipos/zipos.internal.h | 5 +- test/libc/calls/fcntl_test.c | 52 + test/libc/calls/lock_test.c | 183 -- test/libc/calls/pledge_test.c | 4 +- test/libc/calls/preadv_test.c | 88 - test/libc/calls/reservefd_test.c | 5 +- test/libc/calls/test.mk | 3 - .../{stdio/ecvt_test.c => fmt/fcvt_test.c} | 0 .../pthread_barrier_wait_test.c | 8 +- .../libc/intrin/pthread_cond_broadcast_test.c | 123 + test/libc/intrin/pthread_mutex_lock2_test.c | 86 +- test/libc/intrin/pthread_mutex_lock_test.c | 11 +- test/libc/intrin/pthread_once_test.c | 10 +- .../pthread_rwlock_rdlock_test.c | 22 +- test/libc/intrin/rand64_test.c | 6 +- test/libc/intrin/test.mk | 3 +- test/libc/nexgen32e/stackrw_test.c | 9 +- test/libc/release/test.mk | 1 - test/libc/runtime/arch_prctl_test.c | 2 +- test/libc/{thread => runtime}/clone_test.c | 32 +- test/libc/runtime/tls_test.c | 35 +- test/libc/stdio/dtoa_test.c | 2 +- test/libc/stdio/fputc_test.c | 2 +- test/libc/test.mk | 1 - test/libc/thread/nsync_test.c | 119 - test/libc/thread/pthread_cond_signal_test.c | 118 - test/libc/thread/pthread_create_test.c | 14 +- test/libc/thread/pthread_key_create_test.c | 2 + test/libc/thread/pthread_setname_np_test.c | 4 +- test/libc/thread/spawn_test.c | 4 +- test/libc/thread/test.mk | 3 +- test/libc/x/makedirs_test.c | 3 +- test/libc/zipos/open_test.c | 56 - test/libc/zipos/test.mk | 63 - test/tool/plinko/plinko_test.c | 1 - third_party/awk/awkgram.tab.c | 2 +- third_party/awk/awkgram.y | 2 +- third_party/awk/lex.c | 2 +- third_party/awk/lib.c | 2 +- third_party/dlmalloc/dlmalloc.mk | 3 +- third_party/dlmalloc/locks.inc | 234 +- third_party/gdtoa/lock.c | 2 +- third_party/gdtoa/lock.h | 4 +- third_party/libcxx/__threading_support | 4 +- third_party/lua/liolib.c | 2 +- third_party/lua/lrepl.c | 2 +- third_party/lua/lrepl.h | 2 +- third_party/nsync/LICENSE.txt | 202 -- third_party/nsync/README.md | 21 - third_party/nsync/atomic.h | 15 - third_party/nsync/atomic.internal.h | 113 - third_party/nsync/common.c | 246 -- third_party/nsync/common.internal.h | 290 --- third_party/nsync/counter.h | 43 - third_party/nsync/cv.h | 157 -- third_party/nsync/debug.h | 39 - third_party/nsync/dll.c | 143 -- third_party/nsync/dll.h | 69 - third_party/nsync/futex.c | 124 - third_party/nsync/futex.internal.h | 15 - third_party/nsync/malloc.c | 49 - third_party/nsync/malloc.internal.h | 10 - third_party/nsync/mu.c | 548 ----- third_party/nsync/mu.h | 103 - third_party/nsync/mu_semaphore.c | 56 - third_party/nsync/mu_semaphore.h | 28 - third_party/nsync/mu_semaphore.internal.h | 20 - third_party/nsync/mu_semaphore_futex.c | 124 - third_party/nsync/mu_semaphore_win32.c | 84 - third_party/nsync/mu_wait.h | 118 - third_party/nsync/note.h | 51 - third_party/nsync/nsync.mk | 58 - third_party/nsync/once.h | 37 - third_party/nsync/panic.c | 30 - third_party/nsync/races.internal.h | 36 - third_party/nsync/time.c | 54 - third_party/nsync/time.h | 53 - third_party/nsync/wait_s.internal.h | 26 - third_party/nsync/waiter.h | 138 -- third_party/nsync/waiter_per_thread.c | 56 - third_party/nsync/yield.c | 27 - third_party/python/Objects/fileobject.c | 2 +- third_party/sed/compile.c | 2 +- third_party/sed/main.c | 2 +- third_party/sed/misc.c | 2 +- third_party/sed/process.c | 2 +- third_party/sqlite3/main.c | 4 - third_party/third_party.mk | 3 +- third_party/zip/zipfile.c | 2 +- tool/build/lib/pty.c | 9 +- tool/build/mkdeps.c | 6 +- tool/build/pstrace.c | 10 +- tool/emacs/cosmo-stuff.el | 4 +- tool/net/help.txt | 12 +- tool/net/redbean.c | 7 +- tool/plinko/plinko.c | 2 +- tool/scripts/check-includes.py | 20 + 419 files changed, 2468 insertions(+), 10224 deletions(-) create mode 100644 examples/hello4.c delete mode 100644 examples/mu_test.c delete mode 100644 examples/vga.c rename libc/{intrin => calls}/arememoryintervalsok.c (100%) rename libc/{intrin => calls}/directmap-metal.c (100%) rename libc/{intrin => calls}/directmap-nt.c (100%) rename libc/{intrin => calls}/directmap.c (100%) delete mode 100644 libc/calls/extend.internal.h rename libc/{intrin => calls}/memtrack.greg.c (99%) rename libc/{intrin => calls}/mman.greg.c (98%) rename libc/calls/{nanos.internal.h => nanos.h} (100%) delete mode 100644 libc/calls/semaphore.internal.h rename libc/{intrin => fmt}/lengthuint64.c (100%) create mode 100644 libc/intrin/bigword.internal.h rename libc/{thread/pthread_cond_signal.c => intrin/cmpxchg.c} (64%) delete mode 100644 libc/intrin/describentoverlapped.c delete mode 100755 libc/intrin/describentoverlapped.internal.h delete mode 100644 libc/intrin/extend.c create mode 100644 libc/intrin/futex.internal.h rename libc/intrin/{unlockfileex.c => futex_wait.c} (63%) rename libc/intrin/{describentlockfileflags.c => futex_wake.c} (70%) delete mode 100644 libc/intrin/leaky.internal.h create mode 100644 libc/intrin/lockcmpxchg.c delete mode 100644 libc/intrin/lockfileex.c rename libc/{calls/_timespec_cmp.c => intrin/lockxadd.c} (66%) rename libc/{thread/pthread_rwlock_unlock.c => intrin/lockxchg.c} (70%) rename libc/intrin/{nopl.internal.h => nopl.h} (100%) rename libc/{thread/pthread_attr_setschedpolicy.c => intrin/phtread_attr_setschedpolicy.c} (98%) rename libc/{thread/pthread_cancel.c => intrin/pthread.c} (92%) create mode 100644 libc/intrin/pthread.h rename libc/{thread/thread2.h => intrin/pthread2.h} (96%) rename libc/{thread => intrin}/pthread_attr_destroy.c (98%) rename libc/{thread => intrin}/pthread_attr_getdetachstate.c (98%) rename libc/{thread => intrin}/pthread_attr_getguardsize.c (98%) rename libc/{thread => intrin}/pthread_attr_getinheritsched.c (98%) rename libc/{thread => intrin}/pthread_attr_getschedparam.c (98%) rename libc/{thread => intrin}/pthread_attr_getschedpolicy.c (98%) rename libc/{thread => intrin}/pthread_attr_getscope.c (98%) rename libc/{thread => intrin}/pthread_attr_getstack.c (98%) rename libc/{thread => intrin}/pthread_attr_getstacksize.c (98%) rename libc/{thread => intrin}/pthread_attr_init.c (98%) rename libc/{thread => intrin}/pthread_attr_setdetachstate.c (98%) rename libc/{thread => intrin}/pthread_attr_setguardsize.c (98%) rename libc/{thread => intrin}/pthread_attr_setinheritsched.c (98%) rename libc/{thread => intrin}/pthread_attr_setschedparam.c (98%) rename libc/{thread => intrin}/pthread_attr_setscope.c (98%) rename libc/{thread => intrin}/pthread_attr_setstack.c (99%) rename libc/{thread => intrin}/pthread_attr_setstacksize.c (98%) rename libc/{thread => intrin}/pthread_barrier_destroy.c (90%) rename libc/{thread => intrin}/pthread_barrier_init.c (86%) rename libc/{thread => intrin}/pthread_barrier_wait.c (74%) rename libc/{thread => intrin}/pthread_barrierattr_destroy.c (98%) rename libc/{thread => intrin}/pthread_barrierattr_getpshared.c (94%) rename libc/{thread => intrin}/pthread_barrierattr_init.c (96%) rename libc/{thread => intrin}/pthread_barrierattr_setpshared.c (94%) create mode 100644 libc/intrin/pthread_cond_broadcast.c rename libc/{thread => intrin}/pthread_cond_destroy.c (93%) rename libc/{thread => intrin}/pthread_cond_init.c (95%) rename libc/{thread => intrin}/pthread_condattr_destroy.c (98%) rename libc/{thread => intrin}/pthread_condattr_getpshared.c (96%) rename libc/{thread => intrin}/pthread_condattr_init.c (96%) rename libc/{thread => intrin}/pthread_condattr_setpshared.c (94%) rename libc/{thread => intrin}/pthread_mutex_destroy.c (92%) rename libc/{thread => intrin}/pthread_mutexattr_destroy.c (98%) rename libc/{thread => intrin}/pthread_mutexattr_getpshared.c (95%) rename libc/{thread => intrin}/pthread_mutexattr_gettype.c (97%) rename libc/{thread => intrin}/pthread_mutexattr_init.c (93%) rename libc/{thread => intrin}/pthread_mutexattr_setpshared.c (95%) rename libc/{thread => intrin}/pthread_mutexattr_settype.c (97%) rename libc/{thread => intrin}/pthread_rwlock_destroy.c (93%) rename libc/{thread => intrin}/pthread_rwlock_init.c (95%) rename libc/{thread => intrin}/pthread_rwlock_rdlock.c (66%) rename libc/{thread/pthread_cond_broadcast.c => intrin/pthread_rwlock_unlock.c} (62%) rename libc/{thread => intrin}/pthread_rwlock_wrlock.c (66%) rename libc/{thread => intrin}/pthread_rwlockattr_destroy.c (98%) rename libc/{thread => intrin}/pthread_rwlockattr_getpshared.c (94%) rename libc/{thread => intrin}/pthread_rwlockattr_init.c (96%) rename libc/{thread => intrin}/pthread_rwlockattr_setpshared.c (94%) rename libc/{thread => intrin}/pthread_spin_destroy.c (98%) rename libc/{thread => intrin}/pthread_spin_init.c (98%) rename libc/{thread => intrin}/pthread_spin_lock.c (98%) rename libc/{thread => intrin}/pthread_spin_trylock.c (98%) rename libc/{thread => intrin}/pthread_spin_unlock.c (98%) rename libc/{thread => intrin}/wait0.c (75%) rename libc/{thread => intrin}/wait0.internal.h (100%) delete mode 100644 libc/intrin/wantcrashreports.c delete mode 100644 libc/isystem/linux/futex.h delete mode 100644 libc/isystem/semaphore.h rename libc/{thread/tls2.h => nexgen32e/gettls.h} (56%) create mode 100644 libc/nexgen32e/threaded.h rename libc/{thread => runtime}/clone-linux.S (100%) rename libc/{thread => runtime}/clone-openbsd.S (100%) rename libc/{thread => runtime}/clone.c (97%) rename libc/{intrin/leaky.S => runtime/construct.S} (79%) rename libc/{intrin => runtime}/printmemoryintervals.c (100%) rename libc/{thread/clone-nt.S => runtime/winthreadlaunch.S} (100%) rename libc/stdio/{lock.internal.h => lock.h} (77%) delete mode 100644 libc/thread/README.md create mode 100644 libc/thread/internal.h rename libc/{intrin/pthread_keys.c => thread/key.c} (94%) delete mode 100644 libc/thread/nsync_counter.c delete mode 100644 libc/thread/nsync_cv.c delete mode 100644 libc/thread/nsync_debug.c delete mode 100644 libc/thread/nsync_mu_wait.c delete mode 100644 libc/thread/nsync_note.c delete mode 100644 libc/thread/nsync_once.c delete mode 100644 libc/thread/nsync_sem_wait.c delete mode 100644 libc/thread/nsync_sem_wait_no_note.c delete mode 100644 libc/thread/nsync_wait.c rename libc/{intrin => thread}/pthread_getspecific.c (95%) rename libc/{intrin => thread}/pthread_key_create.c (91%) rename libc/{intrin => thread}/pthread_key_delete.c (91%) rename libc/{intrin => thread}/pthread_key_destruct.c (89%) rename libc/{intrin => thread}/pthread_setspecific.c (96%) delete mode 100644 libc/thread/tls.h rename libc/thread/{pthread_ungarbage.c => ungarbage.c} (93%) rename libc/thread/{pthread_zombies.c => zombie.c} (98%) delete mode 100644 libc/vga/readv-vga.c delete mode 100755 libc/zipos/zipos.h delete mode 100644 test/libc/calls/lock_test.c delete mode 100644 test/libc/calls/preadv_test.c rename test/libc/{stdio/ecvt_test.c => fmt/fcvt_test.c} (100%) rename test/libc/{thread => intrin}/pthread_barrier_wait_test.c (95%) create mode 100644 test/libc/intrin/pthread_cond_broadcast_test.c rename test/libc/{thread => intrin}/pthread_rwlock_rdlock_test.c (83%) rename test/libc/{thread => runtime}/clone_test.c (86%) delete mode 100644 test/libc/thread/nsync_test.c delete mode 100644 test/libc/thread/pthread_cond_signal_test.c delete mode 100644 test/libc/zipos/open_test.c delete mode 100644 test/libc/zipos/test.mk delete mode 100644 third_party/nsync/LICENSE.txt delete mode 100644 third_party/nsync/README.md delete mode 100644 third_party/nsync/atomic.h delete mode 100644 third_party/nsync/atomic.internal.h delete mode 100644 third_party/nsync/common.c delete mode 100644 third_party/nsync/common.internal.h delete mode 100644 third_party/nsync/counter.h delete mode 100644 third_party/nsync/cv.h delete mode 100644 third_party/nsync/debug.h delete mode 100644 third_party/nsync/dll.c delete mode 100644 third_party/nsync/dll.h delete mode 100644 third_party/nsync/futex.c delete mode 100644 third_party/nsync/futex.internal.h delete mode 100644 third_party/nsync/malloc.c delete mode 100644 third_party/nsync/malloc.internal.h delete mode 100644 third_party/nsync/mu.c delete mode 100644 third_party/nsync/mu.h delete mode 100644 third_party/nsync/mu_semaphore.c delete mode 100644 third_party/nsync/mu_semaphore.h delete mode 100644 third_party/nsync/mu_semaphore.internal.h delete mode 100644 third_party/nsync/mu_semaphore_futex.c delete mode 100644 third_party/nsync/mu_semaphore_win32.c delete mode 100644 third_party/nsync/mu_wait.h delete mode 100644 third_party/nsync/note.h delete mode 100644 third_party/nsync/nsync.mk delete mode 100644 third_party/nsync/once.h delete mode 100644 third_party/nsync/panic.c delete mode 100644 third_party/nsync/races.internal.h delete mode 100644 third_party/nsync/time.c delete mode 100644 third_party/nsync/time.h delete mode 100644 third_party/nsync/wait_s.internal.h delete mode 100644 third_party/nsync/waiter.h delete mode 100644 third_party/nsync/waiter_per_thread.c delete mode 100644 third_party/nsync/yield.c create mode 100755 tool/scripts/check-includes.py diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3a2eede21..2b69d7f53 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,12 +4,10 @@ on: push: branches: - "master" - - "vista" - "flake" pull_request: branches: - "master" - - "vista" # run workflow manually from the Actions tab workflow_dispatch: diff --git a/Makefile b/Makefile index a8c92725b..de71e9b39 100644 --- a/Makefile +++ b/Makefile @@ -122,7 +122,6 @@ include libc/vga/vga.mk #─┘ include libc/calls/calls.mk #─┐ include libc/runtime/runtime.mk # ├──SYSTEMS RUNTIME include libc/crt/crt.mk # │ You can issue system calls -include third_party/nsync/nsync.mk # │ include third_party/dlmalloc/dlmalloc.mk #─┘ include libc/mem/mem.mk #─┐ include libc/zipos/zipos.mk # ├──DYNAMIC RUNTIME @@ -212,7 +211,6 @@ include test/libc/fmt/test.mk include test/libc/dns/test.mk include test/libc/time/test.mk include test/libc/stdio/test.mk -include test/libc/zipos/test.mk include test/libc/release/test.mk include test/libc/test.mk include test/net/http/test.mk @@ -300,7 +298,6 @@ COSMOPOLITAN_OBJECTS = \ LIBC_MEM \ THIRD_PARTY_DLMALLOC \ LIBC_RUNTIME \ - THIRD_PARTY_NSYNC \ LIBC_ELF \ LIBC_CALLS \ LIBC_SYSV_CALLS \ @@ -340,7 +337,6 @@ COSMOPOLITAN_HEADERS = \ LIBC_RUNTIME \ LIBC_SOCK \ LIBC_STDIO \ - THIRD_PARTY_NSYNC \ THIRD_PARTY_XED \ LIBC_STR \ LIBC_SYSV \ diff --git a/README.md b/README.md index e4f2323c4..0e514a34c 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,7 @@ ![Cosmopolitan Honeybadger](usr/share/img/honeybadger.png) [![build](https://github.com/jart/cosmopolitan/actions/workflows/build.yml/badge.svg)](https://github.com/jart/cosmopolitan/actions/workflows/build.yml) -# Cosmopolitan (Vista Edition) - -**This is a community supported legacy branch of Cosmopolitan 2.0 that's -intended to preserve compatibility with Windows Vista and Windows 7.** +# Cosmopolitan [Cosmopolitan Libc](https://justine.lol/cosmopolitan/index.html) makes C a build-once run-anywhere language, like Java, except it doesn't need an diff --git a/ape/ape.S b/ape/ape.S index 873c1b523..9ff9f4cd9 100644 --- a/ape/ape.S +++ b/ape/ape.S @@ -329,7 +329,7 @@ dsknfo: push %bx movpp %es,%ds xor %si,%si mov %si,%es - mov $0x1d10,%si # mman::pc_drive_base_table + mov $0x1510,%si # mman::pc_drive_base_table xchg %si,%di movsw #→ headunloadtime, headloadtime movsw #→ shutofftime, bytespersector @@ -382,11 +382,11 @@ pcread: push %ax add $512>>4,%si mov %si,%es inc %al # ++sector - cmp 0x1d1c,%al # mman::pc_drive_last_sector + cmp 0x151c,%al # mman::pc_drive_last_sector jbe 2f mov $1,%al inc %dh # ++head - cmp 0x1d20,%dh # mman::pc_drive_last_head + cmp 0x1520,%dh # mman::pc_drive_last_head jbe 2f xor %dh,%dh inc %cx # ++cylinder @@ -485,19 +485,19 @@ ape.mbrpad: ape_disk: .stub ape.diskid,quad .org 0x1be,0x00 - .macro .partn x:req sta h0 s0 c0 fs h9 s9 c9 lba0 nsec - .stub ape.part\x\().status,byte,\sta # 0=non-boot / 0x80=active - .stub ape.part\x\().first.head,byte,\h0 - .stub ape.part\x\().first.sector,byte,\s0 # in low 6 bits - .stub ape.part\x\().first.cylinder,byte,\c0 - .stub ape.part\x\().filesystem,byte,\fs - .stub ape.part\x\().last.head,byte,\h9 - .stub ape.part\x\().last.sector,byte,\s9 - .stub ape.part\x\().last.cylinder,byte,\c9 - .stub ape.part\x\().lba,long,\lba0 # c₀*Cₙ + h₀*Hₙ + s₀*Sₙ - .stub ape.part\x\().sector.count,long,\nsec # sectors are 512 bytes + .macro .partn x + .stub ape.part\x\().status,byte # 0=absent / 0x80=present + .stub ape.part\x\().first.head,byte # in low 6 bits + .stub ape.part\x\().first.cylinder,byte + .stub ape.part\x\().first.sector,byte + .stub ape.part\x\().filesystem,byte + .stub ape.part\x\().last.head,byte + .stub ape.part\x\().last.cylinder,byte + .stub ape.part\x\().last.sector,byte + .stub ape.part\x\().lba,long # c₀*Cₙ + h₀*Hₙ + s₀*Sₙ + .stub ape.part\x\().sector.count,long # sectors are 512 bytes .endm - .partn 1,0x80,0,1,0,0x7f,0xff,0xff,0xff,0,0xffffffff + .partn 1 .partn 2 .partn 3 .partn 4 @@ -569,11 +569,11 @@ apesh: .ascii "'\n#'\"\n" # sixth edition shebang .ascii "t=\"${TMPDIR:-${HOME:-.}}/.ape\"\n" .ascii "[ -x \"$t\" ] || {\n" .ascii "mkdir -p \"${t%/*}\" &&\n" - .ascii "dd if=\"$o\" of=\"$t.$$\" skip=" + .ascii "dd if=\"$o\" of=\"$t.$$\" skip=\"" .shstub ape_loader_dd_skip,2 - .ascii " count=" + .ascii "\" count=\"" .shstub ape_loader_dd_count,2 - .ascii " bs=64 2>/dev/null\n" + .ascii "\" bs=64 2>/dev/null\n" #if SupportsXnu() .ascii "[ -d /Applications ] && " .ascii "dd if=\"$t.$$\"" @@ -637,11 +637,11 @@ apesh: .ascii "'\n#'\"\n" # sixth edition shebang .ascii "dd if=\"$o\"" .ascii " of=\"$o\"" .ascii " bs=8" - .ascii " skip=" + .ascii " skip=\"" .shstub ape_macho_dd_skip,2 - .ascii " count=" + .ascii "\" count=\"" .shstub ape_macho_dd_count,2 - .ascii " conv=notrunc 2>/dev/null\n" + .ascii "\" conv=notrunc 2>/dev/null\n" #endif /* XNU */ .ascii "[ x\"$1\" = x--assimilate ] && exit 0\n" #ifndef APE_NO_MODIFY_SELF @@ -1291,38 +1291,27 @@ lcheck: pushf # check for i8086 / i8088 / i80186 // Gets memory map from BIOS. e820: mov $0x0510>>4,%di # mman::e820 mov %di,%es - xor %di,%di # es:di is destination buffer + xor %edi,%edi # es:di is destination buffer xor %ebx,%ebx # ebx is an api state tracker 1: mov $0xE820,%eax # magic mov $8+8+4+4,%ecx # sizeof(struct SmapEntry) mov $0x534d4150,%edx # magic number - movl $1,8+8+4/*SmapEntry::acpi3*/(%di) # prefill ACPI attributes; - # apparently some buggy BIOSes say - # that they return this field, yet - # do not fill it correctly int $0x15 # ax,bx,cx,dx,di → ax,bx,cx jc 9f # cf = unsupported or abuse cmp %edx,%eax # more magic means success jne 9f test %cx,%cx # discard empty results jz 5f - mov 8/*LODWORD(SmapEntry::size)*/(%di),%eax - or 8+4/*HIDWORD(SmapEntry::size)*/(%di),%eax - jz 5f cmp $8+8+4+1,%cx # discard if ignore flag jb 4f - testb $1/*don't ignore*/,8+8+4/*SmapEntry::acpi3*/(%di) - jz 5f + testb $1/*ignore*/,8+8+4/*SmapEntry::__acpi3*/(%di) + jnz 5f 4: add $8+8+4+4,%di # keep entry 5: test %ebx,%ebx # last entry? jz 8f - cmp $(256-1)*(8+8+4+4),%di + cmp $0x1000,%di jb 1b -8: xor %ax,%ax # add a blank sentinel entry - mov $(8+8)/2,%cx - cld - rep stosw - ret +8: ret 9: mov $REAL(str.e820),%di call rldie .endfn e820 @@ -1392,16 +1381,9 @@ a20: cli // Initializes long mode paging. pinit: push %ds - push %es #define SEG 0x79000 mov $SEG>>4,%ax mov %ax,%ds - mov %ax,%es - xor %di,%di - xor %ax,%ax - mov $(0x7f000-SEG)/2,%cx - cld - rep stosw movl $0x7d000+PAGE_V+PAGE_RW,0x7e000-SEG # PDPT←PML4T (+) movl $0x7c000+PAGE_V+PAGE_RW,0x7e800-SEG # PDPT←PML4T (-) movl $0x7b000+PAGE_V+PAGE_RW,0x7d000-SEG # PDT←PDPT (+) @@ -1410,14 +1392,13 @@ pinit: push %ds movl $0x79000+PAGE_V+PAGE_RW,0x7a000-SEG # PD←PDT (-) mov $512,%cx # PD±2MB mov $PAGE_V+PAGE_RW,%eax - xor %di,%di -0: stosl + xor %si,%si +0: mov %eax,(%si) add $0x1000,%eax - scasl # di += 4 + add $8,%si loop 0b mov $0x7e000,%eax # PML4T←CR3 mov %eax,%cr3 - pop %es pop %ds ret .endfn pinit @@ -1426,7 +1407,7 @@ pinit: push %ds // // @see Intel Manual V3A §4.1.2 golong: cli - lidt 0x1d22 # mman::bad_idt + lidt 0x1522 # mman::bad_idt mov %cr4,%eax or $CR4_PAE|CR4_PGE|CR4_OSFXSR,%eax mov %eax,%cr4 @@ -1444,8 +1425,8 @@ golong: cli // Long mode is long. .code64 -long: xor %eax,%eax - mov $GDT_LONG_DATA,%al +long: push $GDT_LONG_DATA + pop %rax mov %eax,%ds mov %eax,%ss mov %eax,%es diff --git a/ape/ape.lds b/ape/ape.lds index 6cb55b593..266b8cfb0 100644 --- a/ape/ape.lds +++ b/ape/ape.lds @@ -180,7 +180,6 @@ #include "libc/elf/def.h" #include "libc/elf/pf2prot.internal.h" #include "libc/nt/pedef.internal.h" -#include "libc/thread/tls.h" #include "libc/zip.h" ENTRY(_start) @@ -384,14 +383,17 @@ SECTIONS { HIDDEN(_ezip = .); . = ALIGN(PAGESIZE); } :Ram + . = ALIGN(PAGESIZE); .tdata . : { _tdata_start = .; *(SORT_BY_ALIGNMENT(.tdata)) *(SORT_BY_ALIGNMENT(.tdata.*)) + . = ALIGN(16); _tdata_end = .; . = ALIGN(PAGESIZE); } :Tls :Ram + . = ALIGN(PAGESIZE); /*END: file content that's loaded by o/s */ /*BEGIN: bss memory void */ @@ -400,7 +402,7 @@ SECTIONS { _tbss_start = .; *(SORT_BY_ALIGNMENT(.tbss)) *(SORT_BY_ALIGNMENT(.tbss.*)) - . = ALIGN(TLS_ALIGNMENT); + . = ALIGN(16); /* the %fs register is based on this location */ _tbss_end = .; } :Tls @@ -486,10 +488,8 @@ PFSTUB4(ape_elf_phnum, (ape_phdrs_end - ape_phdrs) / 56); PFSTUB4(ape_elf_shnum, 0); PFSTUB4(ape_elf_shstrndx, 0); -HIDDEN(_tls_size = _tbss_end - _tdata_start); HIDDEN(_tdata_size = _tdata_end - _tdata_start); -HIDDEN(_tbss_size = _tbss_end - _tbss_start); -HIDDEN(_tbss_offset = _tbss_start - _tdata_start); +HIDDEN(_tls_size = _tbss_end - _tdata_start); HIDDEN(_tls_content = (_tdata_end - _tdata_start) + (_tbss_end - _tbss_start)); HIDDEN(__privileged_addr = ROUNDDOWN(__privileged_start, PAGESIZE)); @@ -717,9 +717,6 @@ ASSERT(IS2POW(ape_stack_memsz), ASSERT(!(ape_stack_vaddr & (ape_stack_memsz - 1)), "ape_stack_vaddr must have ape_stack_memsz alignment; try using STATIC_STACK_ADDR(0x700000000000 - ape_stack_memsz);"); -ASSERT(ALIGNOF(.tdata) <= TLS_ALIGNMENT && ALIGNOF(.tbss) <= TLS_ALIGNMENT, - "_Thread_local _Alignof can't exceed TLS_ALIGNMENT"); - /* Let's not be like Knight Capital. */ /* NOCROSSREFS_TO(.test .text) */ diff --git a/ape/ape.mk b/ape/ape.mk index 02ab10ded..179790eef 100644 --- a/ape/ape.mk +++ b/ape/ape.mk @@ -63,7 +63,6 @@ o/$(MODE)/ape/ape.lds: \ ape/macros.internal.h \ ape/relocations.h \ libc/intrin/bits.h \ - libc/thread/tls.h \ libc/calls/struct/timespec.h \ libc/dce.h \ libc/elf/def.h \ @@ -80,7 +79,6 @@ o/$(MODE)/ape/public/ape.lds: \ ape/macros.internal.h \ ape/relocations.h \ libc/intrin/bits.h \ - libc/thread/tls.h \ libc/calls/struct/timespec.h \ libc/dce.h \ libc/elf/def.h \ diff --git a/ape/loader.c b/ape/loader.c index 1e0ae6950..08a736709 100644 --- a/ape/loader.c +++ b/ape/loader.c @@ -179,7 +179,7 @@ struct ElfPhdr { extern char ehdr[]; extern char _end[]; -static void *syscall_; +static void *syscall; static char relocated; static struct PathSearcher ps; extern char __syscall_loader[]; @@ -275,7 +275,7 @@ __attribute__((__noreturn__)) static void Exit(int rc, int os) { asm volatile("call\t*%2" : /* no outputs */ : "a"((IsLinux() ? 60 : 1) | (IsXnu() ? 0x2000000 : 0)), "D"(rc), - "rm"(syscall_) + "rm"(syscall) : "memory"); __builtin_unreachable(); } @@ -285,7 +285,7 @@ static void Close(int fd, int os) { asm volatile("call\t*%4" : "=a"(ax), "=D"(di) : "0"((IsLinux() ? 3 : 6) | (IsXnu() ? 0x2000000 : 0)), "1"(fd), - "rm"(syscall_) + "rm"(syscall) : "rcx", "rdx", "rsi", "r8", "r9", "r10", "r11", "memory", "cc"); } @@ -295,7 +295,7 @@ static int Read(int fd, void *data, int size, int os) { asm volatile("call\t*%8" : "=a"(ax), "=D"(di), "=S"(si), "=d"(dx) : "0"((IsLinux() ? 0 : 3) | (IsXnu() ? 0x2000000 : 0)), "1"(fd), - "2"(data), "3"(size), "rm"(syscall_) + "2"(data), "3"(size), "rm"(syscall) : "rcx", "r8", "r9", "r10", "r11", "memory"); return ax; } @@ -306,7 +306,7 @@ static void Write(int fd, const void *data, int size, int os) { asm volatile("call\t*%8" : "=a"(ax), "=D"(di), "=S"(si), "=d"(dx) : "0"((IsLinux() ? 1 : 4) | (IsXnu() ? 0x2000000 : 0)), "1"(fd), - "2"(data), "3"(size), "rm"(syscall_) + "2"(data), "3"(size), "rm"(syscall) : "rcx", "r8", "r9", "r10", "r11", "memory", "cc"); } @@ -315,7 +315,7 @@ static void Execve(const char *prog, char **argv, char **envp, int os) { asm volatile("call\t*%8" : "=a"(ax), "=D"(di), "=S"(si), "=d"(dx) : "0"(59 | (IsXnu() ? 0x2000000 : 0)), "1"(prog), "2"(argv), - "3"(envp), "rm"(syscall_) + "3"(envp), "rm"(syscall) : "rcx", "r8", "r9", "r10", "r11", "memory", "cc"); } @@ -325,7 +325,7 @@ static int Access(const char *path, int mode, int os) { asm volatile("call\t*%7" : "=a"(ax), "=D"(di), "=S"(si), "=d"(dx) : "0"((IsLinux() ? 21 : 33) | (IsXnu() ? 0x2000000 : 0)), - "1"(path), "2"(mode), "rm"(syscall_) + "1"(path), "2"(mode), "rm"(syscall) : "rcx", "r8", "r9", "r10", "r11", "memory", "cc"); return ax; } @@ -338,7 +338,7 @@ static int Msyscall(long p, long n, int os) { } else { asm volatile("call\t*%6" : "=a"(ax), "=D"(di), "=S"(si) - : "0"(37), "1"(p), "2"(n), "rm"(syscall_) + : "0"(37), "1"(p), "2"(n), "rm"(syscall) : "rcx", "rdx", "r8", "r9", "r10", "r11", "memory", "cc"); return ax; } @@ -350,7 +350,7 @@ static int Open(const char *path, int flags, int mode, int os) { asm volatile("call\t*%8" : "=a"(ax), "=D"(di), "=S"(si), "=d"(dx) : "0"((IsLinux() ? 2 : 5) | (IsXnu() ? 0x2000000 : 0)), - "1"(path), "2"(flags), "3"(mode), "rm"(syscall_) + "1"(path), "2"(flags), "3"(mode), "rm"(syscall) : "rcx", "r8", "r9", "r10", "r11", "memory", "cc"); return ax; } @@ -369,7 +369,7 @@ __attribute__((__noinline__)) static long Mmap(long addr, long size, int prot, "pop\t%%r9" : "=a"(ax), "=D"(di), "=S"(si), "=d"(dx), "+r"(flags_), "+r"(fd_), "+r"(off_) - : "rm"(syscall_), + : "rm"(syscall), "0"((IsLinux() ? 9 : IsFreebsd() ? 477 : 197) | @@ -589,7 +589,7 @@ __attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd, // since it probably means a userspace program executed this loader // and passed us a custom syscall function earlier. if (Msyscall(code, codesize, os) != -1) { - syscall_ = 0; + syscall = 0; } #if TROUBLESHOOT @@ -600,7 +600,7 @@ __attribute__((__noreturn__)) static void Spawn(int os, const char *exe, int fd, // to extend the behavior of this loader in the future. we don't need // to clear the xmm registers since the ape loader should be compiled // with the -mgeneral-regs-only flag. - register void *r8 asm("r8") = syscall_; + register void *r8 asm("r8") = syscall; asm volatile("xor\t%%eax,%%eax\n\t" "xor\t%%r9d,%%r9d\n\t" "xor\t%%r10d,%%r10d\n\t" @@ -659,10 +659,10 @@ __attribute__((__noreturn__)) void ApeLoader(long di, long *sp, char dl, } // get syscall function pointer - if (handoff && handoff->systemcall) { - syscall_ = handoff->systemcall; + if (handoff && handoff->syscall) { + syscall = handoff->syscall; } else { - syscall_ = __syscall_loader; + syscall = __syscall_loader; } if (handoff) { diff --git a/ape/loader.h b/ape/loader.h index 45a7ae2b1..7080f1370 100644 --- a/ape/loader.h +++ b/ape/loader.h @@ -14,7 +14,7 @@ struct ApeLoader { int os; char *prog; char *page; - void *systemcall; + void *syscall; }; #endif /* COSMOPOLITAN_APE_LOADER_H_ */ diff --git a/ape/macros.internal.h b/ape/macros.internal.h index c17344116..973602a60 100644 --- a/ape/macros.internal.h +++ b/ape/macros.internal.h @@ -221,41 +221,44 @@ /** * Binary coded decimal support. * - *

This allows linker scripts to generate dd commands, e.g. ape.lds. - * There are a few ways to pad each number to the necessary 8 bytes. - * Spaces cannot be prepended because busybox refuses to parse them. - * Zeros cannot be prepended because Mac will take numbers as octal. - * That leaves appending spaces. The user's shell ought to treat any - * unquoted run of spaces as if there was only one, so this is safe. + *

This allows linker scripts to generate dd commands. Leading spaces + * need to be inserted so Mac doesn't consider them octal; therefore, + * parameters must be quoted; and eight digits should be good enough. */ -#define SHSTUB2(SYM, X) \ - HIDDEN(SYM##_bcs0 = BCD_LEFT(X)); \ - HIDDEN(SYM##_bcs1 = BCD_RIGHT(X)) -#define BCD_SMEAR(X) ((X) + (X) * 10000) -#define BCD_LEFT(X) \ - (((X)) < 10000 ? BCD_RIGHT(BCD_SMEAR(X)) | 0x10 \ - : (X) < 100000 ? BCD_RIGHT(BCD_SMEAR((X) / 10)) \ - : (X) < 1000000 ? BCD_RIGHT(BCD_SMEAR((X) / 100)) \ - : (X) < 10000000 ? BCD_RIGHT(BCD_SMEAR((X) / 1000)) \ - : (X) < 100000000 ? BCD_RIGHT(BCD_SMEAR((X) / 10000)) \ - : 0xffffffffffffffff) -#define BCD_RIGHT(X) \ - (((X)) < 10000 ? 0x20202020 \ - : (X) < 100000 ? 0x20202030 + \ - (X) % 10 \ - : (X) < 1000000 ? 0x20203030 + \ - ((X) / 10) % 10 + \ - (X) % 10 * 0x100 \ - : (X) < 10000000 ? 0x20303030 + \ - ((X) / 100) % 10 + \ - ((X) / 10) % 10 * 0x100 + \ - (X) % 10 * 0x10000 \ - : (X) < 100000000 ? 0x30303030 + \ - ((X) / 1000) % 10 + \ - ((X) / 100) % 10 * 0x100 + \ - ((X) / 10) % 10 * 0x10000 + \ - (X) % 10 * 0x1000000 \ - : 0xffffffffffffffff) +#define SHSTUB2(SYM, X) \ + HIDDEN(SYM##_bcs0 = BCD10K(X)); \ + HIDDEN(SYM##_bcs1 = BCD(X)) +#define BCD(X) \ + ((X) == 0 \ + ? 0x20202030 \ + : (X) < 10 ? 0x30202020 + (((X) % 10) << 24) \ + : (X) < 100 ? 0x30302020 + (((X) % 10) << 24) + \ + (((X) / 10 % 10) << 16) \ + : (X) < 1000 ? 0x30303020 + (((X) % 10) << 24) + \ + (((X) / 10 % 10) << 16) + \ + (((X) / 100 % 10) << 8) \ + : 0x30303030 + (((X) % 10) << 24) + \ + (((X) / 10 % 10) << 16) + \ + (((X) / 100 % 10) << 8) + \ + (((X) / 1000 % 10) << 0)) +#define BCD10K(X) \ + ((X) < 10000 \ + ? 0x20202020 \ + : (X) < 100000 \ + ? 0x30202020 + (((X) / 10000 % 10) << 24) \ + : (X) < 1000000 \ + ? 0x30302020 + (((X) / 10000 % 10) << 24) + \ + (((X) / 100000 % 10) << 16) \ + : (X) < 10000000 \ + ? 0x30303020 + (((X) / 10000 % 10) << 24) + \ + (((X) / 100000 % 10) << 16) + \ + (((X) / 1000000 % 10) << 8) \ + : (X) < 100000000 \ + ? 0x30303030 + (((X) / 10000 % 10) << 24) + \ + (((X) / 100000 % 10) << 16) + \ + (((X) / 1000000 % 10) << 8) + \ + (((X) / 10000000 % 10) << 0) \ + : 0xffffffffffffffff) #endif /* __ASSEMBLER__ */ #endif /* APE_MACROS_H_ */ diff --git a/examples/examples.mk b/examples/examples.mk index d18fe533d..1f1134f09 100644 --- a/examples/examples.mk +++ b/examples/examples.mk @@ -80,7 +80,6 @@ EXAMPLES_DIRECTDEPS = \ THIRD_PARTY_LUA \ THIRD_PARTY_MBEDTLS \ THIRD_PARTY_MUSL \ - THIRD_PARTY_NSYNC \ THIRD_PARTY_QUICKJS \ THIRD_PARTY_STB \ THIRD_PARTY_XED \ diff --git a/examples/greenbean.c b/examples/greenbean.c index 484fa361c..7169a6180 100644 --- a/examples/greenbean.c +++ b/examples/greenbean.c @@ -8,7 +8,6 @@ ╚─────────────────────────────────────────────────────────────────*/ #endif #include "libc/assert.h" -#include "libc/atomic.h" #include "libc/calls/calls.h" #include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/sigset.h" @@ -19,11 +18,14 @@ #include "libc/fmt/itoa.h" #include "libc/intrin/atomic.h" #include "libc/intrin/kprintf.h" +#include "libc/intrin/pthread.h" +#include "libc/intrin/wait0.internal.h" #include "libc/limits.h" #include "libc/log/check.h" #include "libc/log/log.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" +#include "libc/nexgen32e/threaded.h" #include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" #include "libc/runtime/stack.h" @@ -46,9 +48,6 @@ #include "libc/sysv/consts/sol.h" #include "libc/sysv/consts/tcp.h" #include "libc/thread/spawn.h" -#include "libc/thread/thread.h" -#include "libc/thread/tls.h" -#include "libc/thread/wait0.internal.h" #include "libc/time/struct/tm.h" #include "libc/time/time.h" #include "net/http/http.h" @@ -105,11 +104,11 @@ "Cache-Control: private; max-age=0\r\n" int threads; -atomic_int workers; -atomic_int messages; -atomic_int listening; -atomic_int connections; -atomic_int closingtime; +_Atomic(int) workers; +_Atomic(int) messages; +_Atomic(int) listening; +_Atomic(int) connections; +_Atomic(int) closingtime; const char *volatile status; void *Worker(void *id) { diff --git a/examples/hello4.c b/examples/hello4.c new file mode 100644 index 000000000..f3aecca99 --- /dev/null +++ b/examples/hello4.c @@ -0,0 +1,20 @@ +#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 "libc/math.h" +#include "libc/stdio/stdio.h" + +STATIC_YOINK("vga_console"); + +int main(int argc, char *argv[]) { + volatile long double x = -.5; + volatile long double y = 1.5; + printf("Hello World! %.19Lg\n", atan2l(x, y)); + return 0; +} diff --git a/examples/mu_test.c b/examples/mu_test.c deleted file mode 100644 index e69aa6614..000000000 --- a/examples/mu_test.c +++ /dev/null @@ -1,2054 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ -│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2016 Google Inc. │ -│ │ -│ Licensed under the Apache License, Version 2.0 (the "License"); │ -│ you may not use this file except in compliance with the License. │ -│ You may obtain a copy of the License at │ -│ │ -│ http://www.apache.org/licenses/LICENSE-2.0 │ -│ │ -│ Unless required by applicable law or agreed to in writing, software │ -│ distributed under the License is distributed on an "AS IS" BASIS, │ -│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │ -│ See the License for the specific language governing permissions and │ -│ limitations under the License. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/calls.h" -#include "libc/calls/weirdtypes.h" -#include "libc/errno.h" -#include "libc/fmt/conv.h" -#include "libc/fmt/fmt.h" -#include "libc/limits.h" -#include "libc/mem/mem.h" -#include "libc/runtime/runtime.h" -#include "libc/stdio/stdio.h" -#include "libc/stdio/temp.h" -#include "libc/str/str.h" -#include "libc/thread/thread.h" -#include "third_party/nsync/atomic.internal.h" -#include "third_party/nsync/cv.h" -#include "third_party/nsync/dll.h" -#include "third_party/nsync/mu.h" -#include "third_party/nsync/mu_wait.h" -#include "third_party/nsync/races.internal.h" -#include "third_party/nsync/time.h" -// clang-format off - -/** - * @fileoverview *NSYNC Mutex Tests / Benchmarks. - * - * make -j8 o//examples/mu_test.com - * o//examples/mu_test.com -b - * - */ - -typedef struct testing_base_s *testing_base; -typedef const struct testing_base_s *const_testing_base; -typedef struct testing_s *testing; - -/* Return a newly initialized testing_base. */ -testing_base testing_new (int argc, char *argv[], int flags); - -/* Return the index of the first argument in argv[] not processed by testing_new() */ -int testing_base_argn (testing_base tb); - -/* exit() with the test's exit status */ -int testing_base_exit (testing_base tb); - -/* Stop and start the benchmark timer. */ -void testing_stop_timer (testing t); -void testing_start_timer (testing t); - -/* Return whether the machine appears to be a uniprocessor. - Some tests get different results on uniprocessors, because - the probability of certain interleavings of thread actions is - greatly reduced. */ -int testing_is_uniprocessor (testing t); - -/* Given a testing_base, run f (t), where t has type testing. - Output will be for a test. */ -#define TEST_RUN(tb, f) testing_run_ ((tb), &f, #f, 0) - -/* Given a testing_base, run f (t), where t has type testing. - Output will be for a benchmark, which should iterate testing_n (t) times. */ -#define BENCHMARK_RUN(tb, f) testing_run_ ((tb), &f, #f, 1) - -/* Return the iteration count for a benchmark. */ -int testing_n (testing t); - -/* Output nul-terminated string msg[] to stderr, then abort(). */ -void testing_panic (const char *msg); - -/* Return a value below 0 if tests should run short, 0 if normal, and a value exceeding - 0 if tests should run long. */ -int testing_longshort (testing t); - -/* Return non-zero if the user requested verbose output. */ -int testing_verbose (testing t); - -/* Output a printf-formated log message associated with *t. - Example: TEST_LOG (t, ("wombat %d", some_int)); - The TEST_ERROR() and TEST_FATAL() forms of the call makr the test as failing. - The TEST_FATAL() form causes other subtests not to run. */ -#define TEST_LOG(t, args) testing_error_ ((t), 0, __FILE__, __LINE__, smprintf args); -#define TEST_ERROR(t, args) testing_error_ ((t), 1, __FILE__, __LINE__, smprintf args); -#define TEST_FATAL(t, args) testing_error_ ((t), 2, __FILE__, __LINE__, smprintf args); - -/* ---------------------------------------- */ - -/* internal details follow */ - -/* An internal routine used by TEST_RUN() and BENCHMARK_RUN(). */ -void testing_run_ (testing_base tb, void (*f) (testing t), const char *name, int is_benchmark); - -/* Output an error message msg, and record status. */ -void testing_error_ (testing t, int status, const char *file, int line, char *msg); - -//////////////////////////////////////////////////////////////////////////////// -// time_extra.h - -/* Return a malloced nul-terminated string representing time t, using - "decimals" decimal places. */ -char *nsync_time_str (nsync_time t, int decimals); - -/* Sleep until the specified time. Returns 0 on success, and EINTR - if the call was interrupted. */ -int nsync_time_sleep_until (nsync_time abs_deadline); - -/* Return t as a double. */ -double nsync_time_to_dbl (nsync_time t); - -/* Return a time corresponding to double d. */ -nsync_time nsync_time_from_dbl (double d); - -//////////////////////////////////////////////////////////////////////////////// -// smprintf.h - -char *smprintf (const char *fmt, ...); - -//////////////////////////////////////////////////////////////////////////////// -// smprintf.c - -char *smprintf (const char *fmt, ...) { - int m = strlen (fmt) * 2 + 1; - char *buf = (char *) malloc (m); - int didnt_fit; - do { - va_list ap; - int x; - va_start (ap, fmt); - x = vsnprintf (buf, m, fmt, ap); - va_end (ap); - if (x >= m) { - buf = (char *) realloc (buf, m = x+1); - didnt_fit = 1; - } else if (x < 0 || x == m-1) { - buf = (char *) realloc (buf, m *= 2); - didnt_fit = 1; - } else { - didnt_fit = 0; - } - } while (didnt_fit); - return (buf); -} - -//////////////////////////////////////////////////////////////////////////////// -// time_extra.c - -char *nsync_time_str (nsync_time t, int decimals) { - static const struct { - const char *suffix; - double multiplier; - } scale[] = { - { "ns", 1.0e-9, }, - { "us", 1e-6, }, - { "ms", 1e-3, }, - { "s", 1.0, }, - { "hr", 3600.0, }, - }; - double s = nsync_time_to_dbl (t); - int i = 0; - while (i + 1 != sizeof (scale) / sizeof (scale[0]) && scale[i + 1].multiplier <= s) { - i++; - } - return (smprintf ("%.*f%s", decimals, s/scale[i].multiplier, scale[i].suffix)); -} - -int nsync_time_sleep_until (nsync_time abs_deadline) { - int result = 0; - nsync_time now; - now = nsync_time_now (); - if (nsync_time_cmp (abs_deadline, now) > 0) { - nsync_time remaining; - remaining = nsync_time_sleep (nsync_time_sub (abs_deadline, now)); - if (nsync_time_cmp (remaining, nsync_time_zero) > 0) { - result = EINTR; - } - } - return (result); -} - -double nsync_time_to_dbl (nsync_time t) { - return (((double) NSYNC_TIME_SEC (t)) + ((double) NSYNC_TIME_NSEC (t) * 1e-9)); -} - -nsync_time nsync_time_from_dbl (double d) { - time_t s = (time_t) d; - if (d < s) { - s--; - } - return (nsync_time_s_ns (s, (unsigned) ((d - (double) s) * 1e9))); -} - -//////////////////////////////////////////////////////////////////////////////// -// start_thread.c - -struct thd_args { - void (*f) (void *); - void *arg; -}; - -static void *body (void *v) { - struct thd_args *args = (struct thd_args *) v; - (*args->f) (args->arg); - free (args); - return (NULL); -} - -void nsync_start_thread_ (void (*f) (void *), void *arg) { - struct thd_args *args = (struct thd_args *) malloc (sizeof (*args)); - pthread_t t; - args->f = f; - args->arg = arg; - pthread_create (&t, NULL, body, args); - pthread_detach (t); -} - -//////////////////////////////////////////////////////////////////////////////// -// closure.h - -/* A run-once, self-freeing closure. */ -typedef struct closure_s { - void (*f0) (void *); -} closure; - -/* Run the closure *cl, and free it. */ -void closure_run (closure *cl); - -/* Fork a new thread running the closure *cl, which will be freed when the - thread exits. */ -void closure_fork (closure *cl); - -/* To create a closure, declare a closure constructor with the right function arguments. - - For functions taking no arguments, use - CLOSURE_DECL_BODY0 (foo) - to generate the static routine: - static closure *closure_foo (void (*f) (void)); - that will return a closure for any function *f that takes no argument. - - For an 1-argument function, use - CLOSURE_DECL_BODY1 (foo, type) - to generate the static routine: - static closure *closure_foo (void (*f) (type), type x); - that will return a closure for any function taking a single argument of the - specified type. - - For an 2-argument function, use - CLOSURE_DECL_BODY2 (foo, type0, type1) - to generate the static routine: - static closure *closure_foo (void (*f) (type0, type1), type0 x0, type1 x1); - that will return a closure for any function taking a "type0" argument, and - a "type1" argument. - - And so on, up to 9 arguments. - - For example, to make closures out of qsort(): - - // First, just once (per module) define: - // static closure *closure_qsort_args ( - // void (*f) (void *, size_t, size_t, int (*)(const void *, const void *)) - // void *x0, size_t x1, size_t x2, int (*x3)(const void *, const void *)); - // by writing: - CLOSURE_DECL_BODY4 (qsort_args, void *, size_t, size_t, int (*)(const void *, const void *)) - - // Second, for each closure to be created, write something like this: - closure *cl = closure_qsort_args (array, n_elements, sizeof (array[0]), &elem_cmp); - - // Then to run (and free) each closure: - closure_run (cl); - // This is like calling - // qsort (array, n_elements, sizeof (array[0]), &elem_cmp); - // free (cl); - */ - - - - -/* ------------------------------------------------------------------ */ -/* Internal macro details follow. */ -#define CLOSURE_S0(x,e) e -#define CLOSURE_S1(x,e) x##0 -#define CLOSURE_S2(x,e) x##0, x##1 -#define CLOSURE_S3(x,e) x##0, x##1, x##2 -#define CLOSURE_S4(x,e) x##0, x##1, x##2, x##3 -#define CLOSURE_S5(x,e) x##0, x##1, x##2, x##3, x##4 -#define CLOSURE_S6(x,e) x##0, x##1, x##2, x##3, x##4, x##5 -#define CLOSURE_S7(x,e) x##0, x##1, x##2, x##3, x##4, x##5, x##6 -#define CLOSURE_S8(x,e) x##0, x##1, x##2, x##3, x##4, x##5, x##6, x##7 -#define CLOSURE_S9(x,e) x##0, x##1, x##2, x##3, x##4, x##5, x##6, x##7, x##8 - -#define CLOSURE_P0(x,y,p,s,t) -#define CLOSURE_P1(x,y,p,s,t) p x##0 y##0 t -#define CLOSURE_P2(x,y,p,s,t) p x##0 y##0 s x##1 y##1 t -#define CLOSURE_P3(x,y,p,s,t) p x##0 y##0 s x##1 y##1 s x##2 y##2 t -#define CLOSURE_P4(x,y,p,s,t) p x##0 y##0 s x##1 y##1 s x##2 y##2 s x##3 y##3 t -#define CLOSURE_P5(x,y,p,s,t) p x##0 y##0 s x##1 y##1 s x##2 y##2 s x##3 y##3 s x##4 y##4 t -#define CLOSURE_P6(x,y,p,s,t) p x##0 y##0 s x##1 y##1 s x##2 y##2 s x##3 y##3 s x##4 y##4 s \ - x##5 y##5 t -#define CLOSURE_P7(x,y,p,s,t) p x##0 y##0 s x##1 y##1 s x##2 y##2 s x##3 y##3 s x##4 y##4 s \ - x##5 y##5 s x##6 y##6 t -#define CLOSURE_P8(x,y,p,s,t) p x##0 y##0 s x##1 y##1 s x##2 y##2 s x##3 y##3 s x##4 y##4 s \ - x##5 y##5 s x##6 y##6 s x##7 y##7 t -#define CLOSURE_P9(x,y,p,s,t) p x##0 y##0 s x##1 y##1 s x##2 y##2 s x##3 y##3 s x##4 y##4 s \ - x##5 y##5 s x##6 y##6 s x##7 y##7 s x##8 y##8 t - -#define CLOSURE_COMMA_ , -#define CLOSURE_SEMI_ ; -#define CLOSURE_BLANK_ - -#define CLOSURE_DECL_BODY_N_(name, n) \ - struct closure_s_##name { \ - void (*f0) (void *); /* must be first; matches closure. */ \ - void (*f) (CLOSURE_S##n (closure_t_##name##_,void)); \ - CLOSURE_P##n (closure_t_##name##_, a, CLOSURE_BLANK_, \ - CLOSURE_SEMI_, CLOSURE_SEMI_) \ - }; \ - static void closure_f0_##name (void *v) { \ - struct closure_s_##name *a = (struct closure_s_##name *) v; \ - (*a->f) (CLOSURE_S##n (a->a,CLOSURE_BLANK_)); \ - free (a); \ - } \ - static closure *closure_##name (void (*f) (CLOSURE_S##n (closure_t_##name##_,void)) \ - CLOSURE_P##n (closure_t_##name##_, a, CLOSURE_COMMA_, \ - CLOSURE_COMMA_, CLOSURE_BLANK_)) { \ - struct closure_s_##name *cl = (struct closure_s_##name *) malloc (sizeof (*cl)); \ - cl->f0 = &closure_f0_##name; \ - cl->f = f; \ - CLOSURE_P##n (cl->a, = a, CLOSURE_BLANK_, CLOSURE_SEMI_, CLOSURE_SEMI_) \ - return ((closure *) cl); \ - } - - -#define CLOSURE_DECL_BODY0(name) \ - CLOSURE_DECL_BODY_N_ (name, 0) -#define CLOSURE_DECL_BODY1(name, t0) \ - typedef t0 closure_t_##name##_0; \ - CLOSURE_DECL_BODY_N_ (name, 1) -#define CLOSURE_DECL_BODY2(name, t0, t1) \ - typedef t0 closure_t_##name##_0; \ - typedef t1 closure_t_##name##_1; \ - CLOSURE_DECL_BODY_N_ (name, 2) -#define CLOSURE_DECL_BODY3(name, t0, t1, t2) \ - typedef t0 closure_t_##name##_0; \ - typedef t1 closure_t_##name##_1; \ - typedef t2 closure_t_##name##_2; \ - CLOSURE_DECL_BODY_N_ (name, 3) -#define CLOSURE_DECL_BODY4(name, t0, t1, t2, t3) \ - typedef t0 closure_t_##name##_0; \ - typedef t1 closure_t_##name##_1; \ - typedef t2 closure_t_##name##_2; \ - typedef t3 closure_t_##name##_3; \ - CLOSURE_DECL_BODY_N_ (name, 4) -#define CLOSURE_DECL_BODY5(name, t0, t1, t2, t3, t4) \ - typedef t0 closure_t_##name##_0; \ - typedef t1 closure_t_##name##_1; \ - typedef t2 closure_t_##name##_2; \ - typedef t3 closure_t_##name##_3; \ - typedef t4 closure_t_##name##_4; \ - CLOSURE_DECL_BODY_N_ (name, 5) -#define CLOSURE_DECL_BODY6(name, t0, t1, t2, t3, t4, t5) \ - typedef t0 closure_t_##name##_0; \ - typedef t1 closure_t_##name##_1; \ - typedef t2 closure_t_##name##_2; \ - typedef t3 closure_t_##name##_3; \ - typedef t4 closure_t_##name##_4; \ - typedef t5 closure_t_##name##_5; \ - CLOSURE_DECL_BODY_N_ (name, 6) -#define CLOSURE_DECL_BODY7(name, t0, t1, t2, t3, t4, t5, t6) \ - typedef t0 closure_t_##name##_0; \ - typedef t1 closure_t_##name##_1; \ - typedef t2 closure_t_##name##_2; \ - typedef t3 closure_t_##name##_3; \ - typedef t4 closure_t_##name##_4; \ - typedef t5 closure_t_##name##_5; \ - typedef t6 closure_t_##name##_6; \ - CLOSURE_DECL_BODY_N_ (name, 7) -#define CLOSURE_DECL_BODY8(name, t0, t1, t2, t3, t4, t5, t6, t7) \ - typedef t0 closure_t_##name##_0; \ - typedef t1 closure_t_##name##_1; \ - typedef t2 closure_t_##name##_2; \ - typedef t3 closure_t_##name##_3; \ - typedef t4 closure_t_##name##_4; \ - typedef t5 closure_t_##name##_5; \ - typedef t6 closure_t_##name##_6; \ - typedef t7 closure_t_##name##_7; \ - CLOSURE_DECL_BODY_N_ (name, 8) -#define CLOSURE_DECL_BODY9(name, t0, t1, t2, t3, t4, t5, t6, t7, t8) \ - typedef t0 closure_t_##name##_0; \ - typedef t1 closure_t_##name##_1; \ - typedef t2 closure_t_##name##_2; \ - typedef t3 closure_t_##name##_3; \ - typedef t4 closure_t_##name##_4; \ - typedef t5 closure_t_##name##_5; \ - typedef t6 closure_t_##name##_6; \ - typedef t7 closure_t_##name##_7; \ - typedef t8 closure_t_##name##_8; \ - CLOSURE_DECL_BODY_N_ (name, 9) - -//////////////////////////////////////////////////////////////////////////////// -// closure.c - -void nsync_start_thread_ (void (*f) (void *), void *arg); - -/* Run the closure *cl. */ -void closure_run (closure *cl) { - (*cl->f0) (cl); -} - -/* Run the closure (closure *), but wrapped to fix the type. */ -static void closure_run_body (void *v) { - closure_run ((closure *)v); -} - -void closure_fork (closure *cl) { - nsync_start_thread_ (&closure_run_body, cl); -} - -//////////////////////////////////////////////////////////////////////////////// -// atm_log.h - -void nsync_atm_log_ (int c, void *p, uint32_t o, uint32_t n, const char *file, int line); -void nsync_atm_log_print_ (void); - -//////////////////////////////////////////////////////////////////////////////// -// atm_log.c - -#ifndef NSYNC_ATM_LOG -#define NSYNC_ATM_LOG 0 -#endif - -struct atm_log { - uintptr_t i; - uintptr_t thd_id; - uintptr_t c; - void *p; - uintptr_t o; - uintptr_t n; - const char *file; - uintptr_t line; -}; - -#define LOG_N 14 - -static struct atm_log log_entries[1 << LOG_N]; -static uint32_t log_i; - -static pthread_mutex_t log_mu; - -static pthread_key_t key; -static pthread_once_t once = PTHREAD_ONCE_INIT; -static void do_once (void) { - pthread_mutex_init (&log_mu, NULL); - pthread_key_create (&key, NULL); -} -static int thread_id; - -void nsync_atm_log_ (int c, void *p, uint32_t o, uint32_t n, const char *file, int line) { - if (NSYNC_ATM_LOG) { - struct atm_log *e; - uint32_t i; - int *pthd_id; - int thd_id; - - pthread_once (&once, &do_once); - pthd_id = (int *) pthread_getspecific (key); - - pthread_mutex_lock (&log_mu); - i = log_i++; - if (pthd_id == NULL) { - thd_id = thread_id++; - pthd_id = (int *) malloc (sizeof (*pthd_id)); - pthread_setspecific (key, pthd_id); - *pthd_id = thd_id; - } else { - thd_id = *pthd_id; - } - pthread_mutex_unlock (&log_mu); - - e = &log_entries[i & ((1 << LOG_N) - 1)]; - e->i = i; - e->thd_id = thd_id; - e->c = c; - e->p = p; - e->o = o; - e->n = n; - e->file = file; - e->line = line; - } -} - -void nsync_atm_log_print_ (void) { - if (NSYNC_ATM_LOG) { - uint32_t i; - pthread_once (&once, &do_once); - pthread_mutex_lock (&log_mu); - for (i = 0; i != (1 << LOG_N); i++) { - struct atm_log *e = &log_entries[i]; - if (e->file != 0) { - fprintf (stderr, "%6lx %3d %c p %16p o %8x n %8x %10s:%d\n", - (unsigned long) e->i, - (int) e->thd_id, - e->c <= ' '? '?' : (char)e->c, - e->p, - (uint32_t) e->o, - (uint32_t) e->n, - e->file, - (int) e->line); - } - } - pthread_mutex_unlock (&log_mu); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// testing.c - -struct testing_base_s { - int flags; /* flags from testing_new(); r/o after init */ - int parallelism; /* max parallelism to use; r/o after init */ - FILE *fp; /* where to send output; pointer is r/o after init */ - int argn; /* first arg not processed by testing_new(); r/o after init */ - int argc; /* argc passed to testing_new(); r/o after init */ - char **argv; /* argv passed to testing_new(); r/o after init */ - char *prog; /* name of programme, from argv[0] */ - int suppress_header; /* supress hreader on benchmarks */ - int run_tests; /* whether to run tests */ - int run_benchmarks; /* whether to run benchmarks */ - int benchmarks_running; /* and benchmarks are now running */ - char *include_pat; /* ,- or |-separated substrings of tests to include */ - char *exclude_pat; /* ,- or |-separated substrings of tests to exclude */ - int longshort; /* 0 normal, -1 short, 1 long */ - int verbose; /* 0 normal; 1 verbose output */ - - nsync_mu testing_mu; /* protects fields below */ - int is_uniprocessor; /* whether the system is a uniprocessor */ - nsync_dll_list_ children; /* list of testing_s structs whose base is this testing_base_s */ - int child_count; /* count of testing_s structs whose base is this testing_base_s */ - int exit_status; /* final exit status */ -}; - -struct testing_s { - struct testing_base_s *base; /* r/o after init */ - int test_status; /* status; merged into common->exit_status */ - int n; /* benchmark iteration count */ - nsync_atomic_uint32_ partial_line; /* whether partial test banner emitted last*/ - FILE *fp; /* where to output; merged into common->fp if != to it */ - nsync_time start_time; /* timer start time; for benchmarks */ - nsync_time stop_time; /* when the timer was stopped; for benchmarks */ - void (*f) (testing); /* test function to run */ - const char *name; /* name of test */ - nsync_dll_element_ siblings; /* part of list of siblings */ -}; - -/* Output the header for benchmarks. */ -static void output_header (FILE *fp, const char *prog) { - int i; - int hdrlen = fprintf (fp, "%-10s%-40s %9s %8s %8s %8s\n", "Benchmark", prog, "ops", "time", - "ops/sec", "time/op"); - for (i = 1; i < hdrlen; i++) { - putc ('-', fp); - } - putc ('\n', fp); - fflush (fp); -} - -/* Process a single flag. *pargn is main's argn */ -static void process_flag (testing_base tb, int *pargn, int argc, char *argv[], int flag, - const char *arg) { - switch (flag) { - case 'b': - tb->run_benchmarks = 1; - break; - case 'B': - tb->run_benchmarks = 1; - tb->run_tests = 0; - break; - case 'H': - output_header (stdout, ""); - exit (0); - case 'h': - tb->suppress_header = 1; - break; - case 'l': - tb->longshort++; - break; - case 'm': - if (*pargn + 1 == argc) { - fprintf (stderr, "%s: -m flag expects ,- or |-separated strings\n", - argv[0]); - exit (2); - } - tb->include_pat = argv[++*pargn]; - break; - case 'n': - if (*pargn + 1 == argc || atoi (argv[1 + *pargn]) < 1) { - fprintf (stderr, "%s: -n flag expects parallelism value >= 1\n", argv[0]); - exit (2); - } - tb->parallelism = atoi (argv[++*pargn]); - break; - case 's': - tb->longshort--; - break; - case 'v': - tb->verbose = 1; - break; - case 'x': - if (*pargn + 1 == argc) { - fprintf (stderr, "%s: -x flag expects ,- or |-separated strings\n", - argv[0]); - exit (2); - } - tb->exclude_pat = argv[++*pargn]; - break; - default: - fprintf (stderr, "%s: unrecognized flag '%c' in arg %d: \"%s\"\n", argv[0], flag, - *pargn, arg); - exit (2); - } -} - -testing_base testing_new (int argc, char *argv[], int flags) { - static const char sep[] = { '/', '\\' }; - int i; - int argn; - testing_base tb = (testing_base)malloc (sizeof (*tb)); - memset ((void *) tb, 0, sizeof (*tb)); - tb->flags = flags; - tb->fp = stderr; - tb->argc = argc; - tb->argv = argv; - tb->parallelism = 1; - tb->run_tests = 1; - tb->is_uniprocessor = -1; - tb->prog = tb->argv[0]; - for (i = 0; i != sizeof (sep) / sizeof (sep[0]); i++) { - char *last = strrchr (tb->prog, sep[i]); - if (last != NULL) { - tb->prog = last + 1; - } - } - for (argn = 1; argn != argc && argv[argn][0] == '-' && - strcmp (argv[argn], "--") != 0; argn++) { - const char *arg = argv[argn]; - const char *f; - for (f = &arg[1]; *f != 0; f++) { - process_flag (tb, &argn, argc, argv, *f, arg); - } - } - tb->argn = argn + (argn != argc && strcmp (argv[argn], "--") == 0); - return (tb); -} - -int testing_verbose (testing t) { - return (t->base->verbose); -} - -int testing_longshort (testing t) { - return (t->base->longshort); -} - -int testing_n (testing t) { - return (t->n); -} - -int testing_base_argn (testing_base tb) { - return (tb->argn); -} - -/* Return whether *(int *)v is zero. Used with nsync_mu_wait(). */ -static int int_is_zero (const void *v) { - return (*(const int *)v == 0); -} - -int testing_base_exit (testing_base tb) { - int exit_status; - nsync_mu_lock (&tb->testing_mu); - nsync_mu_wait (&tb->testing_mu, &int_is_zero, &tb->child_count, NULL); - exit_status = tb->exit_status; - nsync_mu_unlock (&tb->testing_mu); - free (tb); - exit (exit_status); - return (exit_status); -} - -/* Cleanup code used after running either a test or a benchmark, - called at the end of run_test() and run_benchmark(). */ -static void finish_run (testing t) { - testing_base tb = t->base; - fflush (t->fp); - nsync_mu_lock (&tb->testing_mu); - if (t->fp != tb->fp) { - int c; - rewind (t->fp); - while ((c = getc (t->fp)) != EOF) { - putc (c, tb->fp); - } - fclose (t->fp); - fflush (tb->fp); - } - if (tb->exit_status < t->test_status) { - tb->exit_status = t->test_status; - } - tb->children = nsync_dll_remove_ (tb->children, &t->siblings); - tb->child_count--; - nsync_mu_unlock (&tb->testing_mu); - free (t); -} - -/* Run the test (*t->f)(t), and report on t->fp how long it took and its final - status, which is set to non-zero if the test reported errors. */ -static void run_test (testing t) { - testing_base tb = t->base; - char *elapsed_str = 0; - fprintf (t->fp, "%-25s %-45s ", tb->prog, t->name); - fflush (t->fp); - ATM_STORE (&t->partial_line, 1); - t->test_status = 0; - t->n = 0; - t->stop_time = nsync_time_zero; - t->start_time = nsync_time_now (); - (*t->f) (t); - elapsed_str = nsync_time_str (nsync_time_sub (nsync_time_now (), t->start_time), 2); - if (!ATM_LOAD (&t->partial_line)) { - fprintf (t->fp, "%-25s %-45s %s %8s\n", tb->prog, t->name, - t->test_status != 0? "failed": "passed", elapsed_str); - } else { - fprintf (t->fp, "%s %8s\n", t->test_status != 0? "failed": "passed", elapsed_str); - } - ATM_STORE (&t->partial_line, 0); - fflush (t->fp); - free (elapsed_str); - finish_run (t); -} - -/* Run the benchmark (*t->f)(t) repeatedly, specifying successively greater - numbers of iterations, measuring how long it takes each time. Eventually, - it takes long enough to get a reasonable estimate of how long each iteration - takes, which is reported on t->fp. */ -static void run_benchmark (testing t) { - char *elapsed_str = 0; - char *time_per_op_str = 0; - double elapsed; - int n = 1; - double target = 2.0; - int longshort = testing_longshort (t); - if (longshort < 0) { - target = 1e-3 * (2000 >> -longshort); - } else if (longshort > 0) { - target = 1e-3 * (2000 << longshort); - } - do { - int32_t mul; - t->test_status = 0; - t->n = n; - t->stop_time = nsync_time_zero; - t->start_time = nsync_time_now (); - (*t->f) (t); - elapsed = nsync_time_to_dbl (nsync_time_sub (nsync_time_now (), t->start_time)); - if (elapsed < 1e-1) { - elapsed = 1e-1; - } - mul = (int32_t) (target / elapsed); - while (elapsed * mul * 4 < target * 5) { - mul++; - } - if (mul > 1 && elapsed * mul * 2 < target * 3 && n < INT_MAX / mul) { - n *= mul; - } else if (n < INT_MAX / 2) { - n *= 2; - } - } while (t->test_status == 0 && elapsed < target && n != t->n); - elapsed_str = nsync_time_str (nsync_time_from_dbl (elapsed), 2); - time_per_op_str = nsync_time_str (nsync_time_from_dbl (elapsed / t->n), 2); - fprintf (t->fp, "%-50s %9d %8s %8.2g %8s%s\n", t->name, t->n, elapsed_str, - ((double)t->n) / elapsed, time_per_op_str, - t->test_status != 0 ? " *** failed ***" : ""); - free (elapsed_str); - free (time_per_op_str); - finish_run (t); -} - -CLOSURE_DECL_BODY1 (testing, testing) - -/* Return whether there's a "spare thread"; that is, whether the current count - of child threads is less than the allowed parallelism. */ -static int spare_thread (const void *v) { - const_testing_base tb = (const_testing_base) v; - return (tb->child_count < tb->parallelism); -} - -/* Return whether nul-terminated string str[] contains a string listed in - comma-separated (or vertical bar-separted) strings in nul-terminated string - pat[]. A dollar at the end of a string in pat[] matches the end of - string in str[]. */ -static int match (const char *pat, const char *str) { - static const char seps[] = ",|"; - int found = 0; - char Xbuf[128]; - int m = sizeof (Xbuf) - 1; - char *mbuf = NULL; - char *buf = Xbuf; - int i = 0; - while (!found && pat[i] != '\0') { - int blen = strcspn (&pat[i], seps); - int e = i + blen; - if (blen > m) { - m = blen + 128; - buf = mbuf = (char *) realloc (mbuf, m + 1); - } - memcpy (buf, &pat[i], blen); - buf[blen] = '\0'; - if (blen > 0 && buf[blen - 1] == '$') { - int slen = strlen (str); - buf[--blen] = 0; - found = (slen >= blen && - strcmp (&str[slen-blen], buf) == 0); - } else { - found = (strstr (str, buf) != NULL); - } - i = e + strspn (&pat[e], seps); - } - free (mbuf); - return (found); -} - -void testing_run_ (testing_base tb, void (*f) (testing t), const char *name, int is_benchmark) { - int exit_status; - nsync_mu_lock (&tb->testing_mu); - exit_status = tb->exit_status; - nsync_mu_unlock (&tb->testing_mu); - if (exit_status < 2 && - (!is_benchmark || tb->run_benchmarks) && - (is_benchmark || tb->run_tests) && - (tb->include_pat == NULL || match (tb->include_pat, name)) && - (tb->exclude_pat == NULL || !match (tb->exclude_pat, name))) { - testing t = (testing) malloc (sizeof (*t)); - memset ((void *) t, 0, sizeof (*t)); - nsync_dll_init_ (&t->siblings, t); - t->base = tb; - t->f = f; - t->name = name; - if (tb->parallelism == 1) { - t->fp = tb->fp; - } else { - t->fp = tmpfile (); - } - if (!is_benchmark) { - if (tb->benchmarks_running) { - nsync_mu_lock (&tb->testing_mu); - nsync_mu_wait (&tb->testing_mu, &int_is_zero, &tb->child_count, NULL); - nsync_mu_unlock (&tb->testing_mu); - tb->benchmarks_running = 0; - } - nsync_mu_lock (&tb->testing_mu); - nsync_mu_wait (&tb->testing_mu, &spare_thread, tb, NULL); - tb->child_count++; - tb->children = nsync_dll_make_last_in_list_ (tb->children, &t->siblings); - nsync_mu_unlock (&tb->testing_mu); - closure_fork (closure_testing (&run_test, t)); - } else { - if (!tb->benchmarks_running) { - nsync_mu_lock (&tb->testing_mu); - nsync_mu_wait (&tb->testing_mu, &int_is_zero, &tb->child_count, NULL); - nsync_mu_unlock (&tb->testing_mu); - if (!tb->suppress_header) { - output_header (tb->fp, tb->prog); - } - tb->benchmarks_running = 1; - } - nsync_mu_lock (&tb->testing_mu); - nsync_mu_wait (&tb->testing_mu, &spare_thread, tb, NULL); - tb->child_count++; - tb->children = nsync_dll_make_last_in_list_ (tb->children, &t->siblings); - nsync_mu_unlock (&tb->testing_mu); - closure_fork (closure_testing (&run_benchmark, t)); - } - } -} - -/* Used to decide whether the test is running on a uniprocessor. */ -struct is_uniprocessor_s { - double count; /* count of iterations while *state==1 */ - nsync_atomic_uint32_ done; /* set to 1 when thread finishes */ - char dummy[256]; /* so structs don't share cache line */ -}; - -/* An anciliary thread that waits until *state is 1, then increments s->count - while *state stays 1, and then sets s->done to 1 before exiting. */ -static void uniprocessor_check (nsync_atomic_uint32_ *state, struct is_uniprocessor_s *s) { - IGNORE_RACES_START (); - while (ATM_LOAD_ACQ (state) != 1) { - } - while (ATM_LOAD_ACQ (state) == 1) { - s->count++; - } - ATM_STORE_REL (&s->done, 1); - IGNORE_RACES_END (); -} - -CLOSURE_DECL_BODY2 (uniprocessor_check, nsync_atomic_uint32_ *, struct is_uniprocessor_s *) - -/* Return whether the test is running on a uniprocessor. - - Some of the tests rely on interleaving of actions between threads. - Particular interleavings are much less likely on uniprocessors, so the tests - do not run, or do not declare an error if the system is a uniprocessor. - - Operating systems vary significantly in how one may ask how many procerssors - are present, so we use a heuristic based on comparing the timing of a single - thread, and two concurrent threads. */ -int testing_is_uniprocessor (testing t) { - int is_uniprocessor; - nsync_mu_lock (&t->base->testing_mu); - is_uniprocessor = t->base->is_uniprocessor; - if (is_uniprocessor == -1) { - int i; - struct is_uniprocessor_s s[3]; - nsync_atomic_uint32_ state; - for (i = 0; i != 3; i++) { - s[i].count = 0.0; - s[i].done = 0; - } - - ATM_STORE_REL (&state, 0); - closure_fork (closure_uniprocessor_check (&uniprocessor_check, &state, &s[0])); - nsync_time_sleep (nsync_time_ms (100)); - ATM_STORE_REL (&state, 1); - nsync_time_sleep (nsync_time_ms (400)); - ATM_STORE_REL (&state, 2); - while (!ATM_LOAD_ACQ (&s[0].done)) { - } - - ATM_STORE_REL (&state, 0); - closure_fork (closure_uniprocessor_check (&uniprocessor_check, &state, &s[1])); - closure_fork (closure_uniprocessor_check (&uniprocessor_check, &state, &s[2])); - nsync_time_sleep (nsync_time_ms (100)); - ATM_STORE_REL (&state, 1); - nsync_time_sleep (nsync_time_ms (400)); - ATM_STORE_REL (&state, 2); - while (!ATM_LOAD_ACQ (&s[1].done) || !ATM_LOAD_ACQ (&s[2].done)) { - } - t->base->is_uniprocessor = ((s[1].count + s[2].count) / s[0].count) < 1.7; - is_uniprocessor = t->base->is_uniprocessor; - } - nsync_mu_unlock (&t->base->testing_mu); - return (is_uniprocessor); -} - -void testing_stop_timer (testing t) { - if (nsync_time_cmp (t->stop_time, nsync_time_zero) != 0) { - abort (); - } - t->stop_time = nsync_time_now (); -} - -void testing_start_timer (testing t) { - if (nsync_time_cmp (t->stop_time, nsync_time_zero) == 0) { - abort (); - } - t->start_time = nsync_time_add (t->start_time, - nsync_time_sub (nsync_time_now (), t->stop_time)); - t->stop_time = nsync_time_zero; -} - -void testing_error_ (testing t, int test_status, const char *file, int line, char *msg) { - int len = strlen (msg); - int addnl = (len == 0 || msg[len - 1] != '\n'); - if (t->test_status < test_status) { - t->test_status = test_status; - } - if (ATM_LOAD (&t->partial_line)) { - ATM_STORE (&t->partial_line, 0); - fprintf (t->fp, "\n%s: %s:%d: %s%s", - test_status == 2? "fatal": test_status == 1? "error": "info", file, line, msg, - addnl? "\n": ""); - } else { - fprintf (t->fp, "%s: %s:%d: %s%s", - test_status == 2? "fatal": test_status == 1? "error": "info", file, line, msg, - addnl? "\n": ""); - } - free (msg); -} - -void nsync_panic_(const char *); - -/* Abort after printing the nul-terminated string s[]. */ -void testing_panic (const char *s) { - nsync_atm_log_print_ (); - nsync_panic_ (s); -} - -//////////////////////////////////////////////////////////////////////////////// -// mu_test.c - -/* The state shared between the threads in each of the tests below. */ -typedef struct test_data_s { - testing t; - int n_threads; /* Number of test threads; constant after init. */ - int loop_count; /* Iteration count for each test thread; constant after init */ - - /* mu_in_use protects i, id, loop_count, and finished_threads. */ - void *mu_in_use; /* points at mu, mutex, or rwmutex depending on which is in use. */ - void (*lock) (void *); /* operations on mu_in_use */ - void (*unlock) (void *); - - nsync_mu mu; - pthread_mutex_t mutex; - pthread_rwlock_t rwmutex; - - int i; /* counter incremented by test loops. */ - volatile int id; /* id of current lock-holding thread in some tests. */ - - nsync_cv done; /* Signalled when finished_threads==n_threads. */ - int finished_threads; /* Count of threads that have finished. */ -} test_data; - -/* Indicate that a thread has finished its operations on test_data - by incrementing td.finished_threads, and signal td.done when it reaches td.n_threads. - See test_data_wait_for_all_threads(). */ -static void test_data_thread_finished (test_data *td) { - (*td->lock) (td->mu_in_use); - td->finished_threads++; - if (td->finished_threads == td->n_threads) { - nsync_cv_broadcast (&td->done); - } - (*td->unlock) (td->mu_in_use); -} - -/* Wait until all td.n_threads have called test_data_thread_finished(), - and then return. */ -static void test_data_wait_for_all_threads (test_data *td) { - (*td->lock) (td->mu_in_use); - while (td->finished_threads != td->n_threads) { - nsync_cv_wait_with_deadline_generic (&td->done, td->mu_in_use, - td->lock, td->unlock, - nsync_time_no_deadline, NULL); - } - (*td->unlock) (td->mu_in_use); -} - -/* --------------------------------------- */ - -/* The body of each thread executed by test_mu_nthread() - and test_mutex_nthread. - *td represents the test data that the threads share, and id is an integer - unique to each test thread. */ -static void counting_loop (test_data *td, int id) { - int n = td->loop_count; - int i = 0; - for (i = 0; i != n; i++) { - (*td->lock) (td->mu_in_use); - td->id = id; - td->i++; - if (td->id != id) { - testing_panic ("td->id != id"); - } - (*td->unlock) (td->mu_in_use); - } - test_data_thread_finished (td); -} - -CLOSURE_DECL_BODY2 (counting, test_data *, int) - -/* Versions of nsync_mu_lock() and nsync_mu_unlock() that take "void *" - arguments, to avoid call through a function pointer of a different type, - which is undefined. */ -static void void_mu_lock (void *mu) { - nsync_mu_lock ((nsync_mu *) mu); -} -static void void_mu_unlock (void *mu) { - nsync_mu_unlock((nsync_mu *) mu); -} - -/* Create a few threads, each of which increments an - integer a fixed number of times, using an nsync_mu for mutual exclusion. - It checks that the integer is incremented the correct number of times. */ -static void test_mu_nthread (testing t) { - int loop_count = 100000; - nsync_time deadline; - deadline = nsync_time_add (nsync_time_now (), nsync_time_ms (1500)); - do { - int i; - test_data td; - memset ((void *) &td, 0, sizeof (td)); - td.t = t; - td.n_threads = 5; - td.loop_count = loop_count; - td.mu_in_use = &td.mu; - td.lock = &void_mu_lock; - td.unlock = &void_mu_unlock; - for (i = 0; i != td.n_threads; i++) { - closure_fork (closure_counting (&counting_loop, &td, i)); - } - test_data_wait_for_all_threads (&td); - if (td.i != td.n_threads*td.loop_count) { - TEST_FATAL (t, ("test_mu_nthread final count inconsistent: want %d, got %d", - td.n_threads*td.loop_count, td.i)); - } - loop_count *= 2; - } while (nsync_time_cmp (nsync_time_now (), deadline) < 0); -} - -/* void pthread_mutex_lock */ -static void void_pthread_mutex_lock (void *mu) { - pthread_mutex_lock ((pthread_mutex_t *) mu); -} - -/* void pthread_mutex_unlock */ -static void void_pthread_mutex_unlock (void *mu) { - pthread_mutex_unlock ((pthread_mutex_t *) mu); -} - -/* Create a few threads, each of which increments an - integer a fixed number of times, using a pthread_mutex_t for mutual exclusion. - It checks that the integer is incremented the correct number of times. */ -static void test_mutex_nthread (testing t) { - int loop_count = 100000; - nsync_time deadline; - deadline = nsync_time_add (nsync_time_now (), nsync_time_ms (1500)); - do { - int i; - test_data td; - memset ((void *) &td, 0, sizeof (td)); - td.t = t; - td.n_threads = 5; - td.loop_count = loop_count; - td.mu_in_use = &td.mutex; - td.lock = &void_pthread_mutex_lock; - td.unlock = &void_pthread_mutex_unlock; - pthread_mutex_init (&td.mutex, NULL); - for (i = 0; i != td.n_threads; i++) { - closure_fork (closure_counting (&counting_loop, &td, i)); - } - test_data_wait_for_all_threads (&td); - if (td.i != td.n_threads*td.loop_count) { - TEST_FATAL (t, ("test_mutex_nthread final count inconsistent: want %d, got %d", - td.n_threads*td.loop_count, td.i)); - } - pthread_mutex_destroy (&td.mutex); - loop_count *= 2; - } while (nsync_time_cmp (nsync_time_now (), deadline) < 0); -} - -/* void pthread_rwlock_wrlock */ -static void void_pthread_rwlock_wrlock (void *mu) { - pthread_rwlock_wrlock ((pthread_rwlock_t *) mu); -} - -/* void pthread_rwlock_unlock */ -static void void_pthread_rwlock_unlock (void *mu) { - pthread_rwlock_unlock ((pthread_rwlock_t *) mu); -} - -/* Create a few threads, each of which increments an - integer a fixed number of times, using a pthread_rwlock_t for mutual exclusion. - It checks that the integer is incremented the correct number of times. */ -static void test_rwmutex_nthread (testing t) { - int loop_count = 100000; - nsync_time deadline; - deadline = nsync_time_add (nsync_time_now (), nsync_time_ms (1500)); - do { - int i; - test_data td; - memset ((void *) &td, 0, sizeof (td)); - td.t = t; - td.n_threads = 5; - td.loop_count = loop_count; - td.mu_in_use = &td.rwmutex; - td.lock = &void_pthread_rwlock_wrlock; - td.unlock = &void_pthread_rwlock_unlock; - pthread_rwlock_init (&td.rwmutex, NULL); - for (i = 0; i != td.n_threads; i++) { - closure_fork (closure_counting (&counting_loop, &td, i)); - } - test_data_wait_for_all_threads (&td); - if (td.i != td.n_threads*td.loop_count) { - TEST_FATAL (t, ("test_mutex_nthread final count inconsistent: want %d, got %d", - td.n_threads*td.loop_count, td.i)); - } - pthread_rwlock_destroy (&td.rwmutex); - loop_count *= 2; - } while (nsync_time_cmp (nsync_time_now (), deadline) < 0); -} - -/* --------------------------------------- */ - -/* The body of each thread executed by test_try_mu_nthread(). - *td represents the test data that the threads share, and id is an integer - unique to each test thread. */ -static void counting_loop_try_mu (test_data *td, int id) { - int i; - int n = td->loop_count; - for (i = 0; i != n; i++) { - while (!nsync_mu_trylock (&td->mu)) { - sched_yield (); - } - td->id = id; - td->i++; - if (td->id != id) { - testing_panic ("td->id != id"); - } - n = td->loop_count; - nsync_mu_unlock (&td->mu); - } - test_data_thread_finished (td); -} - -/* Test that acquiring an nsync_mu with nsync_mu_trylock() - using several threads provides mutual exclusion. */ -static void test_try_mu_nthread (testing t) { - int loop_count = 100000; - nsync_time deadline; - deadline = nsync_time_add (nsync_time_now (), nsync_time_ms (1500)); - do { - int i; - test_data td; - memset ((void *) &td, 0, sizeof (td)); - td.t = t; - td.n_threads = 5; - td.loop_count = loop_count; - td.mu_in_use = &td.mu; - td.lock = &void_mu_lock; - td.unlock = &void_mu_unlock; - for (i = 0; i != td.n_threads; i++) { - closure_fork (closure_counting (&counting_loop_try_mu, &td, i)); - } - test_data_wait_for_all_threads (&td); - if (td.i != td.n_threads*td.loop_count) { - TEST_FATAL (t, ("test_try_mu_nthread final count inconsistent: want %d, got %d", - td.n_threads*td.loop_count, td.i)); - } - loop_count *= 2; - } while (nsync_time_cmp (nsync_time_now (), deadline) < 0); -} - -/* --------------------------------------- */ - -/* An integer protected by a mutex, and with an associated - condition variable that is signalled when the counter reaches 0. */ -typedef struct counter_s { - nsync_mu mu; /* protects value */ - int value; - nsync_cv cv; /* signalled when value becomes 0 */ -} counter; - -/* Return a counter with initial value "initial". */ -static counter *counter_new (int initial) { - counter *c = (counter *) malloc (sizeof (*c)); - memset ((void *) c, 0, sizeof (*c)); - c->value = initial; - return (c); -} - -/* Increment *c by "increment". */ -static void counter_inc (counter *c, int increment) { - if (increment != 0) { - nsync_mu_lock (&c->mu); - c->value += increment; - if (c->value == 0) { - nsync_cv_broadcast (&c->cv); - } - nsync_mu_unlock (&c->mu); - } -} - -/* Wait on *c's condition variable until the counter - becomes 0, or abs_deadline is reached. */ -static int counter_wait_for_zero_with_deadline (counter *c, nsync_time abs_deadline) { - int value; - nsync_mu_rlock (&c->mu); - while (c->value != 0 && - nsync_cv_wait_with_deadline (&c->cv, &c->mu, abs_deadline, NULL) == 0) { - } - value = c->value; - nsync_mu_runlock (&c->mu); - return (value); -} - -/* Wait on *c's condition variable until the counter becomes 0. */ -static void counter_wait_for_zero (counter *c) { - int value = counter_wait_for_zero_with_deadline (c, nsync_time_no_deadline); - if (value != 0) { - testing_panic (smprintf ("wait_for_zero() about to return with " - "non-zero value %d", value)); - } -} - -/* Return the current value of *c. */ -static int counter_value (counter *c) { - int value; - nsync_mu_rlock (&c->mu); - value = c->value; - nsync_mu_runlock (&c->mu); - return (value); -} - -/* --------------------------------------- */ - -CLOSURE_DECL_BODY9 (attempt_trylock, testing , const char *, int, nsync_mu *, - int, int, int *, int, counter *) - -/* Call nsync_mu_trylock(), and compares the result to expected_acquire. - If the lock was acquired, then: - - if expected_value != -1, compare *value against expected_value. - - increment *value. - - if release is non-zero, release the lock before returning. - In any case, the counter *done is decremented. */ -static void attempt_trylock (testing t, const char *id, int verbose, - nsync_mu *mu, int expected_acquire, int release, - int *value, int expected_value, counter *done) { - int acquired = nsync_mu_trylock (mu); - if (acquired != expected_acquire) { - testing_panic (smprintf ("attempt_trylock %s: expected " - "nsync_mu_trylock() to return %d but got %d", - id, expected_acquire, acquired)); - } - if (verbose) { - TEST_LOG (t, ("attempt_trylock %s %d\n", id, acquired)); - } - if (acquired) { - nsync_mu_assert_held (mu); - if (expected_value != -1 && *value != expected_value) { - testing_panic (smprintf ("attempt_trylock %s expected " - "value %d, *value=%d", - id, expected_value, *value)); - } - (*value)++; - if (verbose) { - TEST_LOG (t, ("attempt_trylock %s incremented value to %d\n", id, *value)); - } - if (release) { - nsync_mu_unlock (mu); - } - } - counter_inc (done, -1); -} - -/* Call nsync_mu_rtrylock(), and compare the result to expected_acquire. - If the lock was acquired, then: - - if expected_value != -1, compare *value against expected_value. - - if release is non-zero, release the lock before returning. - In any case, decrement *done. */ -static void attempt_rtrylock (testing t, const char *id, int verbose, - nsync_mu *mu, int expected_acquire, int release, - int *value, int expected_value, counter *done) { - int acquired = nsync_mu_rtrylock (mu); - if (acquired != expected_acquire) { - testing_panic (smprintf ("attempt_rtrylock %s: expected " - "nsync_mu_rtrylock() to return %d but got %d", - id, expected_acquire, acquired)); - } - if (verbose) { - TEST_LOG (t, ("attempt_rtrylock %s %d\n", id, acquired)); - } - if (acquired) { - nsync_mu_rassert_held (mu); - if (expected_value != -1 && *value != expected_value) { - testing_panic (smprintf ("attempt_rtrylock %s expected " - "value %d, *value=%d", - id, expected_value, *value)); - } - if (release) { - nsync_mu_runlock (mu); - } - } - counter_inc (done, -1); -} - -CLOSURE_DECL_BODY9 (lock_unlock, testing, const char *, int, nsync_mu *, - int *, int, nsync_time, counter *, counter *) - -/* First acquire *mu, then: - - if expected_value != -1, compare *value against expected_value. - - increment *value. - - sleep for "sleep". - Then release *mu and decrement *done. */ -static void lock_unlock (testing t, const char *id, int verbose, nsync_mu *mu, int *value, - int expected_value, nsync_time sleep, counter *sleeping, counter *done) { - if (verbose) { - TEST_LOG (t, ("lock_unlock %s\n", id)); - } - if (sleeping != NULL) { - counter_inc (sleeping, -1); - } - nsync_mu_lock (mu); - nsync_mu_assert_held (mu); - if (expected_value != -1 && *value != expected_value) { - testing_panic (smprintf ("lock_unlock %s expected " - "value %d, *value=%d", - id, expected_value, *value)); - } - (*value)++; - if (verbose) { - TEST_LOG (t, ("lock_unlock %s incremented value to %d\n", id, *value)); - } - nsync_time_sleep (sleep); - nsync_mu_unlock (mu); - counter_inc (done, -1); -} - -/* First acquire *mu in read mode, then: - - if expected_value != -1, compare *value against expected_value. - - sleep for "sleep". - Then release *mu and decrement *done. */ -static void rlock_runlock (testing t, const char *id, int verbose, nsync_mu *mu, - int *value, int expected_value, nsync_time sleep, - counter *sleeping, counter *done) { - if (verbose) { - TEST_LOG (t, ("rlock_runlock %s\n", id)); - } - if (sleeping != NULL) { - counter_inc (sleeping, -1); - } - nsync_mu_rlock (mu); - nsync_mu_rassert_held (mu); - if (expected_value != -1 && *value != expected_value) { - testing_panic (smprintf ("rlock_runlock %s expected " - "value %d, *value=%d", id, expected_value, *value)); - } - nsync_time_sleep (sleep); - nsync_mu_runlock (mu); - counter_inc (done, -1); -} - -/* Check that the time since start_time is between expected_duration-1ms. - If the time exceeds expected_duration+slop_duration, return 1, else 0. */ -static int check_times (testing t, const char *id, nsync_time start_time, - nsync_time expected_duration, nsync_time slop_duration) { - int exceeds_count = 0; - nsync_time now; - nsync_time measured_duration; - now = nsync_time_now (); - measured_duration = nsync_time_sub (now, start_time); - if (nsync_time_cmp (measured_duration, - nsync_time_sub (expected_duration, nsync_time_ms (5))) < 0) { - char *m_str = nsync_time_str (measured_duration, 2); - char *e_str = nsync_time_str (expected_duration, 2); - TEST_ERROR (t, ("check_times %s too short a delay: %s instead of %s", - id, m_str, e_str)); - free (m_str); - free (e_str); - } - if (nsync_time_cmp (nsync_time_add (expected_duration, slop_duration), measured_duration) < 0) { - exceeds_count++; - } - return (exceeds_count); -} - -/* Check the operation of nsync_mu as a reader/writer lock. */ -static void test_rlock (testing t) { - int loop; - int i; - int max_write_wait_exceeded; - int max_read_wait_exceeded; - nsync_time time_unit; - nsync_time slop_duration; - nsync_time delay_duration; - nsync_time writer_duration; - nsync_time reader_duration; - static const int loop_count = 5; - static const int read_threads = 3; - static const int limit = 3; - static const int verbose = 0; - max_write_wait_exceeded = 0; - max_read_wait_exceeded = 0; - - time_unit = nsync_time_ms (100); - slop_duration = nsync_time_add (nsync_time_add (time_unit, time_unit), time_unit); - delay_duration = time_unit; - writer_duration = time_unit; - reader_duration = nsync_time_add (time_unit, time_unit); - - max_write_wait_exceeded = 0; - max_read_wait_exceeded = 0; - for (loop = 0; loop != loop_count; loop++) { - counter *lock_unlock_sleeping; - counter *rlock_runlock_sleeping; - counter *lock_unlock_done; - counter *rlock_runlock_done; - nsync_time read_start_time; - nsync_mu mu; - int value = 0; - counter *thread_done; - - nsync_time start_time; - nsync_mu_init (&mu); - start_time = nsync_time_now (); - - /* ------------------------------------ */ - /* Acquire lock with nsync_mu_rtrylock(). This thread will - hold a read lock until the next line with =====. */ - thread_done = counter_new (1); - attempt_rtrylock (t, "a", verbose, &mu, 1, 0, &value, 0, thread_done); - counter_wait_for_zero (thread_done); - - nsync_mu_rassert_held (&mu); - - counter_inc (thread_done, 1); - /* Can get read lock holding read lock. */ - closure_fork (closure_attempt_trylock (&attempt_rtrylock, - t, "b", verbose, &mu, 1, 1, &value, 0, thread_done)); - counter_wait_for_zero (thread_done); - - nsync_mu_rassert_held (&mu); - - counter_inc (thread_done, 1); - /* Can't get write lock holding read lock. */ - closure_fork (closure_attempt_trylock (&attempt_trylock, t, "c", verbose, - &mu, 0, 1, &value, -1, thread_done)); - counter_wait_for_zero (thread_done); - - if (!nsync_mu_is_reader (&mu)) { - TEST_FATAL(t, ("expected mu held in reader mode")); - } - - counter_inc (thread_done, 1); - closure_fork (closure_lock_unlock (&rlock_runlock, t, "d", verbose, - &mu, &value, 0, nsync_time_zero /*no delay*/, - NULL, thread_done)); - counter_wait_for_zero (thread_done); - - nsync_mu_rassert_held (&mu); - - lock_unlock_done = counter_new (1); - lock_unlock_sleeping = counter_new (1); - closure_fork (closure_lock_unlock (&lock_unlock, t, "e", verbose, - &mu, &value, 0, writer_duration, - lock_unlock_sleeping, lock_unlock_done)); - - counter_wait_for_zero (lock_unlock_sleeping); - nsync_time_sleep (delay_duration); /* give time for lock_unlock() thread to wait. */ - - nsync_mu_rassert_held (&mu); - - rlock_runlock_done = counter_new (read_threads); - rlock_runlock_sleeping = counter_new (read_threads); - for (i = 0; i != read_threads; i++) { - /* read lock will be acquired after lock_unlock() completes */ - closure_fork (closure_lock_unlock (&rlock_runlock, t, "f", verbose, - &mu, &value, 1, reader_duration, - rlock_runlock_sleeping, - rlock_runlock_done)); - } - - nsync_mu_rassert_held (&mu); - - counter_wait_for_zero (rlock_runlock_sleeping); - nsync_time_sleep (delay_duration); /* time for rlock_runlock() threads to wait. */ - - nsync_mu_rassert_held (&mu); - - if (counter_value (lock_unlock_done) == 0) { - TEST_FATAL (t, ("thread was able to acquire write lock while read lock held")); - } - if (counter_value (rlock_runlock_done) == 0) { - TEST_FATAL (t, ("thread was able to acquire read lock with " - "other reader and waiting writer")); - } - - nsync_mu_rassert_held (&mu); - - counter_inc (thread_done, 1); - /* Still can't get write lock. */ - closure_fork (closure_attempt_trylock (&attempt_trylock, t, "g", verbose, - &mu, 0, 1, &value, -1, thread_done)); - counter_wait_for_zero (thread_done); - - counter_inc (thread_done, 1); - /* Now can't get read lock because a writer is waiting. */ - closure_fork (closure_attempt_trylock (&attempt_rtrylock, t, "h", verbose, - &mu, 0, 1, &value, -1, thread_done)); - counter_wait_for_zero (thread_done); - - nsync_mu_runlock (&mu); - /* ==================================== */ - - read_start_time = nsync_time_now (); - counter_wait_for_zero (lock_unlock_done); /* Now can get write lock. */ - max_write_wait_exceeded += check_times (t, "i", start_time, - nsync_time_add (nsync_time_add (delay_duration, delay_duration), writer_duration), - slop_duration); - - counter_wait_for_zero (rlock_runlock_done); /* And now an get read lock again. */ - max_read_wait_exceeded += check_times (t, "j", read_start_time, - reader_duration, slop_duration); - - free (thread_done); - free (lock_unlock_done); - free (rlock_runlock_done); - free (lock_unlock_sleeping); - free (rlock_runlock_sleeping); - } - if (verbose) { - TEST_LOG (t, ("read lock max_write_wait_exceeded %d max_read_wait_exceeded %d\n", - max_write_wait_exceeded, max_read_wait_exceeded)); - } - if (max_write_wait_exceeded > limit) { - TEST_ERROR (t, ("lock_unlock() took too long %d " - "(more than %d) times out of %d", - max_write_wait_exceeded, limit, loop_count)); - } - if (max_read_wait_exceeded > limit) { - TEST_ERROR (t, ("rlock_runlock() took too long %d " - "(more than %d) times out of %d", - max_read_wait_exceeded, limit, loop_count)); - } - - max_write_wait_exceeded = 0; - max_read_wait_exceeded = 0; - for (loop = 0; loop != loop_count; loop++) { - counter *lock_unlock_sleeping; - counter *rlock_runlock_sleeping; - counter *lock_unlock_done; - counter *rlock_runlock_done; - nsync_time read_start_time; - nsync_mu mu; - int value = 0; - counter *thread_done; - - nsync_time start_time; - - nsync_mu_init (&mu); - start_time = nsync_time_now (); - - /* ------------------------------------ */ - /* Acquire lock with nsync_mu_trylock(). This thread will hold - a write lock until the next line with =====. */ - thread_done = counter_new (1); - attempt_trylock (t, "A", verbose, &mu, 1, 0, &value, 0, thread_done); - counter_wait_for_zero (thread_done); - - nsync_mu_assert_held (&mu); - nsync_mu_rassert_held (&mu); - - counter_inc (thread_done, 1); - /* Can't get read lock while holding write lock. */ - closure_fork (closure_attempt_trylock (&attempt_rtrylock, t, "B", verbose, - &mu, 0, 1, &value, -1, thread_done)); - counter_wait_for_zero (thread_done); - - if (nsync_mu_is_reader (&mu)) { - TEST_FATAL (t, ("expected mu held in write mode")); - } - nsync_mu_assert_held (&mu); - nsync_mu_rassert_held (&mu); - - counter_inc (thread_done, 1); - /* Can't get write lock while holding write lock. */ - closure_fork (closure_attempt_trylock (&attempt_trylock, t, "C", verbose, - &mu, 0, 1, &value, -1, thread_done)); - counter_wait_for_zero (thread_done); - - nsync_mu_assert_held (&mu); - nsync_mu_rassert_held (&mu); - - lock_unlock_done = counter_new (1); - lock_unlock_sleeping = counter_new (1); - closure_fork (closure_lock_unlock (&lock_unlock, t, "D", verbose, - &mu, &value, 1, writer_duration, - lock_unlock_sleeping, lock_unlock_done)); - - counter_wait_for_zero (lock_unlock_sleeping); - nsync_time_sleep (delay_duration); /* give time for lock_unlock() thread to wait. */ - - nsync_mu_assert_held (&mu); - nsync_mu_rassert_held (&mu); - - rlock_runlock_done = counter_new (read_threads); - rlock_runlock_sleeping = counter_new (read_threads); - for (i = 0; i != read_threads; i++) { - /* not guaranteed will complete after lock_unlock() above */ - closure_fork (closure_lock_unlock (&rlock_runlock, t, "E", verbose, - &mu, &value, -1, reader_duration, - rlock_runlock_sleeping, - rlock_runlock_done)); - } - - nsync_mu_assert_held (&mu); - nsync_mu_rassert_held (&mu); - - counter_wait_for_zero (rlock_runlock_sleeping); - nsync_time_sleep (delay_duration); /* time for rlock_runlock() threads to wait. */ - - nsync_mu_assert_held (&mu); - nsync_mu_rassert_held (&mu); - - if (counter_value (lock_unlock_done) == 0) { - TEST_FATAL (t, ("thread was able to acquire write lock " - "while other write lock held")); - } - if (counter_value (rlock_runlock_done) == 0) { - TEST_FATAL (t, ("thread was able to acquire read lock " - "while write lock held")); - } - - nsync_mu_assert_held (&mu); - nsync_mu_rassert_held (&mu); - - counter_inc (thread_done, 1); - /* Still can't get read lock while holding write lock. */ - closure_fork (closure_attempt_trylock (&attempt_rtrylock, t, "F", verbose, - &mu, 0, 1, &value, -1, thread_done)); - counter_wait_for_zero (thread_done); - - nsync_mu_assert_held (&mu); - nsync_mu_rassert_held (&mu); - - counter_inc (thread_done, 1); - /* Still can't get write lock while holding write lock. */ - closure_fork (closure_attempt_trylock (&attempt_trylock, t, "G", verbose, - &mu, 0, 1, &value, -1, thread_done)); - counter_wait_for_zero (thread_done); - - nsync_mu_assert_held (&mu); - nsync_mu_rassert_held (&mu); - - nsync_mu_unlock (&mu); - /* ==================================== */ - - read_start_time = nsync_time_now (); - counter_wait_for_zero (lock_unlock_done); /* Now can get write lock. */ - max_write_wait_exceeded += check_times (t, "H", start_time, - nsync_time_add (nsync_time_add (delay_duration, delay_duration), writer_duration), - slop_duration); - - counter_wait_for_zero (rlock_runlock_done); /* And now can get read lock again. */ - max_read_wait_exceeded += check_times (t, "I", read_start_time, - reader_duration, slop_duration); - - free (thread_done); - free (lock_unlock_done); - free (rlock_runlock_done); - free (lock_unlock_sleeping); - free (rlock_runlock_sleeping); - } - if (verbose) { - TEST_LOG (t, ("write lock max_write_wait_exceeded %d " - "max_read_wait_exceeded %d\n", - max_write_wait_exceeded, max_read_wait_exceeded)); - } - if (max_write_wait_exceeded > limit) { - TEST_ERROR (t, ("lock_unlock() took too long %d (more than %d) " - "times out of %d", - max_write_wait_exceeded, limit, loop_count)); - } - if (max_read_wait_exceeded > limit) { - TEST_ERROR (t, ("rlock_runlock() took too long %d (more than %d) " - "times out of %d", - max_read_wait_exceeded, limit, loop_count)); - } -} - -/* --------------------------------------- */ - -/* Measure the performance of an uncontended nsync_mu. */ -static void benchmark_mu_uncontended (testing t) { - int i; - int n = testing_n (t); - nsync_mu mu; - nsync_mu_init (&mu); - for (i = 0; i != n; i++) { - nsync_mu_lock (&mu); - nsync_mu_unlock (&mu); - } -} - -/* Return whether int *value is one. */ -static int int_is_1 (const void *value) { return (*(const int *)value == 1); } - -/* Return whether int *value is two. */ -static int int_is_2 (const void *value) { return (*(const int *)value == 2); } - -/* Return whether int *value is three. */ -static int int_is_3 (const void *value) { return (*(const int *)value == 3); } - -/* Set *value to 1, wait for it to become 2, then set it to 3. *value is under - *mu */ -static void waiter (nsync_mu *mu, int *value) { - nsync_mu_lock (mu); - *value = 1; - nsync_mu_wait (mu, &int_is_2, value, NULL); - *value = 3; - nsync_mu_unlock (mu); -} - -CLOSURE_DECL_BODY2 (waiter, nsync_mu *, int *) - -/* Measure the performance of an uncontended nsync_mu - with a blocked waiter. */ -static void benchmark_mu_uncontended_waiter (testing t) { - int i; - int n = testing_n (t); - nsync_mu mu; - int value = 0; - nsync_mu_init (&mu); - closure_fork (closure_waiter (&waiter, &mu, &value)); - nsync_mu_lock (&mu); - nsync_mu_wait (&mu, &int_is_1, &value, NULL); - nsync_mu_unlock (&mu); - for (i = 0; i != n; i++) { - nsync_mu_lock (&mu); - nsync_mu_unlock (&mu); - } - nsync_mu_lock (&mu); - value = 2; - nsync_mu_wait (&mu, &int_is_3, &value, NULL); - nsync_mu_unlock (&mu); -} - -/* Measure the performance of an uncontended nsync_mu - with a blocked waiter using nsync_mu_unlock_without_wakeup. */ -static void benchmark_mu_uncontended_no_wakeup (testing t) { - int i; - int n = testing_n (t); - nsync_mu mu; - int value = 0; - nsync_mu_init (&mu); - closure_fork (closure_waiter (&waiter, &mu, &value)); - nsync_mu_lock (&mu); - nsync_mu_wait (&mu, &int_is_1, &value, NULL); - nsync_mu_unlock (&mu); - for (i = 0; i != n; i++) { - nsync_mu_lock (&mu); - nsync_mu_unlock_without_wakeup (&mu); - } - nsync_mu_lock (&mu); - value = 2; - nsync_mu_wait (&mu, &int_is_3, &value, NULL); - nsync_mu_unlock (&mu); -} - -/* Measure the performance of an uncontended - nsync_mu in read mode. */ -static void benchmark_rmu_uncontended (testing t) { - int i; - int n = testing_n (t); - nsync_mu mu; - nsync_mu_init (&mu); - for (i = 0; i != n; i++) { - nsync_mu_rlock (&mu); - nsync_mu_runlock (&mu); - } -} - -/* Measure the performance of an uncontended nsync_mu - in read mode with a blocked waiter. */ -static void benchmark_rmu_uncontended_waiter (testing t) { - int i; - int n = testing_n (t); - nsync_mu mu; - int value = 0; - nsync_mu_init (&mu); - closure_fork (closure_waiter (&waiter, &mu, &value)); - nsync_mu_lock (&mu); - nsync_mu_wait (&mu, &int_is_1, &value, NULL); - nsync_mu_unlock (&mu); - for (i = 0; i != n; i++) { - nsync_mu_rlock (&mu); - nsync_mu_runlock (&mu); - } - nsync_mu_lock (&mu); - value = 2; - nsync_mu_wait (&mu, &int_is_3, &value, NULL); - nsync_mu_unlock (&mu); -} - -/* Measure the performance of an uncontended pthread_mutex_t. */ -static void benchmark_mutex_uncontended (testing t) { - int i; - int n = testing_n (t); - pthread_mutex_t mu; - pthread_mutex_init (&mu, NULL); - for (i = 0; i != n; i++) { - pthread_mutex_lock (&mu); - pthread_mutex_unlock (&mu); - } - pthread_mutex_destroy (&mu); -} - -/* Measure the performance of an uncontended pthread_rwlock_t. */ -static void benchmark_wmutex_uncontended (testing t) { - int i; - int n = testing_n (t); - pthread_rwlock_t mu; - pthread_rwlock_init (&mu, NULL); - for (i = 0; i != n; i++) { - pthread_rwlock_wrlock (&mu); - pthread_rwlock_unlock (&mu); - } - pthread_rwlock_destroy (&mu); -} - -/* Measure the performance of an uncontended - pthread_rwlock_t in read mode. */ -static void benchmark_rmutex_uncontended (testing t) { - int i; - int n = testing_n (t); - pthread_rwlock_t mu; - pthread_rwlock_init (&mu, NULL); - for (i = 0; i != n; i++) { - pthread_rwlock_rdlock (&mu); - pthread_rwlock_unlock (&mu); - } - pthread_rwlock_destroy (&mu); -} - -/* --------------------------------------- - Benchmarks for contended locks. */ - -/* It's hard to write these as benchmark functions, since we wish to measure - throughput over an extended period (a second or two), rather than get the - latency of a few iterations. */ - -/* A contended_state represents state shared between threads - in the contended benchmarks. */ -typedef struct contended_state_s { - testing t; - - /* locks to test */ - nsync_mu mu; - pthread_mutex_t mutex; - pthread_rwlock_t rwmutex; - int count; /* counter protected by a lock above */ - - nsync_mu start_done_mu; - int start; /* whether threads should start, under start_done_mu */ - int not_yet_done; /* threads not yet complete, under start_done_mu */ -} contended_state; - -static int contended_state_may_start (const void *v) { - return (((const contended_state *)v)->start); -} - -static int contended_state_all_done (const void *v) { - return (((const contended_state *)v)->not_yet_done == 0); -} - -/* Wait for cs.start to become non-zero, then loop, acquiring and - releasing mu on each iteration until cs.deadline is reached, then decrement - cs.not_yet_done. */ -static void contended_state_contend_loop (contended_state *cs, - void *mu, void (*lock) (void *), - void (*unlock) (void *)) { - int n = testing_n (cs->t); - int j; - int i; - nsync_mu_rlock (&cs->start_done_mu); - nsync_mu_wait (&cs->start_done_mu, &contended_state_may_start, cs, NULL); - nsync_mu_runlock (&cs->start_done_mu); - - for (j = 0; j < n; j += 10000) { - for (i = 0; i != 10000; i++) { - (*lock) (mu); - cs->count++; - (*unlock) (mu); - } - } - - nsync_mu_lock (&cs->start_done_mu); - cs->not_yet_done--; - nsync_mu_unlock (&cs->start_done_mu); -} - -typedef void (*func_any) (void *); -CLOSURE_DECL_BODY4 (contended_state_contend_loop, contended_state *, void *, func_any, func_any) - -/* Start the threads in a contended test, wait for them to finish, - and print the number of iterations achieved. */ -static void contended_state_run_test (contended_state *cs, testing t, - void *mu, void (*lock) (void *), - void (*unlock) (void *)) { - int i; - cs->t = t; - cs->not_yet_done = 4; /* number of threads */ - cs->start = 0; - cs->count = 0; - for (i = 0; i != cs->not_yet_done; i++) { - closure_fork (closure_contended_state_contend_loop ( - &contended_state_contend_loop, cs, mu, lock, unlock)); - } - nsync_mu_lock (&cs->start_done_mu); - cs->start = 1; - nsync_mu_wait (&cs->start_done_mu, &contended_state_all_done, cs, NULL); - nsync_mu_unlock (&cs->start_done_mu); -} - -/* Measure the performance of highly contended - nsync_mu locks, with small critical sections. */ -static void benchmark_mu_contended (testing t) { - contended_state cs; - memset ((void *) &cs, 0, sizeof (cs)); - contended_state_run_test (&cs, t, &cs.mu, (void (*) (void*))&nsync_mu_lock, - (void (*) (void*))&nsync_mu_unlock); -} - -/* Measure the performance of highly contended - pthread_mutex_t locks, with small critical sections. */ -static void benchmark_mutex_contended (testing t) { - contended_state cs; - memset ((void *) &cs, 0, sizeof (cs)); - pthread_mutex_init (&cs.mutex, NULL); - contended_state_run_test (&cs, t, &cs.mutex, &void_pthread_mutex_lock, - &void_pthread_mutex_unlock); - pthread_mutex_destroy (&cs.mutex); -} - -/* Measure the performance of highly contended - pthread_rwlock_t locks, with small critical sections. */ -static void benchmark_wmutex_contended (testing t) { - contended_state cs; - memset ((void *) &cs, 0, sizeof (cs)); - pthread_rwlock_init (&cs.rwmutex, NULL); - contended_state_run_test (&cs, t, &cs.rwmutex, &void_pthread_rwlock_wrlock, - &void_pthread_rwlock_unlock); - pthread_rwlock_destroy (&cs.rwmutex); -} - -int main (int argc, char *argv[]) { - testing_base tb = testing_new (argc, argv, 0); - - TEST_RUN (tb, test_rlock); - TEST_RUN (tb, test_mu_nthread); - TEST_RUN (tb, test_mutex_nthread); - TEST_RUN (tb, test_rwmutex_nthread); - TEST_RUN (tb, test_try_mu_nthread); - - BENCHMARK_RUN (tb, benchmark_mu_contended); - BENCHMARK_RUN (tb, benchmark_mutex_contended); - BENCHMARK_RUN (tb, benchmark_wmutex_contended); - - BENCHMARK_RUN (tb, benchmark_mu_uncontended); - BENCHMARK_RUN (tb, benchmark_rmu_uncontended); - BENCHMARK_RUN (tb, benchmark_mutex_uncontended); - BENCHMARK_RUN (tb, benchmark_wmutex_uncontended); - BENCHMARK_RUN (tb, benchmark_rmutex_uncontended); - BENCHMARK_RUN (tb, benchmark_mu_uncontended_waiter); - BENCHMARK_RUN (tb, benchmark_mu_uncontended_no_wakeup); - BENCHMARK_RUN (tb, benchmark_rmu_uncontended_waiter); - - return (testing_base_exit (tb)); -} diff --git a/examples/thread.c b/examples/thread.c index d59a0eb4a..f019d7107 100644 --- a/examples/thread.c +++ b/examples/thread.c @@ -7,7 +7,7 @@ │ • http://creativecommons.org/publicdomain/zero/1.0/ │ ╚─────────────────────────────────────────────────────────────────*/ #endif -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" #include "libc/stdio/stdio.h" /** diff --git a/examples/vga.c b/examples/vga.c deleted file mode 100644 index 262dc2a30..000000000 --- a/examples/vga.c +++ /dev/null @@ -1,60 +0,0 @@ -#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 "libc/calls/calls.h" -#include "libc/calls/termios.h" -#include "libc/isystem/unistd.h" -#include "libc/math.h" -#include "libc/stdio/stdio.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/termios.h" - -/** - * @fileoverview Bare Metal VGA TTY demo. - * - * This program can boot as an operating system. Try it out: - * - * make -j8 o//examples/vga.com - * qemu-system-x86_64 -hda o//examples/vga.com -serial stdio - * - * Please note that, by default, APE binaries only use the serial port - * for stdio. To get the VGA console as an added bonus: - * - * STATIC_YOINK("vga_console"); - * - * Should be added to the top of your main() program source file. - */ - -STATIC_YOINK("vga_console"); - -int main(int argc, char *argv[]) { - volatile long double x = -.5; - volatile long double y = 1.5; - struct termios tio; - char buf[16]; - ssize_t res; - if (tcgetattr(0, &tio) != -1) { - tio.c_lflag &= ~(ECHO | ICANON); - tcsetattr(0, TCSANOW, &tio); - } - write(1, "\e[5n", 4); - res = read(0, buf, 4); - if (res != 4 || memcmp(buf, "\e[0n", 4) != 0) { - printf("res = %d?\n", res); - return -1; - } - printf("Hello World! %.19Lg\n", atan2l(x, y)); - - // read/print loop so machine doesn't reset on metal - for (;;) { - if ((res = readansi(0, buf, 16)) > 0) { - printf("got %`'.*s\r\n", res, buf); - } - } -} diff --git a/examples/walk.c b/examples/walk.c index 6b6253a70..b9eca8eba 100644 --- a/examples/walk.c +++ b/examples/walk.c @@ -7,12 +7,16 @@ │ • http://creativecommons.org/publicdomain/zero/1.0/ │ ╚─────────────────────────────────────────────────────────────────*/ #endif +#include "libc/calls/calls.h" +#include "libc/calls/struct/stat.h" #include "libc/errno.h" +#include "libc/runtime/gc.h" #include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/sysv/consts/exit.h" #include "libc/sysv/consts/s.h" +#include "libc/x/x.h" #include "third_party/musl/ftw.h" /** diff --git a/libc/assert.h b/libc/assert.h index e6ef79237..ce6933b2a 100644 --- a/libc/assert.h +++ b/libc/assert.h @@ -17,20 +17,6 @@ void __assert_fail(const char *, const char *, int) hidden relegated; #define static_assert _Static_assert #endif -#define _unassert(x) \ - do { \ - if (__builtin_expect(!(x), 0)) { \ - unreachable; \ - } \ - } while (0) - -#define _npassert(x) \ - do { \ - if (__builtin_expect(!(x), 0)) { \ - notpossible; \ - } \ - } while (0) - COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_ASSERT_H_ */ diff --git a/libc/atomic.h b/libc/atomic.h index f132e5ed2..dd8f831f8 100644 --- a/libc/atomic.h +++ b/libc/atomic.h @@ -1,6 +1,5 @@ #ifndef COSMOPOLITAN_LIBC_ATOMIC_H_ #define COSMOPOLITAN_LIBC_ATOMIC_H_ -#include "libc/inttypes.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ diff --git a/libc/calls/_timespec_real.c b/libc/calls/_timespec_real.c index 80912dc1f..805c4b9d6 100644 --- a/libc/calls/_timespec_real.c +++ b/libc/calls/_timespec_real.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" #include "libc/calls/struct/timespec.h" #include "libc/sysv/consts/clock.h" #include "libc/sysv/consts/nr.h" @@ -25,6 +26,6 @@ struct timespec _timespec_real(void) { int ax, dx; struct timespec ts; ax = clock_gettime(CLOCK_REALTIME_FAST, &ts); - if (ax) notpossible; + assert(!ax); return ts; } diff --git a/libc/calls/_timespec_sub.c b/libc/calls/_timespec_sub.c index 0f1fe4c35..5ba0d0fb4 100644 --- a/libc/calls/_timespec_sub.c +++ b/libc/calls/_timespec_sub.c @@ -21,12 +21,12 @@ /** * Subtracts two nanosecond timestamps. */ -struct timespec _timespec_sub(struct timespec a, struct timespec b) { - a.tv_sec -= b.tv_sec; - if (a.tv_nsec < b.tv_nsec) { - a.tv_nsec += 1000000000; - a.tv_sec--; +struct timespec _timespec_sub(struct timespec x, struct timespec y) { + x.tv_sec -= y.tv_sec; + x.tv_nsec -= y.tv_nsec; + if (x.tv_nsec < 0) { + x.tv_nsec += 1000000000; + x.tv_sec -= 1; } - a.tv_nsec -= b.tv_nsec; - return a; + return x; } diff --git a/libc/intrin/arememoryintervalsok.c b/libc/calls/arememoryintervalsok.c similarity index 100% rename from libc/intrin/arememoryintervalsok.c rename to libc/calls/arememoryintervalsok.c diff --git a/libc/calls/calls.mk b/libc/calls/calls.mk index 10177d083..bab0d35be 100644 --- a/libc/calls/calls.mk +++ b/libc/calls/calls.mk @@ -79,6 +79,14 @@ o/$(MODE)/libc/calls/vdsofunc.greg.o: private \ -ffreestanding \ -fno-sanitize=address +# we can't use asan because: +# asan guard pages haven't been allocated yet +o/$(MODE)/libc/calls/directmap.o \ +o/$(MODE)/libc/calls/directmap-nt.o: private \ + OVERRIDE_COPTS += \ + -ffreestanding \ + -fno-sanitize=address + # we can't use asan because: # ntspawn allocates 128kb of heap memory via win32 o/$(MODE)/libc/calls/ntspawn.o \ @@ -136,6 +144,12 @@ o/$(MODE)/libc/calls/ioctl-siocgifconf-nt.o: private \ -ffunction-sections \ -fdata-sections +# we want small code size because: +# to keep .text.head under 4096 bytes +o/$(MODE)/libc/calls/mman.greg.o: private \ + OVERRIDE_COPTS += \ + -Os + # we always want -Os because: # va_arg codegen is very bloated in default mode o//libc/calls/open.o \ diff --git a/libc/calls/clock_gettime-mono.c b/libc/calls/clock_gettime-mono.c index 550af8dcd..42a88162e 100644 --- a/libc/calls/clock_gettime-mono.c +++ b/libc/calls/clock_gettime-mono.c @@ -17,7 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/struct/timespec.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" #include "libc/nexgen32e/rdtsc.h" #include "libc/nexgen32e/x86feature.h" #include "libc/sysv/consts/clock.h" diff --git a/libc/intrin/directmap-metal.c b/libc/calls/directmap-metal.c similarity index 100% rename from libc/intrin/directmap-metal.c rename to libc/calls/directmap-metal.c diff --git a/libc/intrin/directmap-nt.c b/libc/calls/directmap-nt.c similarity index 100% rename from libc/intrin/directmap-nt.c rename to libc/calls/directmap-nt.c diff --git a/libc/intrin/directmap.c b/libc/calls/directmap.c similarity index 100% rename from libc/intrin/directmap.c rename to libc/calls/directmap.c diff --git a/libc/calls/extend.internal.h b/libc/calls/extend.internal.h deleted file mode 100644 index 6de407ddc..000000000 --- a/libc/calls/extend.internal.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_CALLS_EXTEND_INTERNAL_H_ -#define COSMOPOLITAN_LIBC_CALLS_EXTEND_INTERNAL_H_ -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -void *_extend(void *, size_t, void *, intptr_t); - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_LIBC_CALLS_EXTEND_INTERNAL_H_ */ diff --git a/libc/calls/fcntl-nt.c b/libc/calls/fcntl-nt.c index 05626e755..9ddd29789 100644 --- a/libc/calls/fcntl-nt.c +++ b/libc/calls/fcntl-nt.c @@ -44,34 +44,22 @@ #include "libc/sysv/consts/o.h" #include "libc/sysv/errfuns.h" -bool __force_sqlite_to_work_until_we_can_fix_it; - static textwindows int sys_fcntl_nt_dupfd(int fd, int cmd, int start) { - if (start < 0) return einval(); return sys_dup_nt(fd, -1, (cmd == F_DUPFD_CLOEXEC ? O_CLOEXEC : 0), start); } static textwindows int sys_fcntl_nt_lock(struct Fd *f, int cmd, uintptr_t arg) { - int e; struct flock *l; uint32_t flags, err; + struct NtOverlapped ov; int64_t pos, off, len, size; struct NtByHandleFileInformation info; - - if (!GetFileInformationByHandle(f->handle, &info)) { - return __winerr(); - } - - pos = 0; - if (!SetFilePointerEx(f->handle, 0, &pos, SEEK_CUR)) { - return __winerr(); - } - + if (!GetFileInformationByHandle(f->handle, &info)) return __winerr(); + if (!SetFilePointerEx(f->handle, 0, &pos, SEEK_CUR)) return __winerr(); l = (struct flock *)arg; len = l->l_len; off = l->l_start; size = (uint64_t)info.nFileSizeHigh << 32 | info.nFileSizeLow; - switch (l->l_whence) { case SEEK_SET: break; @@ -84,64 +72,37 @@ static textwindows int sys_fcntl_nt_lock(struct Fd *f, int cmd, uintptr_t arg) { default: return einval(); } - - if (!len) { - len = size - off; - } - - if (off < 0 || len < 0) { - return einval(); - } - - bool32 ok; - struct NtOverlapped ov = {.hEvent = f->handle, - .Pointer = (void *)(uintptr_t)off}; - + if (!len) len = size - off; + if (off < 0 || len < 0) return einval(); + _offset2overlap(f->handle, off, &ov); if (l->l_type == F_RDLCK || l->l_type == F_WRLCK) { flags = 0; - if (cmd != F_SETLKW) { - flags |= kNtLockfileFailImmediately; - } - if (l->l_type == F_WRLCK) { - flags |= kNtLockfileExclusiveLock; - } - e = errno; - ok = LockFileEx(f->handle, flags, 0, len, len >> 32, &ov); - if (cmd == F_GETLK) { - if (ok) { - l->l_type = F_UNLCK; - if (!UnlockFileEx(f->handle, 0, len, len >> 32, &ov)) { - notpossible; - } - } else { - l->l_pid = -1; - ok = true; - } - } - if (ok) { - return 0; - } else if (__force_sqlite_to_work_until_we_can_fix_it) { - errno = e; + if (cmd == F_SETLK) flags |= kNtLockfileFailImmediately; + /* TODO: How can we make SQLite locks on Windows to work? */ + /* if (l->l_type == F_WRLCK) flags |= kNtLockfileExclusiveLock; */ + if (LockFileEx(f->handle, flags, 0, len, len >> 32, &ov)) { return 0; } else { + err = GetLastError(); + if (err == kNtErrorLockViolation) err = EAGAIN; + errno = err; return -1; } - } - - if (l->l_type == F_UNLCK) { - if (cmd == F_GETLK) return einval(); - e = errno; + } else if (l->l_type == F_UNLCK) { if (UnlockFileEx(f->handle, 0, len, len >> 32, &ov)) { return 0; - } else if (errno == ENOLCK) { - errno = e; - return 0; } else { - return 0; + err = GetLastError(); + if (err == kNtErrorNotLocked) { + return 0; + } else { + errno = err; + return -1; + } } + } else { + return einval(); } - - return einval(); } textwindows int sys_fcntl_nt(int fd, int cmd, uintptr_t arg) { diff --git a/libc/calls/fcntl-sysv.c b/libc/calls/fcntl-sysv.c index 2cfbf8d0c..66b398274 100644 --- a/libc/calls/fcntl-sysv.c +++ b/libc/calls/fcntl-sysv.c @@ -20,14 +20,12 @@ #include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/syscall_support-sysv.internal.h" #include "libc/dce.h" -#include "libc/errno.h" #include "libc/intrin/asan.internal.h" #include "libc/sysv/consts/f.h" -#include "libc/sysv/consts/o.h" #include "libc/sysv/errfuns.h" int sys_fcntl(int fd, int cmd, uintptr_t arg) { - int e, rc; + int rc; bool islock; if ((islock = cmd == F_GETLK || // cmd == F_SETLK || // @@ -39,13 +37,9 @@ int sys_fcntl(int fd, int cmd, uintptr_t arg) { } cosmo2flock(arg); } - e = errno; rc = __sys_fcntl(fd, cmd, arg); if (islock) { flock2cosmo(arg); - } else if (rc == -1 && cmd == F_DUPFD_CLOEXEC && errno == EINVAL) { - errno = e; - rc = __fixupnewfd(__sys_fcntl(fd, F_DUPFD, arg), O_CLOEXEC); } return rc; } diff --git a/libc/calls/fstatat64.S b/libc/calls/fstatat64.S index 8b0c3e5f8..09086d2dd 100644 --- a/libc/calls/fstatat64.S +++ b/libc/calls/fstatat64.S @@ -1,5 +1,5 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ │ This is free and unencumbered software released into the public domain. │ │ │ @@ -26,6 +26,5 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/macros.internal.h" -fstatat64: - jmp fstatat +fstatat64: jmp fstatat .endfn fstatat64,globl diff --git a/libc/calls/ftruncate64.S b/libc/calls/ftruncate64.S index a4a2a5080..fbb6c5aa2 100644 --- a/libc/calls/ftruncate64.S +++ b/libc/calls/ftruncate64.S @@ -1,5 +1,5 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ │ This is free and unencumbered software released into the public domain. │ │ │ @@ -26,6 +26,5 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/macros.internal.h" -ftruncate64: - jmp ftruncate +ftruncate64: jmp ftruncate .endfn ftruncate64,globl diff --git a/libc/calls/g_sighandrvas.c b/libc/calls/g_sighandrvas.c index 3e0780d59..df628bc0e 100644 --- a/libc/calls/g_sighandrvas.c +++ b/libc/calls/g_sighandrvas.c @@ -17,7 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/state.internal.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" // TODO(jart): These should be _Thread_local but doing that currently // causes a regression with runitd.com on Windows. diff --git a/libc/calls/getloadavg-nt.c b/libc/calls/getloadavg-nt.c index 6a82d5bbc..1eba10b65 100644 --- a/libc/calls/getloadavg-nt.c +++ b/libc/calls/getloadavg-nt.c @@ -20,7 +20,7 @@ #include "libc/calls/syscall_support-nt.internal.h" #include "libc/dce.h" #include "libc/fmt/conv.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" #include "libc/macros.internal.h" #include "libc/nt/accounting.h" #include "libc/runtime/sysconf.h" diff --git a/libc/calls/interrupts-nt.c b/libc/calls/interrupts-nt.c index 749d0a192..82ccd8e6e 100644 --- a/libc/calls/interrupts-nt.c +++ b/libc/calls/interrupts-nt.c @@ -27,7 +27,7 @@ #include "libc/dce.h" #include "libc/intrin/lockcmpxchgp.h" #include "libc/intrin/weaken.h" -#include "libc/thread/tls.h" +#include "libc/nexgen32e/threaded.h" textwindows bool _check_interrupts(bool restartable, struct Fd *fd) { bool res; diff --git a/libc/calls/lstat64.S b/libc/calls/lstat64.S index 7cfe4b329..24d9d81e0 100644 --- a/libc/calls/lstat64.S +++ b/libc/calls/lstat64.S @@ -1,5 +1,5 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ │ This is free and unencumbered software released into the public domain. │ │ │ @@ -26,6 +26,5 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/macros.internal.h" -lstat64: - jmp lstat +lstat64: jmp lstat .endfn lstat64,globl diff --git a/libc/intrin/memtrack.greg.c b/libc/calls/memtrack.greg.c similarity index 99% rename from libc/intrin/memtrack.greg.c rename to libc/calls/memtrack.greg.c index 4f5a2482a..b35066329 100644 --- a/libc/intrin/memtrack.greg.c +++ b/libc/calls/memtrack.greg.c @@ -17,14 +17,14 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" +#include "libc/intrin/bits.h" +#include "libc/intrin/likely.h" +#include "libc/intrin/weaken.h" #include "libc/calls/calls.h" #include "libc/calls/strace.internal.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/asan.internal.h" -#include "libc/intrin/bits.h" -#include "libc/intrin/likely.h" -#include "libc/intrin/weaken.h" #include "libc/log/libfatal.internal.h" #include "libc/log/log.h" #include "libc/macros.internal.h" @@ -201,9 +201,6 @@ int TrackMemoryInterval(struct MemoryIntervals *mm, int x, int y, long h, unsigned i; #if IsModeDbg() assert(y >= x); - if (!AreMemoryIntervalsOk(mm)) { - PrintMemoryIntervals(2, mm); - } assert(AreMemoryIntervalsOk(mm)); #endif diff --git a/libc/intrin/mman.greg.c b/libc/calls/mman.greg.c similarity index 98% rename from libc/intrin/mman.greg.c rename to libc/calls/mman.greg.c index 6e0cc8c47..fa0623e50 100644 --- a/libc/intrin/mman.greg.c +++ b/libc/calls/mman.greg.c @@ -74,7 +74,6 @@ noasan textreal uint64_t *__get_virtual(struct mman *mm, uint64_t *t, if (!(*e & PAGE_V)) { if (!maketables) return NULL; if (!(p = __new_page(mm))) return NULL; - __clear_page(BANE + p); *e = p | PAGE_V | PAGE_RW; } t = (uint64_t *)(BANE + (*e & PAGE_TA)); @@ -118,7 +117,7 @@ static noasan textreal void __invert_memory(struct mman *mm, uint64_t *pml4t) { uint64_t i, j, *m, p, pe; for (i = 0; i < mm->e820n; ++i) { for (p = mm->e820[i].addr, pe = mm->e820[i].addr + mm->e820[i].size; - p != pe + 0x200000; p += 4096) { + p + 0x200000 < pe; p += 4096) { m = __get_virtual(mm, pml4t, BANE + p, true); if (m && !(*m & PAGE_V)) { *m = p | PAGE_V | PAGE_RW; @@ -151,7 +150,7 @@ noasan textreal void __map_phdrs(struct mman *mm, uint64_t *pml4t, uint64_t b) { v = b + p->p_offset + i; m = MAX(m, v); } else { - v = __clear_page(BANE + __new_page(mm)); + v = __clear_page(__new_page(mm)); } *__get_virtual(mm, pml4t, p->p_vaddr + i, true) = (v & PAGE_TA) | f; } diff --git a/libc/calls/nanos.internal.h b/libc/calls/nanos.h similarity index 100% rename from libc/calls/nanos.internal.h rename to libc/calls/nanos.h diff --git a/libc/calls/preadv.c b/libc/calls/preadv.c index 349c00988..25ec37f4c 100644 --- a/libc/calls/preadv.c +++ b/libc/calls/preadv.c @@ -16,15 +16,12 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/state.internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/iovec.h" #include "libc/calls/struct/iovec.internal.h" -#include "libc/calls/struct/sigset.h" -#include "libc/calls/struct/sigset.internal.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/errno.h" @@ -36,89 +33,74 @@ #include "libc/intrin/weaken.h" #include "libc/macros.internal.h" #include "libc/sysv/consts/iov.h" -#include "libc/sysv/consts/sig.h" #include "libc/sysv/errfuns.h" #include "libc/zipos/zipos.internal.h" static ssize_t Preadv(int fd, struct iovec *iov, int iovlen, int64_t off) { - int e, i; - size_t got; - bool masked; - ssize_t rc, toto; - sigset_t mask, oldmask; - - if (fd < 0) { - return ebadf(); - } - - if (iovlen < 0) { - return einval(); - } - - if (IsAsan() && !__asan_is_valid_iov(iov, iovlen)) { - return efault(); - } + static bool once, demodernize; + int i, err; + ssize_t rc; + size_t got, toto; + if (fd < 0) return einval(); + if (iovlen < 0) return einval(); + if (IsAsan() && !__asan_is_valid_iov(iov, iovlen)) return efault(); if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) { return weaken(__zipos_read)( (struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, iov, iovlen, off); - } - - if (IsMetal()) { - return espipe(); // must be serial or console if not zipos - } - - if (IsWindows()) { + } else if (IsWindows()) { if (fd < g_fds.n) { return sys_read_nt(g_fds.p + fd, iov, iovlen, off); } else { return ebadf(); } - } - - while (iovlen && !iov->iov_len) { - --iovlen; - ++iov; - } - - if (!iovlen) { - return sys_pread(fd, 0, 0, off, off); + } else if (IsMetal()) { + return enosys(); } if (iovlen == 1) { - return sys_pread(fd, iov->iov_base, iov->iov_len, off, off); + return sys_pread(fd, iov[0].iov_base, iov[0].iov_len, off, off); } - e = errno; - rc = sys_preadv(fd, iov, iovlen, off, off); - if (rc != -1 || errno != ENOSYS) return rc; - errno = e; + /* + * NT, 2018-era XNU, and 2007-era Linux don't support this system call + */ + if (!__vforked && !once) { + err = errno; + rc = sys_preadv(fd, iov, iovlen, off, off); + if (rc == -1 && errno == ENOSYS) { + errno = err; + once = true; + demodernize = true; + STRACE("demodernizing %s() due to %s", "preadv", "ENOSYS"); + } else { + once = true; + return rc; + } + } + + if (!demodernize) { + return sys_preadv(fd, iov, iovlen, off, off); + } + + if (!iovlen) { + return sys_pread(fd, NULL, 0, off, off); + } for (toto = i = 0; i < iovlen; ++i) { rc = sys_pread(fd, iov[i].iov_base, iov[i].iov_len, off, off); if (rc == -1) { - if (!toto) { - toto = -1; + if (toto && (errno == EINTR || errno == EAGAIN)) { + return toto; + } else { + return -1; } - break; } - got = rc; toto += got; - off += got; if (got != iov[i].iov_len) { break; } - - if (!masked) { - sigfillset(&mask); - _npassert(!sys_sigprocmask(SIG_SETMASK, &mask, &oldmask)); - masked = true; - } - } - - if (masked) { - _npassert(!sys_sigprocmask(SIG_SETMASK, &oldmask, 0)); } return toto; diff --git a/libc/calls/pwritev.c b/libc/calls/pwritev.c index eda149625..120da1dba 100644 --- a/libc/calls/pwritev.c +++ b/libc/calls/pwritev.c @@ -16,13 +16,11 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/iovec.h" #include "libc/calls/struct/iovec.internal.h" -#include "libc/calls/struct/sigset.internal.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/errno.h" @@ -33,90 +31,75 @@ #include "libc/intrin/weaken.h" #include "libc/macros.internal.h" #include "libc/sysv/consts/iov.h" -#include "libc/sysv/consts/sig.h" #include "libc/sysv/errfuns.h" #include "libc/zipos/zipos.internal.h" static ssize_t Pwritev(int fd, const struct iovec *iov, int iovlen, int64_t off) { - int i, e; - bool masked; - size_t sent; - ssize_t rc, toto; - sigset_t mask, oldmask; - - if (fd < 0) { - return ebadf(); - } - - if (iovlen < 0) { - return einval(); - } - - if (IsAsan() && !__asan_is_valid_iov(iov, iovlen)) { - return efault(); - } + static bool once, demodernize; + int i, err; + ssize_t rc; + size_t sent, toto; + if (fd < 0) return einval(); + if (iovlen < 0) return einval(); + if (IsAsan() && !__asan_is_valid_iov(iov, iovlen)) return efault(); if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) { return weaken(__zipos_write)( (struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, iov, iovlen, off); - } - - if (IsWindows()) { + } else if (IsWindows()) { if (fd < g_fds.n) { return sys_write_nt(fd, iov, iovlen, off); } else { return ebadf(); } - } - - if (IsMetal()) { - return espipe(); // must be serial or console if not zipos - } - - while (iovlen && !iov->iov_len) { - --iovlen; - ++iov; - } - - if (!iovlen) { - return sys_pwrite(fd, 0, 0, off, off); + } else if (IsMetal()) { + return enosys(); } if (iovlen == 1) { - return sys_pwrite(fd, iov->iov_base, iov->iov_len, off, off); + return sys_pwrite(fd, iov[0].iov_base, iov[0].iov_len, off, off); } - e = errno; - rc = sys_pwritev(fd, iov, iovlen, off, off); - if (rc != -1 || errno != ENOSYS) return rc; - errno = e; + /* + * NT, 2018-era XNU, and 2007-era Linux don't support this system call + */ + if (!once) { + err = errno; + rc = sys_pwritev(fd, iov, iovlen, off, off); + if (rc == -1 && errno == ENOSYS) { + errno = err; + once = true; + demodernize = true; + STRACE("demodernizing %s() due to %s", "pwritev", "ENOSYS"); + } else { + once = true; + return rc; + } + } + + if (!demodernize) { + return sys_pwritev(fd, iov, iovlen, off, off); + } + + if (!iovlen) { + return sys_pwrite(fd, NULL, 0, off, off); + } for (toto = i = 0; i < iovlen; ++i) { rc = sys_pwrite(fd, iov[i].iov_base, iov[i].iov_len, off, off); if (rc == -1) { - if (!toto) { - toto = -1; + if (toto && (errno == EINTR || errno == EAGAIN)) { + return toto; + } else { + return -1; } - break; } - sent = rc; toto += sent; - off += sent; if (sent != iov[i].iov_len) { break; } - - if (!masked) { - sigfillset(&mask); - _npassert(!sys_sigprocmask(SIG_SETMASK, &mask, &oldmask)); - masked = true; - } - } - - if (masked) { - _npassert(!sys_sigprocmask(SIG_SETMASK, &oldmask, 0)); } return toto; diff --git a/libc/calls/raise.c b/libc/calls/raise.c index b94c49e55..d2fb4927b 100644 --- a/libc/calls/raise.c +++ b/libc/calls/raise.c @@ -21,7 +21,7 @@ #include "libc/calls/strace.internal.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" -#include "libc/thread/tls.h" +#include "libc/nexgen32e/threaded.h" #include "libc/runtime/internal.h" #include "libc/sysv/consts/sicode.h" #include "libc/sysv/consts/sig.h" diff --git a/libc/calls/readv-metal.c b/libc/calls/readv-metal.c index 3e283a1f3..bb2cf19bb 100644 --- a/libc/calls/readv-metal.c +++ b/libc/calls/readv-metal.c @@ -20,28 +20,15 @@ #include "libc/calls/struct/fd.internal.h" #include "libc/calls/struct/iovec.h" #include "libc/calls/struct/iovec.internal.h" -#include "libc/intrin/weaken.h" #include "libc/macros.internal.h" #include "libc/str/str.h" #include "libc/sysv/errfuns.h" -#include "libc/vga/vga.internal.h" ssize_t sys_readv_metal(struct Fd *fd, const struct iovec *iov, int iovlen) { int i; size_t got, toto; struct MetalFile *file; switch (fd->kind) { - case kFdConsole: - /* - * The VGA teletypewriter code may wish to send out "status report" - * escape sequences, in response to requests sent to it via write(). - * Read & return these if they are available. - */ - if (weaken(sys_readv_vga)) { - ssize_t res = weaken(sys_readv_vga)(fd, iov, iovlen); - if (res > 0) return res; - } - /* fall through */ case kFdSerial: return sys_readv_serial(fd, iov, iovlen); case kFdFile: diff --git a/libc/calls/reservefd.c b/libc/calls/reservefd.c index d0a4f3e34..ee27f7b6f 100644 --- a/libc/calls/reservefd.c +++ b/libc/calls/reservefd.c @@ -18,7 +18,6 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" #include "libc/calls/calls.h" -#include "libc/calls/extend.internal.h" #include "libc/calls/internal.h" #include "libc/calls/state.internal.h" #include "libc/calls/strace.internal.h" @@ -47,11 +46,33 @@ static volatile size_t mapsize; * @asyncsignalsafe */ int __ensurefds_unlocked(int fd) { - bool relocate; + uint64_t addr; + int prot, flags; + size_t size, chunk; + struct DirectMap dm; if (fd < g_fds.n) return fd; - g_fds.n = fd + 1; - g_fds.e = - _extend(g_fds.p, g_fds.n * sizeof(*g_fds.p), g_fds.e, 0x6ff000000000); + STRACE("__ensurefds(%d) extending", fd); + size = mapsize; + chunk = FRAMESIZE; + if (IsAsan()) chunk *= 8; + addr = kMemtrackFdsStart + size; + prot = PROT_READ | PROT_WRITE; + flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED; + dm = sys_mmap((char *)addr, chunk, prot, flags, -1, 0); + TrackMemoryInterval(&_mmi, addr >> 16, (addr + chunk - 1) >> 16, dm.maphandle, + prot, flags, false, false, 0, chunk); + if (IsAsan()) { + addr = (addr >> 3) + 0x7fff8000; + dm = sys_mmap((char *)addr, FRAMESIZE, prot, flags, -1, 0); + TrackMemoryInterval(&_mmi, addr >> 16, addr >> 16, dm.maphandle, prot, + flags, false, false, 0, FRAMESIZE); + } + if (!size) { + g_fds.p = memcpy((char *)kMemtrackFdsStart, g_fds.__init_p, + sizeof(g_fds.__init_p)); + } + g_fds.n = (size + chunk) / sizeof(*g_fds.p); + mapsize = size + chunk; return fd; } diff --git a/libc/calls/samplepids.c b/libc/calls/samplepids.c index b1a6c8016..035f43356 100644 --- a/libc/calls/samplepids.c +++ b/libc/calls/samplepids.c @@ -19,8 +19,8 @@ #include "libc/assert.h" #include "libc/calls/internal.h" #include "libc/dce.h" -#include "libc/thread/thread.h" -#include "libc/thread/tls.h" +#include "libc/intrin/pthread.h" +#include "libc/nexgen32e/threaded.h" #include "libc/stdio/lcg.internal.h" /** diff --git a/libc/calls/sched_setscheduler.c b/libc/calls/sched_setscheduler.c index e5fb93996..211f14326 100644 --- a/libc/calls/sched_setscheduler.c +++ b/libc/calls/sched_setscheduler.c @@ -22,7 +22,7 @@ #include "libc/calls/struct/sched_param.internal.h" #include "libc/dce.h" #include "libc/intrin/describeflags.internal.h" -#include "libc/thread/tls.h" +#include "libc/nexgen32e/threaded.h" #include "libc/str/str.h" #include "libc/sysv/errfuns.h" diff --git a/libc/calls/semaphore.internal.h b/libc/calls/semaphore.internal.h deleted file mode 100644 index 71e7d6602..000000000 --- a/libc/calls/semaphore.internal.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_CALLS_SEMAPHORE_H_ -#define COSMOPOLITAN_LIBC_CALLS_SEMAPHORE_H_ -#include "libc/calls/struct/timespec.h" -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -#define SEM_FAILED ((sem_t *)0) - -typedef struct { - volatile int __val[4 * sizeof(long) / sizeof(int)]; -} sem_t; - -int sem_close(sem_t *); -int sem_destroy(sem_t *); -int sem_getvalue(sem_t *, int *); -int sem_init(sem_t *, int, unsigned); -sem_t *sem_open(const char *, int, ...); -int sem_post(sem_t *); -int sem_timedwait(sem_t *, const struct timespec *); -int sem_trywait(sem_t *); -int sem_unlink(const char *); -int sem_wait(sem_t *); - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_LIBC_CALLS_SEMAPHORE_H_ */ diff --git a/libc/calls/sig2.c b/libc/calls/sig2.c index 9d4c0bd19..606514086 100644 --- a/libc/calls/sig2.c +++ b/libc/calls/sig2.c @@ -16,7 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/calls.h" #include "libc/calls/sig.internal.h" #include "libc/calls/state.internal.h" #include "libc/calls/strace.internal.h" diff --git a/libc/calls/siglock.c b/libc/calls/siglock.c index 9d3632dea..c9feb3ba2 100644 --- a/libc/calls/siglock.c +++ b/libc/calls/siglock.c @@ -17,7 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/state.internal.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" static pthread_mutex_t __sig_lock_obj; @@ -30,5 +30,5 @@ void(__sig_unlock)(void) { } __attribute__((__constructor__)) static void init(void) { - __sig_lock_obj._type = PTHREAD_MUTEX_RECURSIVE; + __sig_lock_obj.type = PTHREAD_MUTEX_RECURSIVE; } diff --git a/libc/calls/stat64.S b/libc/calls/stat64.S index e61b7bb04..7b6738b40 100644 --- a/libc/calls/stat64.S +++ b/libc/calls/stat64.S @@ -1,5 +1,5 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ │ This is free and unencumbered software released into the public domain. │ │ │ diff --git a/libc/calls/state.internal.h b/libc/calls/state.internal.h index 4f9a4dc3b..b5eacf1ba 100644 --- a/libc/calls/state.internal.h +++ b/libc/calls/state.internal.h @@ -1,8 +1,8 @@ #ifndef COSMOPOLITAN_LIBC_CALLS_STATE_INTERNAL_H_ #define COSMOPOLITAN_LIBC_CALLS_STATE_INTERNAL_H_ -#include "libc/intrin/nopl.internal.h" -#include "libc/thread/thread.h" -#include "libc/thread/tls.h" +#include "libc/intrin/nopl.h" +#include "libc/intrin/pthread.h" +#include "libc/nexgen32e/threaded.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ diff --git a/libc/calls/struct/fd.internal.h b/libc/calls/struct/fd.internal.h index fe117b253..17d586236 100644 --- a/libc/calls/struct/fd.internal.h +++ b/libc/calls/struct/fd.internal.h @@ -25,9 +25,10 @@ struct Fd { }; struct Fds { - int f; /* lowest free slot */ - size_t n; - struct Fd *p, *e; + int f; /* lowest free slot */ + size_t n; /* monotonic capacity */ + struct Fd *p; + struct Fd __init_p[OPEN_MAX]; }; COSMOPOLITAN_C_END_ diff --git a/libc/calls/struct/timespec.h b/libc/calls/struct/timespec.h index 3bc0cb042..4c555e637 100644 --- a/libc/calls/struct/timespec.h +++ b/libc/calls/struct/timespec.h @@ -17,7 +17,6 @@ int utimensat(int, const char *, const struct timespec[2], int); int timespec_get(struct timespec *, int); int timespec_getres(struct timespec *, int); -int _timespec_cmp(struct timespec, struct timespec) pureconst; bool _timespec_eq(struct timespec, struct timespec) pureconst; bool _timespec_gte(struct timespec, struct timespec) pureconst; int64_t _timespec_tomicros(struct timespec) pureconst; diff --git a/libc/calls/symlinkat-nt.c b/libc/calls/symlinkat-nt.c index cdb7acc49..dd36786bd 100644 --- a/libc/calls/symlinkat-nt.c +++ b/libc/calls/symlinkat-nt.c @@ -19,7 +19,7 @@ #include "libc/calls/calls.h" #include "libc/calls/syscall_support-nt.internal.h" #include "libc/errno.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" #include "libc/nt/enum/accessmask.h" #include "libc/nt/enum/fileflagandattributes.h" #include "libc/nt/enum/symboliclink.h" diff --git a/libc/calls/unveil.c b/libc/calls/unveil.c index b439553b5..759ad3db8 100644 --- a/libc/calls/unveil.c +++ b/libc/calls/unveil.c @@ -30,7 +30,7 @@ #include "libc/errno.h" #include "libc/fmt/conv.h" #include "libc/macros.internal.h" -#include "libc/thread/tls.h" +#include "libc/nexgen32e/threaded.h" #include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" #include "libc/runtime/stack.h" diff --git a/libc/dns/gethoststxt.c b/libc/dns/gethoststxt.c index 55fb566a7..32ff8dfce 100644 --- a/libc/dns/gethoststxt.c +++ b/libc/dns/gethoststxt.c @@ -23,7 +23,7 @@ #include "libc/dce.h" #include "libc/dns/hoststxt.h" #include "libc/fmt/fmt.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" #include "libc/macros.internal.h" #include "libc/nt/systeminfo.h" #include "libc/runtime/runtime.h" diff --git a/libc/dns/getresolvconf.c b/libc/dns/getresolvconf.c index 1546ee9c6..e226ac13f 100644 --- a/libc/dns/getresolvconf.c +++ b/libc/dns/getresolvconf.c @@ -20,7 +20,7 @@ #include "libc/dce.h" #include "libc/dns/resolvconf.h" #include "libc/fmt/fmt.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" #include "libc/macros.internal.h" #include "libc/runtime/runtime.h" #include "libc/sock/sock.h" diff --git a/libc/fmt/conv.h b/libc/fmt/conv.h index 71fb95067..630cbac7b 100644 --- a/libc/fmt/conv.h +++ b/libc/fmt/conv.h @@ -118,8 +118,7 @@ imaxdiv_t imaxdiv(intmax_t, intmax_t) pureconst; #define lldiv(num, den) ((lldiv_t){(num) / (den), (num) % (den)}) #endif -#if (__GNUC__ * 100 + __GNUC_MINOR__ >= 406 || defined(__llvm__)) && \ - !defined(__STRICT_ANSI__) +#if __GNUC__ * 100 + __GNUC_MINOR__ >= 406 || defined(__llvm__) int128_t i128abs(int128_t) libcesque pureconst; int128_t strtoi128(const char *, char **, int) paramsnonnull((1)); uint128_t strtou128(const char *, char **, int) paramsnonnull((1)); diff --git a/libc/intrin/lengthuint64.c b/libc/fmt/lengthuint64.c similarity index 100% rename from libc/intrin/lengthuint64.c rename to libc/fmt/lengthuint64.c diff --git a/libc/integral/c.inc b/libc/integral/c.inc index cfc14baf5..68bcd7090 100644 --- a/libc/integral/c.inc +++ b/libc/integral/c.inc @@ -123,9 +123,7 @@ typedef __UINT64_TYPE__ uint64_t; typedef __INTMAX_TYPE__ intmax_t; typedef __UINTMAX_TYPE__ uintmax_t; -#if ((__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 406 || \ - defined(__llvm__)) && \ - !defined(__STRICT_ANSI__) +#if (__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 406 || defined(__llvm__) typedef signed __int128 int128_t; typedef unsigned __int128 uint128_t; #endif diff --git a/libc/integral/normalize.inc b/libc/integral/normalize.inc index 30be7c9b9..513778444 100644 --- a/libc/integral/normalize.inc +++ b/libc/integral/normalize.inc @@ -67,8 +67,8 @@ #endif /* TODO(jart): Remove this in favor of GetStackSize() */ -#if defined(COSMO) && (defined(MODE_DBG) || defined(__SANITIZE_ADDRESS__)) -#define STACKSIZE 262144 /* 256kb stack */ +#if defined(COSMO) && defined(MODE_DBG) +#define STACKSIZE 131072 /* 128kb stack */ #elif defined(COSMO) #define STACKSIZE 65536 /* 64kb stack */ #else diff --git a/libc/intrin/asan.c b/libc/intrin/asan.c index dc0315568..5fb061e14 100644 --- a/libc/intrin/asan.c +++ b/libc/intrin/asan.c @@ -25,10 +25,10 @@ #include "libc/intrin/asan.internal.h" #include "libc/intrin/asancodes.h" #include "libc/intrin/kprintf.h" -#include "libc/intrin/leaky.internal.h" #include "libc/intrin/likely.h" #include "libc/intrin/lockcmpxchg.h" #include "libc/intrin/nomultics.internal.h" +#include "libc/intrin/pthread.h" #include "libc/intrin/weaken.h" #include "libc/log/backtrace.internal.h" #include "libc/log/internal.h" @@ -39,7 +39,9 @@ #include "libc/mem/mem.h" #include "libc/mem/reverse.internal.h" #include "libc/nexgen32e/gc.internal.h" +#include "libc/nexgen32e/gettls.h" #include "libc/nexgen32e/stackframe.h" +#include "libc/nexgen32e/threaded.h" #include "libc/nt/enum/version.h" #include "libc/nt/runtime.h" #include "libc/runtime/directmap.internal.h" @@ -56,7 +58,6 @@ #include "libc/sysv/consts/prot.h" #include "libc/sysv/errfuns.h" #include "libc/thread/thread.h" -#include "libc/thread/tls.h" #include "third_party/dlmalloc/dlmalloc.h" STATIC_YOINK("_init_asan"); @@ -171,7 +172,7 @@ struct ReportOriginHeap { }; static int __asan_noreentry; -static pthread_spinlock_t __asan_lock; +static pthread_mutex_t __asan_lock; static struct AsanMorgue __asan_morgue; #define __asan_unreachable() \ @@ -868,25 +869,25 @@ dontdiscard __asan_die_f *__asan_report_memory_fault(void *addr, int size, void *__asan_morgue_add(void *p) { int i; void *r; - if (__threaded) pthread_spin_lock(&__asan_lock); + if (__threaded) pthread_mutex_lock(&__asan_lock); i = __asan_morgue.i++ & (ARRAYLEN(__asan_morgue.p) - 1); r = __asan_morgue.p[i]; __asan_morgue.p[i] = p; - if (__threaded) pthread_spin_unlock(&__asan_lock); + if (__threaded) pthread_mutex_unlock(&__asan_lock); return r; } static void __asan_morgue_flush(void) { int i; void *p; - if (__threaded) pthread_spin_lock(&__asan_lock); + if (__threaded) pthread_mutex_lock(&__asan_lock); for (i = 0; i < ARRAYLEN(__asan_morgue.p); ++i) { if (__asan_morgue.p[i] && weaken(dlfree)) { weaken(dlfree)(__asan_morgue.p[i]); } __asan_morgue.p[i] = 0; } - if (__threaded) pthread_spin_unlock(&__asan_lock); + if (__threaded) pthread_mutex_unlock(&__asan_lock); } static size_t __asan_user_size(size_t n) { @@ -937,7 +938,7 @@ static void __asan_trace(struct AsanTrace *bt, const struct StackFrame *bp) { size_t i, gi; intptr_t addr; struct Garbages *garbage; - garbage = __tls_enabled ? __get_tls()->tib_garbages : 0; + garbage = __tls_enabled ? ((cthread_t)__get_tls())->garbages : 0; gi = garbage ? garbage->i : 0; for (f1 = -1, i = 0; bp && i < ARRAYLEN(bt->p); ++i, bp = bp->next) { if (f1 != (f2 = ((intptr_t)bp >> 16))) { @@ -1053,30 +1054,6 @@ int __asan_print_trace(void *p) { return 0; } -// Returns true if `p` was allocated by an IGNORE_LEAKS(function). -int __asan_is_leaky(void *p) { - int sym; - size_t c, i, n; - intptr_t f, *l; - struct AsanExtra *e; - struct SymbolTable *st; - if (!weaken(GetSymbolTable)) notpossible; - if (!(e = __asan_get_extra(p, &c))) return 0; - if (!__asan_read48(e->size, &n)) return 0; - if (!__asan_is_mapped((((intptr_t)p >> 3) + 0x7fff8000) >> 16)) return 0; - if (!(st = GetSymbolTable())) return 0; - for (i = 0; i < ARRAYLEN(e->bt.p) && e->bt.p[i]; ++i) { - if ((sym = weaken(__get_symbol)(st, e->bt.p[i])) == -1) continue; - f = st->addr_base + st->symbols[sym].x; - for (l = _leaky_start; l < _leaky_end; ++l) { - if (f == *l) { - return 1; - } - } - } - return 0; -} - static void __asan_deallocate(char *p, long kind) { size_t c, n; struct AsanExtra *e; diff --git a/libc/intrin/asan.internal.h b/libc/intrin/asan.internal.h index eee7b4797..5dc92372f 100644 --- a/libc/intrin/asan.internal.h +++ b/libc/intrin/asan.internal.h @@ -3,7 +3,6 @@ #include "libc/calls/struct/iovec.h" #include "libc/intrin/asancodes.h" #include "libc/macros.internal.h" -#include "libc/thread/thread.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ @@ -30,7 +29,6 @@ struct AsanFault __asan_check(const void *, long) nosideeffect; void __asan_free(void *); void *__asan_malloc(size_t); -int __asan_is_leaky(void *); int __asan_malloc_trim(size_t); int __asan_print_trace(void *); void *__asan_calloc(size_t, size_t); diff --git a/libc/intrin/bigword.internal.h b/libc/intrin/bigword.internal.h new file mode 100644 index 000000000..4bf33dced --- /dev/null +++ b/libc/intrin/bigword.internal.h @@ -0,0 +1,16 @@ +#ifndef COSMOPOLITAN_LIBC_BITS_BIGWORD_H_ +#define COSMOPOLITAN_LIBC_BITS_BIGWORD_H_ + +#ifndef BIGWORD +#if __AVX512F__ + 0 +#define BIGWORD 64 +#elif __AVX2__ + 0 +#define BIGWORD 32 +#elif __SSE2__ + 0 +#define BIGWORD 16 +#else +#define BIGWORD __BIGGEST_ALIGNMENT__ +#endif +#endif /*BIGWORD*/ + +#endif /* COSMOPOLITAN_LIBC_BITS_BIGWORD_H_ */ diff --git a/libc/thread/pthread_cond_signal.c b/libc/intrin/cmpxchg.c similarity index 64% rename from libc/thread/pthread_cond_signal.c rename to libc/intrin/cmpxchg.c index 11dfd11c4..f8e363138 100644 --- a/libc/thread/pthread_cond_signal.c +++ b/libc/intrin/cmpxchg.c @@ -1,7 +1,7 @@ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ Copyright 2021 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 │ @@ -16,27 +16,32 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" -#include "third_party/nsync/cv.h" +#include "libc/intrin/cmpxchg.h" /** - * Wakes at least one thread waiting on condition, e.g. + * Compares and exchanges. * - * pthread_cond_t cond = PTHREAD_COND_INITIALIZER; - * pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; - * // ... - * pthread_mutex_lock(&lock); - * pthread_cond_signal(&cond, &lock); - * pthread_mutex_unlock(&lock); - * - * This function has no effect if there aren't any threads currently - * waiting on the condition. - * - * @return 0 on success, or errno on error - * @see pthread_cond_broadcast - * @see pthread_cond_wait + * @param ifthing is uint𝑘_t[hasatleast 1] where 𝑘 ∈ {8,16,32,64} + * @param size is automatically supplied by macro wrapper + * @return true if value was exchanged, otherwise false + * @see _lockcmpxchg() */ -int pthread_cond_signal(pthread_cond_t *cond) { - nsync_cv_signal((nsync_cv *)cond); - return 0; +bool(_cmpxchg)(void *ifthing, intptr_t isequaltome, intptr_t replaceitwithme, + size_t size) { + switch (size) { + case 1: + return _cmpxchg((int8_t *)ifthing, (int8_t)isequaltome, + (int8_t)replaceitwithme); + case 2: + return _cmpxchg((int16_t *)ifthing, (int16_t)isequaltome, + (int16_t)replaceitwithme); + case 4: + return _cmpxchg((int32_t *)ifthing, (int32_t)isequaltome, + (int32_t)replaceitwithme); + case 8: + return _cmpxchg((int64_t *)ifthing, (int64_t)isequaltome, + (int64_t)replaceitwithme); + default: + return false; + } } diff --git a/libc/intrin/cmpxchg.h b/libc/intrin/cmpxchg.h index 54e45d5dc..3ba5a734a 100644 --- a/libc/intrin/cmpxchg.h +++ b/libc/intrin/cmpxchg.h @@ -4,6 +4,8 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ +bool _cmpxchg(void *, intptr_t, intptr_t, size_t); + #if defined(__GNUC__) && !defined(__STRICT_ANSI__) && defined(__x86__) #define _cmpxchg(IFTHING, ISEQUALTOME, REPLACEITWITHME) \ ({ \ @@ -17,6 +19,9 @@ COSMOPOLITAN_C_START_ : "cc"); \ DidIt; \ }) +#else +#define _cmpxchg(MEM, CMP, VAL) \ + _cmpxchg(MEM, (intptr_t)(CMP), (intptr_t)(VAL), sizeof(*(MEM))) #endif /* GNUC && !ANSI && x86 */ COSMOPOLITAN_C_END_ diff --git a/libc/intrin/cxaatexit.internal.h b/libc/intrin/cxaatexit.internal.h index bf96a4e47..99e1e9036 100644 --- a/libc/intrin/cxaatexit.internal.h +++ b/libc/intrin/cxaatexit.internal.h @@ -1,7 +1,7 @@ #ifndef COSMOPOLITAN_LIBC_RUNTIME_CXAATEXIT_H_ #define COSMOPOLITAN_LIBC_RUNTIME_CXAATEXIT_H_ -#include "libc/intrin/nopl.internal.h" -#include "libc/thread/tls.h" +#include "libc/intrin/nopl.h" +#include "libc/nexgen32e/threaded.h" #include "libc/stdio/stdio.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ diff --git a/libc/intrin/cxalock.c b/libc/intrin/cxalock.c index 1f7b8d652..49b2d96b6 100644 --- a/libc/intrin/cxalock.c +++ b/libc/intrin/cxalock.c @@ -17,7 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/intrin/cxaatexit.internal.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" static pthread_mutex_t __cxa_lock_obj; diff --git a/libc/intrin/describeflags.internal.h b/libc/intrin/describeflags.internal.h index 8ec3b0aa5..0bc065de1 100644 --- a/libc/intrin/describeflags.internal.h +++ b/libc/intrin/describeflags.internal.h @@ -17,7 +17,7 @@ const char *DescribeCapability(char[20], int); const char *DescribeClockName(char[32], int); const char *DescribeDirfd(char[12], int); const char *DescribeFrame(char[32], int); -const char *DescribeFutexOp(char[64], int); +const char *DescribeFutexOp(int); const char *DescribeFutexResult(char[12], int); const char *DescribeHow(char[12], int); const char *DescribeMapFlags(char[64], int); @@ -30,7 +30,6 @@ const char *DescribeNtFileFlagAttr(char[256], uint32_t); const char *DescribeNtFileMapFlags(char[64], uint32_t); const char *DescribeNtFileShareFlags(char[64], uint32_t); const char *DescribeNtFiletypeFlags(char[64], uint32_t); -const char *DescribeNtLockFileFlags(char[64], uint32_t); const char *DescribeNtMovFileInpFlags(char[256], uint32_t); const char *DescribeNtPageFlags(char[64], uint32_t); const char *DescribeNtPipeModeFlags(char[64], uint32_t); @@ -61,7 +60,6 @@ const char *DescribeWhence(char[12], int); #define DescribeClockName(x) DescribeClockName(alloca(32), x) #define DescribeDirfd(x) DescribeDirfd(alloca(12), x) #define DescribeFrame(x) DescribeFrame(alloca(32), x) -#define DescribeFutexOp(x) DescribeFutexOp(alloca(64), x) #define DescribeFutexResult(x) DescribeFutexResult(alloca(12), x) #define DescribeHow(x) DescribeHow(alloca(12), x) #define DescribeMapFlags(x) DescribeMapFlags(alloca(64), x) @@ -73,7 +71,6 @@ const char *DescribeWhence(char[12], int); #define DescribeNtFileMapFlags(x) DescribeNtFileMapFlags(alloca(64), x) #define DescribeNtFileShareFlags(x) DescribeNtFileShareFlags(alloca(64), x) #define DescribeNtFiletypeFlags(x) DescribeNtFiletypeFlags(alloca(64), x) -#define DescribeNtLockFileFlags(x) DescribeNtLockFileFlags(alloca(64), x) #define DescribeNtMovFileInpFlags(x) DescribeNtMovFileInpFlags(alloca(256), x) #define DescribeNtPageFlags(x) DescribeNtPageFlags(alloca(64), x) #define DescribeNtPipeModeFlags(x) DescribeNtPipeModeFlags(alloca(64), x) diff --git a/libc/intrin/describefutexop.c b/libc/intrin/describefutexop.c index 80993d8d7..f956658d0 100644 --- a/libc/intrin/describefutexop.c +++ b/libc/intrin/describefutexop.c @@ -16,47 +16,16 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/fmt/itoa.h" #include "libc/intrin/describeflags.internal.h" -#include "libc/str/str.h" #include "libc/sysv/consts/futex.h" -const char *(DescribeFutexOp)(char buf[64], int x) { - - bool priv = false; - if (x & FUTEX_PRIVATE_FLAG) { - priv = true; - x &= ~FUTEX_PRIVATE_FLAG; - } - - bool real = false; - if (x & FUTEX_CLOCK_REALTIME) { - real = true; - x &= ~FUTEX_CLOCK_REALTIME; - } - - char *p = buf; - - if (x == FUTEX_WAIT) { - p = stpcpy(p, "FUTEX_WAIT"); - } else if (x == FUTEX_WAKE) { - p = stpcpy(p, "FUTEX_WAKE"); - } else if (x == FUTEX_REQUEUE) { - p = stpcpy(p, "FUTEX_REQUEUE"); - } else if (x == FUTEX_WAIT_BITSET) { - p = stpcpy(p, "FUTEX_WAIT_BITSET"); - } else { - p = stpcpy(p, "FUTEX_"); - p = FormatUint32(p, x); - } - - if (priv) { - p = stpcpy(p, "_PRIVATE"); - } - - if (real) { - p = stpcpy(p, "|FUTEX_CLOCK_REALTIME"); - } - - return buf; +const char *DescribeFutexOp(int x) { + if (x == FUTEX_WAIT) return "FUTEX_WAIT"; + if (x == FUTEX_WAKE) return "FUTEX_WAKE"; + if (x == FUTEX_REQUEUE) return "FUTEX_REQUEUE"; + // order matters (the private bit might be zero) + if (x == FUTEX_WAIT_PRIVATE) return "FUTEX_WAIT_PRIVATE"; + if (x == FUTEX_WAKE_PRIVATE) return "FUTEX_WAKE_PRIVATE"; + if (x == FUTEX_REQUEUE_PRIVATE) return "FUTEX_REQUEUE_PRIVATE"; + return "FUTEX_???"; } diff --git a/libc/intrin/describentoverlapped.c b/libc/intrin/describentoverlapped.c deleted file mode 100644 index 3a4b33a74..000000000 --- a/libc/intrin/describentoverlapped.c +++ /dev/null @@ -1,50 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2022 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/describentoverlapped.internal.h" -#include "libc/intrin/kprintf.h" -#include "libc/macros.internal.h" -#include "libc/nt/struct/overlapped.h" - -const char *(DescribeNtOverlapped)(char b[128], struct NtOverlapped *o) { - int i = 0, n = 128; - bool gotsome = false; - if (!o) return "NULL"; - i += ksnprintf(b + i, MAX(0, n - i), "{"); - - if (o->hEvent) { - if (gotsome) { - i += ksnprintf(b + i, MAX(0, n - i), ", "); - } else { - gotsome = true; - } - i += ksnprintf(b + i, MAX(0, n - i), ".hEvent = %ld", o->hEvent); - } - - if (o->Pointer) { - if (gotsome) { - i += ksnprintf(b + i, MAX(0, n - i), ", "); - } else { - gotsome = true; - } - i += ksnprintf(b + i, MAX(0, n - i), ".Pointer = (void *)%p", o->Pointer); - } - - i += ksnprintf(b + i, MAX(0, n - i), "}"); - return b; -} diff --git a/libc/intrin/describentoverlapped.internal.h b/libc/intrin/describentoverlapped.internal.h deleted file mode 100755 index 8d33a7611..000000000 --- a/libc/intrin/describentoverlapped.internal.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_INTRIN_DESCRIBENTOVERLAPPED_INTERNAL_H_ -#define COSMOPOLITAN_LIBC_INTRIN_DESCRIBENTOVERLAPPED_INTERNAL_H_ -#include "libc/mem/alloca.h" -#include "libc/nt/struct/overlapped.h" -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -const char *DescribeNtOverlapped(char[128], struct NtOverlapped *); -#define DescribeNtOverlapped(x) DescribeNtOverlapped(alloca(128), x) - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_LIBC_INTRIN_DESCRIBENTOVERLAPPED_INTERNAL_H_ */ diff --git a/libc/intrin/extend.c b/libc/intrin/extend.c deleted file mode 100644 index 3a24354f4..000000000 --- a/libc/intrin/extend.c +++ /dev/null @@ -1,86 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2022 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/assert.h" -#include "libc/calls/calls.h" -#include "libc/dce.h" -#include "libc/intrin/asan.internal.h" -#include "libc/intrin/asancodes.h" -#include "libc/macros.internal.h" -#include "libc/runtime/directmap.internal.h" -#include "libc/runtime/memtrack.internal.h" -#include "libc/sysv/consts/map.h" -#include "libc/sysv/consts/prot.h" - -#define G FRAMESIZE - -static void _mapframe(void *p) { - int prot, flags; - struct DirectMap dm; - prot = PROT_READ | PROT_WRITE; - flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED; - if ((dm = sys_mmap(p, G, prot, flags, -1, 0)).addr != p) { - notpossible; - } - __mmi_lock(); - if (TrackMemoryInterval(&_mmi, (uintptr_t)p >> 16, (uintptr_t)p >> 16, - dm.maphandle, prot, flags, false, false, 0, G)) { - notpossible; - } - __mmi_unlock(); -} - -/** - * Extends static allocation. - * - * This simple fixed allocator has unusual invariants - * - * !(p & 0xffff) && !(((p >> 3) + 0x7fff8000) & 0xffff) - * - * which must be the case when selecting a starting address. - * - * @param p points to start of memory region - * @param n specifies how many bytes are needed - * @param e points to end of memory that's allocated - * @param h is highest address to which `e` may grow - * @return new value for `e` - */ -noasan void *_extend(void *p, size_t n, void *e, intptr_t h) { - char *q; -#ifndef NDEBUG - if ((uintptr_t)SHADOW(p) & (G - 1)) notpossible; - if ((uintptr_t)p + (G << kAsanScale) > h) notpossible; -#endif - for (q = e; q < ((char *)p + n); q += 8) { - if (!((uintptr_t)q & (G - 1))) { - if (q + G > (char *)h) notpossible; - _mapframe(q); - if (IsAsan()) { - if (!((uintptr_t)SHADOW(q) & (G - 1))) { - _mapframe(SHADOW(q)); - __asan_poison(q, G << kAsanScale, kAsanProtected); - } - } - } - if (IsAsan()) { - *SHADOW(q) = 0; - } - } - asm("mfence"); - return q; -} diff --git a/libc/intrin/fds_lock.c b/libc/intrin/fds_lock.c index 6097b4a5a..b01bccb9e 100644 --- a/libc/intrin/fds_lock.c +++ b/libc/intrin/fds_lock.c @@ -17,7 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/state.internal.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" void(__fds_lock)(void) { pthread_mutex_lock(&__fds_lock_obj); diff --git a/libc/intrin/ftrace.c b/libc/intrin/ftrace.c index e63f11d68..202fe746c 100644 --- a/libc/intrin/ftrace.c +++ b/libc/intrin/ftrace.c @@ -16,7 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/atomic.h" #include "libc/runtime/runtime.h" /** @@ -40,4 +39,4 @@ * though under normal circumstances, `__ftrace` should only be either * zero or one. */ -atomic_int __ftrace; +_Atomic(int) __ftrace; diff --git a/libc/intrin/futex.S b/libc/intrin/futex.S index 1ff350b72..419bf82ba 100644 --- a/libc/intrin/futex.S +++ b/libc/intrin/futex.S @@ -20,14 +20,11 @@ #include "libc/macros.internal.h" .privileged -_futex: push %rbp - mov %rsp,%rbp - mov %rcx,%r10 +_futex: mov %rcx,%r10 mov __NR_futex,%eax clc syscall jnc 1f neg %eax -1: pop %rbp - ret +1: ret .endfn _futex,globl,hidden diff --git a/libc/intrin/futex.internal.h b/libc/intrin/futex.internal.h new file mode 100644 index 000000000..732c10891 --- /dev/null +++ b/libc/intrin/futex.internal.h @@ -0,0 +1,12 @@ +#ifndef COSMOPOLITAN_LIBC_INTRIN_FUTEX_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_INTRIN_FUTEX_INTERNAL_H_ +#include "libc/calls/struct/timespec.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +int _futex_wait(void *, int, char, struct timespec *) hidden; +int _futex_wake(void *, int, char) hidden; + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_INTRIN_FUTEX_INTERNAL_H_ */ diff --git a/libc/intrin/unlockfileex.c b/libc/intrin/futex_wait.c similarity index 63% rename from libc/intrin/unlockfileex.c rename to libc/intrin/futex_wait.c index 8d2908463..bb6a6c5c6 100644 --- a/libc/intrin/unlockfileex.c +++ b/libc/intrin/futex_wait.c @@ -17,29 +17,34 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/strace.internal.h" -#include "libc/calls/syscall_support-nt.internal.h" -#include "libc/intrin/describentoverlapped.internal.h" -#include "libc/nt/files.h" -#include "libc/nt/struct/overlapped.h" +#include "libc/calls/struct/timespec.h" +#include "libc/calls/struct/timespec.internal.h" +#include "libc/dce.h" +#include "libc/errno.h" +#include "libc/intrin/describeflags.internal.h" +#include "libc/intrin/futex.internal.h" +#include "libc/intrin/pthread.h" +#include "libc/sysv/consts/futex.h" -__msabi extern typeof(UnlockFileEx) *const __imp_UnlockFileEx; +int _futex(void *, int, int, struct timespec *) hidden; -/** - * Unlocks file on the New Technology. - * - * @return handle, or -1 on failure - * @note this wrapper takes care of ABI, STRACE(), and __winerr() - */ -bool32 UnlockFileEx(int64_t hFile, uint32_t dwReserved, - uint32_t nNumberOfBytesToUnlockLow, - uint32_t nNumberOfBytesToUnlockHigh, - struct NtOverlapped *lpOverlapped) { - bool32 ok; - ok = __imp_UnlockFileEx(hFile, dwReserved, nNumberOfBytesToUnlockLow, - nNumberOfBytesToUnlockHigh, lpOverlapped); - if (!ok) __winerr(); - STRACE("UnlockFileEx(%ld, %#x, %'zu, %s) → %hhhd% m", hFile, dwReserved, - (uint64_t)nNumberOfBytesToUnlockHigh << 32 | nNumberOfBytesToUnlockLow, - DescribeNtOverlapped(lpOverlapped), ok); - return ok; +int _futex_wait(void *addr, int expect, char pshared, + struct timespec *timeout) { + int op, ax, pf; + if (IsLinux() || IsOpenbsd()) { + pf = pshared == PTHREAD_PROCESS_PRIVATE ? FUTEX_PRIVATE_FLAG : 0; + op = FUTEX_WAIT | pf; + ax = _futex(addr, op, expect, timeout); + if (SupportsLinux() && pf && ax == -ENOSYS) { + // RHEL5 doesn't support FUTEX_PRIVATE_FLAG + op = FUTEX_WAIT; + ax = _futex(addr, op, expect, timeout); + } + if (IsOpenbsd() && ax > 0) ax = -ax; // yes openbsd does this w/o cf + STRACE("futex(%t, %s, %d, %s) → %s", addr, DescribeFutexOp(op), expect, + DescribeTimespec(0, timeout), DescribeFutexResult(ax)); + return ax; + } else { + return pthread_yield(); + } } diff --git a/libc/intrin/describentlockfileflags.c b/libc/intrin/futex_wake.c similarity index 70% rename from libc/intrin/describentlockfileflags.c rename to libc/intrin/futex_wake.c index d7afc98c6..d53330a57 100644 --- a/libc/intrin/describentlockfileflags.c +++ b/libc/intrin/futex_wake.c @@ -16,16 +16,31 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/strace.internal.h" +#include "libc/dce.h" +#include "libc/errno.h" #include "libc/intrin/describeflags.internal.h" -#include "libc/macros.internal.h" -#include "libc/nt/enum/filelockflags.h" +#include "libc/intrin/futex.internal.h" +#include "libc/intrin/pthread.h" +#include "libc/sysv/consts/futex.h" -static const struct DescribeFlags kNtLockFileFlags[] = { - {kNtLockfileFailImmediately, "FailImmediately"}, // - {kNtLockfileExclusiveLock, "ExclusiveLock"}, // -}; +int _futex(void *, int, int) hidden; -const char *(DescribeNtLockFileFlags)(char buf[64], uint32_t x) { - return DescribeFlags(buf, 64, kNtLockFileFlags, ARRAYLEN(kNtLockFileFlags), - "kNtLockfile", x); +int _futex_wake(void *addr, int count, char pshared) { + int op, ax, pf; + if (IsLinux() || IsOpenbsd()) { + pf = pshared == PTHREAD_PROCESS_PRIVATE ? FUTEX_PRIVATE_FLAG : 0; + op = FUTEX_WAKE | pf; + ax = _futex(addr, op, count); + if (SupportsLinux() && pf && ax == -ENOSYS) { + // RHEL5 doesn't support FUTEX_PRIVATE_FLAG + op = FUTEX_WAKE; + ax = _futex(addr, op, count); + } + STRACE("futex(%t, %s, %d) → %s", addr, DescribeFutexOp(op), count, + DescribeFutexResult(ax)); + return ax; + } else { + return 0; + } } diff --git a/libc/intrin/g_fds.c b/libc/intrin/g_fds.c index f3c0cfe8b..192476725 100644 --- a/libc/intrin/g_fds.c +++ b/libc/intrin/g_fds.c @@ -16,14 +16,13 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/extend.internal.h" #include "libc/calls/internal.h" #include "libc/calls/state.internal.h" +#include "libc/intrin/pthread.h" #include "libc/intrin/pushpop.h" #include "libc/intrin/weaken.h" #include "libc/nt/runtime.h" #include "libc/sysv/consts/o.h" -#include "libc/thread/thread.h" STATIC_YOINK("_init_g_fds"); @@ -34,39 +33,38 @@ static textwindows dontinline void SetupWinStd(struct Fds *fds, int i, int x) { int64_t h; h = GetStdHandle(x); if (!h || h == -1) return; - fds->p[i].kind = pushpop(kFdFile); - fds->p[i].handle = h; + fds->__init_p[i].kind = pushpop(kFdFile); + fds->__init_p[i].handle = h; fds->f = i + 1; } textstartup void InitializeFileDescriptors(void) { struct Fds *fds; - __fds_lock_obj._type = PTHREAD_MUTEX_RECURSIVE; + __fds_lock_obj.type = PTHREAD_MUTEX_RECURSIVE; fds = VEIL("r", &g_fds); - fds->p = fds->e = (void *)0x6fe000040000; - fds->n = 4; + pushmov(&fds->n, ARRAYLEN(fds->__init_p)); fds->f = 3; - fds->e = _extend(fds->p, fds->n * sizeof(*fds->p), fds->e, 0x6ff000000000); + fds->p = fds->__init_p; if (IsMetal()) { extern const char vga_console[]; pushmov(&fds->f, 3ull); if (weaken(vga_console)) { - fds->p[0].kind = pushpop(kFdConsole); - fds->p[1].kind = pushpop(kFdConsole); - fds->p[2].kind = pushpop(kFdConsole); + fds->__init_p[0].kind = pushpop(kFdConsole); + fds->__init_p[1].kind = pushpop(kFdConsole); + fds->__init_p[2].kind = pushpop(kFdConsole); } else { - fds->p[0].kind = pushpop(kFdSerial); - fds->p[1].kind = pushpop(kFdSerial); - fds->p[2].kind = pushpop(kFdSerial); + fds->__init_p[0].kind = pushpop(kFdSerial); + fds->__init_p[1].kind = pushpop(kFdSerial); + fds->__init_p[2].kind = pushpop(kFdSerial); } - fds->p[0].handle = VEIL("r", 0x3F8ull); - fds->p[1].handle = VEIL("r", 0x3F8ull); - fds->p[2].handle = VEIL("r", 0x3F8ull); + fds->__init_p[0].handle = VEIL("r", 0x3F8ull); + fds->__init_p[1].handle = VEIL("r", 0x3F8ull); + fds->__init_p[2].handle = VEIL("r", 0x3F8ull); } else if (IsWindows()) { SetupWinStd(fds, 0, kNtStdInputHandle); SetupWinStd(fds, 1, kNtStdOutputHandle); SetupWinStd(fds, 2, kNtStdErrorHandle); } - fds->p[1].flags = O_WRONLY | O_APPEND; - fds->p[2].flags = O_WRONLY | O_APPEND; + fds->__init_p[1].flags = O_WRONLY | O_APPEND; + fds->__init_p[2].flags = O_WRONLY | O_APPEND; } diff --git a/libc/intrin/gettid.c b/libc/intrin/gettid.c index 8b1018399..77ac11678 100644 --- a/libc/intrin/gettid.c +++ b/libc/intrin/gettid.c @@ -16,10 +16,13 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" #include "libc/calls/calls.h" #include "libc/calls/state.internal.h" #include "libc/calls/syscall-sysv.internal.h" -#include "libc/thread/tls.h" +#include "libc/dce.h" +#include "libc/nexgen32e/gettls.h" +#include "libc/nexgen32e/threaded.h" /** * Returns current thread id. @@ -28,6 +31,22 @@ * if this is the main thread. On NetBSD, gettid() for the main thread * is always 1. * + * This function issues a system call. That stops being the case as soon + * as __install_tls() is called. That'll happen automatically, when you + * call clone() and provide the TLS parameter. We assume that when a TLS + * block exists, then + * + * *(int *)(__get_tls() + 0x38) + * + * will contain the thread id. Therefore when issuing clone() calls, the + * `CLONE_CHILD_SETTID` and `CLONE_CHILD_CLEARTID` flags should use that + * index as its `ctid` memory. + * + * gettid (single threaded) l: 126𝑐 41𝑛𝑠 + * gettid (tls enabled) l: 2𝑐 1𝑛𝑠 + * + * The TLS convention is important for reentrant lock performance. + * * @return thread id greater than zero or -1 w/ errno * @asyncsignalsafe * @threadsafe @@ -36,7 +55,7 @@ int gettid(void) { int tid; if (__tls_enabled && !__vforked) { - tid = __get_tls()->tib_tid; + tid = *(int *)(__get_tls() + 0x38); if (tid > 0) { return tid; } diff --git a/libc/intrin/intrin.mk b/libc/intrin/intrin.mk index ddc5615e2..891ae56c0 100644 --- a/libc/intrin/intrin.mk +++ b/libc/intrin/intrin.mk @@ -43,20 +43,6 @@ $(LIBC_INTRIN_A).pkg: \ $(LIBC_INTRIN_A_OBJS) \ $(foreach x,$(LIBC_INTRIN_A_DIRECTDEPS),$($(x)_A).pkg) -# we can't use asan because: -# asan guard pages haven't been allocated yet -o/$(MODE)/libc/intrin/directmap.o \ -o/$(MODE)/libc/intrin/directmap-nt.o: private \ - OVERRIDE_COPTS += \ - -ffreestanding \ - -fno-sanitize=address - -# we want small code size because: -# to keep .text.head under 4096 bytes -o/$(MODE)/libc/intrin/mman.greg.o: private \ - OVERRIDE_COPTS += \ - -Os - # we can't use asan and ubsan because: # this is asan and ubsan o/$(MODE)/libc/intrin/asan.o \ @@ -93,6 +79,11 @@ o/$(MODE)/libc/intrin/futex_wait.o \ o/$(MODE)/libc/intrin/futex_wake.o \ o/$(MODE)/libc/intrin/gettid.greg.o \ o/$(MODE)/libc/intrin/sys_gettid.greg.o \ +o/$(MODE)/libc/intrin/pthread_mutex_lock.o \ +o/$(MODE)/libc/intrin/pthread_mutex_wait.o \ +o/$(MODE)/libc/intrin/pthread_mutex_wake.o \ +o/$(MODE)/libc/intrin/pthread_mutex_unlock.o \ +o/$(MODE)/libc/intrin/pthread_mutex_trylock.o \ o/$(MODE)/libc/intrin/_trylock_debug_4.o \ o/$(MODE)/libc/intrin/_spinlock_debug_4.o: private \ OVERRIDE_CFLAGS += \ diff --git a/libc/intrin/kprintf.greg.c b/libc/intrin/kprintf.greg.c index fd072467d..6d7bbf1ef 100644 --- a/libc/intrin/kprintf.greg.c +++ b/libc/intrin/kprintf.greg.c @@ -24,8 +24,6 @@ #include "libc/errno.h" #include "libc/fmt/divmod10.internal.h" #include "libc/fmt/fmt.h" -#include "libc/intrin/asan.internal.h" -#include "libc/intrin/asancodes.h" #include "libc/intrin/bits.h" #include "libc/intrin/cmpxchg.h" #include "libc/intrin/kprintf.h" @@ -37,7 +35,9 @@ #include "libc/limits.h" #include "libc/log/internal.h" #include "libc/macros.internal.h" +#include "libc/nexgen32e/gettls.h" #include "libc/nexgen32e/rdtsc.h" +#include "libc/nexgen32e/threaded.h" #include "libc/nexgen32e/uart.internal.h" #include "libc/nt/process.h" #include "libc/nt/runtime.h" @@ -52,8 +52,6 @@ #include "libc/str/utf16.h" #include "libc/sysv/consts/nr.h" #include "libc/sysv/consts/prot.h" -#include "libc/thread/tls.h" -#include "libc/thread/tls2.h" extern hidden struct SymbolTable *__symtab; @@ -183,22 +181,19 @@ privileged static void klog(const char *b, size_t n) { : "=a"(rax), "=D"(rdi), "=S"(rsi), "=d"(rdx) : "0"(__NR_write), "1"(2), "2"(b), "3"(n) : "rcx", "r8", "r9", "r10", "r11", "memory", "cc"); - if (rax < 0) { - notpossible; - } } } privileged static size_t kformat(char *b, size_t n, const char *fmt, va_list va) { - int si, y; + int si; wint_t t, u; const char *abet; signed char type; const char *s, *f; unsigned long long x; unsigned i, j, m, rem, sign, hash, cols, prec; - char c, *p, *e, pdot, zero, flip, dang, base, quot, uppr, ansi, z[128]; + char c, *p, *e, pdot, zero, flip, dang, base, quot, uppr, z[128]; if (kistextpointer(b) || kisdangerous(b)) n = 0; if (!kistextpointer(fmt)) fmt = "!!WONTFMT"; p = b; @@ -317,17 +312,7 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt, if (!__tls_enabled) { x = __pid; } else { - x = __get_tls_privileged()->tib_tid; - } - if (!__nocolor && p + 7 <= e) { - *p++ = '\e'; - *p++ = '['; - *p++ = '1'; - *p++ = ';'; - *p++ = '3'; - *p++ = '0' + x % 8; - *p++ = 'm'; - ansi = true; + x = *(int *)(__get_tls_privileged() + 0x38); } } else { x = 666; @@ -705,15 +690,6 @@ privileged static size_t kformat(char *b, size_t n, const char *fmt, } break; } - if (ansi) { - if (p + 4 <= e) { - *p++ = '\e'; - *p++ = '['; - *p++ = '0'; - *p++ = 'm'; - } - ansi = false; - } break; } } diff --git a/libc/intrin/leaky.internal.h b/libc/intrin/leaky.internal.h deleted file mode 100644 index 225671a9a..000000000 --- a/libc/intrin/leaky.internal.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_INTRIN_LEAKY_INTERNAL_H_ -#define COSMOPOLITAN_LIBC_INTRIN_LEAKY_INTERNAL_H_ -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -#define IGNORE_LEAKS(FUNC) \ - STATIC_YOINK("_leaky_start"); \ - void *_leaky_##FUNC[] _Section(".piro.relo.sort.leaky.2." #FUNC \ - ",\"aw\",@init_array #") = {FUNC} - -extern intptr_t _leaky_end[] __attribute__((__weak__)); -extern intptr_t _leaky_start[] __attribute__((__weak__)); - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_LIBC_INTRIN_LEAKY_INTERNAL_H_ */ diff --git a/libc/intrin/lockcmpxchg.c b/libc/intrin/lockcmpxchg.c new file mode 100644 index 000000000..126659419 --- /dev/null +++ b/libc/intrin/lockcmpxchg.c @@ -0,0 +1,47 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ 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/lockcmpxchg.h" + +/** + * Compares and exchanges w/ lock prefix. + * + * @param ifthing is uint𝑘_t[hasatleast 1] where 𝑘 ∈ {8,16,32,64} + * @param size is automatically supplied by macro wrapper + * @return true if value was exchanged, otherwise false + * @see cmpxchg() if only written by one thread + */ +bool(_lockcmpxchg)(void *ifthing, intptr_t isequaltome, + intptr_t replaceitwithme, size_t size) { + switch (size) { + case 1: + return _lockcmpxchg((int8_t *)ifthing, (int8_t)isequaltome, + (int8_t)replaceitwithme); + case 2: + return _lockcmpxchg((int16_t *)ifthing, (int16_t)isequaltome, + (int16_t)replaceitwithme); + case 4: + return _lockcmpxchg((int32_t *)ifthing, (int32_t)isequaltome, + (int32_t)replaceitwithme); + case 8: + return _lockcmpxchg((int64_t *)ifthing, (int64_t)isequaltome, + (int64_t)replaceitwithme); + default: + return false; + } +} diff --git a/libc/intrin/lockcmpxchg.h b/libc/intrin/lockcmpxchg.h index d025e8d58..6f8b687ed 100644 --- a/libc/intrin/lockcmpxchg.h +++ b/libc/intrin/lockcmpxchg.h @@ -4,6 +4,8 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ +bool _lockcmpxchg(void *, intptr_t, intptr_t, size_t); + #if defined(__GNUC__) && !defined(__STRICT_ANSI__) && defined(__x86__) #define _lockcmpxchg(IFTHING, ISEQUALTOME, REPLACEITWITHME) \ ({ \ @@ -17,6 +19,9 @@ COSMOPOLITAN_C_START_ : "cc"); \ DidIt; \ }) +#else +#define _lockcmpxchg(MEM, CMP, VAL) \ + _lockcmpxchg(MEM, (intptr_t)(CMP), (intptr_t)(VAL), sizeof(*(MEM))) #endif /* GNUC && !ANSI && x86 */ COSMOPOLITAN_C_END_ diff --git a/libc/intrin/lockfileex.c b/libc/intrin/lockfileex.c deleted file mode 100644 index b9d97587c..000000000 --- a/libc/intrin/lockfileex.c +++ /dev/null @@ -1,50 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2022 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/calls/strace.internal.h" -#include "libc/calls/syscall_support-nt.internal.h" -#include "libc/intrin/describeflags.internal.h" -#include "libc/intrin/describentoverlapped.internal.h" -#include "libc/nt/files.h" - -__msabi extern typeof(LockFileEx) *const __imp_LockFileEx; - -/** - * Locks file on the New Technology. - * - * @return handle, or -1 on failure - * @note this wrapper takes care of ABI, STRACE(), and __winerr() - */ -bool32 LockFileEx(int64_t hFile, uint32_t dwFlags, uint32_t dwReserved, - uint32_t nNumberOfBytesToLockLow, - uint32_t nNumberOfBytesToLockHigh, - struct NtOverlapped *lpOverlapped) { - bool32 ok; - STRACE("LockFileEx(%ld, %s, %#x, %'zu, %s) → ...", hFile, - DescribeNtLockFileFlags(dwFlags), dwReserved, - (uint64_t)nNumberOfBytesToLockHigh << 32 | nNumberOfBytesToLockLow, - DescribeNtOverlapped(lpOverlapped)); - ok = __imp_LockFileEx(hFile, dwFlags, dwReserved, nNumberOfBytesToLockLow, - nNumberOfBytesToLockHigh, lpOverlapped); - if (!ok) __winerr(); - STRACE("LockFileEx(%ld, %s, %#x, %'zu, [%s]) → %hhhd% m", hFile, - DescribeNtLockFileFlags(dwFlags), dwReserved, - (uint64_t)nNumberOfBytesToLockHigh << 32 | nNumberOfBytesToLockLow, - DescribeNtOverlapped(lpOverlapped), ok); - return ok; -} diff --git a/libc/calls/_timespec_cmp.c b/libc/intrin/lockxadd.c similarity index 66% rename from libc/calls/_timespec_cmp.c rename to libc/intrin/lockxadd.c index 46b9bf122..653a6377d 100644 --- a/libc/calls/_timespec_cmp.c +++ b/libc/intrin/lockxadd.c @@ -1,7 +1,7 @@ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ Copyright 2021 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 │ @@ -16,15 +16,29 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/struct/timespec.h" +#include "libc/intrin/lockxadd.h" +#include "libc/runtime/runtime.h" /** - * Compares two nanosecond timestamps. + * Compares and exchanges w/ lock prefix. + * + * @param ifthing is uint𝑘_t[hasatleast 1] where 𝑘 ∈ {8,16,32,64} + * @param size is automatically supplied by macro wrapper + * @return value at location `*ifthing` *before* addition + * @see InterlockedAdd() for a very similar API + * @see xadd() if only written by one thread */ -int _timespec_cmp(struct timespec a, struct timespec b) { - int cmp; - if (!(cmp = (a.tv_sec > b.tv_sec) - (a.tv_sec < b.tv_sec))) { - cmp = (a.tv_nsec > b.tv_nsec) - (a.tv_nsec < b.tv_nsec); +intptr_t(_lockxadd)(void *ifthing, intptr_t replaceitwithme, size_t size) { + switch (size) { + case 1: + return _lockxadd((int8_t *)ifthing, (int8_t)replaceitwithme); + case 2: + return _lockxadd((int16_t *)ifthing, (int16_t)replaceitwithme); + case 4: + return _lockxadd((int32_t *)ifthing, (int32_t)replaceitwithme); + case 8: + return _lockxadd((int64_t *)ifthing, (int64_t)replaceitwithme); + default: + abort(); } - return cmp; } diff --git a/libc/thread/pthread_rwlock_unlock.c b/libc/intrin/lockxchg.c similarity index 70% rename from libc/thread/pthread_rwlock_unlock.c rename to libc/intrin/lockxchg.c index 617746146..a372ff997 100644 --- a/libc/thread/pthread_rwlock_unlock.c +++ b/libc/intrin/lockxchg.c @@ -1,7 +1,7 @@ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ Copyright 2021 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 │ @@ -16,21 +16,27 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" -#include "third_party/nsync/mu.h" +#include "libc/intrin/lockxchg.h" /** - * Unlocks read-write lock. + * Compares and exchanges w/ lock prefix. * - * @return 0 on success, or errno on error - * @raise EINVAL if lock is in a bad state + * @param memory is uint𝑘_t[hasatleast 1] where 𝑘 ∈ {8,16,32,64} + * @param size is automatically supplied by macro wrapper + * @return true if value was exchanged, otherwise false + * @see xchg() */ -int pthread_rwlock_unlock(pthread_rwlock_t *rwlock) { - if (rwlock->_iswrite) { - rwlock->_iswrite = 0; - nsync_mu_unlock((nsync_mu *)rwlock); - } else { - nsync_mu_runlock((nsync_mu *)rwlock); +intptr_t(lockxchg)(void *memory, void *localvar, size_t size) { + switch (size) { + case 1: + return lockxchg((int8_t *)memory, (int8_t *)localvar); + case 2: + return lockxchg((int16_t *)memory, (int16_t *)localvar); + case 4: + return lockxchg((int32_t *)memory, (int32_t *)localvar); + case 8: + return lockxchg((int64_t *)memory, (int64_t *)localvar); + default: + return false; } - return 0; } diff --git a/libc/intrin/mmi.init.S b/libc/intrin/mmi.init.S index 5910d3c0f..54842d9c8 100644 --- a/libc/intrin/mmi.init.S +++ b/libc/intrin/mmi.init.S @@ -16,11 +16,11 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" #include "libc/macros.internal.h" .init.start 200,_init__mmi movb $OPEN_MAX,_mmi+8 movl $_mmi+24,_mmi+16 - movb $PTHREAD_MUTEX_RECURSIVE,__mmi_lock_obj+16(%rip) + movb $PTHREAD_MUTEX_RECURSIVE,__mmi_lock_obj(%rip) .init.end 200,_init__mmi diff --git a/libc/intrin/mmi_lock.c b/libc/intrin/mmi_lock.c index 0bcbed171..7dc44fd97 100644 --- a/libc/intrin/mmi_lock.c +++ b/libc/intrin/mmi_lock.c @@ -16,10 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/intrin/pthread.h" #include "libc/runtime/memtrack.internal.h" -#include "libc/thread/thread.h" - -// this lock currently needs to be (1) recursive and (2) not nsync extern pthread_mutex_t __mmi_lock_obj; diff --git a/libc/intrin/mmi_lock_obj.c b/libc/intrin/mmi_lock_obj.c index e33861b9d..c34d116bc 100644 --- a/libc/intrin/mmi_lock_obj.c +++ b/libc/intrin/mmi_lock_obj.c @@ -16,6 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" pthread_mutex_t __mmi_lock_obj; // recursive :'( diff --git a/libc/intrin/nopl.internal.h b/libc/intrin/nopl.h similarity index 100% rename from libc/intrin/nopl.internal.h rename to libc/intrin/nopl.h diff --git a/libc/thread/pthread_attr_setschedpolicy.c b/libc/intrin/phtread_attr_setschedpolicy.c similarity index 98% rename from libc/thread/pthread_attr_setschedpolicy.c rename to libc/intrin/phtread_attr_setschedpolicy.c index dafa1f46c..4de984b9d 100644 --- a/libc/thread/pthread_attr_setschedpolicy.c +++ b/libc/intrin/phtread_attr_setschedpolicy.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" /** * Sets thread scheduler policy attribute, e.g. diff --git a/libc/thread/pthread_cancel.c b/libc/intrin/pthread.c similarity index 92% rename from libc/thread/pthread_cancel.c rename to libc/intrin/pthread.c index 876589fe1..0eed78739 100644 --- a/libc/thread/pthread_cancel.c +++ b/libc/intrin/pthread.c @@ -1,7 +1,7 @@ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ @@ -17,8 +17,12 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/errno.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" int pthread_cancel(pthread_t thread) { return ESRCH; } + +void *__tls_get_addr(size_t v[2]) { + return NULL; +} diff --git a/libc/intrin/pthread.h b/libc/intrin/pthread.h new file mode 100644 index 000000000..576beb427 --- /dev/null +++ b/libc/intrin/pthread.h @@ -0,0 +1,205 @@ +#ifndef COSMOPOLITAN_LIBC_RUNTIME_PTHREAD_H_ +#define COSMOPOLITAN_LIBC_RUNTIME_PTHREAD_H_ + +#define PTHREAD_KEYS_MAX 64 +#define PTHREAD_STACK_MIN FRAMESIZE +#define PTHREAD_DESTRUCTOR_ITERATIONS 4 + +#define PTHREAD_BARRIER_SERIAL_THREAD 31337 + +#define PTHREAD_MUTEX_DEFAULT PTHREAD_MUTEX_NORMAL +#define PTHREAD_MUTEX_NORMAL 0 +#define PTHREAD_MUTEX_RECURSIVE 1 +#define PTHREAD_MUTEX_ERRORCHECK 2 +#define PTHREAD_MUTEX_STALLED 0 +#define PTHREAD_MUTEX_ROBUST 1 + +#define PTHREAD_PROCESS_DEFAULT PTHREAD_PROCESS_PRIVATE +#define PTHREAD_PROCESS_PRIVATE 0 +#define PTHREAD_PROCESS_SHARED 1 + +#define PTHREAD_CREATE_JOINABLE 0 +#define PTHREAD_CREATE_DETACHED 1 + +#define PTHREAD_INHERIT_SCHED 0 +#define PTHREAD_EXPLICIT_SCHED 1 + +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +/* clang-format off */ +#define PTHREAD_ONCE_INIT {0} +#define PTHREAD_COND_INITIALIZER {PTHREAD_PROCESS_DEFAULT} +#define PTHREAD_BARRIER_INITIALIZER {PTHREAD_PROCESS_DEFAULT} +#define PTHREAD_RWLOCK_INITIALIZER {PTHREAD_PROCESS_DEFAULT} +#define PTHREAD_MUTEX_INITIALIZER {PTHREAD_MUTEX_DEFAULT, \ + PTHREAD_PROCESS_DEFAULT} +/* clang-format on */ + +typedef uintptr_t pthread_t; +typedef int pthread_id_np_t; +typedef char pthread_condattr_t; +typedef char pthread_rwlockattr_t; +typedef char pthread_barrierattr_t; +typedef unsigned pthread_key_t; +typedef void (*pthread_key_dtor)(void *); + +typedef struct pthread_once_s { + _Atomic(char) lock; +} pthread_once_t; + +typedef struct pthread_spinlock_s { + _Atomic(char) lock; +} pthread_spinlock_t; + +typedef struct pthread_mutex_s { + char type; + char pshared; + int reent; + _Atomic(int) lock; + _Atomic(int) waits; +} pthread_mutex_t; + +typedef struct pthread_mutexattr_s { + char type; + char pshared; +} pthread_mutexattr_t; + +typedef struct pthread_cond_s { + char pshared; + _Atomic(int) waits; + _Atomic(unsigned) seq; +} pthread_cond_t; + +typedef struct pthread_barrier_s { + char pshared; + int count; + _Atomic(int) waits; + _Atomic(int) popped; +} pthread_barrier_t; + +typedef struct pthread_rwlock_s { + char pshared; + _Atomic(int) lock; + _Atomic(int) waits; +} pthread_rwlock_t; + +typedef struct pthread_attr_s { + char detachstate; + char inheritsched; + int schedparam; + int schedpolicy; + int scope; + unsigned guardsize; + unsigned stacksize; + char *stackaddr; +} pthread_attr_t; + +int pthread_yield(void); +void pthread_exit(void *) wontreturn; +pthread_t pthread_self(void) pureconst; +pthread_id_np_t pthread_getthreadid_np(void); +int64_t pthread_getunique_np(pthread_t); +int pthread_setname_np(pthread_t, const char *); +int pthread_getname_np(pthread_t, char *, size_t); +int pthread_getattr_np(pthread_t, pthread_attr_t *); +int pthread_attr_init(pthread_attr_t *); +int pthread_attr_destroy(pthread_attr_t *); +int pthread_attr_getdetachstate(const pthread_attr_t *, int *); +int pthread_attr_setdetachstate(pthread_attr_t *, int); +int pthread_attr_getguardsize(const pthread_attr_t *, size_t *); +int pthread_attr_setguardsize(pthread_attr_t *, size_t); +int pthread_attr_getinheritsched(const pthread_attr_t *, int *); +int pthread_attr_setinheritsched(pthread_attr_t *, int); +int pthread_attr_getschedpolicy(const pthread_attr_t *, int *); +int pthread_attr_setschedpolicy(pthread_attr_t *, int); +int pthread_attr_getscope(const pthread_attr_t *, int *); +int pthread_attr_setscope(pthread_attr_t *, int); +int pthread_attr_getstack(const pthread_attr_t *, void **, size_t *); +int pthread_attr_setstack(pthread_attr_t *, void *, size_t); +int pthread_attr_getstacksize(const pthread_attr_t *, size_t *); +int pthread_attr_setstacksize(pthread_attr_t *, size_t); +int pthread_create(pthread_t *, const pthread_attr_t *, void *(*)(void *), + void *); +int pthread_detach(pthread_t); +int pthread_cancel(pthread_t); +int pthread_join(pthread_t, void **); +int pthread_equal(pthread_t, pthread_t); +int pthread_once(pthread_once_t *, void (*)(void)); +int pthread_spin_init(pthread_spinlock_t *, int); +int pthread_spin_destroy(pthread_spinlock_t *); +int pthread_spin_lock(pthread_spinlock_t *); +int pthread_spin_unlock(pthread_spinlock_t *); +int pthread_spin_trylock(pthread_spinlock_t *); +int pthread_mutexattr_init(pthread_mutexattr_t *); +int pthread_mutexattr_destroy(pthread_mutexattr_t *); +int pthread_mutexattr_gettype(const pthread_mutexattr_t *, int *); +int pthread_mutexattr_settype(pthread_mutexattr_t *, int); +int pthread_mutexattr_setpshared(pthread_mutexattr_t *, int); +int pthread_mutexattr_getpshared(const pthread_mutexattr_t *, int *); +int pthread_mutex_init(pthread_mutex_t *, const pthread_mutexattr_t *); +int pthread_mutex_lock(pthread_mutex_t *); +int pthread_mutex_unlock(pthread_mutex_t *); +int pthread_mutex_trylock(pthread_mutex_t *); +int pthread_mutex_destroy(pthread_mutex_t *); +int pthread_mutex_consistent(pthread_mutex_t *); +int pthread_condattr_init(pthread_condattr_t *); +int pthread_condattr_destroy(pthread_condattr_t *); +int pthread_condattr_setpshared(pthread_condattr_t *, int); +int pthread_condattr_getpshared(const pthread_condattr_t *, int *); +int pthread_cond_init(pthread_cond_t *, const pthread_condattr_t *); +int pthread_cond_destroy(pthread_cond_t *); +int pthread_cond_wait(pthread_cond_t *, pthread_mutex_t *); +int pthread_cond_broadcast(pthread_cond_t *); +int pthread_cond_signal(pthread_cond_t *); +int pthread_rwlockattr_init(pthread_rwlockattr_t *); +int pthread_rwlockattr_destroy(pthread_rwlockattr_t *); +int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *, int); +int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *, int *); +int pthread_rwlock_init(pthread_rwlock_t *, const pthread_rwlockattr_t *); +int pthread_rwlock_destroy(pthread_rwlock_t *); +int pthread_rwlock_rdlock(pthread_rwlock_t *); +int pthread_rwlock_tryrdlock(pthread_rwlock_t *); +int pthread_rwlock_wrlock(pthread_rwlock_t *); +int pthread_rwlock_trywrlock(pthread_rwlock_t *); +int pthread_rwlock_unlock(pthread_rwlock_t *); +int pthread_key_create(pthread_key_t *, pthread_key_dtor); +int pthread_key_delete(pthread_key_t); +int pthread_setspecific(pthread_key_t, void *); +void *pthread_getspecific(pthread_key_t); +int pthread_barrierattr_init(pthread_barrierattr_t *); +int pthread_barrierattr_destroy(pthread_barrierattr_t *); +int pthread_barrierattr_getpshared(const pthread_barrierattr_t *, int *); +int pthread_barrierattr_setpshared(pthread_barrierattr_t *, int); +int pthread_barrier_wait(pthread_barrier_t *); +int pthread_barrier_destroy(pthread_barrier_t *); +int pthread_barrier_init(pthread_barrier_t *, const pthread_barrierattr_t *, + unsigned); + +#define pthread_spin_init(pSpin, multiprocess) ((pSpin)->lock = 0, 0) +#define pthread_spin_destroy(pSpin) ((pSpin)->lock = -1, 0) +#if (__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 407 && \ + !defined(__STRICT_ANSI__) +extern const errno_t EBUSY; +#define pthread_spin_lock(pSpin) \ + ({ \ + pthread_spinlock_t *_s = pSpin; \ + while (__atomic_test_and_set(&_s->lock, __ATOMIC_SEQ_CST)) donothing; \ + 0; \ + }) +#define pthread_spin_unlock(pSpin) \ + ({ \ + pthread_spinlock_t *_s = pSpin; \ + __atomic_store_n(&_s->lock, 0, __ATOMIC_RELAXED); \ + 0; \ + }) +#define pthread_spin_trylock(pSpin) \ + ({ \ + pthread_spinlock_t *_s = pSpin; \ + __atomic_test_and_set(&_s->lock, __ATOMIC_SEQ_CST) ? EBUSY : 0; \ + }) +#endif /* GCC 4.7+ */ + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_RUNTIME_PTHREAD_H_ */ diff --git a/libc/thread/thread2.h b/libc/intrin/pthread2.h similarity index 96% rename from libc/thread/thread2.h rename to libc/intrin/pthread2.h index ec6b4a216..0dffe6892 100644 --- a/libc/thread/thread2.h +++ b/libc/intrin/pthread2.h @@ -3,8 +3,8 @@ #include "libc/calls/struct/cpuset.h" #include "libc/calls/struct/sched_param.h" #include "libc/calls/struct/timespec.h" +#include "libc/intrin/pthread.h" #include "libc/runtime/stack.h" -#include "libc/thread/thread.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ diff --git a/libc/thread/pthread_attr_destroy.c b/libc/intrin/pthread_attr_destroy.c similarity index 98% rename from libc/thread/pthread_attr_destroy.c rename to libc/intrin/pthread_attr_destroy.c index d93ffa7d2..b8690c31f 100644 --- a/libc/thread/pthread_attr_destroy.c +++ b/libc/intrin/pthread_attr_destroy.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" #include "libc/str/str.h" /** diff --git a/libc/thread/pthread_attr_getdetachstate.c b/libc/intrin/pthread_attr_getdetachstate.c similarity index 98% rename from libc/thread/pthread_attr_getdetachstate.c rename to libc/intrin/pthread_attr_getdetachstate.c index 742d26b00..ada3daf78 100644 --- a/libc/thread/pthread_attr_getdetachstate.c +++ b/libc/intrin/pthread_attr_getdetachstate.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" /** * Gets thread detachable attribute. diff --git a/libc/thread/pthread_attr_getguardsize.c b/libc/intrin/pthread_attr_getguardsize.c similarity index 98% rename from libc/thread/pthread_attr_getguardsize.c rename to libc/intrin/pthread_attr_getguardsize.c index 6b82d638f..b91416e39 100644 --- a/libc/thread/pthread_attr_getguardsize.c +++ b/libc/intrin/pthread_attr_getguardsize.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" /** * Returns size of unmapped pages at bottom of stack. diff --git a/libc/thread/pthread_attr_getinheritsched.c b/libc/intrin/pthread_attr_getinheritsched.c similarity index 98% rename from libc/thread/pthread_attr_getinheritsched.c rename to libc/intrin/pthread_attr_getinheritsched.c index f8b07caa1..1ec8dfaa4 100644 --- a/libc/thread/pthread_attr_getinheritsched.c +++ b/libc/intrin/pthread_attr_getinheritsched.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" /** * Returns thread inherit schedule attribute. diff --git a/libc/thread/pthread_attr_getschedparam.c b/libc/intrin/pthread_attr_getschedparam.c similarity index 98% rename from libc/thread/pthread_attr_getschedparam.c rename to libc/intrin/pthread_attr_getschedparam.c index 25e8634b6..b6170a6da 100644 --- a/libc/thread/pthread_attr_getschedparam.c +++ b/libc/intrin/pthread_attr_getschedparam.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread2.h" +#include "libc/intrin/pthread2.h" /** * Gets thread scheduler parameter attribute. diff --git a/libc/thread/pthread_attr_getschedpolicy.c b/libc/intrin/pthread_attr_getschedpolicy.c similarity index 98% rename from libc/thread/pthread_attr_getschedpolicy.c rename to libc/intrin/pthread_attr_getschedpolicy.c index 88f8e277a..a19325f93 100644 --- a/libc/thread/pthread_attr_getschedpolicy.c +++ b/libc/intrin/pthread_attr_getschedpolicy.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" /** * Gets thread scheduler policy attribute diff --git a/libc/thread/pthread_attr_getscope.c b/libc/intrin/pthread_attr_getscope.c similarity index 98% rename from libc/thread/pthread_attr_getscope.c rename to libc/intrin/pthread_attr_getscope.c index 3759ae815..fa24f5432 100644 --- a/libc/thread/pthread_attr_getscope.c +++ b/libc/intrin/pthread_attr_getscope.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" int pthread_attr_getscope(const pthread_attr_t *a, int *x) { *x = a->scope; diff --git a/libc/thread/pthread_attr_getstack.c b/libc/intrin/pthread_attr_getstack.c similarity index 98% rename from libc/thread/pthread_attr_getstack.c rename to libc/intrin/pthread_attr_getstack.c index c569c7d65..bed6b5c46 100644 --- a/libc/thread/pthread_attr_getstack.c +++ b/libc/intrin/pthread_attr_getstack.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" #include "libc/runtime/stack.h" /** diff --git a/libc/thread/pthread_attr_getstacksize.c b/libc/intrin/pthread_attr_getstacksize.c similarity index 98% rename from libc/thread/pthread_attr_getstacksize.c rename to libc/intrin/pthread_attr_getstacksize.c index 746ed4f7a..d4c5489b1 100644 --- a/libc/thread/pthread_attr_getstacksize.c +++ b/libc/intrin/pthread_attr_getstacksize.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" #include "libc/runtime/stack.h" /** diff --git a/libc/thread/pthread_attr_init.c b/libc/intrin/pthread_attr_init.c similarity index 98% rename from libc/thread/pthread_attr_init.c rename to libc/intrin/pthread_attr_init.c index b12dfea81..ac6f31166 100644 --- a/libc/thread/pthread_attr_init.c +++ b/libc/intrin/pthread_attr_init.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" #include "libc/runtime/stack.h" /** diff --git a/libc/thread/pthread_attr_setdetachstate.c b/libc/intrin/pthread_attr_setdetachstate.c similarity index 98% rename from libc/thread/pthread_attr_setdetachstate.c rename to libc/intrin/pthread_attr_setdetachstate.c index 22e03d62c..5d37579a5 100644 --- a/libc/thread/pthread_attr_setdetachstate.c +++ b/libc/intrin/pthread_attr_setdetachstate.c @@ -17,7 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/errno.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" /** * Sets thread detachable attribute, e.g. diff --git a/libc/thread/pthread_attr_setguardsize.c b/libc/intrin/pthread_attr_setguardsize.c similarity index 98% rename from libc/thread/pthread_attr_setguardsize.c rename to libc/intrin/pthread_attr_setguardsize.c index 40b6a77b8..27f5077e5 100644 --- a/libc/thread/pthread_attr_setguardsize.c +++ b/libc/intrin/pthread_attr_setguardsize.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" /** * Sets size of unmapped pages at bottom of stack. diff --git a/libc/thread/pthread_attr_setinheritsched.c b/libc/intrin/pthread_attr_setinheritsched.c similarity index 98% rename from libc/thread/pthread_attr_setinheritsched.c rename to libc/intrin/pthread_attr_setinheritsched.c index eb2737720..74d6fc352 100644 --- a/libc/thread/pthread_attr_setinheritsched.c +++ b/libc/intrin/pthread_attr_setinheritsched.c @@ -18,7 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" #include "libc/errno.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" /** * Sets thread scheduler inheritance attribute, e.g. diff --git a/libc/thread/pthread_attr_setschedparam.c b/libc/intrin/pthread_attr_setschedparam.c similarity index 98% rename from libc/thread/pthread_attr_setschedparam.c rename to libc/intrin/pthread_attr_setschedparam.c index afcf4e381..70a348d58 100644 --- a/libc/thread/pthread_attr_setschedparam.c +++ b/libc/intrin/pthread_attr_setschedparam.c @@ -18,7 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/struct/sched_param.h" #include "libc/errno.h" -#include "libc/thread/thread2.h" +#include "libc/intrin/pthread2.h" /** * Sets thread scheduler parameter attribute, e.g. diff --git a/libc/thread/pthread_attr_setscope.c b/libc/intrin/pthread_attr_setscope.c similarity index 98% rename from libc/thread/pthread_attr_setscope.c rename to libc/intrin/pthread_attr_setscope.c index 89ad4eaa8..365a40120 100644 --- a/libc/thread/pthread_attr_setscope.c +++ b/libc/intrin/pthread_attr_setscope.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" int pthread_attr_setscope(pthread_attr_t *a, int x) { a->scope = x; diff --git a/libc/thread/pthread_attr_setstack.c b/libc/intrin/pthread_attr_setstack.c similarity index 99% rename from libc/thread/pthread_attr_setstack.c rename to libc/intrin/pthread_attr_setstack.c index 93aec84a0..6994b97d8 100644 --- a/libc/thread/pthread_attr_setstack.c +++ b/libc/intrin/pthread_attr_setstack.c @@ -19,7 +19,7 @@ #include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/asan.internal.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" /** * Configures custom allocated stack for thread, e.g. diff --git a/libc/thread/pthread_attr_setstacksize.c b/libc/intrin/pthread_attr_setstacksize.c similarity index 98% rename from libc/thread/pthread_attr_setstacksize.c rename to libc/intrin/pthread_attr_setstacksize.c index 353d708ab..893f68714 100644 --- a/libc/thread/pthread_attr_setstacksize.c +++ b/libc/intrin/pthread_attr_setstacksize.c @@ -17,7 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/errno.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" /** * Defines minimum stack size for thread. diff --git a/libc/thread/pthread_barrier_destroy.c b/libc/intrin/pthread_barrier_destroy.c similarity index 90% rename from libc/thread/pthread_barrier_destroy.c rename to libc/intrin/pthread_barrier_destroy.c index c249bbe60..9dca7a62a 100644 --- a/libc/thread/pthread_barrier_destroy.c +++ b/libc/intrin/pthread_barrier_destroy.c @@ -16,9 +16,10 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" +#include "libc/errno.h" +#include "libc/intrin/pthread.h" #include "libc/str/str.h" -#include "libc/thread/thread.h" -#include "third_party/nsync/counter.h" /** * Destroys barrier. @@ -27,9 +28,10 @@ * @raise EINVAL if threads are still inside the barrier */ int pthread_barrier_destroy(pthread_barrier_t *barrier) { - if (barrier->_nsync) { - nsync_counter_free(barrier->_nsync); - barrier->_nsync = 0; + if (barrier->waits || barrier->popped) { + assert(!"deadlock"); + return EINVAL; } + memset(barrier, -1, sizeof(*barrier)); return 0; } diff --git a/libc/thread/pthread_barrier_init.c b/libc/intrin/pthread_barrier_init.c similarity index 86% rename from libc/thread/pthread_barrier_init.c rename to libc/intrin/pthread_barrier_init.c index b83a35230..f6ea185b8 100644 --- a/libc/thread/pthread_barrier_init.c +++ b/libc/intrin/pthread_barrier_init.c @@ -16,9 +16,10 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" #include "libc/errno.h" -#include "libc/thread/thread.h" -#include "third_party/nsync/counter.h" +#include "libc/intrin/pthread.h" +#include "libc/limits.h" /** * Initializes barrier. @@ -27,14 +28,18 @@ * @param count is how many threads need to call pthread_barrier_wait() * before the barrier is released, which must be greater than zero * @return 0 on success, or error number on failure - * @raise EINVAL if `count` isn't greater than zero - * @raise ENOMEM if insufficient memory exists + * @raise EINVAL if `count` isn't greater than zero or overflows */ int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned count) { - nsync_counter c; - if (!count) return EINVAL; - if (!(c = nsync_counter_new(count))) return ENOMEM; - *barrier = (pthread_barrier_t){._nsync = c}; - return 0; + if (count && count < INT_MAX / 2) { + *barrier = (pthread_barrier_t){ + attr ? *attr : PTHREAD_PROCESS_DEFAULT, + count, + }; + return 0; + } else { + assert(!"bad count"); + return EINVAL; + } } diff --git a/libc/thread/pthread_barrier_wait.c b/libc/intrin/pthread_barrier_wait.c similarity index 74% rename from libc/thread/pthread_barrier_wait.c rename to libc/intrin/pthread_barrier_wait.c index cfd3a891f..d1931b4e9 100644 --- a/libc/thread/pthread_barrier_wait.c +++ b/libc/intrin/pthread_barrier_wait.c @@ -17,12 +17,9 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/intrin/atomic.h" -#include "libc/intrin/kprintf.h" +#include "libc/intrin/futex.internal.h" +#include "libc/intrin/pthread.h" #include "libc/limits.h" -#include "libc/thread/thread.h" -#include "third_party/nsync/counter.h" -#include "third_party/nsync/futex.internal.h" -#include "third_party/nsync/time.h" /** * Waits for all threads to arrive at barrier. @@ -36,10 +33,22 @@ * thread which was the last arrival, or an errno on error */ int pthread_barrier_wait(pthread_barrier_t *barrier) { - if (nsync_counter_add(barrier->_nsync, -1)) { - nsync_counter_wait(barrier->_nsync, nsync_time_no_deadline); - return 0; - } else { + if (atomic_fetch_add(&barrier->waits, 1) + 1 == barrier->count) { + if (atomic_fetch_add(&barrier->waits, 1) + 1 < barrier->count * 2) { + atomic_store_explicit(&barrier->popped, 1, memory_order_relaxed); + do { + _futex_wake(&barrier->popped, INT_MAX, barrier->pshared); + } while (atomic_load_explicit(&barrier->waits, memory_order_relaxed) < + barrier->count * 2); + atomic_store_explicit(&barrier->popped, 0, memory_order_relaxed); + } + atomic_store_explicit(&barrier->waits, 0, memory_order_relaxed); return PTHREAD_BARRIER_SERIAL_THREAD; } + do { + _futex_wait(&barrier->popped, 0, barrier->pshared, 0); + } while (atomic_load_explicit(&barrier->waits, memory_order_relaxed) < + barrier->count); + atomic_fetch_add(&barrier->waits, 1); + return 0; } diff --git a/libc/thread/pthread_barrierattr_destroy.c b/libc/intrin/pthread_barrierattr_destroy.c similarity index 98% rename from libc/thread/pthread_barrierattr_destroy.c rename to libc/intrin/pthread_barrierattr_destroy.c index 3777f2b3f..9a51e9f7f 100644 --- a/libc/thread/pthread_barrierattr_destroy.c +++ b/libc/intrin/pthread_barrierattr_destroy.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" #include "libc/str/str.h" /** diff --git a/libc/thread/pthread_barrierattr_getpshared.c b/libc/intrin/pthread_barrierattr_getpshared.c similarity index 94% rename from libc/thread/pthread_barrierattr_getpshared.c rename to libc/intrin/pthread_barrierattr_getpshared.c index bd2c547b0..63e9228a8 100644 --- a/libc/thread/pthread_barrierattr_getpshared.c +++ b/libc/intrin/pthread_barrierattr_getpshared.c @@ -16,14 +16,14 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" /** * Gets barrier process sharing. * * @param pshared is set to one of the following - * - `PTHREAD_PROCESS_PRIVATE` (default) - * - `PTHREAD_PROCESS_SHARED` (unsupported) + * - `PTHREAD_PROCESS_SHARED` + * - `PTHREAD_PROCESS_PRIVATE` * @return 0 on success, or error on failure */ int pthread_barrierattr_getpshared(const pthread_barrierattr_t *attr, diff --git a/libc/thread/pthread_barrierattr_init.c b/libc/intrin/pthread_barrierattr_init.c similarity index 96% rename from libc/thread/pthread_barrierattr_init.c rename to libc/intrin/pthread_barrierattr_init.c index b009911e2..9a79d1460 100644 --- a/libc/thread/pthread_barrierattr_init.c +++ b/libc/intrin/pthread_barrierattr_init.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" /** * Initializes barrier attributes. @@ -24,6 +24,6 @@ * @return 0 on success, or error on failure */ int pthread_barrierattr_init(pthread_barrierattr_t *attr) { - *attr = 0; + *attr = PTHREAD_PROCESS_DEFAULT; return 0; } diff --git a/libc/thread/pthread_barrierattr_setpshared.c b/libc/intrin/pthread_barrierattr_setpshared.c similarity index 94% rename from libc/thread/pthread_barrierattr_setpshared.c rename to libc/intrin/pthread_barrierattr_setpshared.c index 4ae2adbb6..d10ee6aac 100644 --- a/libc/thread/pthread_barrierattr_setpshared.c +++ b/libc/intrin/pthread_barrierattr_setpshared.c @@ -17,19 +17,20 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/errno.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" /** * Sets barrier process sharing. * * @param pshared can be one of - * - `PTHREAD_PROCESS_PRIVATE` (default) - * - `PTHREAD_PROCESS_SHARED` (unsupported) + * - `PTHREAD_PROCESS_SHARED` + * - `PTHREAD_PROCESS_PRIVATE` * @return 0 on success, or error on failure * @raises EINVAL if `pshared` is invalid */ int pthread_barrierattr_setpshared(pthread_barrierattr_t *attr, int pshared) { switch (pshared) { + case PTHREAD_PROCESS_SHARED: case PTHREAD_PROCESS_PRIVATE: *attr = pshared; return 0; diff --git a/libc/intrin/pthread_cond_broadcast.c b/libc/intrin/pthread_cond_broadcast.c new file mode 100644 index 000000000..5c1cb8264 --- /dev/null +++ b/libc/intrin/pthread_cond_broadcast.c @@ -0,0 +1,96 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 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/dce.h" +#include "libc/intrin/atomic.h" +#include "libc/intrin/futex.internal.h" +#include "libc/intrin/pthread.h" +#include "libc/limits.h" + +static dontinline int pthread_cond_signal_impl(pthread_cond_t *cond, int n) { + if (atomic_load_explicit(&cond->waits, memory_order_relaxed)) { + atomic_fetch_add(&cond->seq, 1); + if (IsLinux() || IsOpenbsd()) { + _futex_wake(&cond->seq, n, cond->pshared); + } + } + return 0; +} + +/** + * Wakes at least one thread waiting on condition, e.g. + * + * pthread_cond_t cond = PTHREAD_COND_INITIALIZER; + * pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + * + * // thread pool waiters + * pthread_mutex_lock(&lock); + * pthread_cond_wait(&cond, &lock); + * pthread_mutex_unlock(&lock); + * + * // waker upper + * pthread_mutex_lock(&lock); + * pthread_cond_signal(&cond); + * pthread_mutex_unlock(&lock); + * + * This function has no effect if there aren't any threads currently + * waiting on the condition. + * + * @return 0 on success, or errno on error + * @see pthread_cond_broadcast + * @see pthread_cond_wait + */ +int pthread_cond_signal(pthread_cond_t *cond) { + return pthread_cond_signal_impl(cond, 1); +} + +/** + * Wakes all threads waiting on condition, e.g. + * + * pthread_mutex_t lock; + * pthread_mutexattr_t mattr; + * pthread_mutexattr_init(&mattr); + * pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_ERRORCHECK); + * pthread_mutex_init(&lock, &mattr); + * + * pthread_cond_t cond; + * pthread_condattr_t cattr; + * pthread_condattr_init(&cattr); + * pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED); + * pthread_cond_init(&cond, &cattr); + * + * // waiting threads + * CHECK_EQ(0, pthread_mutex_lock(&lock)); + * CHECK_EQ(0, pthread_cond_wait(&cond, &lock)); + * pthread_mutex_unlock(&lock); + * + * // notifying thread + * CHECK_EQ(0, pthread_mutex_lock(&lock)); + * pthread_cond_broadcast(&cond); + * pthread_mutex_unlock(&lock); + * + * This function has no effect if there aren't any threads currently + * waiting on the condition. + * + * @return 0 on success, or errno on error + * @see pthread_cond_signal + * @see pthread_cond_wait + */ +int pthread_cond_broadcast(pthread_cond_t *cond) { + return pthread_cond_signal_impl(cond, INT_MAX); +} diff --git a/libc/thread/pthread_cond_destroy.c b/libc/intrin/pthread_cond_destroy.c similarity index 93% rename from libc/thread/pthread_cond_destroy.c rename to libc/intrin/pthread_cond_destroy.c index 579b2399a..0fc53ba88 100644 --- a/libc/thread/pthread_cond_destroy.c +++ b/libc/intrin/pthread_cond_destroy.c @@ -16,8 +16,10 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" +#include "libc/errno.h" +#include "libc/intrin/pthread.h" #include "libc/str/str.h" -#include "libc/thread/thread.h" /** * Destroys condition. @@ -26,6 +28,10 @@ * @raise EINVAL if threads are still waiting on condition */ int pthread_cond_destroy(pthread_cond_t *cond) { + if (cond->waits) { + assert(!"deadlock"); + return EINVAL; + } memset(cond, -1, sizeof(*cond)); return 0; } diff --git a/libc/thread/pthread_cond_init.c b/libc/intrin/pthread_cond_init.c similarity index 95% rename from libc/thread/pthread_cond_init.c rename to libc/intrin/pthread_cond_init.c index aa5e8e8b7..f683b6350 100644 --- a/libc/thread/pthread_cond_init.c +++ b/libc/intrin/pthread_cond_init.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" /** * Initializes condition. @@ -25,6 +25,6 @@ * @return 0 on success, or error number on failure */ int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr) { - *cond = (pthread_cond_t){0}; + *cond = (pthread_cond_t){attr ? *attr : PTHREAD_PROCESS_DEFAULT}; return 0; } diff --git a/libc/thread/pthread_condattr_destroy.c b/libc/intrin/pthread_condattr_destroy.c similarity index 98% rename from libc/thread/pthread_condattr_destroy.c rename to libc/intrin/pthread_condattr_destroy.c index abf437dd5..7ea243c59 100644 --- a/libc/thread/pthread_condattr_destroy.c +++ b/libc/intrin/pthread_condattr_destroy.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" #include "libc/str/str.h" /** diff --git a/libc/thread/pthread_condattr_getpshared.c b/libc/intrin/pthread_condattr_getpshared.c similarity index 96% rename from libc/thread/pthread_condattr_getpshared.c rename to libc/intrin/pthread_condattr_getpshared.c index dab698903..b340781ff 100644 --- a/libc/thread/pthread_condattr_getpshared.c +++ b/libc/intrin/pthread_condattr_getpshared.c @@ -16,14 +16,14 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" /** * Gets condition process sharing. * * @param pshared is set to one of the following - * - `PTHREAD_PROCESS_PRIVATE` (default) * - `PTHREAD_PROCESS_SHARED` + * - `PTHREAD_PROCESS_PRIVATE` * @return 0 on success, or error on failure */ int pthread_condattr_getpshared(const pthread_condattr_t *attr, int *pshared) { diff --git a/libc/thread/pthread_condattr_init.c b/libc/intrin/pthread_condattr_init.c similarity index 96% rename from libc/thread/pthread_condattr_init.c rename to libc/intrin/pthread_condattr_init.c index 7e61998fd..40a35f179 100644 --- a/libc/thread/pthread_condattr_init.c +++ b/libc/intrin/pthread_condattr_init.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" /** * Initializes condition attributes. @@ -24,6 +24,6 @@ * @return 0 on success, or error on failure */ int pthread_condattr_init(pthread_condattr_t *attr) { - *attr = 0; + *attr = PTHREAD_PROCESS_DEFAULT; return 0; } diff --git a/libc/thread/pthread_condattr_setpshared.c b/libc/intrin/pthread_condattr_setpshared.c similarity index 94% rename from libc/thread/pthread_condattr_setpshared.c rename to libc/intrin/pthread_condattr_setpshared.c index ccd7ae242..9c8929a27 100644 --- a/libc/thread/pthread_condattr_setpshared.c +++ b/libc/intrin/pthread_condattr_setpshared.c @@ -17,19 +17,20 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/errno.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" /** * Sets condition process sharing. * * @param pshared can be one of - * - `PTHREAD_PROCESS_PRIVATE` (default) - * - `PTHREAD_PROCESS_SHARED` (unsupported) + * - `PTHREAD_PROCESS_SHARED` + * - `PTHREAD_PROCESS_PRIVATE` * @return 0 on success, or error on failure * @raises EINVAL if `pshared` is invalid */ int pthread_condattr_setpshared(pthread_condattr_t *attr, int pshared) { switch (pshared) { + case PTHREAD_PROCESS_SHARED: case PTHREAD_PROCESS_PRIVATE: *attr = pshared; return 0; diff --git a/libc/thread/pthread_mutex_destroy.c b/libc/intrin/pthread_mutex_destroy.c similarity index 92% rename from libc/thread/pthread_mutex_destroy.c rename to libc/intrin/pthread_mutex_destroy.c index 87f4efa72..5a5312910 100644 --- a/libc/thread/pthread_mutex_destroy.c +++ b/libc/intrin/pthread_mutex_destroy.c @@ -16,8 +16,10 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" +#include "libc/errno.h" +#include "libc/intrin/pthread.h" #include "libc/str/str.h" -#include "libc/thread/thread.h" /** * Destroys mutex. @@ -26,6 +28,10 @@ * @raise EINVAL if mutex is locked in our implementation */ int pthread_mutex_destroy(pthread_mutex_t *mutex) { + if (mutex->lock || mutex->waits) { + assert(!"deadlock"); + return EINVAL; + } memset(mutex, -1, sizeof(*mutex)); return 0; } diff --git a/libc/intrin/pthread_mutex_init.c b/libc/intrin/pthread_mutex_init.c index f7fda57cd..0645c6a0e 100644 --- a/libc/intrin/pthread_mutex_init.c +++ b/libc/intrin/pthread_mutex_init.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" /** * Initializes mutex. @@ -27,8 +27,8 @@ int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) { *mutex = (pthread_mutex_t){ - ._type = attr ? attr->_type : 0, - ._pshared = attr ? attr->_pshared : 0, + attr ? attr->type : PTHREAD_MUTEX_DEFAULT, + attr ? attr->pshared : PTHREAD_PROCESS_DEFAULT, }; return 0; } diff --git a/libc/intrin/pthread_mutex_lock.c b/libc/intrin/pthread_mutex_lock.c index 2d4d04818..374f97a2d 100644 --- a/libc/intrin/pthread_mutex_lock.c +++ b/libc/intrin/pthread_mutex_lock.c @@ -16,14 +16,32 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" #include "libc/calls/calls.h" #include "libc/errno.h" +#include "libc/intrin/asmflag.h" #include "libc/intrin/atomic.h" -#include "libc/intrin/likely.h" -#include "libc/intrin/weaken.h" -#include "libc/thread/thread.h" -#include "libc/thread/tls.h" -#include "third_party/nsync/mu.h" +#include "libc/intrin/futex.internal.h" +#include "libc/intrin/pthread.h" +#include "libc/linux/futex.h" +#include "libc/nexgen32e/threaded.h" +#include "libc/sysv/consts/futex.h" +#include "libc/sysv/consts/nr.h" + +static int pthread_mutex_lock_spin(pthread_mutex_t *mutex, int expect, + int tries) { + if (tries < 7) { + volatile int i; + for (i = 0; i != 1 << tries; i++) { + } + tries++; + } else { + atomic_fetch_add(&mutex->waits, 1); + _futex_wait(&mutex->lock, expect, mutex->pshared, &(struct timespec){1}); + atomic_fetch_sub(&mutex->waits, 1); + } + return tries; +} /** * Locks mutex. @@ -56,58 +74,57 @@ * pthread_mutex_unlock(&lock); * pthread_mutex_destroy(&lock); * + * Alternatively, Cosmopolitan lets you do the folllowing instead: + * + * pthread_mutex_t lock = {PTHREAD_MUTEX_RECURSIVE}; + * pthread_mutex_lock(&lock); + * // do work... + * pthread_mutex_unlock(&lock); + * * @return 0 on success, or error number on failure * @see pthread_spin_lock() */ int pthread_mutex_lock(pthread_mutex_t *mutex) { - int c, d, t; - - if (LIKELY(__tls_enabled && // - mutex->_type == PTHREAD_MUTEX_NORMAL && // - mutex->_pshared == PTHREAD_PROCESS_PRIVATE && // - weaken(nsync_mu_lock))) { - weaken(nsync_mu_lock)((nsync_mu *)mutex); - return 0; - } - - t = gettid(); - if (mutex->_type == PTHREAD_MUTEX_ERRORCHECK) { - c = atomic_load_explicit(&mutex->_lock, memory_order_relaxed); - if ((c & 0x000fffff) == t) { - return EDEADLK; - } - } - - for (;;) { - c = 0; - d = 0x10100000 | t; - if (atomic_compare_exchange_weak_explicit( - &mutex->_lock, &c, d, memory_order_acquire, memory_order_relaxed)) { - break; - } else { - if ((c & 0x000fffff) == t) { - if ((c & 0x0ff00000) < 0x0ff00000) { - c = atomic_fetch_add_explicit(&mutex->_lock, 0x00100000, - memory_order_relaxed); + int c, me, owner, tries; + switch (mutex->type) { + case PTHREAD_MUTEX_NORMAL: + // From Futexes Are Tricky Version 1.1 § Mutex, Take 3; + // Ulrich Drepper, Red Hat Incorporated, June 27, 2004. + c = 0; + if (!atomic_compare_exchange_strong_explicit(&mutex->lock, &c, 1, + memory_order_acquire, + memory_order_relaxed)) { + if (c != 2) { + c = atomic_exchange_explicit(&mutex->lock, 2, memory_order_acquire); + } + while (c) { + _futex_wait(&mutex->lock, 2, mutex->pshared, 0); + c = atomic_exchange_explicit(&mutex->lock, 2, memory_order_acquire); + } + } + return 0; + case PTHREAD_MUTEX_RECURSIVE: + case PTHREAD_MUTEX_ERRORCHECK: + for (tries = 0, me = gettid();;) { + owner = atomic_load_explicit(&mutex->lock, memory_order_relaxed); + if (!owner && atomic_compare_exchange_weak_explicit( + &mutex->lock, &owner, me, memory_order_acquire, + memory_order_relaxed)) { break; - } else { - return EAGAIN; + } else if (owner == me) { + if (mutex->type != PTHREAD_MUTEX_ERRORCHECK) { + break; + } else { + assert(!"deadlock"); + return EDEADLK; + } } + tries = pthread_mutex_lock_spin(mutex, owner, tries); } - if ((c & 0xf0000000) == 0x10000000) { - d = 0x20000000 | c; - if (atomic_compare_exchange_weak_explicit(&mutex->_lock, &c, d, - memory_order_acquire, - memory_order_relaxed)) { - c = d; - } - } - if ((c & 0xf0000000) == 0x20000000) { - // _futex_wait(&mutex->_lock, c, mutex->_pshared, 0); - pthread_yield(); - } - } + ++mutex->reent; + return 0; + default: + assert(!"badlock"); + return EINVAL; } - - return 0; } diff --git a/libc/intrin/pthread_mutex_trylock.c b/libc/intrin/pthread_mutex_trylock.c index 9621115e0..6d1a20151 100644 --- a/libc/intrin/pthread_mutex_trylock.c +++ b/libc/intrin/pthread_mutex_trylock.c @@ -20,11 +20,7 @@ #include "libc/calls/calls.h" #include "libc/errno.h" #include "libc/intrin/atomic.h" -#include "libc/intrin/likely.h" -#include "libc/intrin/weaken.h" -#include "libc/thread/thread.h" -#include "libc/thread/tls.h" -#include "third_party/nsync/mu.h" +#include "libc/intrin/pthread.h" /** * Locks mutex if it isn't locked already. @@ -37,42 +33,36 @@ * @raise ENOTRECOVERABLE if `mutex` is corrupted */ int pthread_mutex_trylock(pthread_mutex_t *mutex) { - int c, d, t; - - if (LIKELY(__tls_enabled && // - mutex->_type == PTHREAD_MUTEX_NORMAL && // - mutex->_pshared == PTHREAD_PROCESS_PRIVATE && // - weaken(nsync_mu_trylock))) { - if (weaken(nsync_mu_trylock)((nsync_mu *)mutex)) { + int c, me, owner; + switch (mutex->type) { + case PTHREAD_MUTEX_NORMAL: + c = 0; + if (atomic_compare_exchange_strong_explicit(&mutex->lock, &c, 1, + memory_order_acquire, + memory_order_relaxed)) { + return 0; + } else { + return EBUSY; + } + case PTHREAD_MUTEX_RECURSIVE: + case PTHREAD_MUTEX_ERRORCHECK: + owner = 0; + me = gettid(); + if (!atomic_compare_exchange_strong_explicit(&mutex->lock, &owner, me, + memory_order_acquire, + memory_order_relaxed)) { + if (owner == me) { + if (mutex->type == PTHREAD_MUTEX_ERRORCHECK) { + return EBUSY; + } + } else { + return EBUSY; + } + } + ++mutex->reent; return 0; - } else { - return EBUSY; - } + default: + assert(!"badlock"); + return ENOTRECOVERABLE; } - - if (mutex->_type == PTHREAD_MUTEX_NORMAL) { - c = 0; - if (atomic_compare_exchange_strong_explicit( - &mutex->_lock, &c, 1, memory_order_acquire, memory_order_relaxed)) { - return 0; - } else { - return EBUSY; - } - } - - c = 0; - t = gettid(); - d = 0x10100000 | t; - if (!atomic_compare_exchange_strong_explicit( - &mutex->_lock, &c, d, memory_order_acquire, memory_order_relaxed)) { - if ((c & 0x000fffff) != t || mutex->_type == PTHREAD_MUTEX_ERRORCHECK) { - return EBUSY; - } - if ((c & 0x0ff00000) == 0x0ff00000) { - return EAGAIN; - } - atomic_fetch_add_explicit(&mutex->_lock, 0x00100000, memory_order_relaxed); - } - - return 0; } diff --git a/libc/intrin/pthread_mutex_unlock.c b/libc/intrin/pthread_mutex_unlock.c index d0e65b732..430b9c8c6 100644 --- a/libc/intrin/pthread_mutex_unlock.c +++ b/libc/intrin/pthread_mutex_unlock.c @@ -16,14 +16,13 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" #include "libc/calls/calls.h" +#include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/atomic.h" -#include "libc/intrin/likely.h" -#include "libc/intrin/weaken.h" -#include "libc/thread/thread.h" -#include "libc/thread/tls.h" -#include "third_party/nsync/mu.h" +#include "libc/intrin/futex.internal.h" +#include "libc/intrin/pthread.h" /** * Releases mutex. @@ -32,32 +31,35 @@ * @raises EPERM if in error check mode and not owned by caller */ int pthread_mutex_unlock(pthread_mutex_t *mutex) { - int c, t; - - if (LIKELY(__tls_enabled && // - mutex->_type == PTHREAD_MUTEX_NORMAL && // - mutex->_pshared == PTHREAD_PROCESS_PRIVATE && // - weaken(nsync_mu_unlock))) { - weaken(nsync_mu_unlock)((nsync_mu *)mutex); - return 0; + int c, me, owner; + switch (mutex->type) { + case PTHREAD_MUTEX_NORMAL: + // From Futexes Are Tricky Version 1.1 § Mutex, Take 3; + // Ulrich Drepper, Red Hat Incorporated, June 27, 2004. + if ((c = atomic_fetch_sub_explicit(&mutex->lock, 1, + memory_order_release)) != 1) { + atomic_store_explicit(&mutex->lock, 0, memory_order_release); + _futex_wake(&mutex->lock, 1, mutex->pshared); + } + return 0; + case PTHREAD_MUTEX_ERRORCHECK: + me = gettid(); + owner = atomic_load_explicit(&mutex->lock, memory_order_relaxed); + if (owner != me) { + assert(!"permlock"); + return EPERM; + } + // fallthrough + case PTHREAD_MUTEX_RECURSIVE: + if (--mutex->reent) return 0; + atomic_store_explicit(&mutex->lock, 0, memory_order_relaxed); + if ((IsLinux() || IsOpenbsd()) && + atomic_load_explicit(&mutex->waits, memory_order_relaxed) > 0) { + return _futex_wake(&mutex->lock, 1, mutex->pshared); + } + return 0; + default: + assert(!"badlock"); + return EINVAL; } - - t = gettid(); - if (mutex->_type == PTHREAD_MUTEX_ERRORCHECK) { - c = atomic_load_explicit(&mutex->_lock, memory_order_relaxed); - if ((c & 0x000fffff) != t) { - return EPERM; - } - } - - c = atomic_fetch_sub(&mutex->_lock, 0x00100000); - if ((c & 0x0ff00000) == 0x00100000) { - atomic_store_explicit(&mutex->_lock, 0, memory_order_release); - if ((c & 0xf0000000) == 0x20000000) { - // _futex_wake(&mutex->_lock, 1, mutex->_pshared); - pthread_yield(); - } - } - - return 0; } diff --git a/libc/thread/pthread_mutexattr_destroy.c b/libc/intrin/pthread_mutexattr_destroy.c similarity index 98% rename from libc/thread/pthread_mutexattr_destroy.c rename to libc/intrin/pthread_mutexattr_destroy.c index 49df496c1..a6fc17e3e 100644 --- a/libc/thread/pthread_mutexattr_destroy.c +++ b/libc/intrin/pthread_mutexattr_destroy.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" #include "libc/str/str.h" /** diff --git a/libc/thread/pthread_mutexattr_getpshared.c b/libc/intrin/pthread_mutexattr_getpshared.c similarity index 95% rename from libc/thread/pthread_mutexattr_getpshared.c rename to libc/intrin/pthread_mutexattr_getpshared.c index c521b91ce..8d76df79b 100644 --- a/libc/thread/pthread_mutexattr_getpshared.c +++ b/libc/intrin/pthread_mutexattr_getpshared.c @@ -16,18 +16,18 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" /** * Gets mutex process sharing. * * @param pshared is set to one of the following - * - `PTHREAD_PROCESS_PRIVATE` (default) * - `PTHREAD_PROCESS_SHARED` + * - `PTHREAD_PROCESS_PRIVATE` * @return 0 on success, or error on failure */ int pthread_mutexattr_getpshared(const pthread_mutexattr_t *attr, int *pshared) { - *pshared = attr->_pshared; + *pshared = attr->pshared; return 0; } diff --git a/libc/thread/pthread_mutexattr_gettype.c b/libc/intrin/pthread_mutexattr_gettype.c similarity index 97% rename from libc/thread/pthread_mutexattr_gettype.c rename to libc/intrin/pthread_mutexattr_gettype.c index 6138568e9..2c7ef3e0a 100644 --- a/libc/thread/pthread_mutexattr_gettype.c +++ b/libc/intrin/pthread_mutexattr_gettype.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" /** * Gets mutex type. @@ -29,6 +29,6 @@ * @return 0 on success, or error on failure */ int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type) { - *type = attr->_type; + *type = attr->type; return 0; } diff --git a/libc/thread/pthread_mutexattr_init.c b/libc/intrin/pthread_mutexattr_init.c similarity index 93% rename from libc/thread/pthread_mutexattr_init.c rename to libc/intrin/pthread_mutexattr_init.c index a12bec7d8..5bb326829 100644 --- a/libc/thread/pthread_mutexattr_init.c +++ b/libc/intrin/pthread_mutexattr_init.c @@ -16,13 +16,16 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" /** * Initializes mutex attr. * @return 0 on success, or error number on failure */ int pthread_mutexattr_init(pthread_mutexattr_t *attr) { - *attr = (pthread_mutexattr_t){0}; + *attr = (pthread_mutexattr_t){ + PTHREAD_MUTEX_DEFAULT, + PTHREAD_PROCESS_DEFAULT, + }; return 0; } diff --git a/libc/thread/pthread_mutexattr_setpshared.c b/libc/intrin/pthread_mutexattr_setpshared.c similarity index 95% rename from libc/thread/pthread_mutexattr_setpshared.c rename to libc/intrin/pthread_mutexattr_setpshared.c index c575cbd95..8152edce2 100644 --- a/libc/thread/pthread_mutexattr_setpshared.c +++ b/libc/intrin/pthread_mutexattr_setpshared.c @@ -17,14 +17,14 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/errno.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" /** * Sets mutex process sharing. * * @param pshared can be one of - * - `PTHREAD_PROCESS_PRIVATE` (default) * - `PTHREAD_PROCESS_SHARED` + * - `PTHREAD_PROCESS_PRIVATE` * @return 0 on success, or error on failure * @raises EINVAL if `pshared` is invalid */ @@ -32,7 +32,7 @@ int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared) { switch (pshared) { case PTHREAD_PROCESS_SHARED: case PTHREAD_PROCESS_PRIVATE: - attr->_pshared = pshared; + attr->pshared = pshared; return 0; default: return EINVAL; diff --git a/libc/thread/pthread_mutexattr_settype.c b/libc/intrin/pthread_mutexattr_settype.c similarity index 97% rename from libc/thread/pthread_mutexattr_settype.c rename to libc/intrin/pthread_mutexattr_settype.c index 766a4b7a4..546459156 100644 --- a/libc/thread/pthread_mutexattr_settype.c +++ b/libc/intrin/pthread_mutexattr_settype.c @@ -17,7 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/errno.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" /** * Sets mutex type. @@ -35,7 +35,7 @@ int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) { case PTHREAD_MUTEX_NORMAL: case PTHREAD_MUTEX_RECURSIVE: case PTHREAD_MUTEX_ERRORCHECK: - attr->_type = type; + attr->type = type; return 0; default: return EINVAL; diff --git a/libc/intrin/pthread_once.c b/libc/intrin/pthread_once.c index dc8ce6fb7..1d13fd863 100644 --- a/libc/intrin/pthread_once.c +++ b/libc/intrin/pthread_once.c @@ -16,12 +16,11 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" #include "libc/calls/calls.h" #include "libc/errno.h" #include "libc/intrin/atomic.h" -#include "libc/intrin/weaken.h" -#include "libc/thread/thread.h" -#include "third_party/nsync/once.h" +#include "libc/intrin/pthread.h" #define INIT 0 #define CALLING 1 @@ -45,30 +44,28 @@ * @return 0 on success, or errno on error */ int pthread_once(pthread_once_t *once, void init(void)) { - uint32_t old; - if (weaken(nsync_run_once)) { - weaken(nsync_run_once)((nsync_once *)once, init); - return 0; - } - switch ((old = atomic_load_explicit(&once->_lock, memory_order_relaxed))) { + char old; + switch ((old = atomic_load_explicit(&once->lock, memory_order_relaxed))) { case INIT: - if (atomic_compare_exchange_strong_explicit(&once->_lock, &old, CALLING, + if (atomic_compare_exchange_strong_explicit(&once->lock, &old, CALLING, memory_order_acquire, memory_order_relaxed)) { init(); - atomic_store(&once->_lock, FINISHED); - return 0; + atomic_store(&once->lock, FINISHED); + break; } // fallthrough case CALLING: do { pthread_yield(); - } while (atomic_load_explicit(&once->_lock, memory_order_relaxed) == + } while (atomic_load_explicit(&once->lock, memory_order_relaxed) == CALLING); - return 0; + break; case FINISHED: - return 0; + break; default: + assert(!"bad once"); return EINVAL; } + return 0; } diff --git a/libc/thread/pthread_rwlock_destroy.c b/libc/intrin/pthread_rwlock_destroy.c similarity index 93% rename from libc/thread/pthread_rwlock_destroy.c rename to libc/intrin/pthread_rwlock_destroy.c index 47bc63c10..eb0b3c906 100644 --- a/libc/thread/pthread_rwlock_destroy.c +++ b/libc/intrin/pthread_rwlock_destroy.c @@ -16,8 +16,10 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" +#include "libc/errno.h" +#include "libc/intrin/pthread.h" #include "libc/str/str.h" -#include "libc/thread/thread.h" /** * Destroys read-write lock. @@ -26,6 +28,10 @@ * @raise EINVAL if any threads still hold the lock */ int pthread_rwlock_destroy(pthread_rwlock_t *rwlock) { + if (rwlock->lock) { + assert(!"deadlock"); + return EINVAL; + } memset(rwlock, -1, sizeof(*rwlock)); return 0; } diff --git a/libc/thread/pthread_rwlock_init.c b/libc/intrin/pthread_rwlock_init.c similarity index 95% rename from libc/thread/pthread_rwlock_init.c rename to libc/intrin/pthread_rwlock_init.c index c9790a9b7..392dde1c0 100644 --- a/libc/thread/pthread_rwlock_init.c +++ b/libc/intrin/pthread_rwlock_init.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" /** * Initializes read-write lock. @@ -26,6 +26,6 @@ */ int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr) { - *rwlock = (pthread_rwlock_t){0}; + *rwlock = (pthread_rwlock_t){attr ? *attr : PTHREAD_PROCESS_DEFAULT}; return 0; } diff --git a/libc/thread/pthread_rwlock_rdlock.c b/libc/intrin/pthread_rwlock_rdlock.c similarity index 66% rename from libc/thread/pthread_rwlock_rdlock.c rename to libc/intrin/pthread_rwlock_rdlock.c index 187b60453..e0502ab7e 100644 --- a/libc/thread/pthread_rwlock_rdlock.c +++ b/libc/intrin/pthread_rwlock_rdlock.c @@ -16,8 +16,24 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" -#include "third_party/nsync/mu.h" +#include "libc/intrin/atomic.h" +#include "libc/intrin/futex.internal.h" +#include "libc/intrin/pthread.h" + +static int pthread_rwlock_rdlock_spin(pthread_rwlock_t *rwlock, int expect, + int tries) { + if (tries < 7) { + volatile int i; + for (i = 0; i != 1 << tries; i++) { + } + tries++; + } else { + atomic_fetch_add(&rwlock->waits, 1); + _futex_wait(&rwlock->lock, expect, rwlock->pshared, &(struct timespec){1}); + atomic_fetch_sub(&rwlock->waits, 1); + } + return tries; +} /** * Acquires read lock on read-write lock. @@ -25,6 +41,18 @@ * @return 0 on success, or errno on error */ int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock) { - nsync_mu_rlock((nsync_mu *)rwlock); - return 0; + int old, tries; + for (tries = 0;;) { + old = atomic_load_explicit(&rwlock->lock, memory_order_relaxed); + if (old >= 0) { + do { + if (atomic_compare_exchange_weak_explicit(&rwlock->lock, &old, old + 1, + memory_order_acquire, + memory_order_relaxed)) { + return 0; + } + } while (old >= 0); + } + tries = pthread_rwlock_rdlock_spin(rwlock, old, tries); + } } diff --git a/libc/thread/pthread_cond_broadcast.c b/libc/intrin/pthread_rwlock_unlock.c similarity index 62% rename from libc/thread/pthread_cond_broadcast.c rename to libc/intrin/pthread_rwlock_unlock.c index 8518a4b45..c90f9ecbd 100644 --- a/libc/thread/pthread_cond_broadcast.c +++ b/libc/intrin/pthread_rwlock_unlock.c @@ -16,27 +16,40 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" -#include "third_party/nsync/cv.h" +#include "libc/assert.h" +#include "libc/dce.h" +#include "libc/errno.h" +#include "libc/intrin/atomic.h" +#include "libc/intrin/futex.internal.h" +#include "libc/intrin/pthread.h" /** - * Wakes all threads waiting on condition, e.g. - * - * pthread_cond_t cond = PTHREAD_COND_INITIALIZER; - * pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; - * // ... - * pthread_mutex_lock(&lock); - * pthread_cond_broadcast(&cond, &lock); - * pthread_mutex_unlock(&lock); - * - * This function has no effect if there aren't any threads currently - * waiting on the condition. + * Unlocks read-write lock. * * @return 0 on success, or errno on error - * @see pthread_cond_signal - * @see pthread_cond_wait + * @raise EINVAL if lock is in a bad state */ -int pthread_cond_broadcast(pthread_cond_t *cond) { - nsync_cv_broadcast((nsync_cv *)cond); - return 0; +int pthread_rwlock_unlock(pthread_rwlock_t *rwlock) { + int old, waits; + for (;;) { + old = atomic_load_explicit(&rwlock->lock, memory_order_relaxed); + if (!old || old < -1) { + assert(!"badlock"); + return EINVAL; + } else if (old == -1 || old == 1) { + waits = atomic_load_explicit(&rwlock->waits, memory_order_relaxed); + if (atomic_compare_exchange_weak_explicit(&rwlock->lock, &old, 0, + memory_order_acquire, + memory_order_relaxed)) { + if (waits && (IsLinux() || IsOpenbsd())) { + _futex_wake(&rwlock->lock, 1, rwlock->pshared); + } + return 0; + } + } else if (atomic_compare_exchange_weak_explicit( + &rwlock->lock, &old, old - 1, memory_order_acquire, + memory_order_relaxed)) { + return 0; + } + } } diff --git a/libc/thread/pthread_rwlock_wrlock.c b/libc/intrin/pthread_rwlock_wrlock.c similarity index 66% rename from libc/thread/pthread_rwlock_wrlock.c rename to libc/intrin/pthread_rwlock_wrlock.c index f97c3ae95..f50ebc966 100644 --- a/libc/thread/pthread_rwlock_wrlock.c +++ b/libc/intrin/pthread_rwlock_wrlock.c @@ -16,8 +16,24 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" -#include "third_party/nsync/mu.h" +#include "libc/intrin/atomic.h" +#include "libc/intrin/futex.internal.h" +#include "libc/intrin/pthread.h" + +static int pthread_rwlock_wrlock_spin(pthread_rwlock_t *rwlock, int expect, + int tries) { + if (tries < 7) { + volatile int i; + for (i = 0; i != 1 << tries; i++) { + } + tries++; + } else { + atomic_fetch_add(&rwlock->waits, 1); + _futex_wait(&rwlock->lock, expect, rwlock->pshared, &(struct timespec){1}); + atomic_fetch_sub(&rwlock->waits, 1); + } + return tries; +} /** * Acquires write lock on read-write lock. @@ -25,7 +41,17 @@ * @return 0 on success, or errno on error */ int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock) { - nsync_mu_lock((nsync_mu *)rwlock); - rwlock->_iswrite = 1; - return 0; + int old, tries; + for (tries = 0;;) { + if (!(old = atomic_load_explicit(&rwlock->lock, memory_order_relaxed))) { + do { + if (atomic_compare_exchange_weak_explicit(&rwlock->lock, &old, -1, + memory_order_acquire, + memory_order_relaxed)) { + return 0; + } + } while (!old); + } + tries = pthread_rwlock_wrlock_spin(rwlock, old, tries); + } } diff --git a/libc/thread/pthread_rwlockattr_destroy.c b/libc/intrin/pthread_rwlockattr_destroy.c similarity index 98% rename from libc/thread/pthread_rwlockattr_destroy.c rename to libc/intrin/pthread_rwlockattr_destroy.c index 624b5fb5d..b190001ce 100644 --- a/libc/thread/pthread_rwlockattr_destroy.c +++ b/libc/intrin/pthread_rwlockattr_destroy.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" #include "libc/str/str.h" /** diff --git a/libc/thread/pthread_rwlockattr_getpshared.c b/libc/intrin/pthread_rwlockattr_getpshared.c similarity index 94% rename from libc/thread/pthread_rwlockattr_getpshared.c rename to libc/intrin/pthread_rwlockattr_getpshared.c index efafcb927..aafde2afe 100644 --- a/libc/thread/pthread_rwlockattr_getpshared.c +++ b/libc/intrin/pthread_rwlockattr_getpshared.c @@ -16,14 +16,14 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" /** * Gets read-write lock process sharing. * * @param pshared is set to one of the following - * - `PTHREAD_PROCESS_PRIVATE` (default) - * - `PTHREAD_PROCESS_SHARED` (unsupported) + * - `PTHREAD_PROCESS_SHARED` + * - `PTHREAD_PROCESS_PRIVATE` * @return 0 on success, or error on failure */ int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attr, diff --git a/libc/thread/pthread_rwlockattr_init.c b/libc/intrin/pthread_rwlockattr_init.c similarity index 96% rename from libc/thread/pthread_rwlockattr_init.c rename to libc/intrin/pthread_rwlockattr_init.c index dee6fb305..f64ded572 100644 --- a/libc/thread/pthread_rwlockattr_init.c +++ b/libc/intrin/pthread_rwlockattr_init.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" /** * Initializes read-write lock attributes. @@ -24,6 +24,6 @@ * @return 0 on success, or error on failure */ int pthread_rwlockattr_init(pthread_rwlockattr_t *attr) { - *attr = 0; + *attr = PTHREAD_PROCESS_DEFAULT; return 0; } diff --git a/libc/thread/pthread_rwlockattr_setpshared.c b/libc/intrin/pthread_rwlockattr_setpshared.c similarity index 94% rename from libc/thread/pthread_rwlockattr_setpshared.c rename to libc/intrin/pthread_rwlockattr_setpshared.c index 5904c4d6c..2139bfd72 100644 --- a/libc/thread/pthread_rwlockattr_setpshared.c +++ b/libc/intrin/pthread_rwlockattr_setpshared.c @@ -17,19 +17,20 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/errno.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" /** * Sets read-write lock process sharing. * * @param pshared can be one of - * - `PTHREAD_PROCESS_PRIVATE` (default) - * - `PTHREAD_PROCESS_SHARED` (unsupported) + * - `PTHREAD_PROCESS_SHARED` + * - `PTHREAD_PROCESS_PRIVATE` * @return 0 on success, or error on failure * @raises EINVAL if `pshared` is invalid */ int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared) { switch (pshared) { + case PTHREAD_PROCESS_SHARED: case PTHREAD_PROCESS_PRIVATE: *attr = pshared; return 0; diff --git a/libc/thread/pthread_spin_destroy.c b/libc/intrin/pthread_spin_destroy.c similarity index 98% rename from libc/thread/pthread_spin_destroy.c rename to libc/intrin/pthread_spin_destroy.c index 18fa1bfeb..309071433 100644 --- a/libc/thread/pthread_spin_destroy.c +++ b/libc/intrin/pthread_spin_destroy.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" /** * Destroys spin lock. diff --git a/libc/thread/pthread_spin_init.c b/libc/intrin/pthread_spin_init.c similarity index 98% rename from libc/thread/pthread_spin_init.c rename to libc/intrin/pthread_spin_init.c index d8ca27cc1..753d4e956 100644 --- a/libc/thread/pthread_spin_init.c +++ b/libc/intrin/pthread_spin_init.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" /** * Initializes spin lock. diff --git a/libc/thread/pthread_spin_lock.c b/libc/intrin/pthread_spin_lock.c similarity index 98% rename from libc/thread/pthread_spin_lock.c rename to libc/intrin/pthread_spin_lock.c index 36ced2007..dca24fb7b 100644 --- a/libc/thread/pthread_spin_lock.c +++ b/libc/intrin/pthread_spin_lock.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" /** * Acquires spin lock. diff --git a/libc/thread/pthread_spin_trylock.c b/libc/intrin/pthread_spin_trylock.c similarity index 98% rename from libc/thread/pthread_spin_trylock.c rename to libc/intrin/pthread_spin_trylock.c index 36bcb55c9..7936b2e8b 100644 --- a/libc/thread/pthread_spin_trylock.c +++ b/libc/intrin/pthread_spin_trylock.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" /** * Acquires spin lock if available. diff --git a/libc/thread/pthread_spin_unlock.c b/libc/intrin/pthread_spin_unlock.c similarity index 98% rename from libc/thread/pthread_spin_unlock.c rename to libc/intrin/pthread_spin_unlock.c index 54fafbd18..ba3318c6f 100644 --- a/libc/thread/pthread_spin_unlock.c +++ b/libc/intrin/pthread_spin_unlock.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" /** * Releases spin lock. diff --git a/libc/intrin/pthread_yield.c b/libc/intrin/pthread_yield.c index 273eabde4..322e0799c 100644 --- a/libc/intrin/pthread_yield.c +++ b/libc/intrin/pthread_yield.c @@ -18,7 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/strace.internal.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" /** * Yields current thread's remaining timeslice to operating system. diff --git a/libc/intrin/quick_exit.c b/libc/intrin/quick_exit.c index fff957397..40532c777 100644 --- a/libc/intrin/quick_exit.c +++ b/libc/intrin/quick_exit.c @@ -16,8 +16,12 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/strace.internal.h" #include "libc/intrin/weaken.h" +#include "libc/calls/strace.internal.h" +#include "libc/dce.h" +#include "libc/nt/console.h" +#include "libc/nt/process.h" +#include "libc/nt/runtime.h" #include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" diff --git a/libc/intrin/rand64.c b/libc/intrin/rand64.c index 3d48c6b94..6b915c2a1 100644 --- a/libc/intrin/rand64.c +++ b/libc/intrin/rand64.c @@ -17,9 +17,9 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/_getauxval.internal.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" #include "libc/nexgen32e/rdtsc.h" -#include "libc/thread/tls.h" +#include "libc/nexgen32e/threaded.h" #include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" diff --git a/libc/intrin/tlsisrequired.c b/libc/intrin/tlsisrequired.c index 08fb4d41a..a428d3144 100644 --- a/libc/intrin/tlsisrequired.c +++ b/libc/intrin/tlsisrequired.c @@ -16,9 +16,9 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/tls.h" +#include "libc/nexgen32e/threaded.h" -void __require_tls(void) { +void TlsIsRequired(void) { if (!__tls_enabled) { notpossible; } diff --git a/libc/thread/wait0.c b/libc/intrin/wait0.c similarity index 75% rename from libc/thread/wait0.c rename to libc/intrin/wait0.c index 380eb5718..0bfa2bf12 100644 --- a/libc/thread/wait0.c +++ b/libc/intrin/wait0.c @@ -16,17 +16,13 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/strace.internal.h" -#include "libc/calls/struct/timespec.h" +#include "libc/calls/calls.h" #include "libc/dce.h" -#include "libc/errno.h" #include "libc/intrin/atomic.h" -#include "libc/intrin/describeflags.internal.h" -#include "libc/sysv/consts/futex.h" -#include "libc/thread/thread.h" -#include "libc/thread/wait0.internal.h" - -int _futex(int *, int, int, const struct timespec *); +#include "libc/intrin/futex.internal.h" +#include "libc/intrin/pthread.h" +#include "libc/intrin/wait0.internal.h" +#include "libc/linux/futex.h" /** * Blocks until memory location becomes zero. @@ -36,24 +32,12 @@ int _futex(int *, int, int, const struct timespec *); * this operation is to know when it's safe to munmap() a threads stack */ void _wait0(const int *ctid) { - int x, rc; - char buf[12]; + int x; for (;;) { if (!(x = atomic_load_explicit(ctid, memory_order_relaxed))) { break; - } else if (IsLinux() || IsOpenbsd()) { - rc = _futex(ctid, FUTEX_WAIT, x, &(struct timespec){2}); - STRACE("futex(%t, FUTEX_WAIT, %d, {2, 0}) → %s", ctid, x, - (DescribeFutexResult)(buf, rc)); - if (IsOpenbsd() && rc > 0) rc = -rc; - if (!(rc == 0 || // - rc == -EINTR || // - rc == -ETIMEDOUT || // - rc == -EWOULDBLOCK)) { - notpossible; - } } else { - pthread_yield(); + _futex_wait(ctid, x, PTHREAD_PROCESS_SHARED, &(struct timespec){2}); } } if (IsOpenbsd()) { diff --git a/libc/thread/wait0.internal.h b/libc/intrin/wait0.internal.h similarity index 100% rename from libc/thread/wait0.internal.h rename to libc/intrin/wait0.internal.h diff --git a/libc/intrin/wantcrashreports.c b/libc/intrin/wantcrashreports.c deleted file mode 100644 index 8b2d88935..000000000 --- a/libc/intrin/wantcrashreports.c +++ /dev/null @@ -1,21 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2022 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/log/internal.h" - -bool _wantcrashreports; diff --git a/libc/isystem/limits.h b/libc/isystem/limits.h index 6eb8b5ff6..a6abd1c8b 100644 --- a/libc/isystem/limits.h +++ b/libc/isystem/limits.h @@ -1,6 +1,6 @@ #ifndef LIBC_ISYSTEM_LIMITS_H_ #define LIBC_ISYSTEM_LIMITS_H_ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" #include "libc/limits.h" #include "libc/sysv/consts/_posix.h" #include "libc/sysv/consts/iov.h" diff --git a/libc/isystem/linux/futex.h b/libc/isystem/linux/futex.h deleted file mode 100644 index b07fc1454..000000000 --- a/libc/isystem/linux/futex.h +++ /dev/null @@ -1,5 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_ISYSTEM_LINUX_FUTEX_H_ -#define COSMOPOLITAN_LIBC_ISYSTEM_LINUX_FUTEX_H_ -#include "libc/sysv/consts/futex.h" -#include "libc/sysv/consts/nr.h" -#endif /* COSMOPOLITAN_LIBC_ISYSTEM_LINUX_FUTEX_H_ */ diff --git a/libc/isystem/pthread.h b/libc/isystem/pthread.h index 77ba04720..9e5e99b40 100644 --- a/libc/isystem/pthread.h +++ b/libc/isystem/pthread.h @@ -1,5 +1,5 @@ #ifndef LIBC_ISYSTEM_PTHREAD_H_ #define LIBC_ISYSTEM_PTHREAD_H_ -#include "libc/thread/thread.h" -#include "libc/thread/thread2.h" +#include "libc/intrin/pthread.h" +#include "libc/intrin/pthread2.h" #endif /* LIBC_ISYSTEM_PTHREAD_H_ */ diff --git a/libc/isystem/semaphore.h b/libc/isystem/semaphore.h deleted file mode 100644 index a672765e1..000000000 --- a/libc/isystem/semaphore.h +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_ISYSTEM_SEMAPHORE_H_ -#define COSMOPOLITAN_LIBC_ISYSTEM_SEMAPHORE_H_ -#include "libc/calls/semaphore.internal.h" -#endif /* COSMOPOLITAN_LIBC_ISYSTEM_SEMAPHORE_H_ */ diff --git a/libc/isystem/stdio.h b/libc/isystem/stdio.h index cff507487..9e5d61618 100644 --- a/libc/isystem/stdio.h +++ b/libc/isystem/stdio.h @@ -2,7 +2,7 @@ #define LIBC_ISYSTEM_STDIO_H_ #include "libc/calls/calls.h" #include "libc/fmt/fmt.h" -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" #include "libc/stdio/temp.h" #endif diff --git a/libc/isystem/time.h b/libc/isystem/time.h index 64a6b69fa..89449f24c 100644 --- a/libc/isystem/time.h +++ b/libc/isystem/time.h @@ -3,7 +3,6 @@ #include "libc/calls/struct/timespec.h" #include "libc/calls/struct/timeval.h" #include "libc/calls/weirdtypes.h" -#include "libc/sysv/consts/clock.h" #include "libc/sysv/consts/sched.h" #include "libc/time/struct/tm.h" #include "libc/time/time.h" diff --git a/libc/log/backtrace2.c b/libc/log/backtrace2.c index 2459941a4..65b00e55e 100644 --- a/libc/log/backtrace2.c +++ b/libc/log/backtrace2.c @@ -37,6 +37,8 @@ #include "libc/mem/alg.h" #include "libc/mem/bisectcarleft.internal.h" #include "libc/nexgen32e/gc.internal.h" +#include "libc/nexgen32e/gettls.h" +#include "libc/nexgen32e/threaded.h" #include "libc/runtime/gc.internal.h" #include "libc/runtime/runtime.h" #include "libc/runtime/stack.h" @@ -47,7 +49,7 @@ #include "libc/sysv/consts/fileno.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/sig.h" -#include "libc/thread/tls.h" +#include "libc/thread/thread.h" #include "libc/x/x.h" #define kBacktraceMaxFrames 128 @@ -107,7 +109,7 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) { argv[i++] = "-a"; /* filter out w/ shell script wrapper for old versions */ argv[i++] = "-pCife"; argv[i++] = debugbin; - garbage = __tls_enabled ? __get_tls()->tib_garbages : 0; + garbage = __tls_enabled ? ((cthread_t)__get_tls())->garbages : 0; gi = garbage ? garbage->i : 0; for (frame = bp; frame && i < kBacktraceMaxFrames - 1; frame = frame->next) { addr = frame->addr; diff --git a/libc/log/backtrace3.c b/libc/log/backtrace3.c index 2e5b3c82d..eb07cc176 100644 --- a/libc/log/backtrace3.c +++ b/libc/log/backtrace3.c @@ -26,12 +26,14 @@ #include "libc/macros.internal.h" #include "libc/mem/bisectcarleft.internal.h" #include "libc/nexgen32e/gc.internal.h" +#include "libc/nexgen32e/gettls.h" #include "libc/nexgen32e/stackframe.h" +#include "libc/nexgen32e/threaded.h" #include "libc/runtime/memtrack.internal.h" #include "libc/runtime/runtime.h" #include "libc/runtime/symbols.internal.h" #include "libc/str/str.h" -#include "libc/thread/tls.h" +#include "libc/thread/thread.h" #define LIMIT 100 @@ -55,7 +57,7 @@ noinstrument noasan int PrintBacktraceUsingSymbols(int fd, struct Garbages *garbage; const struct StackFrame *frame; if (!bp) bp = __builtin_frame_address(0); - garbage = __tls_enabled ? __get_tls()->tib_garbages : 0; + garbage = __tls_enabled ? ((cthread_t)__get_tls())->garbages : 0; gi = garbage ? garbage->i : 0; for (i = 0, frame = bp; frame; frame = frame->next) { if (++i == LIMIT) { diff --git a/libc/log/internal.h b/libc/log/internal.h index 0d7480f73..51fd64b46 100644 --- a/libc/log/internal.h +++ b/libc/log/internal.h @@ -7,7 +7,6 @@ COSMOPOLITAN_C_START_ extern hidden bool __nocolor; extern hidden int kCrashSigs[8]; -extern hidden bool _wantcrashreports; extern hidden bool g_isrunningundermake; void __start_fatal(const char *, int) hidden; diff --git a/libc/log/leaks.c b/libc/log/leaks.c index 4ec7371f4..2b9d0c70c 100644 --- a/libc/log/leaks.c +++ b/libc/log/leaks.c @@ -16,10 +16,9 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/strace.internal.h" -#include "libc/dce.h" -#include "libc/intrin/asan.internal.h" #include "libc/intrin/bits.h" +#include "libc/calls/strace.internal.h" +#include "libc/intrin/asan.internal.h" #include "libc/intrin/kprintf.h" #include "libc/intrin/lockcmpxchg.h" #include "libc/mem/mem.h" @@ -38,7 +37,7 @@ static bool hasleaks; static noasan void CheckLeak(void *x, void *y, size_t n, void *a) { if (n) { if (IsAsan()) { - if (__asan_get_heap_size(x) && !__asan_is_leaky(x)) { + if (__asan_get_heap_size(x)) { hasleaks = true; } } else { @@ -54,10 +53,9 @@ static noasan void OnMemory(void *x, void *y, size_t n, void *a) { if (i < MAXLEAKS) { ++i; kprintf("%p %,lu bytes [dlmalloc]", x, n); - if (__asan_is_leaky(x)) { - kprintf(" [leaky]"); + if (IsAsan()) { + __asan_print_trace(x); } - __asan_print_trace(x); kprintf("\n"); } else if (i == MAXLEAKS) { ++i; @@ -81,7 +79,6 @@ static noasan bool HasLeaks(void) { */ noasan void CheckForMemoryLeaks(void) { struct mallinfo mi; - if (!IsAsan()) return; // we need traces to exclude leaky if (!_lockcmpxchg(&once, false, true)) { kprintf("CheckForMemoryLeaks() may only be called once\n"); exit(1); diff --git a/libc/log/libfatal.internal.h b/libc/log/libfatal.internal.h index 8ff3934ee..ddf054af6 100644 --- a/libc/log/libfatal.internal.h +++ b/libc/log/libfatal.internal.h @@ -3,6 +3,7 @@ #include "libc/calls/calls.h" #include "libc/dce.h" #include "libc/macros.internal.h" +#include "libc/nexgen32e/bsr.h" #include "libc/nt/runtime.h" #include "libc/sysv/consts/nr.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) @@ -205,7 +206,7 @@ forceinline char *__fixcpy(char p[hasatleast 17], uint64_t x, uint8_t k) { } forceinline char *__hexcpy(char p[hasatleast 17], uint64_t x) { - return __fixcpy(p, x, ROUNDUP(x ? (__builtin_clzll(x) ^ 63) + 1 : 1, 4)); + return __fixcpy(p, x, ROUNDUP(x ? bsrl(x) + 1 : 1, 4)); } forceinline const void *__memchr(const void *s, unsigned char c, size_t n) { diff --git a/libc/log/oncrash.c b/libc/log/oncrash.c index 5f722b23d..8fffe7269 100644 --- a/libc/log/oncrash.c +++ b/libc/log/oncrash.c @@ -35,7 +35,7 @@ #include "libc/log/log.h" #include "libc/macros.internal.h" #include "libc/nexgen32e/stackframe.h" -#include "libc/thread/tls.h" +#include "libc/nexgen32e/threaded.h" #include "libc/runtime/internal.h" #include "libc/runtime/pc.internal.h" #include "libc/runtime/runtime.h" diff --git a/libc/log/printgarbage.c b/libc/log/printgarbage.c index c14d51f51..911a1d986 100644 --- a/libc/log/printgarbage.c +++ b/libc/log/printgarbage.c @@ -20,8 +20,10 @@ #include "libc/intrin/kprintf.h" #include "libc/log/log.h" #include "libc/nexgen32e/gc.internal.h" +#include "libc/nexgen32e/gettls.h" +#include "libc/nexgen32e/threaded.h" #include "libc/stdio/stdio.h" -#include "libc/thread/tls.h" +#include "libc/thread/thread.h" // clang-format off /** @@ -36,7 +38,7 @@ void PrintGarbage(void) { kprintf(" SHADOW STACK @ %p\n", __builtin_frame_address(0)); kprintf("garbage ent. parent frame original ret callback arg \n"); kprintf("------------ ------------ ------------------ ------------------ ------------------\n"); - if ((g = __tls_enabled ? __get_tls()->tib_garbages:0) && g->i) { + if ((g = __tls_enabled ? ((cthread_t)__get_tls())->garbages:0) && g->i) { for (i = g->i; i--;) { symbol = __get_symbol_by_addr(g->p[i].ret); if (symbol) { diff --git a/libc/log/showcrashreports.c b/libc/log/showcrashreports.c index 66615ef89..fafdddf1c 100644 --- a/libc/log/showcrashreports.c +++ b/libc/log/showcrashreports.c @@ -105,7 +105,6 @@ static void FreeSigAltStack(void *p) { void ShowCrashReports(void) { char *sp; struct sigaltstack ss; - _wantcrashreports = true; /* : showcrashreports.c, oncrashthunks.S, oncrash.c */ kCrashSigs[0] = SIGQUIT; /* ctrl+\ aka ctrl+break */ kCrashSigs[1] = SIGFPE; /* 1 / 0 */ @@ -122,7 +121,8 @@ void ShowCrashReports(void) { ss.ss_size = GetStackSize(); // FreeBSD sigaltstack() will EFAULT if we use MAP_STACK here // OpenBSD sigaltstack() auto-applies MAP_STACK to the memory - if ((sp = _mapanon(GetStackSize()))) { + if ((sp = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)) != MAP_FAILED) { ss.ss_sp = sp; if (!sigaltstack(&ss, &g_oldsigaltstack)) { __cxa_atexit(FreeSigAltStack, ss.ss_sp, 0); diff --git a/libc/log/vflogf.c b/libc/log/vflogf.c index 627889fa4..c8941e48b 100644 --- a/libc/log/vflogf.c +++ b/libc/log/vflogf.c @@ -32,7 +32,7 @@ #include "libc/math.h" #include "libc/nexgen32e/nexgen32e.h" #include "libc/runtime/runtime.h" -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/sysv/consts/fileno.h" diff --git a/libc/mem/gc.c b/libc/mem/gc.c index 96e493c53..3e0dfde84 100644 --- a/libc/mem/gc.c +++ b/libc/mem/gc.c @@ -20,10 +20,12 @@ #include "libc/intrin/likely.h" #include "libc/mem/mem.h" #include "libc/nexgen32e/gc.internal.h" +#include "libc/nexgen32e/gettls.h" #include "libc/nexgen32e/stackframe.h" +#include "libc/nexgen32e/threaded.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" -#include "libc/thread/tls.h" +#include "libc/thread/thread.h" static inline bool PointerNotOwnedByParentStackFrame(struct StackFrame *frame, struct StackFrame *parent, @@ -34,11 +36,11 @@ static inline bool PointerNotOwnedByParentStackFrame(struct StackFrame *frame, static void TeardownGc(void) { int i; + cthread_t tls; struct Garbages *g; - struct CosmoTib *t; if (__tls_enabled) { - t = __get_tls(); - if ((g = t->tib_garbages)) { + tls = (cthread_t)__get_tls(); + if ((g = tls->garbages)) { // exit() currently doesn't use _gclongjmp() like pthread_exit() // so we need to run the deferred functions manually. while (g->i) { @@ -59,18 +61,18 @@ __attribute__((__constructor__)) static void InitializeGc(void) { // then rewrite caller's return address on stack. static void DeferFunction(struct StackFrame *frame, void *fn, void *arg) { int n2; + cthread_t tls; struct Garbage *p2; struct Garbages *g; - struct CosmoTib *t; - __require_tls(); - t = __get_tls(); - g = t->tib_garbages; + TlsIsRequired(); + tls = (cthread_t)__get_tls(); + g = tls->garbages; if (UNLIKELY(!g)) { if (!(g = malloc(sizeof(struct Garbages)))) notpossible; g->i = 0; g->n = 4; if (!(g->p = malloc(g->n * sizeof(struct Garbage)))) notpossible; - t->tib_garbages = g; + tls->garbages = g; } else if (UNLIKELY(g->i == g->n)) { p2 = g->p; n2 = g->n + (g->n >> 1); diff --git a/libc/nexgen32e/bsr.h b/libc/nexgen32e/bsr.h index c35b731b9..7a4179bdd 100644 --- a/libc/nexgen32e/bsr.h +++ b/libc/nexgen32e/bsr.h @@ -6,9 +6,9 @@ COSMOPOLITAN_C_START_ int bsr(int) pureconst; int bsrl(long) pureconst; int bsrll(long long) pureconst; +int bsr128(uint128_t) pureconst; #if defined(__GNUC__) && defined(__x86_64__) && !defined(__STRICT_ANSI__) -int bsr128(uint128_t) pureconst; #define bsr(u) \ ({ \ unsigned BiTs; \ diff --git a/libc/nexgen32e/gc.S b/libc/nexgen32e/gc.S index d9a41adff..83143e336 100644 --- a/libc/nexgen32e/gc.S +++ b/libc/nexgen32e/gc.S @@ -34,7 +34,7 @@ // @see test/libc/runtime/gc_test.c // @threadsafe __gc: mov %fs:0,%rcx # __get_tls() - mov 0x18(%rcx),%rcx # tls::garbages + mov 0x18(%rcx),%rcx # cthread_t::garbages decl (%rcx) # ++g->i mov (%rcx),%r8d # r8 = g->i mov 8(%rcx),%r9 # r9 = g->p diff --git a/libc/nexgen32e/gclongjmp.S b/libc/nexgen32e/gclongjmp.S index fc98ca623..4bfdd3755 100644 --- a/libc/nexgen32e/gclongjmp.S +++ b/libc/nexgen32e/gclongjmp.S @@ -35,7 +35,7 @@ _gclongjmp: mov %rsp,%rbp .profilable mov %fs:0,%r12 # __get_tls() - mov 0x18(%r12),%r12 # Tls::garbages + mov 0x18(%r12),%r12 # cthread_t::garbages test %r12,%r12 jz 0f movl (%r12),%r13d # garbages.i diff --git a/libc/thread/tls2.h b/libc/nexgen32e/gettls.h similarity index 56% rename from libc/thread/tls2.h rename to libc/nexgen32e/gettls.h index f5ef79147..e9a930989 100644 --- a/libc/thread/tls2.h +++ b/libc/nexgen32e/gettls.h @@ -1,10 +1,23 @@ -#ifndef COSMOPOLITAN_LIBC_THREAD_TLS2_H_ -#define COSMOPOLITAN_LIBC_THREAD_TLS2_H_ +#ifndef COSMOPOLITAN_LIBC_NEXGEN32E_GETTLS_H_ +#define COSMOPOLITAN_LIBC_NEXGEN32E_GETTLS_H_ #include "libc/dce.h" -#include "libc/thread/tls.h" +#include "libc/nexgen32e/threaded.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ +#if defined(__GNUC__) && defined(__x86_64__) && !defined(__STRICT_ANSI__) +/** + * Returns location of thread information block. + * + * This can't be used in privileged functions. + */ +static noasan inline char *__get_tls(void) { + char *tib; + asm("mov\t%%fs:0,%0" : "=r"(tib) : /* no inputs */ : "memory"); + return tib; +} +#endif /* GNU x86-64 */ + #if defined(__GNUC__) && defined(__x86_64__) && !defined(__STRICT_ANSI__) /** * Returns location of thread information block. @@ -12,7 +25,7 @@ COSMOPOLITAN_C_START_ * This should be favored over __get_tls() for .privileged code that * can't be self-modified by __enable_tls(). */ -static noasan inline struct CosmoTib *__get_tls_privileged(void) { +static noasan inline char *__get_tls_privileged(void) { char *tib, *lin = (char *)0x30; if (IsLinux() || IsFreebsd() || IsNetbsd() || IsOpenbsd() || IsMetal()) { asm("mov\t%%fs:(%1),%0" : "=a"(tib) : "r"(lin) : "memory"); @@ -22,10 +35,10 @@ static noasan inline struct CosmoTib *__get_tls_privileged(void) { tib = *(char **)(tib + 0x1480 + __tls_index * 8); } } - return (struct CosmoTib *)tib; + return tib; } #endif /* GNU x86-64 */ COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_LIBC_THREAD_TLS2_H_ */ +#endif /* COSMOPOLITAN_LIBC_NEXGEN32E_GETTLS_H_ */ diff --git a/libc/nexgen32e/threaded.c b/libc/nexgen32e/threaded.c index 6226e1bff..83c56b1a9 100644 --- a/libc/nexgen32e/threaded.c +++ b/libc/nexgen32e/threaded.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/tls.h" +#include "libc/nexgen32e/threaded.h" /** * Contains TID of main thread or 0 if threading isn't enabled. diff --git a/libc/nexgen32e/threaded.h b/libc/nexgen32e/threaded.h new file mode 100644 index 000000000..7878eb841 --- /dev/null +++ b/libc/nexgen32e/threaded.h @@ -0,0 +1,16 @@ +#ifndef COSMOPOLITAN_LIBC_NEXGEN32E_THREADED_H_ +#define COSMOPOLITAN_LIBC_NEXGEN32E_THREADED_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +extern int __threaded; +extern bool __tls_enabled; +extern unsigned __tls_index; + +void TlsIsRequired(void); +void *__initialize_tls(char[64]); +void __install_tls(char[64]); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_NEXGEN32E_THREADED_H_ */ diff --git a/libc/nt/codegen.sh b/libc/nt/codegen.sh index 35e3f46f0..d1e64d2a5 100644 --- a/libc/nt/codegen.sh +++ b/libc/nt/codegen.sh @@ -1,4 +1,4 @@ -/usr/bin/env echo ' -*-mode:sh;indent-tabs-mode:nil;tab-width:8;coding:utf-8-*-│ +/usr/bin/env echo ' -*- mode:sh; indent-tabs-mode:nil; tab-width:8; coding:utf-8 -*-│ │vi: set net ft=sh ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ │ Copyright 2020 Justine Alexandra Roberts Tunney │ diff --git a/libc/nt/kernel32/CreateSemaphoreW.s b/libc/nt/kernel32/CreateSemaphoreW.s index febaeb9b6..bcac9a2e9 100644 --- a/libc/nt/kernel32/CreateSemaphoreW.s +++ b/libc/nt/kernel32/CreateSemaphoreW.s @@ -1,12 +1,2 @@ .include "o/libc/nt/codegen.inc" .imp kernel32,__imp_CreateSemaphoreW,CreateSemaphoreW,0 - - .text.windows -CreateSemaphore: - push %rbp - mov %rsp,%rbp - .profilable - mov __imp_CreateSemaphoreW(%rip),%rax - jmp __sysv2nt - .endfn CreateSemaphore,globl - .previous diff --git a/libc/nt/kernel32/LockFileEx.s b/libc/nt/kernel32/LockFileEx.s index f74843927..9aba0a595 100644 --- a/libc/nt/kernel32/LockFileEx.s +++ b/libc/nt/kernel32/LockFileEx.s @@ -2,11 +2,11 @@ .imp kernel32,__imp_LockFileEx,LockFileEx,0 .text.windows -__LockFileEx: +LockFileEx: push %rbp mov %rsp,%rbp .profilable mov __imp_LockFileEx(%rip),%rax jmp __sysv2nt6 - .endfn __LockFileEx,globl + .endfn LockFileEx,globl .previous diff --git a/libc/nt/kernel32/SuspendThread.s b/libc/nt/kernel32/SuspendThread.s index 76654fa04..350b06471 100644 --- a/libc/nt/kernel32/SuspendThread.s +++ b/libc/nt/kernel32/SuspendThread.s @@ -1,15 +1,2 @@ .include "o/libc/nt/codegen.inc" .imp kernel32,__imp_SuspendThread,SuspendThread,0 - - .text.windows -SuspendThread: - push %rbp - mov %rsp,%rbp - .profilable - mov %rdi,%rcx - sub $32,%rsp - call *__imp_SuspendThread(%rip) - leave - ret - .endfn SuspendThread,globl - .previous diff --git a/libc/nt/kernel32/UnlockFileEx.s b/libc/nt/kernel32/UnlockFileEx.s index 0dedd8b8b..577232d60 100644 --- a/libc/nt/kernel32/UnlockFileEx.s +++ b/libc/nt/kernel32/UnlockFileEx.s @@ -2,11 +2,11 @@ .imp kernel32,__imp_UnlockFileEx,UnlockFileEx,0 .text.windows -__UnlockFileEx: +UnlockFileEx: push %rbp mov %rsp,%rbp .profilable mov __imp_UnlockFileEx(%rip),%rax jmp __sysv2nt6 - .endfn __UnlockFileEx,globl + .endfn UnlockFileEx,globl .previous diff --git a/libc/nt/master.sh b/libc/nt/master.sh index 415068d57..29a438991 100755 --- a/libc/nt/master.sh +++ b/libc/nt/master.sh @@ -166,7 +166,7 @@ imp 'CreateMutexEx' CreateMutexExW kernel32 0 imp 'CreatePrivateNamespace' CreatePrivateNamespaceW kernel32 0 imp 'CreateRemoteThread' CreateRemoteThread kernel32 0 imp 'CreateRemoteThreadEx' CreateRemoteThreadEx kernel32 0 -imp 'CreateSemaphore' CreateSemaphoreW kernel32 0 4 +imp 'CreateSemaphore' CreateSemaphoreW kernel32 0 imp 'CreateSemaphoreEx' CreateSemaphoreExW kernel32 0 imp 'CreateSymbolicLinkTransacted' CreateSymbolicLinkTransactedW kernel32 238 imp 'CreateTapePartition' CreateTapePartition kernel32 240 @@ -700,6 +700,7 @@ imp 'LocalUnlock' LocalUnlock kernel32 0 imp 'LocaleNameToLCID' LocaleNameToLCID kernel32 0 imp 'LocateXStateFeature' LocateXStateFeature kernel32 0 imp 'LockFile' LockFile kernel32 0 5 +imp 'LockFileEx' LockFileEx kernel32 0 6 imp 'LockResource' LockResource kernel32 0 1 imp 'MapUserPhysicalPages' MapUserPhysicalPages kernel32 0 imp 'MapUserPhysicalPagesScatter' MapUserPhysicalPagesScatter kernel32 986 @@ -982,7 +983,7 @@ imp 'SleepConditionVariableSRW' SleepConditionVariableSRW kernel32 0 imp 'SleepEx' SleepEx kernel32 0 2 imp 'SortCloseHandle' SortCloseHandle kernel32 1416 imp 'SortGetHandle' SortGetHandle kernel32 1417 -imp 'SuspendThread' SuspendThread kernel32 0 1 +imp 'SuspendThread' SuspendThread kernel32 0 imp 'SwitchToFiber' SwitchToFiber kernel32 0 imp 'SwitchToThread' SwitchToThread kernel32 0 imp 'SystemTimeToFileTime' SystemTimeToFileTime kernel32 0 2 @@ -1023,6 +1024,7 @@ imp 'UTUnRegister' UTUnRegister kernel32 1459 imp 'UmsThreadYield' UmsThreadYield kernel32 1460 imp 'UnhandledExceptionFilter' UnhandledExceptionFilter kernel32 0 imp 'UnlockFile' UnlockFile kernel32 0 5 +imp 'UnlockFileEx' UnlockFileEx kernel32 0 5 imp 'UnmapViewOfFile2' UnmapViewOfFile2 kernel32 0 2 imp 'UnmapViewOfFileEx' UnmapViewOfFileEx kernel32 0 3 imp 'UnregisterApplicationRecoveryCallback' UnregisterApplicationRecoveryCallback kernel32 1466 @@ -1118,7 +1120,6 @@ imp '__FlushViewOfFile' FlushViewOfFile kernel32 0 2 imp '__GenerateConsoleCtrlEvent' GenerateConsoleCtrlEvent kernel32 0 2 imp '__GetExitCodeProcess' GetExitCodeProcess kernel32 0 2 imp '__GetFileAttributes' GetFileAttributesW kernel32 0 1 -imp '__LockFileEx' LockFileEx kernel32 0 6 imp '__MapViewOfFileEx' MapViewOfFileEx kernel32 0 6 imp '__MapViewOfFileExNuma' MapViewOfFileExNuma kernel32 0 7 imp '__MoveFileEx' MoveFileExW kernel32 0 3 @@ -1127,7 +1128,6 @@ imp '__ReOpenFile' ReOpenFile kernel32 0 4 # TODO(jart): 6.2 and highe imp '__RemoveDirectory' RemoveDirectoryW kernel32 0 1 imp '__SetCurrentDirectory' SetCurrentDirectoryW kernel32 0 1 imp '__TerminateProcess' TerminateProcess kernel32 0 2 -imp '__UnlockFileEx' UnlockFileEx kernel32 0 5 imp '__UnmapViewOfFile' UnmapViewOfFile kernel32 0 1 imp '__VirtualProtect' VirtualProtect kernel32 0 4 imp '__WaitForMultipleObjects' WaitForMultipleObjects kernel32 0 4 diff --git a/libc/nt/synchronization.h b/libc/nt/synchronization.h index 5c0fe55eb..cdf052d95 100644 --- a/libc/nt/synchronization.h +++ b/libc/nt/synchronization.h @@ -82,10 +82,6 @@ bool32 SetWaitableTimer(int64_t hTimer, const int64_t *lpDueTimeAsFtOrNegRela, int32_t opt_lPeriodMs, NtTimerapcroutine opt_callback, void *lpArgToCallback, bool32 fUnsleepSystem); -int64_t CreateSemaphore(struct NtSecurityAttributes *opt_lpSemaphoreAttributes, - uint32_t lInitialCount, uint32_t lMaximumCount, - const char16_t *opt_lpName); - int32_t SetEvent(int64_t hEvent); int32_t ResetEvent(int64_t hEvent); int32_t PulseEvent(int64_t hEvent); diff --git a/libc/thread/clone-linux.S b/libc/runtime/clone-linux.S similarity index 100% rename from libc/thread/clone-linux.S rename to libc/runtime/clone-linux.S diff --git a/libc/thread/clone-openbsd.S b/libc/runtime/clone-openbsd.S similarity index 100% rename from libc/thread/clone-openbsd.S rename to libc/runtime/clone-openbsd.S diff --git a/libc/thread/clone.c b/libc/runtime/clone.c similarity index 97% rename from libc/thread/clone.c rename to libc/runtime/clone.c index 37f1bce6b..e869c4f13 100644 --- a/libc/thread/clone.c +++ b/libc/runtime/clone.c @@ -23,8 +23,11 @@ #include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/asan.internal.h" +#include "libc/intrin/pthread.h" #include "libc/limits.h" #include "libc/macros.internal.h" +#include "libc/nexgen32e/gettls.h" +#include "libc/nexgen32e/threaded.h" #include "libc/nt/runtime.h" #include "libc/nt/thread.h" #include "libc/nt/thunk/msabi.h" @@ -39,8 +42,6 @@ #include "libc/sysv/errfuns.h" #include "libc/thread/freebsd.internal.h" #include "libc/thread/openbsd.internal.h" -#include "libc/thread/thread.h" -#include "libc/thread/tls.h" #include "libc/thread/xnu.internal.h" #define __NR_thr_new 455 @@ -215,7 +216,7 @@ static int CloneXnu(int (*fn)(void *), char *stk, size_t stksz, int flags, wt->ctid = flags & CLONE_CHILD_SETTID ? ctid : &wt->tid; wt->ztid = flags & CLONE_CHILD_CLEARTID ? ctid : &wt->tid; wt->tls = flags & CLONE_SETTLS ? tls : 0; - wt->lock._lock = 1; + wt->lock.lock = 1; if ((rc = bsdthread_create(fn, arg, wt, 0, PTHREAD_START_CUSTOM_XNU)) != -1) { pthread_spin_lock(&wt->lock); rc = wt->tid; @@ -494,6 +495,20 @@ static int CloneLinux(int (*func)(void *arg, int tid), char *stk, size_t stksz, * _Exit1() which terminates the thread. Even though the callback says * it supports a return code, that'll only work on Linux and Windows. * + * The `tls` parameter is for thread-local storage. If you specify this + * then clone() will implicitly rewire libc (e.g. errno) to use TLS: + * + * static char tib[64]; + * __initialize_tls(tib); + * __install_tls(tib); + * + * If you want a main process TLS size that's larger call it manually. + * Once you've done the above and/or started creating your own threads + * you'll be able to access your `tls` thread information block, using + * + * char *p = __get_tls(); + * printf("errno is %d\n", *(int *)(p + 0x3c)); + * * This function follows the same ABI convention as the Linux userspace * libraries, with a few small changes. The varargs has been removed to * help prevent broken code, and the stack size and tls size parameters diff --git a/libc/intrin/leaky.S b/libc/runtime/construct.S similarity index 79% rename from libc/intrin/leaky.S rename to libc/runtime/construct.S index d79d752d2..953e8b4d1 100644 --- a/libc/intrin/leaky.S +++ b/libc/runtime/construct.S @@ -1,7 +1,7 @@ /*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ │vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ Copyright 2020 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 │ @@ -16,24 +16,19 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/runtime/internal.h" #include "libc/macros.internal.h" +.text.startup -// Decentralized section for leaky functions. - .section .piro.relo.sort.leaky.1,"aw",@nobits - .type _leaky_start,@object - .type _leaky_end,@object - .globl _leaky_start,_leaky_end - .hidden _leaky_start,_leaky_end - .byte 0 - .align __SIZEOF_POINTER__ - .underrun -_leaky_start: - .previous/* - ... - decentralized content - ... - */.section .piro.relo.sort.leaky.3,"aw",@nobits -_leaky_end: - .quad 0 - .overrun - .previous +// Calls global initialization functions. +// +// @param r12 is argc +// @param r13 is argv +// @param r14 is environ +// @param r15 is auxv +_construct: + push %rbp + mov %rsp,%rbp + pop %rbp + ret + .endfn _construct,globl diff --git a/libc/runtime/enable_threads.c b/libc/runtime/enable_threads.c index 47a17ee3c..08ededed8 100644 --- a/libc/runtime/enable_threads.c +++ b/libc/runtime/enable_threads.c @@ -19,7 +19,7 @@ #include "libc/assert.h" #include "libc/calls/calls.h" #include "libc/calls/strace.internal.h" -#include "libc/thread/tls.h" +#include "libc/nexgen32e/threaded.h" #include "libc/runtime/runtime.h" extern int __threadcalls_end[]; diff --git a/libc/runtime/enable_tls.c b/libc/runtime/enable_tls.c index 88b6d6e53..b6916d131 100644 --- a/libc/runtime/enable_tls.c +++ b/libc/runtime/enable_tls.c @@ -18,23 +18,23 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" #include "libc/calls/calls.h" +#include "libc/calls/strace.internal.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/errno.h" -#include "libc/intrin/asan.internal.h" -#include "libc/intrin/asancodes.h" #include "libc/intrin/bits.h" #include "libc/intrin/weaken.h" #include "libc/log/libfatal.internal.h" #include "libc/macros.internal.h" #include "libc/nexgen32e/msr.h" +#include "libc/nexgen32e/threaded.h" #include "libc/nt/thread.h" #include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" #include "libc/stdalign.internal.h" #include "libc/str/str.h" #include "libc/sysv/consts/nrlinux.h" -#include "libc/thread/tls.h" +#include "libc/thread/thread.h" #include "third_party/xed/x86.h" #define __NR_sysarch 0x000000a5 // freebsd+netbsd @@ -49,7 +49,7 @@ #define _TLSZ ((intptr_t)_tls_size) #define _TLDZ ((intptr_t)_tdata_size) -#define _TIBZ sizeof(struct CosmoTib) +#define _TIBZ sizeof(struct cthread_descriptor_t) int sys_enable_tls(); @@ -59,7 +59,7 @@ __msabi extern typeof(TlsAlloc) *const __imp_TlsAlloc; extern unsigned char __tls_mov_nt_rax[]; extern unsigned char __tls_add_nt_rax[]; -_Alignas(TLS_ALIGNMENT) static char __static_tls[5008]; +_Alignas(long) static char __static_tls[5008]; /** * Enables thread local storage for main process. @@ -98,13 +98,14 @@ _Alignas(TLS_ALIGNMENT) static char __static_tls[5008]; */ privileged void __enable_tls(void) { size_t siz; - struct CosmoTib *tib; + cthread_t tib; char *mem, *tls; siz = ROUNDUP(_TLSZ + _TIBZ, alignof(__static_tls)); if (siz <= sizeof(__static_tls)) { // if tls requirement is small then use the static tls block // which helps avoid a system call for appes with little tls // this is crucial to keeping life.com 16 kilobytes in size! + _Static_assert(alignof(__static_tls) >= alignof(cthread_t)); mem = __static_tls; } else { // if this binary needs a hefty tls block then we'll bank on @@ -116,23 +117,17 @@ privileged void __enable_tls(void) { mem = weaken(_mapanon)(siz); assert(mem); } - if (IsAsan()) { - // poison the space between .tdata and .tbss - __asan_poison(mem + (intptr_t)_tdata_size, - (intptr_t)_tbss_offset - (intptr_t)_tdata_size, - kAsanProtected); - } - tib = (struct CosmoTib *)(mem + siz - _TIBZ); + tib = (cthread_t)(mem + siz - _TIBZ); tls = mem + siz - _TIBZ - _TLSZ; - tib->tib_self = tib; - tib->tib_self2 = tib; - tib->tib_errno = __errno; + tib->self = tib; + tib->self2 = tib; + tib->err = __errno; if (IsLinux()) { // gnu/systemd guarantees pid==tid for the main thread so we can // avoid issuing a superfluous system call at startup in program - tib->tib_tid = __pid; + tib->tid = __pid; } else { - tib->tib_tid = sys_gettid(); + tib->tid = sys_gettid(); } __repmovsb(tls, _tdata_start, _TLDZ); diff --git a/libc/runtime/fork-nt.c b/libc/runtime/fork-nt.c index 73ebcb2e6..a80a6a75b 100644 --- a/libc/runtime/fork-nt.c +++ b/libc/runtime/fork-nt.c @@ -29,7 +29,7 @@ #include "libc/mem/alloca.h" #include "libc/mem/mem.h" #include "libc/nexgen32e/nt2sysv.h" -#include "libc/thread/tls.h" +#include "libc/nexgen32e/threaded.h" #include "libc/nt/console.h" #include "libc/nt/createfile.h" #include "libc/nt/enum/accessmask.h" @@ -226,9 +226,9 @@ textwindows void WinMainForked(void) { // rewrap the stdin named pipe hack // since the handles closed on fork struct Fds *fds = VEIL("r", &g_fds); - fds->p[0].handle = GetStdHandle(kNtStdInputHandle); - fds->p[1].handle = GetStdHandle(kNtStdOutputHandle); - fds->p[2].handle = GetStdHandle(kNtStdErrorHandle); + fds->p[0].handle = fds->__init_p[0].handle = GetStdHandle(kNtStdInputHandle); + fds->p[1].handle = fds->__init_p[1].handle = GetStdHandle(kNtStdOutputHandle); + fds->p[2].handle = fds->__init_p[2].handle = GetStdHandle(kNtStdErrorHandle); // untrack children of parent since we specify with both // CreateProcess() and CreateThread() as non-inheritable diff --git a/libc/runtime/fork.c b/libc/runtime/fork.c index 1d50b3fc1..e717e421e 100644 --- a/libc/runtime/fork.c +++ b/libc/runtime/fork.c @@ -22,10 +22,11 @@ #include "libc/calls/syscall-nt.internal.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" +#include "libc/nexgen32e/gettls.h" +#include "libc/nexgen32e/threaded.h" #include "libc/nt/process.h" #include "libc/runtime/internal.h" #include "libc/sysv/consts/sig.h" -#include "libc/thread/tls.h" /** * Creates new process. @@ -60,7 +61,7 @@ int fork(void) { parent = __pid; __pid = dx; if (__tls_enabled) { - __get_tls()->tib_tid = IsLinux() ? dx : sys_gettid(); + *(int *)(__get_tls() + 0x38) = IsLinux() ? dx : sys_gettid(); } STRACE("fork() → 0 (child of %d)", parent); sigprocmask(SIG_SETMASK, &old, 0); diff --git a/libc/runtime/ftracer.c b/libc/runtime/ftracer.c index 9e3ae1d7d..44d7a611d 100644 --- a/libc/runtime/ftracer.c +++ b/libc/runtime/ftracer.c @@ -21,14 +21,15 @@ #include "libc/fmt/itoa.h" #include "libc/intrin/cmpxchg.h" #include "libc/intrin/kprintf.h" -#include "libc/intrin/nopl.internal.h" +#include "libc/intrin/nopl.h" #include "libc/macros.internal.h" +#include "libc/nexgen32e/gettls.h" #include "libc/nexgen32e/stackframe.h" +#include "libc/nexgen32e/threaded.h" #include "libc/runtime/internal.h" #include "libc/runtime/stack.h" #include "libc/runtime/symbols.internal.h" -#include "libc/thread/tls.h" -#include "libc/thread/tls2.h" +#include "libc/thread/thread.h" #define MAX_NESTING 512 @@ -43,7 +44,7 @@ void ftrace_hook(void); static int g_stackdigs; -static struct CosmoFtrace g_ftrace; +static struct Ftrace g_ftrace; static privileged inline int GetNestingLevelImpl(struct StackFrame *frame) { int nesting = -2; @@ -54,12 +55,12 @@ static privileged inline int GetNestingLevelImpl(struct StackFrame *frame) { return MAX(0, nesting); } -static privileged inline int GetNestingLevel(struct CosmoFtrace *ft, +static privileged inline int GetNestingLevel(struct Ftrace *ft, struct StackFrame *sf) { int nesting; nesting = GetNestingLevelImpl(sf); - if (nesting < ft->ft_skew) ft->ft_skew = nesting; - nesting -= ft->ft_skew; + if (nesting < ft->skew) ft->skew = nesting; + nesting -= ft->skew; return MIN(MAX_NESTING, nesting); } @@ -72,27 +73,27 @@ static privileged inline int GetNestingLevel(struct CosmoFtrace *ft, */ privileged void ftracer(void) { long stackuse; - struct CosmoFtrace *ft; + struct Ftrace *ft; struct StackFrame *sf; if (__tls_enabled) { - ft = &__get_tls_privileged()->tib_ftrace; + ft = (struct Ftrace *)(__get_tls_privileged() + 0x08); } else { ft = &g_ftrace; } - if (_cmpxchg(&ft->ft_once, false, true)) { - ft->ft_lastaddr = -1; - ft->ft_skew = GetNestingLevelImpl(__builtin_frame_address(0)); + if (_cmpxchg(&ft->once, false, true)) { + ft->lastaddr = -1; + ft->skew = GetNestingLevelImpl(__builtin_frame_address(0)); } - if (_cmpxchg(&ft->ft_noreentry, false, true)) { + if (_cmpxchg(&ft->noreentry, false, true)) { sf = __builtin_frame_address(0); sf = sf->next; - if (sf->addr != ft->ft_lastaddr) { + if (sf->addr != ft->lastaddr) { stackuse = GetStackAddr() + GetStackSize() - (intptr_t)sf; kprintf("%rFUN %6P %'13T %'*ld %*s%t\n", g_stackdigs, stackuse, GetNestingLevel(ft, sf) * 2, "", sf->addr); - ft->ft_lastaddr = sf->addr; + ft->lastaddr = sf->addr; } - ft->ft_noreentry = false; + ft->noreentry = false; } } diff --git a/libc/runtime/getsymboltable.c b/libc/runtime/getsymboltable.c index 44c897b14..742504416 100644 --- a/libc/runtime/getsymboltable.c +++ b/libc/runtime/getsymboltable.c @@ -20,7 +20,7 @@ #include "libc/calls/strace.internal.h" #include "libc/intrin/bits.h" #include "libc/intrin/promises.internal.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" #include "libc/intrin/weaken.h" #include "libc/macros.internal.h" #include "libc/runtime/internal.h" diff --git a/libc/runtime/hook.greg.c b/libc/runtime/hook.greg.c index 24d7de0a3..c68cbb904 100644 --- a/libc/runtime/hook.greg.c +++ b/libc/runtime/hook.greg.c @@ -16,9 +16,16 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" #include "libc/calls/struct/sigset.h" +#include "libc/dce.h" +#include "libc/errno.h" +#include "libc/intrin/bits.h" +#include "libc/log/libfatal.internal.h" #include "libc/runtime/runtime.h" #include "libc/runtime/symbols.internal.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/prot.h" /** * Rewrites code in memory to hook function calls. diff --git a/libc/runtime/internal.h b/libc/runtime/internal.h index 2b0e25534..dd4e9877b 100644 --- a/libc/runtime/internal.h +++ b/libc/runtime/internal.h @@ -23,8 +23,6 @@ extern unsigned char _tdata_end[]; extern unsigned char _tdata_size[]; extern unsigned char _tbss_start[]; extern unsigned char _tbss_end[]; -extern unsigned char _tbss_size[]; -extern unsigned char _tbss_offset[]; extern unsigned char _tls_size[]; extern unsigned char _tls_content[]; diff --git a/libc/runtime/memtrack.internal.h b/libc/runtime/memtrack.internal.h index ff2488f0a..746602693 100644 --- a/libc/runtime/memtrack.internal.h +++ b/libc/runtime/memtrack.internal.h @@ -3,9 +3,9 @@ #include "libc/assert.h" #include "libc/intrin/midpoint.h" #include "libc/dce.h" -#include "libc/intrin/nopl.internal.h" +#include "libc/intrin/nopl.h" #include "libc/macros.internal.h" -#include "libc/thread/tls.h" +#include "libc/nexgen32e/threaded.h" #include "libc/nt/version.h" #include "libc/runtime/stack.h" #include "libc/sysv/consts/ss.h" diff --git a/libc/runtime/memtrack64.txt b/libc/runtime/memtrack64.txt index a16347005..2f5356616 100644 --- a/libc/runtime/memtrack64.txt +++ b/libc/runtime/memtrack64.txt @@ -1808,9 +1808,9 @@ 6f900000-6f9fffff 64gb free 6fa00000-6fafffff 64gb free 6fb00000-6fbfffff 64gb free -6fc00004-6fcfffff 64gb nsync +6fc00000-6fcfffff 64gb free 6fd00000-6fdfffff 64gb zipos -6fe00004-6feffffc 64gb g_fds +6fe00000-6fefffff 64gb g_fds 6ff00000-6ffffffd 64gb free 6ffffffe-6fffffff 128kb winargs diff --git a/libc/runtime/mman.internal.h b/libc/runtime/mman.internal.h index 8c582a52d..9863579c1 100644 --- a/libc/runtime/mman.internal.h +++ b/libc/runtime/mman.internal.h @@ -7,16 +7,16 @@ COSMOPOLITAN_C_START_ struct mman { int64_t pdp; /* 0x0500 */ int32_t pdpi; /* 0x0508 */ - int32_t e820n; /* 0x050c */ + int32_t e820n; /* 0x050a */ struct SmapEntry e820[256]; /* 0x0510 */ - char pc_drive_base_table[11]; /* 0x1d10 */ - unsigned char pc_drive_type; /* 0x1d1b */ - unsigned char pc_drive_last_sector; /* 0x1d1c */ - unsigned short pc_drive_last_cylinder; /* 0x1d1d */ - unsigned char pc_drives_attached; /* 0x1d1f */ - unsigned char pc_drive_last_head; /* 0x1d20 */ - unsigned char pc_drive; /* 0x1d21 */ - char bad_idt[6]; /* 0x1d22 */ + char pc_drive_base_table[11]; /* 0x1510 */ + unsigned char pc_drive_type; /* 0x151b */ + unsigned char pc_drive_last_sector; /* 0x151c */ + unsigned short pc_drive_last_cylinder; /* 0x151d */ + unsigned char pc_drives_attached; /* 0x151f */ + unsigned char pc_drive_last_head; /* 0x1520 */ + unsigned char pc_drive; /* 0x1521 */ + char bad_idt[6]; /* 0x1522 */ }; COSMOPOLITAN_C_END_ diff --git a/libc/runtime/mmap.c b/libc/runtime/mmap.c index 71efc97da..4bdfeaaf9 100644 --- a/libc/runtime/mmap.c +++ b/libc/runtime/mmap.c @@ -28,7 +28,7 @@ #include "libc/intrin/bits.h" #include "libc/intrin/describeflags.internal.h" #include "libc/intrin/likely.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" #include "libc/intrin/safemacros.internal.h" #include "libc/intrin/weaken.h" #include "libc/limits.h" diff --git a/libc/intrin/printmemoryintervals.c b/libc/runtime/printmemoryintervals.c similarity index 100% rename from libc/intrin/printmemoryintervals.c rename to libc/runtime/printmemoryintervals.c diff --git a/libc/thread/clone-nt.S b/libc/runtime/winthreadlaunch.S similarity index 100% rename from libc/thread/clone-nt.S rename to libc/runtime/winthreadlaunch.S diff --git a/libc/sock/sendfile.c b/libc/sock/sendfile.c index 4908c2f23..2a1350a33 100644 --- a/libc/sock/sendfile.c +++ b/libc/sock/sendfile.c @@ -23,7 +23,6 @@ #include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/syscall_support-nt.internal.h" #include "libc/dce.h" -#include "libc/intrin/asan.internal.h" #include "libc/intrin/safemacros.internal.h" #include "libc/nt/enum/wait.h" #include "libc/nt/errors.h" @@ -107,13 +106,10 @@ static ssize_t sendfile_linux2bsd(int outfd, int infd, if (IsFreebsd()) { rc = sys_sendfile_freebsd(infd, outfd, offset, uptobytes, 0, &sbytes, 0); } else { - sbytes = uptobytes; rc = sys_sendfile_xnu(infd, outfd, offset, &sbytes, 0, 0); } if (rc != -1) { - if (inout_opt_inoffset) { - *inout_opt_inoffset += sbytes; - } + if (inout_opt_inoffset) *inout_opt_inoffset += sbytes; return sbytes; } else { return -1; @@ -135,25 +131,15 @@ static ssize_t sendfile_linux2bsd(int outfd, int infd, */ ssize_t sendfile(int outfd, int infd, int64_t *inout_opt_inoffset, size_t uptobytes) { - int rc; - if (!uptobytes) { - rc = einval(); - } else if (IsAsan() && inout_opt_inoffset && - !__asan_is_valid(inout_opt_inoffset, - sizeof(*inout_opt_inoffset))) { - rc = efault(); - } else if (uptobytes > 0x7ffffffe /* Microsoft's off-by-one */) { - rc = eoverflow(); - } else if (IsLinux()) { - rc = sys_sendfile(outfd, infd, inout_opt_inoffset, uptobytes); + if (!uptobytes) return einval(); + if (uptobytes > 0x7ffffffe /* Microsoft's off-by-one */) return eoverflow(); + if (IsLinux()) { + return sys_sendfile(outfd, infd, inout_opt_inoffset, uptobytes); } else if (IsFreebsd() || IsXnu()) { - rc = sendfile_linux2bsd(outfd, infd, inout_opt_inoffset, uptobytes); + return sendfile_linux2bsd(outfd, infd, inout_opt_inoffset, uptobytes); } else if (IsWindows()) { - rc = sendfile_linux2nt(outfd, infd, inout_opt_inoffset, uptobytes); + return sendfile_linux2nt(outfd, infd, inout_opt_inoffset, uptobytes); } else { - rc = copyfd(infd, inout_opt_inoffset, outfd, NULL, uptobytes, 0); + return copyfd(infd, inout_opt_inoffset, outfd, NULL, uptobytes, 0); } - STRACE("sendfile(%d, %d, %p, %'zu) → %ld% m", outfd, infd, inout_opt_inoffset, - uptobytes, rc); - return rc; } diff --git a/libc/stdio/clearerr.c b/libc/stdio/clearerr.c index 1ff0ae2b6..723847951 100644 --- a/libc/stdio/clearerr.c +++ b/libc/stdio/clearerr.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" /** diff --git a/libc/stdio/dirstream.c b/libc/stdio/dirstream.c index e382cd254..6a62df49c 100644 --- a/libc/stdio/dirstream.c +++ b/libc/stdio/dirstream.c @@ -25,8 +25,8 @@ #include "libc/calls/syscall_support-nt.internal.h" #include "libc/errno.h" #include "libc/intrin/asan.internal.h" -#include "libc/intrin/nopl.internal.h" -#include "libc/thread/thread.h" +#include "libc/intrin/nopl.h" +#include "libc/intrin/pthread.h" #include "libc/intrin/weaken.h" #include "libc/mem/mem.h" #include "libc/nt/enum/fileflagandattributes.h" diff --git a/libc/stdio/ecvt.c b/libc/stdio/ecvt.c index 2eba91728..61f4455d9 100644 --- a/libc/stdio/ecvt.c +++ b/libc/stdio/ecvt.c @@ -24,7 +24,6 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/fmt/fmt.h" #include "libc/mem/mem.h" -#include "libc/runtime/runtime.h" #include "libc/str/str.h" #include "third_party/gdtoa/gdtoa.h" @@ -34,24 +33,10 @@ Copyright (c) 2002, 2006, 2010 Todd C. Miller \""); asm(".include \"libc/disclaimer.inc\""); // clang-format off -static char *s; - -static void -__cvt_atexit(void) -{ - free(s); - s = 0; -} - -static void __attribute__((__constructor__)) -__cvt_init(void) -{ - atexit(__cvt_atexit); -} - static char * __cvt(double value, int ndigit, int *decpt, int *sign, int fmode, int pad) { + static char *s; char *p, *rve, c; size_t siz; diff --git a/libc/stdio/fdopen.c b/libc/stdio/fdopen.c index de8875d62..4a25238cd 100644 --- a/libc/stdio/fdopen.c +++ b/libc/stdio/fdopen.c @@ -17,12 +17,12 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/intrin/pthread.h" #include "libc/mem/mem.h" #include "libc/stdio/internal.h" #include "libc/stdio/stdio.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/errfuns.h" -#include "libc/thread/thread.h" /** * Allocates stream object for already-opened file descriptor. @@ -38,7 +38,7 @@ FILE *fdopen(int fd, const char *mode) { f->fd = fd; f->bufmode = ischardev(fd) ? _IOLBF : _IOFBF; f->iomode = fopenflags(mode); - f->lock._type = PTHREAD_MUTEX_RECURSIVE; + f->lock.type = PTHREAD_MUTEX_RECURSIVE; f->size = BUFSIZ; if ((f->buf = malloc(f->size))) { if ((f->iomode & O_ACCMODE) != O_RDONLY) { diff --git a/libc/stdio/feof.c b/libc/stdio/feof.c index ac225d98a..e115f9715 100644 --- a/libc/stdio/feof.c +++ b/libc/stdio/feof.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" /** diff --git a/libc/stdio/ferror.c b/libc/stdio/ferror.c index 7b8905d0f..22b46d0c0 100644 --- a/libc/stdio/ferror.c +++ b/libc/stdio/ferror.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" /** diff --git a/libc/stdio/fflush.c b/libc/stdio/fflush.c index 90da7fa6b..8f454dde7 100644 --- a/libc/stdio/fflush.c +++ b/libc/stdio/fflush.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" /** diff --git a/libc/stdio/fflush.internal.h b/libc/stdio/fflush.internal.h index cc3343d3a..a00e504fb 100644 --- a/libc/stdio/fflush.internal.h +++ b/libc/stdio/fflush.internal.h @@ -1,7 +1,7 @@ #ifndef COSMOPOLITAN_LIBC_STDIO_FFLUSH_H_ #define COSMOPOLITAN_LIBC_STDIO_FFLUSH_H_ -#include "libc/intrin/nopl.internal.h" -#include "libc/thread/thread.h" +#include "libc/intrin/nopl.h" +#include "libc/intrin/pthread.h" #include "libc/stdio/stdio.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ diff --git a/libc/stdio/fflush_unlocked.c b/libc/stdio/fflush_unlocked.c index a00ebe969..0746536be 100644 --- a/libc/stdio/fflush_unlocked.c +++ b/libc/stdio/fflush_unlocked.c @@ -19,7 +19,7 @@ #include "libc/calls/calls.h" #include "libc/errno.h" #include "libc/intrin/bits.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" #include "libc/intrin/pushpop.h" #include "libc/macros.internal.h" #include "libc/mem/arraylist.internal.h" diff --git a/libc/stdio/fgetc.c b/libc/stdio/fgetc.c index 8dda15ace..b04afd66c 100644 --- a/libc/stdio/fgetc.c +++ b/libc/stdio/fgetc.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" /** diff --git a/libc/stdio/fgetln.c b/libc/stdio/fgetln.c index 04f8edfca..a131e9dfc 100644 --- a/libc/stdio/fgetln.c +++ b/libc/stdio/fgetln.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" /** diff --git a/libc/stdio/fgets.c b/libc/stdio/fgets.c index 121260ee9..2807ef537 100644 --- a/libc/stdio/fgets.c +++ b/libc/stdio/fgets.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" /** diff --git a/libc/stdio/fgetwc.c b/libc/stdio/fgetwc.c index aecf07615..d7e1e41f8 100644 --- a/libc/stdio/fgetwc.c +++ b/libc/stdio/fgetwc.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" /** diff --git a/libc/stdio/fgetws.c b/libc/stdio/fgetws.c index f116a51f5..3c96e30d9 100644 --- a/libc/stdio/fgetws.c +++ b/libc/stdio/fgetws.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" /** diff --git a/libc/stdio/fileno.c b/libc/stdio/fileno.c index 696e7f7a2..e739d1cb5 100644 --- a/libc/stdio/fileno.c +++ b/libc/stdio/fileno.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" #include "libc/sysv/errfuns.h" diff --git a/libc/stdio/flockfile.c b/libc/stdio/flockfile.c index 36ae8a3e5..e1a3872ca 100644 --- a/libc/stdio/flockfile.c +++ b/libc/stdio/flockfile.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" /** diff --git a/libc/stdio/flushlbf.c b/libc/stdio/flushlbf.c index a254e3c3c..69bd5e4c9 100644 --- a/libc/stdio/flushlbf.c +++ b/libc/stdio/flushlbf.c @@ -17,9 +17,9 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" #include "libc/stdio/fflush.internal.h" -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" #include "libc/stdio/stdio_ext.h" diff --git a/libc/stdio/fmemopen.c b/libc/stdio/fmemopen.c index 3183982a3..28c12cd74 100644 --- a/libc/stdio/fmemopen.c +++ b/libc/stdio/fmemopen.c @@ -16,12 +16,12 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/intrin/pthread.h" #include "libc/mem/mem.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/errfuns.h" -#include "libc/thread/thread.h" /** * Opens buffer as stream. @@ -54,7 +54,7 @@ FILE *fmemopen(void *buf, size_t size, const char *mode) { f->end = size; f->size = size; f->iomode = fopenflags(mode); - f->lock._type = PTHREAD_MUTEX_RECURSIVE; + f->lock.type = PTHREAD_MUTEX_RECURSIVE; if (f->iomode & O_APPEND) { if ((p = memchr(buf, '\0', size))) { f->beg = p - (char *)buf; diff --git a/libc/stdio/fprintf.c b/libc/stdio/fprintf.c index fd512ca11..796394dc7 100644 --- a/libc/stdio/fprintf.c +++ b/libc/stdio/fprintf.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" /** diff --git a/libc/stdio/fputc.c b/libc/stdio/fputc.c index 28b8a3b72..faf8af218 100644 --- a/libc/stdio/fputc.c +++ b/libc/stdio/fputc.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" /** diff --git a/libc/stdio/fputs.c b/libc/stdio/fputs.c index f7adc65d9..85bf483e6 100644 --- a/libc/stdio/fputs.c +++ b/libc/stdio/fputs.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" /** diff --git a/libc/stdio/fputwc.c b/libc/stdio/fputwc.c index 6356c1aed..44afd3e04 100644 --- a/libc/stdio/fputwc.c +++ b/libc/stdio/fputwc.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" /** diff --git a/libc/stdio/fputws.c b/libc/stdio/fputws.c index c828e0878..e93151095 100644 --- a/libc/stdio/fputws.c +++ b/libc/stdio/fputws.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" /** diff --git a/libc/stdio/fread.c b/libc/stdio/fread.c index f5f96007a..f926a9517 100644 --- a/libc/stdio/fread.c +++ b/libc/stdio/fread.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" /** diff --git a/libc/stdio/freopen.c b/libc/stdio/freopen.c index bcebc8fd7..8799d6775 100644 --- a/libc/stdio/freopen.c +++ b/libc/stdio/freopen.c @@ -17,7 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" #include "libc/sysv/consts/f.h" #include "libc/sysv/consts/fd.h" diff --git a/libc/stdio/fseeko.c b/libc/stdio/fseeko.c index d0d8d265f..b29b98c30 100644 --- a/libc/stdio/fseeko.c +++ b/libc/stdio/fseeko.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" /** diff --git a/libc/stdio/ftello.c b/libc/stdio/ftello.c index 0f12ba119..7ce952ca2 100644 --- a/libc/stdio/ftello.c +++ b/libc/stdio/ftello.c @@ -20,7 +20,7 @@ #include "libc/errno.h" #include "libc/runtime/runtime.h" #include "libc/stdio/internal.h" -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" #include "libc/sysv/consts/o.h" diff --git a/libc/stdio/fwrite.c b/libc/stdio/fwrite.c index 915b0a21c..4522b636b 100644 --- a/libc/stdio/fwrite.c +++ b/libc/stdio/fwrite.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" /** diff --git a/libc/stdio/getdelim.c b/libc/stdio/getdelim.c index 040cfc968..055c22ead 100644 --- a/libc/stdio/getdelim.c +++ b/libc/stdio/getdelim.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" /** diff --git a/libc/stdio/iconv.c b/libc/stdio/iconv.c index 8a09fda96..b71ed9160 100644 --- a/libc/stdio/iconv.c +++ b/libc/stdio/iconv.c @@ -27,10 +27,11 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/errno.h" #include "libc/mem/mem.h" +#include "libc/nexgen32e/gettls.h" #include "libc/stdio/iconv.h" -#include "libc/str/locale.h" #include "libc/str/str.h" -#include "libc/thread/tls.h" +#include "libc/thread/thread.h" +#include "libc/str/locale.h" // clang-format off asm(".ident\t\"\\n\\n\ @@ -282,7 +283,7 @@ size_t iconv(iconv_t cd, char **restrict in, size_t *restrict inb, char **restri int err; unsigned char type = map[-1]; unsigned char totype = tomap[-1]; - locale_t *ploc = (locale_t *)&__get_tls()->tib_locale; + locale_t *ploc = &((cthread_t)__get_tls())->locale; locale_t loc = *ploc; if (!in || !*in || !*inb) return 0; diff --git a/libc/stdio/lock.internal.h b/libc/stdio/lock.h similarity index 77% rename from libc/stdio/lock.internal.h rename to libc/stdio/lock.h index 99775cb82..fe38e179d 100644 --- a/libc/stdio/lock.internal.h +++ b/libc/stdio/lock.h @@ -1,11 +1,15 @@ #ifndef COSMOPOLITAN_LIBC_STDIO_LOCK_H_ #define COSMOPOLITAN_LIBC_STDIO_LOCK_H_ -#include "libc/intrin/nopl.internal.h" +#include "libc/intrin/nopl.h" +#include "libc/nexgen32e/threaded.h" #include "libc/stdio/stdio.h" -#include "libc/thread/tls.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ +void flockfile(FILE *) paramsnonnull(); +void funlockfile(FILE *) paramsnonnull(); +int ftrylockfile(FILE *) paramsnonnull(); + #ifdef _NOPL1 #define flockfile(f) _NOPL1("__threadcalls", flockfile, f) #define funlockfile(f) _NOPL1("__threadcalls", funlockfile, f) diff --git a/libc/stdio/puts.c b/libc/stdio/puts.c index cae291488..aafa06732 100644 --- a/libc/stdio/puts.c +++ b/libc/stdio/puts.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" diff --git a/libc/stdio/readdir64.S b/libc/stdio/readdir64.S index ea6608a79..471f01bdb 100644 --- a/libc/stdio/readdir64.S +++ b/libc/stdio/readdir64.S @@ -1,5 +1,5 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ │ This is free and unencumbered software released into the public domain. │ │ │ @@ -26,6 +26,5 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/macros.internal.h" -readdir64: - jmp readdir +readdir64: jmp readdir .endfn readdir64,globl diff --git a/libc/stdio/rewind.c b/libc/stdio/rewind.c index 6ad7a680d..d97050f43 100644 --- a/libc/stdio/rewind.c +++ b/libc/stdio/rewind.c @@ -17,7 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" /** diff --git a/libc/stdio/setvbuf.c b/libc/stdio/setvbuf.c index ac58cf08e..1c2aa00eb 100644 --- a/libc/stdio/setvbuf.c +++ b/libc/stdio/setvbuf.c @@ -17,7 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/runtime/runtime.h" -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" #include "libc/sysv/errfuns.h" diff --git a/libc/stdio/stderr-init.S b/libc/stdio/stderr-init.S index 24b3ec928..b5be107f5 100644 --- a/libc/stdio/stderr-init.S +++ b/libc/stdio/stderr-init.S @@ -20,7 +20,7 @@ #include "libc/dce.h" #include "libc/calls/calls.h" #include "libc/sysv/consts/fileno.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" #include "libc/macros.internal.h" .init.start 400,_init_stderr @@ -34,6 +34,6 @@ ezlea __stderr_buf,cx mov %rcx,0x18(%rax) #→ f.buf movl $BUFSIZ,0x20(%rax) #→ f.size - movb $PTHREAD_MUTEX_RECURSIVE,0x38+16(%rax) #→ f.lock.attr + movb $PTHREAD_MUTEX_RECURSIVE,0x38(%rax) #→ f.lock.attr mov %rax,stderr(%rip) .init.end 400,_init_stderr,globl,hidden diff --git a/libc/stdio/stdin-init.S b/libc/stdio/stdin-init.S index 86051c810..2f6d03d0d 100644 --- a/libc/stdio/stdin-init.S +++ b/libc/stdio/stdin-init.S @@ -20,7 +20,7 @@ #include "libc/dce.h" #include "libc/calls/calls.h" #include "libc/sysv/consts/fileno.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" #include "libc/macros.internal.h" .init.start 400,_init_stdin @@ -30,6 +30,6 @@ ezlea __stdin_buf,cx mov %rcx,0x18(%rax) #→ f.buf movl $BUFSIZ,0x20(%rax) #→ f.size - movb $PTHREAD_MUTEX_RECURSIVE,0x38+16(%rax) #→ f.lock.attr + movb $PTHREAD_MUTEX_RECURSIVE,0x38(%rax) #→ f.lock.attr mov %rax,stdin(%rip) .init.end 400,_init_stdin,globl,hidden diff --git a/libc/stdio/stdio.h b/libc/stdio/stdio.h index 401e344e2..7180a1457 100644 --- a/libc/stdio/stdio.h +++ b/libc/stdio/stdio.h @@ -1,7 +1,7 @@ #ifndef COSMOPOLITAN_LIBC_STDIO_STDIO_H_ #define COSMOPOLITAN_LIBC_STDIO_STDIO_H_ #include "libc/fmt/pflink.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" #define _STDIO_H #define L_ctermid 20 @@ -50,9 +50,6 @@ int fileno(FILE *) paramsnonnull() nosideeffect; int fputc(int, FILE *) paramsnonnull(); int fputs(const char *, FILE *) paramsnonnull(); int fputws(const wchar_t *, FILE *) paramsnonnull(); -void flockfile(FILE *) paramsnonnull(); -void funlockfile(FILE *) paramsnonnull(); -int ftrylockfile(FILE *) paramsnonnull(); char *fgets(char *, int, FILE *) paramsnonnull(); wchar_t *fgetws(wchar_t *, int, FILE *) paramsnonnull(); wint_t putwc(wchar_t, FILE *) paramsnonnull(); diff --git a/libc/stdio/stdout-init.S b/libc/stdio/stdout-init.S index 1711b69b6..19c549e10 100644 --- a/libc/stdio/stdout-init.S +++ b/libc/stdio/stdout-init.S @@ -20,7 +20,7 @@ #include "libc/dce.h" #include "libc/calls/calls.h" #include "libc/sysv/consts/fileno.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" #include "libc/macros.internal.h" .init.start 400,_init_stdout @@ -32,6 +32,6 @@ ezlea __stdout_buf,cx mov %rcx,0x18(%rax) #→ f.buf movl $BUFSIZ,0x20(%rax) #→ f.size - movb $PTHREAD_MUTEX_RECURSIVE,0x38+16(%rax) #→ f.lock.attr + movb $PTHREAD_MUTEX_RECURSIVE,0x38(%rax) #→ f.lock.attr mov %rax,stdout(%rip) .init.end 400,_init_stdout,globl,hidden diff --git a/libc/stdio/ungetc.c b/libc/stdio/ungetc.c index 7c1bb6535..df066d05e 100644 --- a/libc/stdio/ungetc.c +++ b/libc/stdio/ungetc.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" /** diff --git a/libc/stdio/ungetwc.c b/libc/stdio/ungetwc.c index 8fb59f6ea..f0c482c9c 100644 --- a/libc/stdio/ungetwc.c +++ b/libc/stdio/ungetwc.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" /** diff --git a/libc/stdio/vfprintf.c b/libc/stdio/vfprintf.c index 6978a0a26..c44db47ea 100644 --- a/libc/stdio/vfprintf.c +++ b/libc/stdio/vfprintf.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" /** diff --git a/libc/str/langinfo.c b/libc/str/langinfo.c index 41aad1030..9451b444d 100644 --- a/libc/str/langinfo.c +++ b/libc/str/langinfo.c @@ -25,10 +25,11 @@ │ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ │ │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/nexgen32e/gettls.h" +#include "libc/thread/thread.h" #include "libc/str/langinfo.h" #include "libc/str/locale.h" #include "libc/str/nltypes.h" -#include "libc/thread/tls.h" asm(".ident\t\"\\n\\n\ Musl libc (MIT License)\\n\ @@ -100,5 +101,5 @@ char *nl_langinfo_l(nl_item item, locale_t loc) char *nl_langinfo(nl_item item) { - return nl_langinfo_l(item, (locale_t)__get_tls()->tib_locale); + return nl_langinfo_l(item, ((cthread_t)__get_tls())->locale); } diff --git a/libc/stubs/ld.S b/libc/stubs/ld.S index b729f18d2..a3570b917 100644 --- a/libc/stubs/ld.S +++ b/libc/stubs/ld.S @@ -50,8 +50,6 @@ _tdata_size = 0 _tbss_start = 0 _tbss_end = 0 - _tbss_offset = 0 - _tbss_size = 0 _tls_size = 0 _tls_content = 0 @@ -75,8 +73,6 @@ .globl _tdata_size .globl _tbss_start .globl _tbss_end - .globl _tbss_size - .globl _tbss_offset .globl _tls_size .globl _tls_content .globl __data_start @@ -104,10 +100,8 @@ .weak _tdata_size .weak _tbss_start .weak _tbss_end - .weak _tbss_size .weak _tls_size .weak _tls_content - .weak _tbss_offset .weak __data_start .weak __data_end .weak __bss_start diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index 3df60aca6..e548083d7 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -579,7 +579,7 @@ syscon sicode SYS_USER_DISPATCH 2 -1 -1 -1 -1 -1 # SIGSYS; syscall # sigaltstack() values # # group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary -syscon ss SIGSTKSZ 8192 131072 34816 28672 28672 8192 # overlaid with FRAMESIZE; you need to #undef SIGSTKSZ to access this symbol +syscon ss SIGSTKSZ 8192 131072 34816 28672 28672 8192 # overlaid with STACKSIZE; you need to #undef SIGSTKSZ to access this symbol syscon ss MINSIGSTKSZ 2048 32768 2048 12288 8192 2048 # overlaid with 32768; you need to #undef MINSIGSTKSZ to access this symbol syscon ss SS_ONSTACK 1 1 1 1 1 1 # unix consensus syscon ss SS_DISABLE 2 4 4 4 4 2 # bsd consensus @@ -1316,9 +1316,9 @@ syscon rusage RUSAGE_BOTH -2 99 99 99 99 99 # woop # # group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary syscon futex FUTEX_WAIT 0 0 0 1 0 0 -syscon futex FUTEX_WAKE 1 0 0 2 0 0 -syscon futex FUTEX_REQUEUE 3 0 0 3 0 0 -syscon futex FUTEX_PRIVATE_FLAG 128 0 0 128 0 0 +syscon futex FUTEX_WAKE 1 0 0 2 1 0 +syscon futex FUTEX_REQUEUE 3 0 0 3 3 0 +syscon futex FUTEX_PRIVATE_FLAG 128 0 0 128 128 0 # lio_listio() magnums # @@ -1336,7 +1336,7 @@ syscon sched SCHED_OTHER 0 127 2 127 0 127 # standard round-robin syscon sched SCHED_FIFO 1 127 1 127 1 127 # [real-time] first-in, first-out policy syscon sched SCHED_RR 2 127 3 127 2 127 # [real-time] round-robin policy syscon sched SCHED_BATCH 3 127 2 127 0 127 # for "batch" style execution of processes; polyfilled as SCHED_OTHER on non-Linux -syscon sched SCHED_IDLE 5 127 2 127 0 127 # for running very low priority background jobs; polyfilled as SCHED_OTHER on non-Linux +𝔰𝔶𝔰𝔠𝔬𝔫 sched SCHED_IDLE 5 127 2 127 0 127 # for running very low priority background jobs; polyfilled as SCHED_OTHER on non-Linux syscon sched SCHED_DEADLINE 6 127 127 127 127 127 # can only be set by sched_setattr() syscon sched SCHED_RESET_ON_FORK 0x40000000 0 0 0 0 0 # Can be ORed in to make sure the process is reverted back to SCHED_NORMAL on fork(); no-op on non-Linux @@ -1847,7 +1847,7 @@ syscon nr __NR_kill 0x003e 0x2000025 0x0025 0x007a 0x025 0xfff syscon nr __NR_killpg 0xfff 0xfff 0x0092 0xfff 0xfff 0xfff syscon nr __NR_clone 0x0038 0xfff 0xfff 0xfff 0x11f 0xfff syscon nr __NR_tkill 0x00c8 0xfff 0xfff 0xfff 0xfff 0xfff -syscon nr __NR_futex 0x00ca 0xfff 0xfff 0x0053 0xfff 0xfff +syscon nr __NR_futex 0x00ca 0xfff 0xfff 0x0053 0x0a6 0xfff syscon nr __NR_set_robust_list 0x0111 0xfff 0xfff 0xfff 0x0a7 0xfff syscon nr __NR_get_robust_list 0x0112 0xfff 0xfff 0xfff 0x0a8 0xfff syscon nr __NR_uname 0x003f 0xfff 0x00a4 0xfff 0xfff 0xfff diff --git a/libc/sysv/consts/FUTEX_PRIVATE_FLAG.s b/libc/sysv/consts/FUTEX_PRIVATE_FLAG.s index 3d21804a5..da3dd6143 100644 --- a/libc/sysv/consts/FUTEX_PRIVATE_FLAG.s +++ b/libc/sysv/consts/FUTEX_PRIVATE_FLAG.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon futex,FUTEX_PRIVATE_FLAG,128,0,0,128,0,0 +.syscon futex,FUTEX_PRIVATE_FLAG,128,0,0,128,128,0 diff --git a/libc/sysv/consts/FUTEX_REQUEUE.s b/libc/sysv/consts/FUTEX_REQUEUE.s index e25e5dffb..b0a8baa21 100644 --- a/libc/sysv/consts/FUTEX_REQUEUE.s +++ b/libc/sysv/consts/FUTEX_REQUEUE.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon futex,FUTEX_REQUEUE,3,0,0,3,0,0 +.syscon futex,FUTEX_REQUEUE,3,0,0,3,3,0 diff --git a/libc/sysv/consts/FUTEX_WAKE.s b/libc/sysv/consts/FUTEX_WAKE.s index acdc80afa..2d6184318 100644 --- a/libc/sysv/consts/FUTEX_WAKE.s +++ b/libc/sysv/consts/FUTEX_WAKE.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon futex,FUTEX_WAKE,1,0,0,2,0,0 +.syscon futex,FUTEX_WAKE,1,0,0,2,1,0 diff --git a/libc/sysv/consts/SOMAXCONN.s b/libc/sysv/consts/SOMAXCONN.s index f711831ce..5c6a6ef77 100644 --- a/libc/sysv/consts/SOMAXCONN.s +++ b/libc/sysv/consts/SOMAXCONN.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon limits,SOMAXCONN,4096,128,128,128,128,2147483647 +.syscon misc,SOMAXCONN,0x80,0x80,0x80,0x80,0x80,0x7fffffff diff --git a/libc/sysv/consts/__NR_futex.s b/libc/sysv/consts/__NR_futex.s index a7bd77d79..bbcc0b744 100644 --- a/libc/sysv/consts/__NR_futex.s +++ b/libc/sysv/consts/__NR_futex.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon nr,__NR_futex,0x00ca,0xfff,0xfff,0x0053,0xfff,0xfff +.syscon nr,__NR_futex,0x00ca,0xfff,0xfff,0x0053,0x0a6,0xfff diff --git a/libc/sysv/consts/futex.h b/libc/sysv/consts/futex.h index 91641a3d7..f6b12cf2b 100644 --- a/libc/sysv/consts/futex.h +++ b/libc/sysv/consts/futex.h @@ -2,25 +2,21 @@ #define COSMOPOLITAN_LIBC_SYSV_CONSTS_FUTEX_H_ #include "libc/runtime/symbolic.h" -#define FUTEX_WAIT SYMBOLIC(FUTEX_WAIT) -#define FUTEX_WAKE SYMBOLIC(FUTEX_WAKE) -#define FUTEX_REQUEUE SYMBOLIC(FUTEX_REQUEUE) -#define FUTEX_PRIVATE_FLAG 128 - +#define FUTEX_WAIT SYMBOLIC(FUTEX_WAIT) +#define FUTEX_WAKE SYMBOLIC(FUTEX_WAKE) +#define FUTEX_REQUEUE SYMBOLIC(FUTEX_REQUEUE) +#define FUTEX_PRIVATE_FLAG SYMBOLIC(FUTEX_PRIVATE_FLAG) #define FUTEX_WAIT_PRIVATE (FUTEX_WAIT | FUTEX_PRIVATE_FLAG) #define FUTEX_WAKE_PRIVATE (FUTEX_WAKE | FUTEX_PRIVATE_FLAG) #define FUTEX_REQUEUE_PRIVATE (FUTEX_REQUEUE | FUTEX_PRIVATE_FLAG) -#define FUTEX_WAIT_BITSET 9 -#define FUTEX_CLOCK_REALTIME 256 -#define FUTEX_BITSET_MATCH_ANY 0xffffffff - #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ extern const int FUTEX_WAIT; extern const int FUTEX_WAKE; extern const int FUTEX_REQUEUE; +extern const int FUTEX_PRIVATE_FLAG; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/sysv/consts/ss.h b/libc/sysv/consts/ss.h index 9f05e231c..68d135fae 100644 --- a/libc/sysv/consts/ss.h +++ b/libc/sysv/consts/ss.h @@ -10,7 +10,7 @@ extern const int SS_DISABLE; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#define SIGSTKSZ FRAMESIZE +#define SIGSTKSZ STACKSIZE #define MINSIGSTKSZ 32768 #define SS_ONSTACK 1 #define SS_DISABLE SS_DISABLE diff --git a/libc/sysv/errno_location.greg.c b/libc/sysv/errno_location.greg.c index fe1fc4cd6..425939f73 100644 --- a/libc/sysv/errno_location.greg.c +++ b/libc/sysv/errno_location.greg.c @@ -17,12 +17,16 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/errno.h" -#include "libc/thread/tls2.h" +#include "libc/nexgen32e/gettls.h" +#include "libc/nexgen32e/threaded.h" /** * Returns address of errno variable. + * + * @see __initialize_tls() + * @see __install_tls() */ privileged nocallersavedregisters errno_t *(__errno_location)(void) { if (!__tls_enabled) return &__errno; - return &__get_tls_privileged()->tib_errno; + return (errno_t *)(__get_tls_privileged() + 0x3c); } diff --git a/libc/sysv/strace.greg.c b/libc/sysv/strace.greg.c index 302fc69c1..0822d65cb 100644 --- a/libc/sysv/strace.greg.c +++ b/libc/sysv/strace.greg.c @@ -16,7 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/atomic.h" #include "libc/runtime/runtime.h" /** @@ -41,4 +40,4 @@ * under normal circumstances, `__strace` should only be either zero or * one. */ -atomic_int __strace; +_Atomic(int) __strace; diff --git a/libc/sysv/syscall.S b/libc/sysv/syscall.S index 6cffdcf8a..275a656da 100644 --- a/libc/sysv/syscall.S +++ b/libc/sysv/syscall.S @@ -16,7 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dce.h" #include "libc/macros.internal.h" .privileged @@ -33,52 +32,12 @@ // @param %rsi,%rdx,%rcx,%r8,%r9 may supply parameters 1 through 5 // @param sixth is optionally pushed on the stack before call // @return %rax has result, or -1 w/ errno on failure -syscall: - push %rbp - mov %rsp,%rbp - .profilable - -// slide arguments into their right places - mov %rdi,%rax # nr - mov %rsi,%rdi # arg 1 - mov %rdx,%rsi # arg 2 - mov %rcx,%rdx # arg 3 - mov %r8,%rcx # arg 4 - mov %r9,%r8 # arg 5 - mov 16(%rbp),%r9 # arg 6 - push 32(%rbp) # arg 8 - push 24(%rbp) # arg 7 - -// convert from consts.sh to syscalls.sh encoding - push %rcx - mov __hostos(%rip),%cl - test $LINUX,%cl - jnz 2f -1: test $FREEBSD,%cl - jz 1f - shl $4*7,%rax - jmp 2f -1: test $OPENBSD,%cl - jz 1f - shl $4*10,%rax - jmp 2f -1: test $NETBSD,%cl - jz 1f - shl $4*13,%rax - jmp 2f -1: test $XNU,%cl - jz 2f - mov %eax,%ecx - and $0x0f000000,%ecx - and $0x00000fff,%eax - shl $4*3,%eax - or %ecx,%eax -2: pop %rcx - -// trigger the system call - call *__systemfive(%rip) - -// clean up stack and return - leave - ret +syscall:mov %rdi,%rax + mov %rsi,%rdi + mov %rdx,%rsi + mov %rcx,%rdx + mov %r8,%rcx # ← intended + mov %r9,%r8 + mov 8(%rsp),%r9 + jmp *__systemfive(%rip) .endfn syscall,globl diff --git a/libc/testlib/globals.c b/libc/testlib/globals.c index e844ce117..c6563c30d 100644 --- a/libc/testlib/globals.c +++ b/libc/testlib/globals.c @@ -16,9 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/atomic.h" #include "libc/testlib/testlib.h" char g_fixturename[256]; -atomic_uint g_testlib_ran; -atomic_uint g_testlib_failed; +_Atomic(unsigned) g_testlib_ran; +_Atomic(unsigned) g_testlib_failed; diff --git a/libc/testlib/testmain.c b/libc/testlib/testmain.c index c15144785..af8ed85fa 100644 --- a/libc/testlib/testmain.c +++ b/libc/testlib/testmain.c @@ -159,8 +159,6 @@ static void SetLimit(int resource, uint64_t soft, uint64_t hard) { } } -#pragma weak main - /** * Generic test program main function. */ @@ -184,13 +182,13 @@ noasan int main(int argc, char *argv[]) { testlib_runalltests(); if (!g_testlib_failed && runbenchmarks_ && weaken(testlib_runallbenchmarks)) { weaken(testlib_runallbenchmarks)(); - if (IsAsan() && !g_testlib_failed) { + if (!g_testlib_failed) { CheckForMemoryLeaks(); } if (!g_testlib_failed && IsRunningUnderMake()) { return 254; // compile.com considers this 0 and propagates output } - } else if (IsAsan() && !g_testlib_failed) { + } else if (!g_testlib_failed) { CheckForMemoryLeaks(); } diff --git a/libc/testlib/testrunner.c b/libc/testlib/testrunner.c index ea8627a26..bd67be048 100644 --- a/libc/testlib/testrunner.c +++ b/libc/testlib/testrunner.c @@ -28,7 +28,7 @@ #include "libc/fmt/fmt.h" #include "libc/fmt/itoa.h" #include "libc/intrin/atomic.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" #include "libc/intrin/weaken.h" #include "libc/log/check.h" #include "libc/log/internal.h" diff --git a/libc/thread/README.md b/libc/thread/README.md deleted file mode 100644 index 99c6a61d4..000000000 --- a/libc/thread/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Cosmpolitan POSIX Threads Library - -Cosmopolitan Libc implements threading as it is written in The Open -Group Base Specifications Issue 7, 2018 edition IEEE Std 1003.1-2017 -(Revision of IEEE Std 1003.1-2008) in addition to GNU extensions. diff --git a/libc/thread/internal.h b/libc/thread/internal.h new file mode 100644 index 000000000..331e7899b --- /dev/null +++ b/libc/thread/internal.h @@ -0,0 +1,15 @@ +#ifndef COSMOPOLITAN_LIBC_THREAD_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_THREAD_INTERNAL_H_ +#include "libc/intrin/pthread.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +hidden extern uint64_t _pthread_key_usage[(PTHREAD_KEYS_MAX + 63) / 64]; +hidden extern pthread_key_dtor _pthread_key_dtor[PTHREAD_KEYS_MAX]; +hidden extern _Thread_local void *_pthread_keys[PTHREAD_KEYS_MAX]; + +void _pthread_key_destruct(void *[PTHREAD_KEYS_MAX]); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_THREAD_INTERNAL_H_ */ diff --git a/libc/intrin/pthread_keys.c b/libc/thread/key.c similarity index 94% rename from libc/intrin/pthread_keys.c rename to libc/thread/key.c index 69be8bafd..63d917840 100644 --- a/libc/intrin/pthread_keys.c +++ b/libc/thread/key.c @@ -16,10 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/posixthread.internal.h" -#include "libc/thread/thread.h" - -pthread_spinlock_t _pthread_keys_lock; +#include "libc/thread/internal.h" // tls value slots for pthread keys api _Thread_local void *_pthread_keys[PTHREAD_KEYS_MAX]; diff --git a/libc/thread/mktls.c b/libc/thread/mktls.c index 8eccb9014..911c2a18e 100644 --- a/libc/thread/mktls.c +++ b/libc/thread/mktls.c @@ -16,20 +16,18 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dce.h" -#include "libc/intrin/asan.internal.h" -#include "libc/intrin/asancodes.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" #include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" #include "libc/thread/spawn.h" -#include "libc/thread/tls.h" +#include "libc/thread/thread.h" -#define I(x) ((intptr_t)x) - -void Bzero(void *, size_t) asm("bzero"); // gcc bug +#define _TLSZ ((intptr_t)_tls_size) +#define _TLDZ ((intptr_t)_tdata_size) +#define _TIBZ sizeof(struct cthread_descriptor_t) +#define _MEMZ ROUNDUP(_TLSZ + _TIBZ, _Alignof(struct cthread_descriptor_t)) /** * Allocates thread-local storage memory for new thread. @@ -37,27 +35,19 @@ void Bzero(void *, size_t) asm("bzero"); // gcc bug */ char *_mktls(char **out_tib) { char *tls; - struct CosmoTib *tib; + cthread_t tib; - // allocate memory for tdata, tbss, and tib - tls = memalign(TLS_ALIGNMENT, I(_tls_size) + sizeof(struct CosmoTib)); - if (!tls) return 0; - - // poison memory between tdata and tbss - if (IsAsan()) { - __asan_poison(tls + I(_tdata_size), I(_tbss_offset) - I(_tdata_size), - kAsanProtected); - } - - // initialize tdata and clear tbss - memmove(tls, _tdata_start, I(_tdata_size)); - Bzero(tls + I(_tbss_offset), I(_tbss_size) + sizeof(struct CosmoTib)); + // Allocate enough TLS memory for all the GNU Linuker (_tls_size) + // organized _Thread_local data, as well as Cosmpolitan Libc (64) + if (!(tls = calloc(1, _MEMZ))) return 0; // set up thread information block - tib = (struct CosmoTib *)(tls + I(_tls_size)); - tib->tib_self = tib; - tib->tib_self2 = tib; - tib->tib_tid = -1; + tib = (cthread_t)(tls + _MEMZ - _TIBZ); + tib->self = tib; + tib->self2 = tib; + tib->err = 0; + tib->tid = -1; + memmove(tls, _tdata_start, _TLDZ); if (out_tib) { *out_tib = (char *)tib; diff --git a/libc/thread/nsync_counter.c b/libc/thread/nsync_counter.c deleted file mode 100644 index dddc67ec4..000000000 --- a/libc/thread/nsync_counter.c +++ /dev/null @@ -1,158 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ -│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2016 Google Inc. │ -│ │ -│ Licensed under the Apache License, Version 2.0 (the "License"); │ -│ you may not use this file except in compliance with the License. │ -│ You may obtain a copy of the License at │ -│ │ -│ http://www.apache.org/licenses/LICENSE-2.0 │ -│ │ -│ Unless required by applicable law or agreed to in writing, software │ -│ distributed under the License is distributed on an "AS IS" BASIS, │ -│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │ -│ See the License for the specific language governing permissions and │ -│ limitations under the License. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/mem/mem.h" -#include "libc/str/str.h" -#include "third_party/nsync/atomic.h" -#include "third_party/nsync/atomic.internal.h" -#include "third_party/nsync/common.internal.h" -#include "third_party/nsync/counter.h" -#include "third_party/nsync/dll.h" -#include "third_party/nsync/mu_semaphore.h" -#include "third_party/nsync/races.internal.h" -#include "third_party/nsync/wait_s.internal.h" -#include "third_party/nsync/waiter.h" - -asm(".ident\t\"\\n\\n\ -*NSYNC (Apache 2.0)\\n\ -Copyright 2016 Google, Inc.\\n\ -https://github.com/google/nsync\""); -// clang-format off - -/* Internal details of nsync_counter. */ -struct nsync_counter_s_ { - nsync_atomic_uint32_ waited; /* wait has been called */ - nsync_mu counter_mu; /* protects fields below except reads of "value" */ - nsync_atomic_uint32_ value; /* value of counter */ - struct nsync_dll_element_s_ *waiters; /* list of waiters */ -}; - -nsync_counter nsync_counter_new (uint32_t value) { - nsync_counter c = (nsync_counter) malloc (sizeof (*c)); - if (c != NULL) { - memset ((void *) c, 0, sizeof (*c)); - ATM_STORE (&c->value, value); - } - return (c); -} - -void nsync_counter_free (nsync_counter c) { - nsync_mu_lock (&c->counter_mu); - ASSERT (nsync_dll_is_empty_ (c->waiters)); - nsync_mu_unlock (&c->counter_mu); - free (c); -} - -uint32_t nsync_counter_add (nsync_counter c, int32_t delta) { - uint32_t value; - IGNORE_RACES_START (); - if (delta == 0) { - value = ATM_LOAD_ACQ (&c->value); - } else { - nsync_mu_lock (&c->counter_mu); - do { - value = ATM_LOAD (&c->value); - } while (!ATM_CAS_RELACQ (&c->value, value, value+delta)); - value += delta; - if (delta > 0) { - /* It's illegal to increase the count from zero if - there has been a waiter. */ - ASSERT (value != (uint32_t) delta || !ATM_LOAD (&c->waited)); - ASSERT (value > value - delta); /* Crash on overflow. */ - } else { - ASSERT (value < value - delta); /* Crash on overflow. */ - } - if (value == 0) { - nsync_dll_element_ *p; - while ((p = nsync_dll_first_ (c->waiters)) != NULL) { - struct nsync_waiter_s *nw = DLL_NSYNC_WAITER (p); - c->waiters = nsync_dll_remove_ (c->waiters, p); - ATM_STORE_REL (&nw->waiting, 0); - nsync_mu_semaphore_v (nw->sem); - } - } - nsync_mu_unlock (&c->counter_mu); - } - IGNORE_RACES_END (); - return (value); -} - -uint32_t nsync_counter_value (nsync_counter c) { - uint32_t result; - IGNORE_RACES_START (); - result = ATM_LOAD_ACQ (&c->value); - IGNORE_RACES_END (); - return (result); -} - -uint32_t nsync_counter_wait (nsync_counter c, nsync_time abs_deadline) { - struct nsync_waitable_s waitable; - struct nsync_waitable_s *pwaitable = &waitable; - uint32_t result = 0; - waitable.v = c; - waitable.funcs = &nsync_counter_waitable_funcs; - if (nsync_wait_n (NULL, NULL, NULL, abs_deadline, 1, &pwaitable) != 0) { - IGNORE_RACES_START (); - result = ATM_LOAD_ACQ (&c->value); - IGNORE_RACES_END (); - } - return (result); -} - -static nsync_time counter_ready_time (void *v, struct nsync_waiter_s *nw) { - nsync_counter c = (nsync_counter) v; - nsync_time r; - ATM_STORE (&c->waited, 1); - r = (ATM_LOAD_ACQ (&c->value) == 0? nsync_time_zero : nsync_time_no_deadline); - return (r); -} - -static int counter_enqueue (void *v, struct nsync_waiter_s *nw) { - nsync_counter c = (nsync_counter) v; - int32_t value; - nsync_mu_lock (&c->counter_mu); - value = ATM_LOAD_ACQ (&c->value); - if (value != 0) { - c->waiters = nsync_dll_make_last_in_list_ (c->waiters, &nw->q); - ATM_STORE (&nw->waiting, 1); - } else { - ATM_STORE (&nw->waiting, 0); - } - nsync_mu_unlock (&c->counter_mu); - return (value != 0); -} - -static int counter_dequeue (void *v, struct nsync_waiter_s *nw) { - nsync_counter c = (nsync_counter) v; - int32_t value; - nsync_mu_lock (&c->counter_mu); - value = ATM_LOAD_ACQ (&c->value); - if (ATM_LOAD_ACQ (&nw->waiting) != 0) { - c->waiters = nsync_dll_remove_ (c->waiters, &nw->q); - ATM_STORE (&nw->waiting, 0); - } - nsync_mu_unlock (&c->counter_mu); - return (value != 0); -} - -const struct nsync_waitable_funcs_s nsync_counter_waitable_funcs = { - &counter_ready_time, - &counter_enqueue, - &counter_dequeue -}; - - diff --git a/libc/thread/nsync_cv.c b/libc/thread/nsync_cv.c deleted file mode 100644 index 5b859f939..000000000 --- a/libc/thread/nsync_cv.c +++ /dev/null @@ -1,497 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ -│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2016 Google Inc. │ -│ │ -│ Licensed under the Apache License, Version 2.0 (the "License"); │ -│ you may not use this file except in compliance with the License. │ -│ You may obtain a copy of the License at │ -│ │ -│ http://www.apache.org/licenses/LICENSE-2.0 │ -│ │ -│ Unless required by applicable law or agreed to in writing, software │ -│ distributed under the License is distributed on an "AS IS" BASIS, │ -│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │ -│ See the License for the specific language governing permissions and │ -│ limitations under the License. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/str/str.h" -#include "third_party/nsync/atomic.internal.h" -#include "third_party/nsync/common.internal.h" -#include "third_party/nsync/cv.h" -#include "third_party/nsync/dll.h" -#include "third_party/nsync/races.internal.h" -#include "third_party/nsync/wait_s.internal.h" -#include "third_party/nsync/waiter.h" - -asm(".ident\t\"\\n\\n\ -*NSYNC (Apache 2.0)\\n\ -Copyright 2016 Google, Inc.\\n\ -https://github.com/google/nsync\""); -// clang-format off - -/* Initialize *cv. */ -void nsync_cv_init (nsync_cv *cv) { - memset ((void *) cv, 0, sizeof (*cv)); -} - -/* Wake the cv waiters in the circular list pointed to by - to_wake_list, which may not be NULL. If the waiter is associated with a - nsync_mu, the "wakeup" may consist of transferring the waiters to the nsync_mu's - queue. Requires that every waiter is associated with the same mutex. - all_readers indicates whether all the waiters on the list are readers. */ -static void wake_waiters (nsync_dll_list_ to_wake_list, int all_readers) { - nsync_dll_element_ *p = NULL; - nsync_dll_element_ *next = NULL; - nsync_dll_element_ *first_waiter = nsync_dll_first_ (to_wake_list); - struct nsync_waiter_s *first_nw = DLL_NSYNC_WAITER (first_waiter); - waiter *first_w = NULL; - nsync_mu *pmu = NULL; - if ((first_nw->flags & NSYNC_WAITER_FLAG_MUCV) != 0) { - first_w = DLL_WAITER (first_waiter); - pmu = first_w->cv_mu; - } - if (pmu != NULL) { /* waiter is associated with the nsync_mu *pmu. */ - /* We will transfer elements of to_wake_list to *pmu if all of: - - some thread holds the lock, and - - *pmu's spinlock is not held, and - - either *pmu cannot be acquired in the mode of the first - waiter, or there's more than one thread on to_wake_list - and not all are readers, and - - we acquire the spinlock on the first try. - The spinlock acquisition also marks *pmu as having waiters. - The requirement that some thread holds the lock ensures - that at least one of the transferred waiters will be woken. - */ - uint32_t old_mu_word = ATM_LOAD (&pmu->word); - int first_cant_acquire = ((old_mu_word & first_w->l_type->zero_to_acquire) != 0); - next = nsync_dll_next_ (to_wake_list, first_waiter); - if ((old_mu_word&MU_ANY_LOCK) != 0 && - (old_mu_word&MU_SPINLOCK) == 0 && - (first_cant_acquire || (next != NULL && !all_readers)) && - ATM_CAS_ACQ (&pmu->word, old_mu_word, - (old_mu_word|MU_SPINLOCK|MU_WAITING) & - ~MU_ALL_FALSE)) { - - uint32_t set_on_release = 0; - - /* For any waiter that should be transferred, rather - than woken, move it from to_wake_list to pmu->waiters. */ - int first_is_writer = first_w->l_type == nsync_writer_type_; - int transferred_a_writer = 0; - int woke_areader = 0; - /* Transfer the first waiter iff it can't acquire *pmu. */ - if (first_cant_acquire) { - to_wake_list = nsync_dll_remove_ (to_wake_list, first_waiter); - pmu->waiters = nsync_dll_make_last_in_list_ (pmu->waiters, first_waiter); - /* tell nsync_cv_wait_with_deadline() that we - moved the waiter to *pmu's queue. */ - first_w->cv_mu = NULL; - /* first_nw.waiting is already 1, from being on - cv's waiter queue. */ - transferred_a_writer = first_is_writer; - } else { - woke_areader = !first_is_writer; - } - /* Now process the other waiters. */ - for (p = next; p != NULL; p = next) { - int p_is_writer; - struct nsync_waiter_s *p_nw = DLL_NSYNC_WAITER (p); - waiter *p_w = NULL; - if ((p_nw->flags & NSYNC_WAITER_FLAG_MUCV) != 0) { - p_w = DLL_WAITER (p); - } - next = nsync_dll_next_ (to_wake_list, p); - p_is_writer = (p_w != NULL && - DLL_WAITER (p)->l_type == nsync_writer_type_); - /* We transfer this element if any of: - - the first waiter can't acquire *pmu, or - - the first waiter is a writer, or - - this element is a writer. */ - if (p_w == NULL) { - /* wake non-native waiter */ - } else if (first_cant_acquire || first_is_writer || p_is_writer) { - to_wake_list = nsync_dll_remove_ (to_wake_list, p); - pmu->waiters = nsync_dll_make_last_in_list_ (pmu->waiters, p); - /* tell nsync_cv_wait_with_deadline() - that we moved the waiter to *pmu's - queue. */ - p_w->cv_mu = NULL; - /* p_nw->waiting is already 1, from - being on cv's waiter queue. */ - transferred_a_writer = transferred_a_writer || p_is_writer; - } else { - woke_areader = woke_areader || !p_is_writer; - } - } - - /* Claim a waiting writer if we transferred one, except if we woke readers, - in which case we want those readers to be able to acquire immediately. */ - if (transferred_a_writer && !woke_areader) { - set_on_release |= MU_WRITER_WAITING; - } - - /* release *pmu's spinlock (MU_WAITING was set by CAS above) */ - old_mu_word = ATM_LOAD (&pmu->word); - while (!ATM_CAS_REL (&pmu->word, old_mu_word, - (old_mu_word|set_on_release) & ~MU_SPINLOCK)) { - old_mu_word = ATM_LOAD (&pmu->word); - } - } - } - - /* Wake any waiters we didn't manage to enqueue on the mu. */ - for (p = nsync_dll_first_ (to_wake_list); p != NULL; p = next) { - struct nsync_waiter_s *p_nw = DLL_NSYNC_WAITER (p); - next = nsync_dll_next_ (to_wake_list, p); - to_wake_list = nsync_dll_remove_ (to_wake_list, p); - /* Wake the waiter. */ - ATM_STORE_REL (&p_nw->waiting, 0); /* release store */ - nsync_mu_semaphore_v (p_nw->sem); - } -} - -/* ------------------------------------------ */ - -/* Versions of nsync_mu_lock() and nsync_mu_unlock() that take "void *" - arguments, to avoid call through a function pointer of a different type, - which is undefined. */ -static void void_mu_lock (void *mu) { - nsync_mu_lock ((nsync_mu *) mu); -} -static void void_mu_unlock (void *mu) { - nsync_mu_unlock ((nsync_mu *) mu); -} - -/* Atomically release *pmu (which must be held on entry) - and block the calling thread on *pcv. Then wait until awakened by a - call to nsync_cv_signal() or nsync_cv_broadcast() (or a spurious wakeup), or by the time - reaching abs_deadline, or by cancel_note being notified. In all cases, - reacquire *pmu, and return the reason for the call returned (0, ETIMEDOUT, - or ECANCELED). Callers should abs_deadline==nsync_time_no_deadline for no - deadline, and cancel_note==NULL for no cancellation. nsync_cv_wait_with_deadline() - should be used in a loop, as with all Mesa-style condition variables. See - examples above. - - There are two reasons for using an absolute deadline, rather than a relative - timeout---these are why pthread_cond_timedwait() also uses an absolute - deadline. First, condition variable waits have to be used in a loop; with - an absolute times, the deadline does not have to be recomputed on each - iteration. Second, in most real programmes, some activity (such as an RPC - to a server, or when guaranteeing response time in a UI), there is a - deadline imposed by the specification or the caller/user; relative delays - can shift arbitrarily with scheduling delays, and so after multiple waits - might extend beyond the expected deadline. Relative delays tend to be more - convenient mostly in tests and trivial examples than they are in real - programmes. */ -int nsync_cv_wait_with_deadline_generic (nsync_cv *pcv, void *pmu, - void (*lock) (void *), void (*unlock) (void *), - nsync_time abs_deadline, - nsync_note cancel_note) { - nsync_mu *cv_mu = NULL; - int is_reader_mu; - uint32_t old_word; - uint32_t remove_count; - int sem_outcome; - unsigned attempts; - int outcome = 0; - waiter *w; - IGNORE_RACES_START (); - w = nsync_waiter_new_ (); - ATM_STORE (&w->nw.waiting, 1); - w->cond.f = NULL; /* Not using a conditional critical section. */ - w->cond.v = NULL; - w->cond.eq = NULL; - if (lock == &void_mu_lock || - lock == (void (*) (void *)) &nsync_mu_lock || - lock == (void (*) (void *)) &nsync_mu_rlock) { - cv_mu = (nsync_mu *) pmu; - } - w->cv_mu = cv_mu; /* If *pmu is an nsync_mu, record its address, else record NULL. */ - is_reader_mu = 0; /* If true, an nsync_mu in reader mode. */ - if (cv_mu == NULL) { - w->l_type = NULL; - } else { - uint32_t old_mu_word = ATM_LOAD (&cv_mu->word); - int is_writer = (old_mu_word & MU_WHELD_IF_NON_ZERO) != 0; - int is_reader = (old_mu_word & MU_RHELD_IF_NON_ZERO) != 0; - if (is_writer) { - if (is_reader) { - nsync_panic_ ("mu held in reader and writer mode simultaneously " - "on entry to nsync_cv_wait_with_deadline()\n"); - } - w->l_type = nsync_writer_type_; - } else if (is_reader) { - w->l_type = nsync_reader_type_; - is_reader_mu = 1; - } else { - nsync_panic_ ("mu not held on entry to nsync_cv_wait_with_deadline()\n"); - } - } - - /* acquire spinlock, set non-empty */ - old_word = nsync_spin_test_and_set_ (&pcv->word, CV_SPINLOCK, CV_SPINLOCK|CV_NON_EMPTY, 0); - pcv->waiters = nsync_dll_make_last_in_list_ (pcv->waiters, &w->nw.q); - remove_count = ATM_LOAD (&w->remove_count); - /* Release the spin lock. */ - ATM_STORE_REL (&pcv->word, old_word|CV_NON_EMPTY); /* release store */ - - /* Release *pmu. */ - if (is_reader_mu) { - nsync_mu_runlock (cv_mu); - } else { - (*unlock) (pmu); - } - - /* wait until awoken or a timeout. */ - sem_outcome = 0; - attempts = 0; - while (ATM_LOAD_ACQ (&w->nw.waiting) != 0) { /* acquire load */ - if (sem_outcome == 0) { - sem_outcome = nsync_sem_wait_with_cancel_ (w, abs_deadline, cancel_note); - } - - if (sem_outcome != 0 && ATM_LOAD (&w->nw.waiting) != 0) { - /* A timeout or cancellation occurred, and no wakeup. - Acquire *pcv's spinlock, and confirm. */ - old_word = nsync_spin_test_and_set_ (&pcv->word, CV_SPINLOCK, - CV_SPINLOCK, 0); - /* Check that w wasn't removed from the queue after we - checked above, but before we acquired the spinlock. - The test of remove_count confirms that the waiter *w - is still governed by *pcv's spinlock; otherwise, some - other thread is about to set w.waiting==0. */ - if (ATM_LOAD (&w->nw.waiting) != 0) { - if (remove_count == ATM_LOAD (&w->remove_count)) { - uint32_t old_value; - /* still in cv waiter queue */ - /* Not woken, so remove *w from cv - queue, and declare a - timeout/cancellation. */ - outcome = sem_outcome; - pcv->waiters = nsync_dll_remove_ (pcv->waiters, - &w->nw.q); - do { - old_value = ATM_LOAD (&w->remove_count); - } while (!ATM_CAS (&w->remove_count, old_value, old_value+1)); - if (nsync_dll_is_empty_ (pcv->waiters)) { - old_word &= ~(CV_NON_EMPTY); - } - ATM_STORE_REL (&w->nw.waiting, 0); /* release store */ - } - } - /* Release spinlock. */ - ATM_STORE_REL (&pcv->word, old_word); /* release store */ - } - - if (ATM_LOAD (&w->nw.waiting) != 0) { - /* The delay here causes this thread ultimately to - yield to another that has dequeued this thread, but - has not yet set the waiting field to zero; a - cancellation or timeout may prevent this thread - from blocking above on the semaphore. */ - attempts = nsync_spin_delay_ (attempts); - } - } - - if (cv_mu != NULL && w->cv_mu == NULL) { /* waiter was moved to *pmu's queue, and woken. */ - /* Requeue on *pmu using existing waiter struct; current thread - is the designated waker. */ - nsync_mu_lock_slow_ (cv_mu, w, MU_DESIG_WAKER, w->l_type); - nsync_waiter_free_ (w); - } else { - /* Traditional case: We've woken from the cv, and need to reacquire *pmu. */ - nsync_waiter_free_ (w); - if (is_reader_mu) { - nsync_mu_rlock (cv_mu); - } else { - (*lock) (pmu); - } - } - IGNORE_RACES_END (); - return (outcome); -} - -/* Wake at least one thread if any are currently blocked on *pcv. If - the chosen thread is a reader on an nsync_mu, wake all readers and, if - possible, a writer. */ -void nsync_cv_signal (nsync_cv *pcv) { - IGNORE_RACES_START (); - if ((ATM_LOAD_ACQ (&pcv->word) & CV_NON_EMPTY) != 0) { /* acquire load */ - nsync_dll_list_ to_wake_list = NULL; /* waiters that we will wake */ - int all_readers = 0; - /* acquire spinlock */ - uint32_t old_word = nsync_spin_test_and_set_ (&pcv->word, CV_SPINLOCK, - CV_SPINLOCK, 0); - if (!nsync_dll_is_empty_ (pcv->waiters)) { - /* Point to first waiter that enqueued itself, and - detach it from all others. */ - struct nsync_waiter_s *first_nw; - nsync_dll_element_ *first = nsync_dll_first_ (pcv->waiters); - pcv->waiters = nsync_dll_remove_ (pcv->waiters, first); - first_nw = DLL_NSYNC_WAITER (first); - if ((first_nw->flags & NSYNC_WAITER_FLAG_MUCV) != 0) { - uint32_t old_value; - do { - old_value = - ATM_LOAD (&DLL_WAITER (first)->remove_count); - } while (!ATM_CAS (&DLL_WAITER (first)->remove_count, - old_value, old_value+1)); - } - to_wake_list = nsync_dll_make_last_in_list_ (to_wake_list, first); - if ((first_nw->flags & NSYNC_WAITER_FLAG_MUCV) != 0 && - DLL_WAITER (first)->l_type == nsync_reader_type_) { - int woke_writer; - /* If the first waiter is a reader, wake all readers, and - if it's possible, one writer. This allows reader-regions - to be added to a monitor without invalidating code in which - a client has optimized broadcast calls by converting them to - signal calls. In particular, we wake a writer when waking - readers because the readers will not invalidate the condition - that motivated the client to call nsync_cv_signal(). But we - wake at most one writer because the first writer may invalidate - the condition; the client is expecting only one writer to be - able make use of the wakeup, or he would have called - nsync_cv_broadcast(). */ - nsync_dll_element_ *p = NULL; - nsync_dll_element_ *next = NULL; - all_readers = 1; - woke_writer = 0; - for (p = nsync_dll_first_ (pcv->waiters); p != NULL; p = next) { - struct nsync_waiter_s *p_nw = DLL_NSYNC_WAITER (p); - int should_wake; - next = nsync_dll_next_ (pcv->waiters, p); - should_wake = 0; - if ((p_nw->flags & NSYNC_WAITER_FLAG_MUCV) != 0 && - DLL_WAITER (p)->l_type == nsync_reader_type_) { - should_wake = 1; - } else if (!woke_writer) { - woke_writer = 1; - all_readers = 0; - should_wake = 1; - } - if (should_wake) { - pcv->waiters = nsync_dll_remove_ (pcv->waiters, p); - if ((p_nw->flags & NSYNC_WAITER_FLAG_MUCV) != 0) { - uint32_t old_value; - do { - old_value = ATM_LOAD ( - &DLL_WAITER (p)->remove_count); - } while (!ATM_CAS (&DLL_WAITER (p)->remove_count, - old_value, old_value+1)); - } - to_wake_list = nsync_dll_make_last_in_list_ ( - to_wake_list, p); - } - } - } - if (nsync_dll_is_empty_ (pcv->waiters)) { - old_word &= ~(CV_NON_EMPTY); - } - } - /* Release spinlock. */ - ATM_STORE_REL (&pcv->word, old_word); /* release store */ - if (!nsync_dll_is_empty_ (to_wake_list)) { - wake_waiters (to_wake_list, all_readers); - } - } - IGNORE_RACES_END (); -} - -/* Wake all threads currently blocked on *pcv. */ -void nsync_cv_broadcast (nsync_cv *pcv) { - IGNORE_RACES_START (); - if ((ATM_LOAD_ACQ (&pcv->word) & CV_NON_EMPTY) != 0) { /* acquire load */ - nsync_dll_element_ *p; - nsync_dll_element_ *next; - int all_readers; - nsync_dll_list_ to_wake_list = NULL; /* waiters that we will wake */ - /* acquire spinlock */ - nsync_spin_test_and_set_ (&pcv->word, CV_SPINLOCK, CV_SPINLOCK, 0); - p = NULL; - next = NULL; - all_readers = 1; - /* Wake entire waiter list, which we leave empty. */ - for (p = nsync_dll_first_ (pcv->waiters); p != NULL; p = next) { - struct nsync_waiter_s *p_nw = DLL_NSYNC_WAITER (p); - next = nsync_dll_next_ (pcv->waiters, p); - all_readers = all_readers && (p_nw->flags & NSYNC_WAITER_FLAG_MUCV) != 0 && - (DLL_WAITER (p)->l_type == nsync_reader_type_); - pcv->waiters = nsync_dll_remove_ (pcv->waiters, p); - if ((p_nw->flags & NSYNC_WAITER_FLAG_MUCV) != 0) { - uint32_t old_value; - do { - old_value = ATM_LOAD (&DLL_WAITER (p)->remove_count); - } while (!ATM_CAS (&DLL_WAITER (p)->remove_count, - old_value, old_value+1)); - } - to_wake_list = nsync_dll_make_last_in_list_ (to_wake_list, p); - } - /* Release spinlock and mark queue empty. */ - ATM_STORE_REL (&pcv->word, 0); /* release store */ - if (!nsync_dll_is_empty_ (to_wake_list)) { /* Wake them. */ - wake_waiters (to_wake_list, all_readers); - } - } - IGNORE_RACES_END (); -} - -/* Wait with deadline, using an nsync_mu. */ -int nsync_cv_wait_with_deadline (nsync_cv *pcv, nsync_mu *pmu, - nsync_time abs_deadline, - nsync_note cancel_note) { - return (nsync_cv_wait_with_deadline_generic (pcv, pmu, &void_mu_lock, - &void_mu_unlock, - abs_deadline, cancel_note)); -} - -/* Atomically release *pmu and block the caller on *pcv. Wait - until awakened by a call to nsync_cv_signal() or nsync_cv_broadcast(), or a spurious - wakeup. Then reacquires *pmu, and return. The call is equivalent to a call - to nsync_cv_wait_with_deadline() with abs_deadline==nsync_time_no_deadline, and a NULL - cancel_note. It should be used in a loop, as with all standard Mesa-style - condition variables. See examples above. */ -void nsync_cv_wait (nsync_cv *pcv, nsync_mu *pmu) { - nsync_cv_wait_with_deadline (pcv, pmu, nsync_time_no_deadline, NULL); -} - -static nsync_time cv_ready_time (void *v, struct nsync_waiter_s *nw) { - nsync_time r; - r = (nw == NULL || ATM_LOAD_ACQ (&nw->waiting) != 0? nsync_time_no_deadline : nsync_time_zero); - return (r); -} - -static int cv_enqueue (void *v, struct nsync_waiter_s *nw) { - nsync_cv *pcv = (nsync_cv *) v; - /* acquire spinlock */ - uint32_t old_word = nsync_spin_test_and_set_ (&pcv->word, CV_SPINLOCK, CV_SPINLOCK, 0); - pcv->waiters = nsync_dll_make_last_in_list_ (pcv->waiters, &nw->q); - ATM_STORE (&nw->waiting, 1); - /* Release spinlock. */ - ATM_STORE_REL (&pcv->word, old_word | CV_NON_EMPTY); /* release store */ - return (1); -} - -static int cv_dequeue (void *v, struct nsync_waiter_s *nw) { - nsync_cv *pcv = (nsync_cv *) v; - int was_queued = 0; - /* acquire spinlock */ - uint32_t old_word = nsync_spin_test_and_set_ (&pcv->word, CV_SPINLOCK, CV_SPINLOCK, 0); - if (ATM_LOAD_ACQ (&nw->waiting) != 0) { - pcv->waiters = nsync_dll_remove_ (pcv->waiters, &nw->q); - ATM_STORE (&nw->waiting, 0); - was_queued = 1; - } - if (nsync_dll_is_empty_ (pcv->waiters)) { - old_word &= ~(CV_NON_EMPTY); - } - /* Release spinlock. */ - ATM_STORE_REL (&pcv->word, old_word); /* release store */ - return (was_queued); -} - -const struct nsync_waitable_funcs_s nsync_cv_waitable_funcs = { - &cv_ready_time, - &cv_enqueue, - &cv_dequeue -}; diff --git a/libc/thread/nsync_debug.c b/libc/thread/nsync_debug.c deleted file mode 100644 index abfeb73ba..000000000 --- a/libc/thread/nsync_debug.c +++ /dev/null @@ -1,292 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ -│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2016 Google Inc. │ -│ │ -│ Licensed under the Apache License, Version 2.0 (the "License"); │ -│ you may not use this file except in compliance with the License. │ -│ You may obtain a copy of the License at │ -│ │ -│ http://www.apache.org/licenses/LICENSE-2.0 │ -│ │ -│ Unless required by applicable law or agreed to in writing, software │ -│ distributed under the License is distributed on an "AS IS" BASIS, │ -│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │ -│ See the License for the specific language governing permissions and │ -│ limitations under the License. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "third_party/nsync/atomic.h" -#include "third_party/nsync/common.internal.h" -#include "third_party/nsync/dll.h" -#include "third_party/nsync/mu_semaphore.h" -#include "third_party/nsync/races.internal.h" -#include "third_party/nsync/wait_s.internal.h" - -asm(".ident\t\"\\n\\n\ -*NSYNC (Apache 2.0)\\n\ -Copyright 2016 Google, Inc.\\n\ -https://github.com/google/nsync\""); -// clang-format off - -/* Routines for debugging. */ - -/* An emit_buf represents a buffer into which debug information can - be written. */ -struct emit_buf { - char *start; /* start of buffer */ - int len; /* pength of buffer */ - int pos; /* position of next character to bve written */ - int overflow; /* non-zero iff buffer overflow has occurred */ -}; - -/* Initialize *b to point to start[0, .., len-1], and return b. - of to an internal static buffer if buf==NULL. */ -static struct emit_buf *emit_init (struct emit_buf *b, char *start, int len) { - b->start = start; - b->len = len; - b->pos = 0; - b->overflow = 0; - return (b); -} - - -/* Write character c to buffer *b. */ -static void emit_c (struct emit_buf *b, int c) { - if (b->pos < b->len) { - b->start[b->pos++] = c; - } else if (!b->overflow) { - static const char suffix[] = "..."; - const char *s = &suffix[sizeof (suffix)]; /* past nul */ - char *p = &b->start[b->len]; /* past end */ - while (s > suffix && p > b->start) { - *--p = *--s; - } - b->overflow = 1; - } -} - -/* A printf-like function that writes to an emit_buf. - It understands only the format specifiers %s (const char *), and %i - (uintptr_t in hex), with no modifiers. */ -static void emit_print (struct emit_buf *b, const char *fmt, ...) { - va_list ap; - va_start (ap, fmt); - while (*fmt != 0) { - int c = *fmt++; - if (c != '%') { - emit_c (b, c); - } else { - c = *fmt++; - if (c == 's') { - const char *s = va_arg (ap, const char *); - while (*s != 0) { - emit_c (b, *s++); - } - } else if (c == 'i') { - uintptr_t n = va_arg (ap, uintptr_t); - int i; - for (i = 0; (n >> i) >= 0x10; i += 4) { - } - for (; i >= 0; i -= 4) { - emit_c (b, "0123456789abcdef"[(n >> i) & 0xf]); - } - } else { - ASSERT (0); - } - } - } - va_end (ap); -} - -/* Map a bit in a uint32_t to a human-readable name. */ -struct bit_name { - uint32_t mask; - const char *name; -}; - -/* names for bits in a mu word */ -static const struct bit_name mu_bit[] = { - { MU_WLOCK, "wlock" }, - { MU_SPINLOCK, "spin" }, - { MU_WAITING, "wait" }, - { MU_DESIG_WAKER, "desig" }, - { MU_CONDITION, "cond" }, - { MU_WRITER_WAITING, "writer" }, - { MU_LONG_WAIT, "long" }, - { MU_ALL_FALSE, "false" }, - { 0, "" } /* sentinel */ -}; - -/* names for bits in a cv word */ -static const struct bit_name cv_bit[] = { - { CV_SPINLOCK, "spin" }, - { CV_NON_EMPTY, "wait" }, - { 0, "" } /* sentinel */ -}; - -/* names for bits in a waiter flags word */ -static const struct bit_name waiter_flags_bit[] = { - { WAITER_RESERVED, "rsrvd" }, - { WAITER_IN_USE, "in_use" }, - { 0, "" } /* sentinel */ -}; - -/* Emit the names of bits in word to buffer *b using names[] */ -static void emit_word (struct emit_buf *b, const struct bit_name *name, uint32_t word) { - int i; - for (i = 0; name[i].mask != 0; i++) { - if ((word & name[i].mask) != 0) { - emit_print (b, " %s", name[i].name); - } - } -} - -/* Emit the waiter queue *q to *b. */ -static void emit_waiters (struct emit_buf *b, nsync_dll_list_ list) { - nsync_dll_element_ *p = nsync_dll_first_ (list); - nsync_dll_element_ *next; - if (p != NULL) { - emit_print (b, "\nwaiters =\n"); - } - for (; p != NULL && !b->overflow; p = next) { - struct nsync_waiter_s *nw = DLL_NSYNC_WAITER (p); - waiter *w = DLL_WAITER (p); - next = NULL; - emit_print (b, " %i", (uintptr_t) w); - if (w->tag != WAITER_TAG) { - emit_print (b, "bad WAITER_TAG %i", - (uintptr_t) w->tag); - } else { - next = nsync_dll_next_ (list, p); - if (nw->tag != NSYNC_WAITER_TAG) { - emit_print (b, " bad WAITER_TAG %i", - (uintptr_t) nw->tag); - } else { - emit_print (b, " embedded=%i waiting=%i", - (uintptr_t) (w->flags & NSYNC_WAITER_FLAG_MUCV), - (uintptr_t) ATM_LOAD (&nw->waiting)); - } - emit_word (b, waiter_flags_bit, w->flags); - emit_print (b, " %s removes=%i cond=(%i %i %i)", - w->l_type == nsync_writer_type_? "writer" : - w->l_type == nsync_reader_type_? "reader" : - "??????", - (uintptr_t) ATM_LOAD (&w->remove_count), - (uintptr_t) w->cond.f, - (uintptr_t) w->cond.v, - (uintptr_t) w->cond.eq); - if (w->same_condition.next != &w->same_condition) { - emit_print (b, " same_as %i", - (uintptr_t) DLL_WAITER_SAMECOND ( - w->same_condition.next)); - } - } - emit_c (b, '\n'); - } -} - -/* Emit to *b the state of *mu, and return a pointer to *b's buffer. - - If blocking!=0, print_waiters!=0, and *mu's waiter list is non-empty, the - call will block until it can acquire the spinlock. - If print_waiters!=0, the waiter list is printed. - The spinlock is released before return if it was acquired. - blocking==0 && print_waiters!=0 is unsafe and is intended for use within - interactive debuggers. */ -static char *emit_mu_state (struct emit_buf *b, nsync_mu *mu, - int blocking, int print_waiters) { - uintptr_t word; - uintptr_t readers; - int acquired = 0; - IGNORE_RACES_START (); - word = ATM_LOAD (&mu->word); - if ((word & MU_WAITING) != 0 && print_waiters && /* can benefit from lock */ - (blocking || (word & MU_SPINLOCK) == 0)) { /* willing, or no need to wait */ - word = nsync_spin_test_and_set_ (&mu->word, MU_SPINLOCK, MU_SPINLOCK, 0); - acquired = 1; - } - readers = word / MU_RLOCK; - emit_print (b, "mu 0x%i -> 0x%i = {", (uintptr_t) mu, word); - emit_word (b, mu_bit, word); - if (readers != 0) { - emit_print (b, " readers=0x%i", readers); - } - emit_print (b, " }"); - if (print_waiters) { - emit_waiters (b, mu->waiters); - } - if (acquired) { - ATM_STORE_REL (&mu->word, word); /* release store */ - } - emit_c (b, 0); - IGNORE_RACES_END (); - return (b->start); -} - -/* Emit to *b the state of *cv, and return a pointer to *b's buffer. - - If blocking!=0, print_waiters!=0, and *cv's waiter list is non-empty, the - call will block until it can acquire the spinlock. - If print_waiters!=0, the waiter list is printed. - The spinlock is released before return if it was acquired. - blocking==0 && print_waiters!=0 is unsafe and is intended for use within - interactive debuggers. */ -static char *emit_cv_state (struct emit_buf *b, nsync_cv *cv, - int blocking, int print_waiters) { - uintptr_t word; - int acquired = 0; - IGNORE_RACES_START (); - word = ATM_LOAD (&cv->word); - if ((word & CV_NON_EMPTY) != 0 && print_waiters && /* can benefit from lock */ - (blocking || (word & CV_SPINLOCK) == 0)) { /* willing, or no need to wait */ - word = nsync_spin_test_and_set_ (&cv->word, CV_SPINLOCK, CV_SPINLOCK, 0); - acquired = 1; - } - emit_print (b, "cv 0x%i -> 0x%i = {", (uintptr_t) cv, word); - emit_word (b, cv_bit, word); - emit_print (b, " }"); - if (print_waiters) { - emit_waiters (b, cv->waiters); - } - if (acquired) { - ATM_STORE_REL (&cv->word, word); /* release store */ - } - emit_c (b, 0); - IGNORE_RACES_END (); - return (b->start); -} - -char *nsync_mu_debug_state (nsync_mu *mu, char *buf, int n) { - struct emit_buf b; - return (emit_mu_state (emit_init (&b, buf, n), mu, 0, 0)); -} - -char *nsync_cv_debug_state (nsync_cv *cv, char *buf, int n) { - struct emit_buf b; - return (emit_cv_state (emit_init (&b, buf, n), cv, 0, 0)); -} - -char *nsync_mu_debug_state_and_waiters (nsync_mu *mu, char *buf, int n) { - struct emit_buf b; - return (emit_mu_state (emit_init (&b, buf, n), mu, 1, 1)); -} - -char *nsync_cv_debug_state_and_waiters (nsync_cv *cv, char *buf, int n) { - struct emit_buf b; - return (emit_cv_state (emit_init (&b, buf, n), cv, 1, 1)); -} - -static char nsync_debug_buf[1024]; - -char *nsync_mu_debugger (nsync_mu *mu) { - struct emit_buf b; - return (emit_mu_state (emit_init (&b, nsync_debug_buf, - (int) sizeof (nsync_debug_buf)), - mu, 0, 1)); -} -char *nsync_cv_debugger (nsync_cv *cv) { - struct emit_buf b; - return (emit_cv_state (emit_init (&b, nsync_debug_buf, - (int) sizeof (nsync_debug_buf)), - cv, 0, 1)); -} diff --git a/libc/thread/nsync_mu_wait.c b/libc/thread/nsync_mu_wait.c deleted file mode 100644 index 9b409e239..000000000 --- a/libc/thread/nsync_mu_wait.c +++ /dev/null @@ -1,322 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ -│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2016 Google Inc. │ -│ │ -│ Licensed under the Apache License, Version 2.0 (the "License"); │ -│ you may not use this file except in compliance with the License. │ -│ You may obtain a copy of the License at │ -│ │ -│ http://www.apache.org/licenses/LICENSE-2.0 │ -│ │ -│ Unless required by applicable law or agreed to in writing, software │ -│ distributed under the License is distributed on an "AS IS" BASIS, │ -│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │ -│ See the License for the specific language governing permissions and │ -│ limitations under the License. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "third_party/nsync/atomic.h" -#include "third_party/nsync/common.internal.h" -#include "third_party/nsync/dll.h" -#include "third_party/nsync/mu_semaphore.h" -#include "third_party/nsync/races.internal.h" -#include "third_party/nsync/wait_s.internal.h" - -asm(".ident\t\"\\n\\n\ -*NSYNC (Apache 2.0)\\n\ -Copyright 2016 Google, Inc.\\n\ -https://github.com/google/nsync\""); -// clang-format off - -/* Attempt to remove waiter *w from *mu's - waiter queue. If successful, leave the lock held in mode *l_type, and - return non-zero; otherwise return zero. Requires that the current thread - hold neither *mu nor its spinlock, that remove_count be the value of - w.remove_count when *w was inserted into the queue (which it will still be if - it has not been removed). - - This is a tricky part of the design. Here is the rationale. - - When a condition times out or is cancelled, we must "turn off" the - condition, making it always true, so the lock will be acquired in the normal - way. The naive approach would be to set a field atomically to tell future - waiters to ignore the condition. Unfortunately, that would violate the - same_condition list invariants, and the same_condition optimization is - probably worth keeping. - - To fixup the same_condition list, we must have mutual exclusion with the loop - in nsync_mu_unlock_slow_() that is examining waiters, evaluating their conditions, and - removing them from the queue. That loop uses both the spinlock (to allow - queue changes), and the mutex itself (to allow condition evaluation). - Therefore, to "turn off" the condition requires acquiring both the spinlock - and the mutex. This has two consequences: - - If we must acquire *mu to "turn off" the condition, we might as well give - the lock to this waiter and return from nsync_cv_wait_with_deadline() after we've - done so. It would be wasted work to put it back on the waiter queue, and - have it wake up and acquire yet again. (There are possibilities for - starvation here that we ignore, under the assumption that the client - avoids timeouts that are extremely short relative to the durations of his - section durations.) - - We can't use *w to wait for the lock to be free, because *w is already on - the waiter queue with the wrong condition; we now want to wait with no - condition. So either we must spin to acquire the lock, or we must - allocate _another_ waiter object. The latter option is feasible, but - delicate: the thread would have two waiter objects, and would have to - handle being woken by either one or both, and possibly removing one that - was not awoken. For the moment, we spin, because it's easier, and seems - not to cause problems in practice, since the spinloop backs off - aggressively. */ -static int mu_try_acquire_after_timeout_or_cancel (nsync_mu *mu, lock_type *l_type, - waiter *w, uint32_t remove_count) { - int success = 0; - unsigned spin_attempts = 0; - uint32_t old_word = ATM_LOAD (&mu->word); - /* Spin until we can acquire the spinlock and a writer lock on *mu. */ - while ((old_word&(MU_WZERO_TO_ACQUIRE|MU_SPINLOCK)) != 0 || - !ATM_CAS_ACQ (&mu->word, old_word, - (old_word+MU_WADD_TO_ACQUIRE+MU_SPINLOCK) & - ~MU_WCLEAR_ON_ACQUIRE)) { - /* Failed to acquire. If we can, set the MU_WRITER_WAITING bit - to avoid being starved by readers. */ - if ((old_word & (MU_WRITER_WAITING | MU_SPINLOCK)) == 0) { - /* If the following CAS succeeds, it effectively - acquires and releases the spinlock atomically, so - must be both an acquire and release barrier. - MU_WRITER_WAITING will be cleared via - MU_WCLEAR_ON_ACQUIRE when this loop succeeds. - An optimization; failures are ignored. */ - ATM_CAS_RELACQ (&mu->word, old_word, - old_word|MU_WRITER_WAITING); - } - spin_attempts = nsync_spin_delay_ (spin_attempts); - old_word = ATM_LOAD (&mu->word); - } - /* Check that w wasn't removed from the queue after our caller checked, - but before we acquired the spinlock. - The check of remove_count confirms that the waiter *w is still - governed by *mu's spinlock. Otherwise, some other thread may be - about to set w.waiting==0. */ - if (ATM_LOAD (&w->nw.waiting) != 0 && remove_count == ATM_LOAD (&w->remove_count)) { - /* This thread's condition is now irrelevant, and it - holds a writer lock. Remove it from the queue, - and possibly convert back to a reader lock. */ - mu->waiters = nsync_remove_from_mu_queue_ (mu->waiters, &w->nw.q); - ATM_STORE (&w->nw.waiting, 0); - - /* Release spinlock but keep desired lock type. */ - ATM_STORE_REL (&mu->word, old_word+l_type->add_to_acquire); /* release store */ - success = 1; - } else { - /* Release spinlock and *mu. */ - ATM_STORE_REL (&mu->word, old_word); /* release store */ - } - return (success); -} - -/* Return when at least one of: the condition is true, the - deadline expires, or cancel_note is notified. It may unlock and relock *mu - while blocked waiting for one of these events, but always returns with *mu - held. It returns 0 iff the condition is true on return, and otherwise - either ETIMEDOUT or ECANCELED, depending on why the call returned early. Use - abs_deadline==nsync_time_no_deadline for no deadline, and cancel_note==NULL for no - cancellation. - - Requires that *mu be held on entry. - Requires that condition.eval() neither modify state protected by *mu, nor - return a value dependent on state not protected by *mu. To depend on time, - use the abs_deadline parameter. - (Conventional use of condition variables have the same restrictions on the - conditions tested by the while-loop.) - The implementation calls condition.eval() only with *mu held, though not - always from the calling thread, and may elect to hold only a read lock - during the call, even if the client is attempting to acquire only write - locks. - - The nsync_mu_wait() and nsync_mu_wait_with_deadline() calls can be used instead of condition - variables. In many straightforward situations they are of equivalent - performance and are somewhat easier to use, because unlike condition - variables, they do not require that the waits be placed in a loop, and they - do not require explicit wakeup calls. In the current implementation, use of - nsync_mu_wait() and nsync_mu_wait_with_deadline() can take longer if many distinct - wait conditions are used. In such cases, use an explicit condition variable - per wakeup condition for best performance. */ -int nsync_mu_wait_with_deadline (nsync_mu *mu, - int (*condition) (const void *condition_arg), - const void *condition_arg, - int (*condition_arg_eq) (const void *a, const void *b), - nsync_time abs_deadline, nsync_note cancel_note) { - lock_type *l_type; - int first_wait; - int condition_is_true; - waiter *w; - int outcome; - /* Work out in which mode the lock is held. */ - uint32_t old_word; - IGNORE_RACES_START (); - old_word = ATM_LOAD (&mu->word); - if ((old_word & MU_ANY_LOCK) == 0) { - nsync_panic_ ("nsync_mu not held in some mode when calling " - "nsync_mu_wait_with_deadline()\n"); - } - l_type = nsync_writer_type_; - if ((old_word & MU_RHELD_IF_NON_ZERO) != 0) { - l_type = nsync_reader_type_; - } - - first_wait = 1; /* first time through the loop below. */ - condition_is_true = (condition == NULL || (*condition) (condition_arg)); - - /* Loop until either the condition becomes true, or "outcome" indicates - cancellation or timeout. */ - w = NULL; - outcome = 0; - while (outcome == 0 && !condition_is_true) { - uint32_t has_condition; - uint32_t remove_count; - uint32_t add_to_acquire; - int had_waiters; - int sem_outcome; - unsigned attempts; - int have_lock; - if (w == NULL) { - w = nsync_waiter_new_ (); /* get a waiter struct if we need one. */ - } - - /* Prepare to wait. */ - w->cv_mu = NULL; /* not a condition variable wait */ - w->l_type = l_type; - w->cond.f = condition; - w->cond.v = condition_arg; - w->cond.eq = condition_arg_eq; - has_condition = 0; /* set to MU_CONDITION if condition is non-NULL */ - if (condition != NULL) { - has_condition = MU_CONDITION; - } - ATM_STORE (&w->nw.waiting, 1); - remove_count = ATM_LOAD (&w->remove_count); - - /* Acquire spinlock. */ - old_word = nsync_spin_test_and_set_ (&mu->word, MU_SPINLOCK, - MU_SPINLOCK|MU_WAITING|has_condition, MU_ALL_FALSE); - had_waiters = ((old_word & (MU_DESIG_WAKER | MU_WAITING)) == MU_WAITING); - /* Queue the waiter. */ - if (first_wait) { - nsync_maybe_merge_conditions_ (nsync_dll_last_ (mu->waiters), - &w->nw.q); - /* first wait goes to end of queue */ - mu->waiters = nsync_dll_make_last_in_list_ (mu->waiters, - &w->nw.q); - first_wait = 0; - } else { - nsync_maybe_merge_conditions_ (&w->nw.q, - nsync_dll_first_ (mu->waiters)); - /* subsequent waits go to front of queue */ - mu->waiters = nsync_dll_make_first_in_list_ (mu->waiters, - &w->nw.q); - } - /* Release spinlock and *mu. */ - do { - old_word = ATM_LOAD (&mu->word); - add_to_acquire = l_type->add_to_acquire; - if (((old_word-l_type->add_to_acquire)&MU_ANY_LOCK) == 0 && had_waiters) { - add_to_acquire = 0; /* release happens in nsync_mu_unlock_slow_ */ - } - } while (!ATM_CAS_REL (&mu->word, old_word, - (old_word - add_to_acquire) & ~MU_SPINLOCK)); - if (add_to_acquire == 0) { - /* The lock will be fully released, there are waiters, and - no designated waker, so wake waiters. */ - nsync_mu_unlock_slow_ (mu, l_type); - } - - /* wait until awoken or a timeout. */ - sem_outcome = 0; - attempts = 0; - have_lock = 0; - while (ATM_LOAD_ACQ (&w->nw.waiting) != 0) { /* acquire load */ - if (sem_outcome == 0) { - sem_outcome = nsync_sem_wait_with_cancel_ (w, abs_deadline, - cancel_note); - if (sem_outcome != 0 && ATM_LOAD (&w->nw.waiting) != 0) { - /* A timeout or cancellation occurred, and no wakeup. - Acquire the spinlock and mu, and confirm. */ - have_lock = mu_try_acquire_after_timeout_or_cancel ( - mu, l_type, w, remove_count); - if (have_lock) { /* Successful acquire. */ - outcome = sem_outcome; - } - } - } - - if (ATM_LOAD (&w->nw.waiting) != 0) { - attempts = nsync_spin_delay_ (attempts); /* will ultimately yield */ - } - } - - if (!have_lock) { - /* If we didn't reacquire due to a cancellation/timeout, acquire now. */ - nsync_mu_lock_slow_ (mu, w, MU_DESIG_WAKER, l_type); - } - condition_is_true = (condition == NULL || (*condition) (condition_arg)); - } - if (w != NULL) { - nsync_waiter_free_ (w); /* free waiter if we allocated one. */ - } - if (condition_is_true) { - outcome = 0; /* condition is true trumps other outcomes. */ - } - IGNORE_RACES_END (); - return (outcome); -} - -/* Return when the condition is true. Perhaps unlock and relock *mu - while blocked waiting for the condition to become true. It is equivalent to - a call to nsync_mu_wait_with_deadline() with abs_deadline==nsync_time_no_deadline, and - cancel_note==NULL. - - Requires that *mu be held on entry. - Calls condition.eval() only with *mu held, though not always from the - calling thread. - See wait_with_deadline() for the restrictions on condition and performance - considerations. */ -void nsync_mu_wait (nsync_mu *mu, int (*condition) (const void *condition_arg), - const void *condition_arg, - int (*condition_arg_eq) (const void *a, const void *b)) { - if (nsync_mu_wait_with_deadline (mu, condition, condition_arg, condition_arg_eq, - nsync_time_no_deadline, NULL) != 0) { - nsync_panic_ ("nsync_mu_wait woke but condition not true\n"); - } -} - -/* Unlock *mu, which must be held in write mode, and wake waiters, if - appropriate. Unlike nsync_mu_unlock(), this call is not required to wake - nsync_mu_wait/nsync_mu_wait_with_deadline calls on conditions that were - false before this thread acquired the lock. This call should be used only - at the end of critical sections for which: - - nsync_mu_wait/nsync_mu_wait_with_deadline are in use on the same mutex, - - this critical section cannot make the condition true for any of those - nsync_mu_wait/nsync_mu_wait_with_deadline waits, and - - when performance is significantly improved by doing so. */ -void nsync_mu_unlock_without_wakeup (nsync_mu *mu) { - IGNORE_RACES_START (); - /* See comment in nsync_mu_unlock(). */ - if (!ATM_CAS_REL (&mu->word, MU_WLOCK, 0)) { - uint32_t old_word = ATM_LOAD (&mu->word); - uint32_t new_word = old_word - MU_WLOCK; - if ((new_word & (MU_RLOCK_FIELD | MU_WLOCK)) != 0) { - if ((old_word & MU_RLOCK_FIELD) != 0) { - nsync_panic_ ("attempt to nsync_mu_unlock() an nsync_mu " - "held in read mode\n"); - } else { - nsync_panic_ ("attempt to nsync_mu_unlock() an nsync_mu " - "not held in write mode\n"); - } - } else if ((old_word & (MU_WAITING | MU_DESIG_WAKER | MU_ALL_FALSE)) == - MU_WAITING || !ATM_CAS_REL (&mu->word, old_word, new_word)) { - nsync_mu_unlock_slow_ (mu, nsync_writer_type_); - } - } - IGNORE_RACES_END (); -} - - diff --git a/libc/thread/nsync_note.c b/libc/thread/nsync_note.c deleted file mode 100644 index f13b0a582..000000000 --- a/libc/thread/nsync_note.c +++ /dev/null @@ -1,307 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ -│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2016 Google Inc. │ -│ │ -│ Licensed under the Apache License, Version 2.0 (the "License"); │ -│ you may not use this file except in compliance with the License. │ -│ You may obtain a copy of the License at │ -│ │ -│ http://www.apache.org/licenses/LICENSE-2.0 │ -│ │ -│ Unless required by applicable law or agreed to in writing, software │ -│ distributed under the License is distributed on an "AS IS" BASIS, │ -│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │ -│ See the License for the specific language governing permissions and │ -│ limitations under the License. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/mem/mem.h" -#include "libc/str/str.h" -#include "third_party/nsync/atomic.h" -#include "third_party/nsync/common.internal.h" -#include "third_party/nsync/dll.h" -#include "third_party/nsync/mu_semaphore.h" -#include "third_party/nsync/mu_wait.h" -#include "third_party/nsync/races.internal.h" -#include "third_party/nsync/wait_s.internal.h" -#include "third_party/nsync/waiter.h" - -asm(".ident\t\"\\n\\n\ -*NSYNC (Apache 2.0)\\n\ -Copyright 2016 Google, Inc.\\n\ -https://github.com/google/nsync\""); -// clang-format off - -/* Locking discipline for the nsync_note implementation: - - Each nsync_note has a lock "note_mu" which protects the "parent" pointer, - "waiters" list, and "disconnecting" count. It also protects the "children" - list; thus each node's "parent_child_link", which links together the - children of a single parent, is protected by the parent's "note_mu". - - To connect a parent to a child, or to disconnect one, the parent's lock must - be held to manipulate its child list, and the child's lock must be held to - change the parent pointer, so both must be held simultaneously. - The locking order is "parent before child". - - Operations like notify and free are given a node pointer n and must - disconnect *n from its parent n->parent. The call must hold n->note_mu to - read n->parent, but need to release n->note_mu to acquire - n->parent->note_mu. The parent could be disconnected and freed while - n->note_mu is not held. The n->disconnecting count handles this; the - operation acquires n->note_mu, increments n->disconnecting, and can then - release n->note_mu, and acquire n->parent->note_mu and n->note_mu is the - correct order. n->disconnecting!=0 indicates that a thread is already in - the processes of disconnecting n from n->parent. A thread freeing or - notifying the parent should not perform the disconnection of that child, but - should instead wait for the "children" list to become empty via - WAIT_FOR_NO_CHILDREN(). WAKEUP_NO_CHILDREN() should be used whenever this - condition could become true. */ - -/* Set the expiry time in *n to t */ -static void set_expiry_time (nsync_note n, nsync_time t) { - n->expiry_time = t; - n->expiry_time_valid = 1; -} - -/* Return a pointer to the note containing nsync_dll_element_ *e. */ -#define DLL_NOTE(e) ((nsync_note)((e)->container)) - -/* Return whether n->children is empty. Assumes n->note_mu held. */ -static int no_children (const void *v) { - return (nsync_dll_is_empty_ (((nsync_note)v)->children)); -} - -#define WAIT_FOR_NO_CHILDREN(pred_, n_) nsync_mu_wait (&(n_)->note_mu, &pred_, (n_), NULL) -#define WAKEUP_NO_CHILDREN(n_) do { } while (0) - -/* -// These lines can be used in place of those above if conditional critical -// sections have been removed from the source. -#define WAIT_FOR_NO_CHILDREN(pred_, n_) do { \ - while (!pred_ (n_)) { nsync_cv_wait (&(n_)->no_children_cv, &(n_)->note_mu); } \ - } while (0) -#define WAKEUP_NO_CHILDREN(n_) nsync_cv_broadcast (&(n_)->no_children_cv) -*/ - -/* Notify *n and all its descendants that are not already disconnnecting. - n->note_mu is held. May release and reacquire n->note_mu. - parent->note_mu is held if parent != NULL. */ -static void note_notify_child (nsync_note n, nsync_note parent) { - nsync_time t; - t = NOTIFIED_TIME (n); - if (nsync_time_cmp (t, nsync_time_zero) > 0) { - nsync_dll_element_ *p; - nsync_dll_element_ *next; - ATM_STORE_REL (&n->notified, 1); - while ((p = nsync_dll_first_ (n->waiters)) != NULL) { - struct nsync_waiter_s *nw = DLL_NSYNC_WAITER (p); - n->waiters = nsync_dll_remove_ (n->waiters, p); - ATM_STORE_REL (&nw->waiting, 0); - nsync_mu_semaphore_v (nw->sem); - } - for (p = nsync_dll_first_ (n->children); p != NULL; p = next) { - nsync_note child = DLL_NOTE (p); - next = nsync_dll_next_ (n->children, p); - nsync_mu_lock (&child->note_mu); - if (child->disconnecting == 0) { - note_notify_child (child, n); - } - nsync_mu_unlock (&child->note_mu); - } - WAIT_FOR_NO_CHILDREN (no_children, n); - if (parent != NULL) { - parent->children = nsync_dll_remove_ (parent->children, - &n->parent_child_link); - WAKEUP_NO_CHILDREN (parent); - n->parent = NULL; - } - } -} - -/* Notify *n and all its descendants that are not already disconnnecting. - No locks are held. */ -static void notify (nsync_note n) { - nsync_time t; - nsync_mu_lock (&n->note_mu); - t = NOTIFIED_TIME (n); - if (nsync_time_cmp (t, nsync_time_zero) > 0) { - nsync_note parent; - n->disconnecting++; - parent = n->parent; - if (parent != NULL && !nsync_mu_trylock (&parent->note_mu)) { - nsync_mu_unlock (&n->note_mu); - nsync_mu_lock (&parent->note_mu); - nsync_mu_lock (&n->note_mu); - } - note_notify_child (n, parent); - if (parent != NULL) { - nsync_mu_unlock (&parent->note_mu); - } - n->disconnecting--; - } - nsync_mu_unlock (&n->note_mu); -} - -/* Return the deadline by which *n is certain to be notified, - setting it to zero if it already has passed that time. - Requires n->note_mu not held on entry. - - Not static; used in sem_wait.c */ -nsync_time nsync_note_notified_deadline_ (nsync_note n) { - nsync_time ntime; - if (ATM_LOAD_ACQ (&n->notified) != 0) { - ntime = nsync_time_zero; - } else { - nsync_mu_lock (&n->note_mu); - ntime = NOTIFIED_TIME (n); - nsync_mu_unlock (&n->note_mu); - if (nsync_time_cmp (ntime, nsync_time_zero) > 0) { - if (nsync_time_cmp (ntime, nsync_time_now ()) <= 0) { - notify (n); - ntime = nsync_time_zero; - } - } - } - return (ntime); -} - -int nsync_note_is_notified (nsync_note n) { - int result; - IGNORE_RACES_START (); - result = (nsync_time_cmp (nsync_note_notified_deadline_ (n), nsync_time_zero) <= 0); - IGNORE_RACES_END (); - return (result); -} - -nsync_note nsync_note_new (nsync_note parent, - nsync_time abs_deadline) { - nsync_note n = (nsync_note) malloc (sizeof (*n)); - if (n != NULL) { - memset ((void *) n, 0, sizeof (*n)); - nsync_dll_init_ (&n->parent_child_link, n); - set_expiry_time (n, abs_deadline); - if (!nsync_note_is_notified (n) && parent != NULL) { - nsync_time parent_time; - nsync_mu_lock (&parent->note_mu); - parent_time = NOTIFIED_TIME (parent); - if (nsync_time_cmp (parent_time, abs_deadline) < 0) { - set_expiry_time (n, parent_time); - } - if (nsync_time_cmp (parent_time, nsync_time_zero) > 0) { - n->parent = parent; - parent->children = nsync_dll_make_last_in_list_ (parent->children, - &n->parent_child_link); - } - nsync_mu_unlock (&parent->note_mu); - } - } - return (n); -} - -void nsync_note_free (nsync_note n) { - nsync_note parent; - nsync_dll_element_ *p; - nsync_dll_element_ *next; - nsync_mu_lock (&n->note_mu); - n->disconnecting++; - ASSERT (nsync_dll_is_empty_ (n->waiters)); - parent = n->parent; - if (parent != NULL && !nsync_mu_trylock (&parent->note_mu)) { - nsync_mu_unlock (&n->note_mu); - nsync_mu_lock (&parent->note_mu); - nsync_mu_lock (&n->note_mu); - } - for (p = nsync_dll_first_ (n->children); p != NULL; p = next) { - nsync_note child = DLL_NOTE (p); - next = nsync_dll_next_ (n->children, p); - nsync_mu_lock (&child->note_mu); - if (child->disconnecting == 0) { - n->children = nsync_dll_remove_ (n->children, - &child->parent_child_link); - if (parent != NULL) { - child->parent = parent; - parent->children = nsync_dll_make_last_in_list_ ( - parent->children, &child->parent_child_link); - } else { - child->parent = NULL; - } - } - nsync_mu_unlock (&child->note_mu); - } - WAIT_FOR_NO_CHILDREN (no_children, n); - if (parent != NULL) { - parent->children = nsync_dll_remove_ (parent->children, - &n->parent_child_link); - WAKEUP_NO_CHILDREN (parent); - n->parent = NULL; - nsync_mu_unlock (&parent->note_mu); - } - n->disconnecting--; - nsync_mu_unlock (&n->note_mu); - free (n); -} - -void nsync_note_notify (nsync_note n) { - IGNORE_RACES_START (); - if (nsync_time_cmp (nsync_note_notified_deadline_ (n), nsync_time_zero) > 0) { - notify (n); - } - IGNORE_RACES_END (); -} - -int nsync_note_wait (nsync_note n, nsync_time abs_deadline) { - struct nsync_waitable_s waitable; - struct nsync_waitable_s *pwaitable = &waitable; - waitable.v = n; - waitable.funcs = &nsync_note_waitable_funcs; - return (nsync_wait_n (NULL, NULL, NULL, abs_deadline, 1, &pwaitable) == 0); -} - -nsync_time nsync_note_expiry (nsync_note n) { - return (n->expiry_time); -} - -static nsync_time note_ready_time (void *v, struct nsync_waiter_s *nw) { - return (nsync_note_notified_deadline_ ((nsync_note)v)); -} - -static int note_enqueue (void *v, struct nsync_waiter_s *nw) { - int waiting = 0; - nsync_note n = (nsync_note) v; - nsync_time ntime; - nsync_mu_lock (&n->note_mu); - ntime = NOTIFIED_TIME (n); - if (nsync_time_cmp (ntime, nsync_time_zero) > 0) { - n->waiters = nsync_dll_make_last_in_list_ (n->waiters, &nw->q); - ATM_STORE (&nw->waiting, 1); - waiting = 1; - } else { - ATM_STORE (&nw->waiting, 0); - waiting = 0; - } - nsync_mu_unlock (&n->note_mu); - return (waiting); -} - -static int note_dequeue (void *v, struct nsync_waiter_s *nw) { - int was_queued = 0; - nsync_note n = (nsync_note) v; - nsync_time ntime; - nsync_note_notified_deadline_ (n); - nsync_mu_lock (&n->note_mu); - ntime = NOTIFIED_TIME (n); - if (nsync_time_cmp (ntime, nsync_time_zero) > 0) { - n->waiters = nsync_dll_remove_ (n->waiters, &nw->q); - ATM_STORE (&nw->waiting, 0); - was_queued = 1; - } - nsync_mu_unlock (&n->note_mu); - return (was_queued); -} - -const struct nsync_waitable_funcs_s nsync_note_waitable_funcs = { - ¬e_ready_time, - ¬e_enqueue, - ¬e_dequeue -}; diff --git a/libc/thread/nsync_once.c b/libc/thread/nsync_once.c deleted file mode 100644 index 381f443c9..000000000 --- a/libc/thread/nsync_once.c +++ /dev/null @@ -1,150 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ -│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2016 Google Inc. │ -│ │ -│ Licensed under the Apache License, Version 2.0 (the "License"); │ -│ you may not use this file except in compliance with the License. │ -│ You may obtain a copy of the License at │ -│ │ -│ http://www.apache.org/licenses/LICENSE-2.0 │ -│ │ -│ Unless required by applicable law or agreed to in writing, software │ -│ distributed under the License is distributed on an "AS IS" BASIS, │ -│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │ -│ See the License for the specific language governing permissions and │ -│ limitations under the License. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "third_party/nsync/atomic.h" -#include "third_party/nsync/atomic.internal.h" -#include "third_party/nsync/common.internal.h" -#include "third_party/nsync/dll.h" -#include "third_party/nsync/mu_semaphore.h" -#include "third_party/nsync/once.h" -#include "third_party/nsync/races.internal.h" -#include "third_party/nsync/wait_s.internal.h" - -asm(".ident\t\"\\n\\n\ -*NSYNC (Apache 2.0)\\n\ -Copyright 2016 Google, Inc.\\n\ -https://github.com/google/nsync\""); -// clang-format off - -/* An once_sync_s struct contains a lock, and a condition variable on which - threads may wait for an nsync_once to be initialized by another thread. - - A separate struct is used only to keep nsync_once small. - - A given nsync_once can be associated with any once_sync_s struct, but cannot - be associated with more than one. nsync_once instances are mapped to - once_sync_s instances by a trivial hashing scheme implemented by - NSYNC_ONCE_SYNC_(). - - The number of once_sync_s structs in the following array is greater than one - only to reduce the probability of contention if a great many distinct - nsync_once variables are initialized concurrently. */ -static struct once_sync_s { - nsync_mu once_mu; - nsync_cv once_cv; -} once_sync[64]; - -/* Return a pointer to the once_sync_s struct associated with the nsync_once *p. */ -#define NSYNC_ONCE_SYNC_(p) &once_sync[(((uintptr_t) (p)) / sizeof (*(p))) % \ - (sizeof (once_sync) / sizeof (once_sync[0]))] - -/* Implement nsync_run_once, nsync_run_once_arg, nsync_run_once_spin, or - nsync_run_once_arg_spin, chosen as described below. - - If s!=NULL, s is required to point to the once_sync_s associated with *once, - and the semantics of nsync_run_once or nsync_run_once_arg are provided. - If s==NULL, the semantics of nsync_run_once_spin, or nsync_run_once_arg_spin - are provided. - - If f!=NULL, the semantics of nsync_run_once or nsync_run_once_spin are - provided. Otherwise, farg is required to be non-NULL, and the semantics of - nsync_run_once_arg or nsync_run_once_arg_spin are provided. */ -static void nsync_run_once_impl (nsync_once *once, struct once_sync_s *s, - void (*f) (void), void (*farg) (void *arg), void *arg) { - uint32_t o = ATM_LOAD_ACQ (once); - if (o != 2) { - unsigned attempts = 0; - if (s != NULL) { - nsync_mu_lock (&s->once_mu); - } - while (o == 0 && !ATM_CAS_ACQ (once, 0, 1)) { - o = ATM_LOAD (once); - } - if (o == 0) { - if (s != NULL) { - nsync_mu_unlock (&s->once_mu); - } - if (f != NULL) { - (*f) (); - } else { - (*farg) (arg); - } - if (s != NULL) { - nsync_mu_lock (&s->once_mu); - nsync_cv_broadcast (&s->once_cv); - } - ATM_STORE_REL (once, 2); - } - while (ATM_LOAD_ACQ (once) != 2) { - if (s != NULL) { - nsync_time deadline; - if (attempts < 50) { - attempts += 10; - } - deadline = nsync_time_add (nsync_time_now (), nsync_time_ms (attempts)); - nsync_cv_wait_with_deadline (&s->once_cv, &s->once_mu, deadline, NULL); - } else { - attempts = nsync_spin_delay_ (attempts); - } - } - if (s != NULL) { - nsync_mu_unlock (&s->once_mu); - } - } -} - -void nsync_run_once (nsync_once *once, void (*f) (void)) { - uint32_t o; - IGNORE_RACES_START (); - o = ATM_LOAD_ACQ (once); - if (o != 2) { - struct once_sync_s *s = NSYNC_ONCE_SYNC_ (once); - nsync_run_once_impl (once, s, f, NULL, NULL); - } - IGNORE_RACES_END (); -} - -void nsync_run_once_arg (nsync_once *once, void (*farg) (void *arg), void *arg) { - uint32_t o; - IGNORE_RACES_START (); - o = ATM_LOAD_ACQ (once); - if (o != 2) { - struct once_sync_s *s = NSYNC_ONCE_SYNC_ (once); - nsync_run_once_impl (once, s, NULL, farg, arg); - } - IGNORE_RACES_END (); -} - -void nsync_run_once_spin (nsync_once *once, void (*f) (void)) { - uint32_t o; - IGNORE_RACES_START (); - o = ATM_LOAD_ACQ (once); - if (o != 2) { - nsync_run_once_impl (once, NULL, f, NULL, NULL); - } - IGNORE_RACES_END (); -} - -void nsync_run_once_arg_spin (nsync_once *once, void (*farg) (void *arg), void *arg) { - uint32_t o; - IGNORE_RACES_START (); - o = ATM_LOAD_ACQ (once); - if (o != 2) { - nsync_run_once_impl (once, NULL, NULL, farg, arg); - } - IGNORE_RACES_END (); -} diff --git a/libc/thread/nsync_sem_wait.c b/libc/thread/nsync_sem_wait.c deleted file mode 100644 index ae467a6a1..000000000 --- a/libc/thread/nsync_sem_wait.c +++ /dev/null @@ -1,85 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ -│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2016 Google Inc. │ -│ │ -│ Licensed under the Apache License, Version 2.0 (the "License"); │ -│ you may not use this file except in compliance with the License. │ -│ You may obtain a copy of the License at │ -│ │ -│ http://www.apache.org/licenses/LICENSE-2.0 │ -│ │ -│ Unless required by applicable law or agreed to in writing, software │ -│ distributed under the License is distributed on an "AS IS" BASIS, │ -│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │ -│ See the License for the specific language governing permissions and │ -│ limitations under the License. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/errno.h" -#include "third_party/nsync/atomic.h" -#include "third_party/nsync/atomic.internal.h" -#include "third_party/nsync/common.internal.h" -#include "third_party/nsync/dll.h" -#include "third_party/nsync/mu_semaphore.h" -#include "third_party/nsync/wait_s.internal.h" - -asm(".ident\t\"\\n\\n\ -*NSYNC (Apache 2.0)\\n\ -Copyright 2016 Google, Inc.\\n\ -https://github.com/google/nsync\""); -// clang-format off - -/* Wait until one of: - w->sem is non-zero----decrement it and return 0. - abs_deadline expires---return ETIMEDOUT. - cancel_note is non-NULL and *cancel_note becomes notified---return ECANCELED. */ -int nsync_sem_wait_with_cancel_ (waiter *w, nsync_time abs_deadline, - nsync_note cancel_note) { - int sem_outcome; - if (cancel_note == NULL) { - sem_outcome = nsync_mu_semaphore_p_with_deadline (&w->sem, abs_deadline); - } else { - nsync_time cancel_time; - cancel_time = nsync_note_notified_deadline_ (cancel_note); - sem_outcome = ECANCELED; - if (nsync_time_cmp (cancel_time, nsync_time_zero) > 0) { - struct nsync_waiter_s nw; - nw.tag = NSYNC_WAITER_TAG; - nw.sem = &w->sem; - nsync_dll_init_ (&nw.q, &nw); - ATM_STORE (&nw.waiting, 1); - nw.flags = 0; - nsync_mu_lock (&cancel_note->note_mu); - cancel_time = NOTIFIED_TIME (cancel_note); - if (nsync_time_cmp (cancel_time, nsync_time_zero) > 0) { - nsync_time local_abs_deadline; - int deadline_is_nearer = 0; - cancel_note->waiters = nsync_dll_make_last_in_list_ ( - cancel_note->waiters, &nw.q); - local_abs_deadline = cancel_time; - if (nsync_time_cmp (abs_deadline, cancel_time) < 0) { - local_abs_deadline = abs_deadline; - deadline_is_nearer = 1; - } - nsync_mu_unlock (&cancel_note->note_mu); - sem_outcome = nsync_mu_semaphore_p_with_deadline (&w->sem, - local_abs_deadline); - if (sem_outcome == ETIMEDOUT && !deadline_is_nearer) { - sem_outcome = ECANCELED; - nsync_note_notify (cancel_note); - } - nsync_mu_lock (&cancel_note->note_mu); - cancel_time = NOTIFIED_TIME (cancel_note); - if (nsync_time_cmp (cancel_time, - nsync_time_zero) > 0) { - cancel_note->waiters = nsync_dll_remove_ ( - cancel_note->waiters, &nw.q); - } - } - nsync_mu_unlock (&cancel_note->note_mu); - } - } - return (sem_outcome); -} - - diff --git a/libc/thread/nsync_sem_wait_no_note.c b/libc/thread/nsync_sem_wait_no_note.c deleted file mode 100644 index c9c3a156b..000000000 --- a/libc/thread/nsync_sem_wait_no_note.c +++ /dev/null @@ -1,33 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ -│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2016 Google Inc. │ -│ │ -│ Licensed under the Apache License, Version 2.0 (the "License"); │ -│ you may not use this file except in compliance with the License. │ -│ You may obtain a copy of the License at │ -│ │ -│ http://www.apache.org/licenses/LICENSE-2.0 │ -│ │ -│ Unless required by applicable law or agreed to in writing, software │ -│ distributed under the License is distributed on an "AS IS" BASIS, │ -│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │ -│ See the License for the specific language governing permissions and │ -│ limitations under the License. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "third_party/nsync/common.internal.h" -#include "third_party/nsync/mu_semaphore.h" - -asm(".ident\t\"\\n\\n\ -*NSYNC (Apache 2.0)\\n\ -Copyright 2016 Google, Inc.\\n\ -https://github.com/google/nsync\""); -// clang-format off - -/* Wait until one of: - w->sem is non-zero----decrement it and return 0. - abs_deadline expires---return ETIMEDOUT. - Ignores cancel_note. */ -int nsync_sem_wait_with_cancel_ (waiter *w, nsync_time abs_deadline, nsync_note cancel_note) { - return (nsync_mu_semaphore_p_with_deadline (&w->sem, abs_deadline)); -} diff --git a/libc/thread/nsync_wait.c b/libc/thread/nsync_wait.c deleted file mode 100644 index de75ab165..000000000 --- a/libc/thread/nsync_wait.c +++ /dev/null @@ -1,109 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ -│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2016 Google Inc. │ -│ │ -│ Licensed under the Apache License, Version 2.0 (the "License"); │ -│ you may not use this file except in compliance with the License. │ -│ You may obtain a copy of the License at │ -│ │ -│ http://www.apache.org/licenses/LICENSE-2.0 │ -│ │ -│ Unless required by applicable law or agreed to in writing, software │ -│ distributed under the License is distributed on an "AS IS" BASIS, │ -│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │ -│ See the License for the specific language governing permissions and │ -│ limitations under the License. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/mem/mem.h" -#include "third_party/nsync/atomic.h" -#include "third_party/nsync/atomic.internal.h" -#include "third_party/nsync/common.internal.h" -#include "third_party/nsync/dll.h" -#include "third_party/nsync/mu_semaphore.h" -#include "third_party/nsync/races.internal.h" -#include "third_party/nsync/wait_s.internal.h" -#include "third_party/nsync/waiter.h" - -asm(".ident\t\"\\n\\n\ -*NSYNC (Apache 2.0)\\n\ -Copyright 2016 Google, Inc.\\n\ -https://github.com/google/nsync\""); -// clang-format off - -int nsync_wait_n (void *mu, void (*lock) (void *), void (*unlock) (void *), - nsync_time abs_deadline, - int count, struct nsync_waitable_s *waitable[]) { - int ready; - IGNORE_RACES_START (); - for (ready = 0; ready != count && - nsync_time_cmp ((*waitable[ready]->funcs->ready_time) ( - waitable[ready]->v, NULL), - nsync_time_zero) > 0; - ready++) { - } - if (ready == count && nsync_time_cmp (abs_deadline, nsync_time_zero) > 0) { - int i; - int unlocked = 0; - int j; - int enqueued = 1; - waiter *w = nsync_waiter_new_ (); - struct nsync_waiter_s nw_set[4]; - struct nsync_waiter_s *nw = nw_set; - if (count > (int) (sizeof (nw_set) / sizeof (nw_set[0]))) { - nw = (struct nsync_waiter_s *) malloc (count * sizeof (nw[0])); - } - for (i = 0; i != count && enqueued; i++) { - nw[i].tag = NSYNC_WAITER_TAG; - nw[i].sem = &w->sem; - nsync_dll_init_ (&nw[i].q, &nw[i]); - ATM_STORE (&nw[i].waiting, 0); - nw[i].flags = 0; - enqueued = (*waitable[i]->funcs->enqueue) (waitable[i]->v, &nw[i]); - } - - if (i == count) { - nsync_time min_ntime; - if (mu != NULL) { - (*unlock) (mu); - unlocked = 1; - } - do { - min_ntime = abs_deadline; - for (j = 0; j != count; j++) { - nsync_time ntime; - ntime = (*waitable[j]->funcs->ready_time) ( - waitable[j]->v, &nw[j]); - if (nsync_time_cmp (ntime, min_ntime) < 0) { - min_ntime = ntime; - } - } - } while (nsync_time_cmp (min_ntime, nsync_time_zero) > 0 && - nsync_mu_semaphore_p_with_deadline (&w->sem, - min_ntime) == 0); - } - - /* An attempt was made above to enqueue waitable[0..i-1]. - Dequeue any that are still enqueued, and remember the index - of the first ready (i.e., not still enqueued) object, if any. */ - for (j = 0; j != i; j++) { - int was_still_enqueued = - (*waitable[j]->funcs->dequeue) (waitable[j]->v, &nw[j]); - if (!was_still_enqueued && ready == count) { - ready = j; - } - } - - if (nw != nw_set) { - free (nw); - } - nsync_waiter_free_ (w); - if (unlocked) { - (*lock) (mu); - } - } - IGNORE_RACES_END (); - return (ready); -} - - diff --git a/libc/thread/posixthread.internal.h b/libc/thread/posixthread.internal.h index bfbf41e9a..7a51695dc 100644 --- a/libc/thread/posixthread.internal.h +++ b/libc/thread/posixthread.internal.h @@ -1,8 +1,8 @@ #ifndef COSMOPOLITAN_LIBC_THREAD_POSIXTHREAD_INTERNAL_H_ #define COSMOPOLITAN_LIBC_THREAD_POSIXTHREAD_INTERNAL_H_ #include "libc/calls/struct/sched_param.h" +#include "libc/intrin/pthread.h" #include "libc/runtime/runtime.h" -#include "libc/thread/thread.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ @@ -62,26 +62,18 @@ struct PosixThread { int *ctid; char *tls; char *tib; - char *altstack; _Atomic(enum PosixThreadStatus) status; jmp_buf exiter; pthread_attr_t attr; }; -hidden extern pthread_spinlock_t _pthread_keys_lock; -hidden extern uint64_t _pthread_key_usage[(PTHREAD_KEYS_MAX + 63) / 64]; -hidden extern pthread_key_dtor _pthread_key_dtor[PTHREAD_KEYS_MAX]; -hidden extern _Thread_local void *_pthread_keys[PTHREAD_KEYS_MAX]; - -int _pthread_reschedule(struct PosixThread *) hidden; -int _pthread_setschedparam_freebsd(int, int, const struct sched_param *) hidden; void _pthread_free(struct PosixThread *) hidden; -void _pthread_ungarbage(void) hidden; void _pthread_wait(struct PosixThread *) hidden; +int _pthread_reschedule(struct PosixThread *) hidden; void _pthread_zombies_add(struct PosixThread *) hidden; void _pthread_zombies_decimate(void) hidden; void _pthread_zombies_harvest(void) hidden; -void _pthread_key_destruct(void *[PTHREAD_KEYS_MAX]); +int _pthread_setschedparam_freebsd(int, int, const struct sched_param *); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/thread/pthread_cond_timedwait.c b/libc/thread/pthread_cond_timedwait.c index 31ecf88ba..1245fb77c 100644 --- a/libc/thread/pthread_cond_timedwait.c +++ b/libc/thread/pthread_cond_timedwait.c @@ -16,12 +16,14 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" +#include "libc/calls/calls.h" +#include "libc/calls/struct/timespec.h" #include "libc/errno.h" -#include "libc/thread/thread.h" -#include "libc/thread/thread2.h" -#include "third_party/nsync/common.internal.h" -#include "third_party/nsync/cv.h" -#include "third_party/nsync/time.h" +#include "libc/intrin/atomic.h" +#include "libc/intrin/futex.internal.h" +#include "libc/intrin/pthread.h" +#include "libc/intrin/pthread2.h" /** * Waits for condition with optional time limit, e.g. @@ -46,13 +48,42 @@ */ int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime) { + int rc, err, seq; + struct timespec now, rel, *tsp; + if (abstime && !(0 <= abstime->tv_nsec && abstime->tv_nsec < 1000000000)) { + assert(!"bad abstime"); return EINVAL; } - if (mutex->_type != PTHREAD_MUTEX_NORMAL) { - nsync_panic_("pthread cond needs normal mutex\n"); + + if ((err = pthread_mutex_unlock(mutex))) { + return err; } - return nsync_cv_wait_with_deadline( - (nsync_cv *)cond, (nsync_mu *)mutex, - abstime ? *abstime : nsync_time_no_deadline, 0); + + atomic_fetch_add(&cond->waits, 1); + + rc = 0; + seq = atomic_load_explicit(&cond->seq, memory_order_relaxed); + do { + if (!abstime) { + tsp = 0; + } else { + now = _timespec_mono(); + if (_timespec_gte(now, *abstime)) { + rc = ETIMEDOUT; + break; + } + rel = _timespec_sub(*abstime, now); + tsp = &rel; + } + _futex_wait(&cond->seq, seq, cond->pshared, tsp); + } while (seq == atomic_load_explicit(&cond->seq, memory_order_relaxed)); + + atomic_fetch_sub(&cond->waits, 1); + + if ((err = pthread_mutex_lock(mutex))) { + return err; + } + + return rc; } diff --git a/libc/thread/pthread_cond_wait.c b/libc/thread/pthread_cond_wait.c index 30920d280..1f48fba32 100644 --- a/libc/thread/pthread_cond_wait.c +++ b/libc/thread/pthread_cond_wait.c @@ -16,19 +16,24 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" -#include "libc/thread/thread2.h" +#include "libc/intrin/pthread2.h" /** * Waits for condition, e.g. * * pthread_cond_t cond = PTHREAD_COND_INITIALIZER; * pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; - * // ... + * + * // waiting threads * pthread_mutex_lock(&lock); * pthread_cond_wait(&cond, &lock); * pthread_mutex_unlock(&lock); * + * // notifying thread + * pthread_mutex_lock(&lock); + * pthread_cond_broadcast(&cond); + * pthread_mutex_unlock(&lock); + * * @param mutex needs to be held by thread when calling this function * @return 0 on success, or errno on error * @raise EPERM if `mutex` is `PTHREAD_MUTEX_ERRORCHECK` and the lock diff --git a/libc/thread/pthread_create.c b/libc/thread/pthread_create.c index 5ae34c19c..a08471e7e 100644 --- a/libc/thread/pthread_create.c +++ b/libc/thread/pthread_create.c @@ -19,34 +19,30 @@ #include "libc/assert.h" #include "libc/calls/calls.h" #include "libc/calls/sched-sysv.internal.h" -#include "libc/calls/struct/sigaltstack.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/atomic.h" #include "libc/intrin/bits.h" -#include "libc/intrin/kprintf.h" +#include "libc/intrin/pthread.h" +#include "libc/intrin/wait0.internal.h" #include "libc/intrin/weaken.h" -#include "libc/log/internal.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" #include "libc/nexgen32e/gc.internal.h" +#include "libc/nexgen32e/gettls.h" +#include "libc/nexgen32e/threaded.h" #include "libc/runtime/runtime.h" #include "libc/runtime/stack.h" #include "libc/sysv/consts/clone.h" #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/prot.h" -#include "libc/sysv/consts/ss.h" #include "libc/sysv/errfuns.h" +#include "libc/thread/internal.h" #include "libc/thread/posixthread.internal.h" #include "libc/thread/spawn.h" #include "libc/thread/thread.h" -#include "libc/thread/tls.h" -#include "libc/thread/wait0.internal.h" - -STATIC_YOINK("nsync_mu_lock"); -STATIC_YOINK("nsync_mu_unlock"); #define MAP_ANON_OPENBSD 0x1000 #define MAP_STACK_OPENBSD 0x4000 @@ -60,12 +56,7 @@ void _pthread_free(struct PosixThread *pt) { if (pt->ownstack && // pt->attr.stackaddr && // pt->attr.stackaddr != MAP_FAILED) { - if (munmap(pt->attr.stackaddr, pt->attr.stacksize)) { - notpossible; - } - } - if (pt->altstack) { - free(pt->altstack); + munmap(&pt->attr.stackaddr, pt->attr.stacksize); } free(pt); } @@ -73,26 +64,17 @@ void _pthread_free(struct PosixThread *pt) { static int PosixThread(void *arg, int tid) { struct PosixThread *pt = arg; enum PosixThreadStatus status; - struct sigaltstack ss; - if (pt->altstack) { - ss.ss_flags = 0; - ss.ss_size = SIGSTKSZ; - ss.ss_sp = pt->altstack; - if (sigaltstack(&ss, 0)) { - notpossible; - } - } if (pt->attr.inheritsched == PTHREAD_EXPLICIT_SCHED) { _pthread_reschedule(pt); } if (!setjmp(pt->exiter)) { - __get_tls()->tib_pthread = (pthread_t)pt; + ((cthread_t)__get_tls())->pthread = (pthread_t)pt; pt->rc = pt->start_routine(pt->arg); } if (weaken(_pthread_key_destruct)) { weaken(_pthread_key_destruct)(0); } - _pthread_ungarbage(); + cthread_ungarbage(); if (atomic_load_explicit(&pt->status, memory_order_acquire) == kPosixThreadDetached) { atomic_store_explicit(&pt->status, kPosixThreadZombie, @@ -187,7 +169,7 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) { int rc, e = errno; struct PosixThread *pt; - __require_tls(); + TlsIsRequired(); _pthread_zombies_decimate(); // create posix thread object @@ -275,11 +257,6 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr, } } - // setup signal handler stack - if (_wantcrashreports && !IsWindows()) { - pt->altstack = malloc(SIGSTKSZ); - } - // set initial status switch (pt->attr.detachstate) { case PTHREAD_CREATE_JOINABLE: diff --git a/libc/thread/pthread_detach.c b/libc/thread/pthread_detach.c index 9fbd94bce..c3aa460dd 100644 --- a/libc/thread/pthread_detach.c +++ b/libc/thread/pthread_detach.c @@ -17,7 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/intrin/atomic.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" #include "libc/thread/posixthread.internal.h" diff --git a/libc/thread/pthread_equal.c b/libc/thread/pthread_equal.c index 6941eb9d3..c7e3afcce 100644 --- a/libc/thread/pthread_equal.c +++ b/libc/thread/pthread_equal.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" #include "libc/thread/posixthread.internal.h" /** diff --git a/libc/thread/pthread_exit.c b/libc/thread/pthread_exit.c index de4d06fd0..842dc7e7f 100644 --- a/libc/thread/pthread_exit.c +++ b/libc/thread/pthread_exit.c @@ -16,11 +16,12 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/intrin/pthread.h" +#include "libc/nexgen32e/gettls.h" #include "libc/runtime/gc.h" #include "libc/thread/posixthread.internal.h" #include "libc/thread/spawn.h" #include "libc/thread/thread.h" -#include "libc/thread/tls.h" /** * Terminates current POSIX thread. @@ -36,7 +37,7 @@ */ wontreturn void pthread_exit(void *rc) { struct PosixThread *pt; - if ((pt = (struct PosixThread *)__get_tls()->tib_pthread)) { + if ((pt = (struct PosixThread *)((cthread_t)__get_tls())->pthread)) { pt->rc = rc; _gclongjmp(pt->exiter, 1); } else { diff --git a/libc/thread/pthread_getaffinity_np.c b/libc/thread/pthread_getaffinity_np.c index 0133fe20c..35064bbf5 100644 --- a/libc/thread/pthread_getaffinity_np.c +++ b/libc/thread/pthread_getaffinity_np.c @@ -19,6 +19,7 @@ #include "libc/calls/struct/cpuset.h" #include "libc/errno.h" #include "libc/thread/posixthread.internal.h" +#include "libc/thread/thread.h" /** * Gets CPU affinity for thread. diff --git a/libc/thread/pthread_getattr_np.c b/libc/thread/pthread_getattr_np.c index 479f4371c..878d7ecd9 100644 --- a/libc/thread/pthread_getattr_np.c +++ b/libc/thread/pthread_getattr_np.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/intrin/pthread.h" #include "libc/str/str.h" #include "libc/thread/posixthread.internal.h" #include "libc/thread/thread.h" diff --git a/libc/thread/pthread_getname_np.c b/libc/thread/pthread_getname_np.c index 413cb4c0a..a306fcd79 100644 --- a/libc/thread/pthread_getname_np.c +++ b/libc/thread/pthread_getname_np.c @@ -27,6 +27,7 @@ #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/pr.h" #include "libc/thread/posixthread.internal.h" +#include "libc/thread/thread.h" /** * Gets name of thread registered with system, e.g. diff --git a/libc/thread/pthread_getschedparam.c b/libc/thread/pthread_getschedparam.c index f382a59dc..b98ef8873 100644 --- a/libc/thread/pthread_getschedparam.c +++ b/libc/thread/pthread_getschedparam.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/intrin/pthread2.h" #include "libc/thread/posixthread.internal.h" -#include "libc/thread/thread2.h" /** * Gets most recently set scheduling of thread. diff --git a/libc/intrin/pthread_getspecific.c b/libc/thread/pthread_getspecific.c similarity index 95% rename from libc/intrin/pthread_getspecific.c rename to libc/thread/pthread_getspecific.c index 404dcb1fd..0aa0771af 100644 --- a/libc/intrin/pthread_getspecific.c +++ b/libc/thread/pthread_getspecific.c @@ -16,7 +16,9 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/posixthread.internal.h" +#include "libc/errno.h" +#include "libc/nexgen32e/gettls.h" +#include "libc/thread/internal.h" #include "libc/thread/thread.h" /** diff --git a/libc/thread/pthread_getthreadid_np.c b/libc/thread/pthread_getthreadid_np.c index 9ba68cb87..e3ceefcdc 100644 --- a/libc/thread/pthread_getthreadid_np.c +++ b/libc/thread/pthread_getthreadid_np.c @@ -17,7 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" /** * Returns thread id of current POSIX thread. diff --git a/libc/thread/pthread_getunique_np.c b/libc/thread/pthread_getunique_np.c index d3bf75032..8e8b58637 100644 --- a/libc/thread/pthread_getunique_np.c +++ b/libc/thread/pthread_getunique_np.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/thread/posixthread.internal.h" +#include "libc/thread/thread.h" /** * Returns thread id of POSIX thread. diff --git a/libc/thread/pthread_join.c b/libc/thread/pthread_join.c index ea9535bfd..8ae82d909 100644 --- a/libc/thread/pthread_join.c +++ b/libc/thread/pthread_join.c @@ -18,7 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" #include "libc/errno.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" #include "libc/thread/posixthread.internal.h" diff --git a/libc/intrin/pthread_key_create.c b/libc/thread/pthread_key_create.c similarity index 91% rename from libc/intrin/pthread_key_create.c rename to libc/thread/pthread_key_create.c index e7c7a9c98..6d0eb1a45 100644 --- a/libc/intrin/pthread_key_create.c +++ b/libc/thread/pthread_key_create.c @@ -18,29 +18,26 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/errno.h" #include "libc/nexgen32e/bsr.h" +#include "libc/nexgen32e/gettls.h" #include "libc/runtime/runtime.h" -#include "libc/thread/posixthread.internal.h" +#include "libc/thread/internal.h" #include "libc/thread/thread.h" -#include "libc/thread/tls.h" /** * Allocates TLS slot. */ int pthread_key_create(pthread_key_t *key, pthread_key_dtor dtor) { - int i, j, rc = EAGAIN; - pthread_spin_lock(&_pthread_keys_lock); + int i, j; for (i = 0; i < (PTHREAD_KEYS_MAX + 63) / 64; ++i) { if (~_pthread_key_usage[i]) { j = bsrl(~_pthread_key_usage[i]); _pthread_key_usage[i] |= 1ul << j; _pthread_key_dtor[i * 64 + j] = dtor; *key = i * 64 + j; - rc = 0; - break; + return 0; } } - pthread_spin_unlock(&_pthread_keys_lock); - return rc; + return EAGAIN; } static textexit void _pthread_key_atexit(void) { diff --git a/libc/intrin/pthread_key_delete.c b/libc/thread/pthread_key_delete.c similarity index 91% rename from libc/intrin/pthread_key_delete.c rename to libc/thread/pthread_key_delete.c index b78120c21..998e78ac0 100644 --- a/libc/intrin/pthread_key_delete.c +++ b/libc/thread/pthread_key_delete.c @@ -17,22 +17,18 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/errno.h" -#include "libc/thread/posixthread.internal.h" +#include "libc/thread/internal.h" #include "libc/thread/thread.h" /** * Deletes TLS slot. */ int pthread_key_delete(pthread_key_t key) { - int rc; - pthread_spin_lock(&_pthread_keys_lock); if (key < PTHREAD_KEYS_MAX) { _pthread_key_usage[key / 64] &= ~(1ul << (key % 64)); _pthread_key_dtor[key] = 0; - rc = 0; + return 0; } else { - rc = EINVAL; + return EINVAL; } - pthread_spin_unlock(&_pthread_keys_lock); - return rc; } diff --git a/libc/intrin/pthread_key_destruct.c b/libc/thread/pthread_key_destruct.c similarity index 89% rename from libc/intrin/pthread_key_destruct.c rename to libc/thread/pthread_key_destruct.c index cfad22162..f9eb6cd0b 100644 --- a/libc/intrin/pthread_key_destruct.c +++ b/libc/thread/pthread_key_destruct.c @@ -17,17 +17,14 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/nexgen32e/bsr.h" -#include "libc/thread/posixthread.internal.h" +#include "libc/thread/internal.h" #include "libc/thread/thread.h" -#include "libc/thread/tls.h" void _pthread_key_destruct(void *key[PTHREAD_KEYS_MAX]) { int i, j; uint64_t x; void *value; pthread_key_dtor dtor; - if (!__tls_enabled) return; - pthread_spin_lock(&_pthread_keys_lock); if (!key) key = _pthread_keys; StartOver: for (i = 0; i < (PTHREAD_KEYS_MAX + 63) / 64; ++i) { @@ -36,13 +33,10 @@ StartOver: j = bsrl(x); if ((value = key[i * 64 + j]) && (dtor = _pthread_key_dtor[i * 64 + j])) { key[i * 64 + j] = 0; - pthread_spin_unlock(&_pthread_keys_lock); dtor(value); - pthread_spin_lock(&_pthread_keys_lock); goto StartOver; } x &= ~(1ul << j); } } - pthread_spin_unlock(&_pthread_keys_lock); } diff --git a/libc/thread/pthread_reschedule.c b/libc/thread/pthread_reschedule.c index c3ad753aa..2b7cc3664 100644 --- a/libc/thread/pthread_reschedule.c +++ b/libc/thread/pthread_reschedule.c @@ -22,6 +22,7 @@ #include "libc/errno.h" #include "libc/sysv/errfuns.h" #include "libc/thread/posixthread.internal.h" +#include "libc/thread/thread.h" int _pthread_reschedule(struct PosixThread *pt) { int rc, e = errno; diff --git a/libc/thread/pthread_self.c b/libc/thread/pthread_self.c index 358679b61..0212ecb83 100644 --- a/libc/thread/pthread_self.c +++ b/libc/thread/pthread_self.c @@ -16,12 +16,13 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/intrin/pthread.h" +#include "libc/nexgen32e/gettls.h" #include "libc/thread/thread.h" -#include "libc/thread/tls.h" /** * Returns current POSIX thread. */ pthread_t pthread_self(void) { - return __get_tls()->tib_pthread; + return ((cthread_t)__get_tls())->pthread; } diff --git a/libc/thread/pthread_setaffinity_np.c b/libc/thread/pthread_setaffinity_np.c index 9605d056e..28a67ceab 100644 --- a/libc/thread/pthread_setaffinity_np.c +++ b/libc/thread/pthread_setaffinity_np.c @@ -19,6 +19,7 @@ #include "libc/calls/struct/cpuset.h" #include "libc/errno.h" #include "libc/thread/posixthread.internal.h" +#include "libc/thread/thread.h" /** * Asks kernel to only schedule thread on particular CPUs. diff --git a/libc/thread/pthread_setname_np.c b/libc/thread/pthread_setname_np.c index 5452299ae..0663b05db 100644 --- a/libc/thread/pthread_setname_np.c +++ b/libc/thread/pthread_setname_np.c @@ -27,6 +27,7 @@ #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/pr.h" #include "libc/thread/posixthread.internal.h" +#include "libc/thread/thread.h" /** * Registers custom name of thread with system, e.g. diff --git a/libc/thread/pthread_setschedparam.c b/libc/thread/pthread_setschedparam.c index e0d2fce57..4d44b2093 100644 --- a/libc/thread/pthread_setschedparam.c +++ b/libc/thread/pthread_setschedparam.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/errno.h" +#include "libc/intrin/pthread2.h" #include "libc/thread/posixthread.internal.h" -#include "libc/thread/thread2.h" /** * Changes scheduling of thread, e.g. diff --git a/libc/thread/pthread_setschedparam_freebsd.c b/libc/thread/pthread_setschedparam_freebsd.c index 4dfb10fe8..2a54cceb4 100644 --- a/libc/thread/pthread_setschedparam_freebsd.c +++ b/libc/thread/pthread_setschedparam_freebsd.c @@ -20,6 +20,7 @@ #include "libc/sysv/consts/sched.h" #include "libc/thread/freebsd.internal.h" #include "libc/thread/posixthread.internal.h" +#include "libc/thread/thread.h" #define RTP_SET_FREEBSD 1 #define PRI_REALTIME_FREEBSD 2 diff --git a/libc/intrin/pthread_setspecific.c b/libc/thread/pthread_setspecific.c similarity index 96% rename from libc/intrin/pthread_setspecific.c rename to libc/thread/pthread_setspecific.c index 18a663150..8d5b699d8 100644 --- a/libc/intrin/pthread_setspecific.c +++ b/libc/thread/pthread_setspecific.c @@ -17,7 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/errno.h" -#include "libc/thread/posixthread.internal.h" +#include "libc/nexgen32e/gettls.h" +#include "libc/thread/internal.h" #include "libc/thread/thread.h" /** diff --git a/libc/thread/pthread_sigmask.c b/libc/thread/pthread_sigmask.c index 8c0340e65..20d87e802 100644 --- a/libc/thread/pthread_sigmask.c +++ b/libc/thread/pthread_sigmask.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/struct/sigset.h" +#include "libc/thread/thread.h" /** * Examines and/or changes blocked signals on current thread. diff --git a/libc/thread/spawn.c b/libc/thread/spawn.c index 6ec8a144d..e58f76a59 100644 --- a/libc/thread/spawn.c +++ b/libc/thread/spawn.c @@ -17,8 +17,11 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/intrin/kprintf.h" +#include "libc/intrin/wait0.internal.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" +#include "libc/nexgen32e/threaded.h" #include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" #include "libc/runtime/stack.h" @@ -28,10 +31,8 @@ #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/prot.h" #include "libc/sysv/errfuns.h" -#include "libc/thread/posixthread.internal.h" #include "libc/thread/spawn.h" -#include "libc/thread/tls.h" -#include "libc/thread/wait0.internal.h" +#include "libc/thread/thread.h" /** * @fileoverview Simple threading API @@ -51,8 +52,8 @@ #define _TLSZ ((intptr_t)_tls_size) #define _TLDZ ((intptr_t)_tdata_size) -#define _TIBZ sizeof(struct CosmoTib) -#define _MEMZ ROUNDUP(_TLSZ + _TIBZ, alignof(struct CosmoTib)) +#define _TIBZ sizeof(struct cthread_descriptor_t) +#define _MEMZ ROUNDUP(_TLSZ + _TIBZ, alignof(struct cthread_descriptor_t)) struct spawner { int (*fun)(void *, int); @@ -63,7 +64,7 @@ static int Spawner(void *arg, int tid) { int rc; struct spawner *spawner = arg; rc = spawner->fun(spawner->arg, tid); - _pthread_ungarbage(); + cthread_ungarbage(); free(spawner); return 0; } @@ -93,7 +94,7 @@ static int Spawner(void *arg, int tid) { int _spawn(int fun(void *, int), void *arg, struct spawn *opt_out_thread) { struct spawn *th, ths; struct spawner *spawner; - __require_tls(); + TlsIsRequired(); if (!fun) return einval(); // we need to to clobber the output memory before calling clone, since diff --git a/libc/thread/thread.h b/libc/thread/thread.h index ab97823ef..1d5b5cc0f 100644 --- a/libc/thread/thread.h +++ b/libc/thread/thread.h @@ -1,194 +1,32 @@ #ifndef COSMOPOLITAN_LIBC_THREAD_THREAD_H_ #define COSMOPOLITAN_LIBC_THREAD_THREAD_H_ - -#define PTHREAD_KEYS_MAX 64 -#define PTHREAD_STACK_MIN FRAMESIZE -#define PTHREAD_DESTRUCTOR_ITERATIONS 4 - -#define PTHREAD_BARRIER_SERIAL_THREAD 31337 - -#define PTHREAD_MUTEX_DEFAULT 0 -#define PTHREAD_MUTEX_NORMAL 0 -#define PTHREAD_MUTEX_RECURSIVE 1 -#define PTHREAD_MUTEX_ERRORCHECK 2 -#define PTHREAD_MUTEX_STALLED 0 -#define PTHREAD_MUTEX_ROBUST 1 - -#define PTHREAD_PROCESS_PRIVATE 0 -#define PTHREAD_PROCESS_SHARED 1 - -#define PTHREAD_CREATE_JOINABLE 0 -#define PTHREAD_CREATE_DETACHED 1 - -#define PTHREAD_INHERIT_SCHED 0 -#define PTHREAD_EXPLICIT_SCHED 1 - +#include "libc/intrin/pthread.h" +#include "libc/runtime/runtime.h" +#include "libc/str/locale.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -#define PTHREAD_ONCE_INIT _PTHREAD_INIT -#define PTHREAD_COND_INITIALIZER _PTHREAD_INIT -#define PTHREAD_RWLOCK_INITIALIZER _PTHREAD_INIT -#define PTHREAD_MUTEX_INITIALIZER _PTHREAD_INIT -#define _PTHREAD_INIT \ - { 0 } +struct Ftrace { /* 16 */ + bool once; /* 0 */ + bool noreentry; /* 1 */ + int skew; /* 4 */ + int64_t lastaddr; /* 8 */ +}; -typedef uintptr_t pthread_t; -typedef int pthread_id_np_t; -typedef char pthread_condattr_t; -typedef char pthread_rwlockattr_t; -typedef char pthread_barrierattr_t; -typedef unsigned pthread_key_t; -typedef void (*pthread_key_dtor)(void *); +struct cthread_descriptor_t { + struct cthread_descriptor_t *self; /* 0x00 */ + struct Ftrace ftrace; /* 0x08 */ + void *garbages; /* 0x18 */ + locale_t locale; /* 0x20 */ + pthread_t pthread; /* 0x28 */ + struct cthread_descriptor_t *self2; /* 0x30 */ + int32_t tid; /* 0x38 */ + int32_t err; /* 0x3c */ +}; -typedef struct pthread_once_s { - _Atomic(uint32_t) _lock; -} pthread_once_t; +typedef struct cthread_descriptor_t *cthread_t; -typedef struct pthread_spinlock_s { - _Atomic(char) _lock; -} pthread_spinlock_t; - -typedef struct pthread_mutex_s { - _Atomic(int) _lock; - void *_nsync; - char _type; - char _pshared; -} pthread_mutex_t; - -typedef struct pthread_mutexattr_s { - char _type; - char _pshared; -} pthread_mutexattr_t; - -typedef struct pthread_cond_s { - void *_nsync[2]; -} pthread_cond_t; - -typedef struct pthread_rwlock_s { - void *_nsync[2]; - char _iswrite; -} pthread_rwlock_t; - -typedef struct pthread_barrier_s { - void *_nsync; -} pthread_barrier_t; - -typedef struct pthread_attr_s { - char detachstate; - char inheritsched; - int schedparam; - int schedpolicy; - int scope; - unsigned guardsize; - unsigned stacksize; - char *stackaddr; -} pthread_attr_t; - -int pthread_yield(void); -void pthread_exit(void *) wontreturn; -pthread_t pthread_self(void) pureconst; -pthread_id_np_t pthread_getthreadid_np(void); -int64_t pthread_getunique_np(pthread_t); -int pthread_setname_np(pthread_t, const char *); -int pthread_getname_np(pthread_t, char *, size_t); -int pthread_getattr_np(pthread_t, pthread_attr_t *); -int pthread_attr_init(pthread_attr_t *); -int pthread_attr_destroy(pthread_attr_t *); -int pthread_attr_getdetachstate(const pthread_attr_t *, int *); -int pthread_attr_setdetachstate(pthread_attr_t *, int); -int pthread_attr_getguardsize(const pthread_attr_t *, size_t *); -int pthread_attr_setguardsize(pthread_attr_t *, size_t); -int pthread_attr_getinheritsched(const pthread_attr_t *, int *); -int pthread_attr_setinheritsched(pthread_attr_t *, int); -int pthread_attr_getschedpolicy(const pthread_attr_t *, int *); -int pthread_attr_setschedpolicy(pthread_attr_t *, int); -int pthread_attr_getscope(const pthread_attr_t *, int *); -int pthread_attr_setscope(pthread_attr_t *, int); -int pthread_attr_getstack(const pthread_attr_t *, void **, size_t *); -int pthread_attr_setstack(pthread_attr_t *, void *, size_t); -int pthread_attr_getstacksize(const pthread_attr_t *, size_t *); -int pthread_attr_setstacksize(pthread_attr_t *, size_t); -int pthread_create(pthread_t *, const pthread_attr_t *, void *(*)(void *), - void *); -int pthread_detach(pthread_t); -int pthread_cancel(pthread_t); -int pthread_join(pthread_t, void **); -int pthread_equal(pthread_t, pthread_t); -int pthread_once(pthread_once_t *, void (*)(void)); -int pthread_spin_init(pthread_spinlock_t *, int); -int pthread_spin_destroy(pthread_spinlock_t *); -int pthread_spin_lock(pthread_spinlock_t *); -int pthread_spin_unlock(pthread_spinlock_t *); -int pthread_spin_trylock(pthread_spinlock_t *); -int pthread_mutexattr_init(pthread_mutexattr_t *); -int pthread_mutexattr_destroy(pthread_mutexattr_t *); -int pthread_mutexattr_gettype(const pthread_mutexattr_t *, int *); -int pthread_mutexattr_settype(pthread_mutexattr_t *, int); -int pthread_mutexattr_setpshared(pthread_mutexattr_t *, int); -int pthread_mutexattr_getpshared(const pthread_mutexattr_t *, int *); -int pthread_mutex_init(pthread_mutex_t *, const pthread_mutexattr_t *); -int pthread_mutex_lock(pthread_mutex_t *); -int pthread_mutex_unlock(pthread_mutex_t *); -int pthread_mutex_trylock(pthread_mutex_t *); -int pthread_mutex_destroy(pthread_mutex_t *); -int pthread_mutex_consistent(pthread_mutex_t *); -int pthread_condattr_init(pthread_condattr_t *); -int pthread_condattr_destroy(pthread_condattr_t *); -int pthread_condattr_setpshared(pthread_condattr_t *, int); -int pthread_condattr_getpshared(const pthread_condattr_t *, int *); -int pthread_cond_init(pthread_cond_t *, const pthread_condattr_t *); -int pthread_cond_destroy(pthread_cond_t *); -int pthread_cond_wait(pthread_cond_t *, pthread_mutex_t *); -int pthread_cond_broadcast(pthread_cond_t *); -int pthread_cond_signal(pthread_cond_t *); -int pthread_rwlockattr_init(pthread_rwlockattr_t *); -int pthread_rwlockattr_destroy(pthread_rwlockattr_t *); -int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *, int); -int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *, int *); -int pthread_rwlock_init(pthread_rwlock_t *, const pthread_rwlockattr_t *); -int pthread_rwlock_destroy(pthread_rwlock_t *); -int pthread_rwlock_rdlock(pthread_rwlock_t *); -int pthread_rwlock_tryrdlock(pthread_rwlock_t *); -int pthread_rwlock_wrlock(pthread_rwlock_t *); -int pthread_rwlock_trywrlock(pthread_rwlock_t *); -int pthread_rwlock_unlock(pthread_rwlock_t *); -int pthread_key_create(pthread_key_t *, pthread_key_dtor); -int pthread_key_delete(pthread_key_t); -int pthread_setspecific(pthread_key_t, void *); -void *pthread_getspecific(pthread_key_t); -int pthread_barrierattr_init(pthread_barrierattr_t *); -int pthread_barrierattr_destroy(pthread_barrierattr_t *); -int pthread_barrierattr_getpshared(const pthread_barrierattr_t *, int *); -int pthread_barrierattr_setpshared(pthread_barrierattr_t *, int); -int pthread_barrier_wait(pthread_barrier_t *); -int pthread_barrier_destroy(pthread_barrier_t *); -int pthread_barrier_init(pthread_barrier_t *, const pthread_barrierattr_t *, - unsigned); - -#define pthread_spin_init(pSpin, multiprocess) ((pSpin)->_lock = 0, 0) -#define pthread_spin_destroy(pSpin) ((pSpin)->_lock = -1, 0) -#if (__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 407 && \ - !defined(__STRICT_ANSI__) -extern const errno_t EBUSY; -#define pthread_spin_lock(pSpin) \ - ({ \ - pthread_spinlock_t *_s = pSpin; \ - while (__atomic_test_and_set(&_s->_lock, __ATOMIC_SEQ_CST)) donothing; \ - 0; \ - }) -#define pthread_spin_unlock(pSpin) \ - ({ \ - pthread_spinlock_t *_s = pSpin; \ - __atomic_store_n(&_s->_lock, 0, __ATOMIC_RELAXED); \ - 0; \ - }) -#define pthread_spin_trylock(pSpin) \ - ({ \ - pthread_spinlock_t *_s = pSpin; \ - __atomic_test_and_set(&_s->_lock, __ATOMIC_SEQ_CST) ? EBUSY : 0; \ - }) -#endif /* GCC 4.7+ */ +void cthread_ungarbage(void); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/thread/thread.mk b/libc/thread/thread.mk index d32ebd6ce..a953bde03 100644 --- a/libc/thread/thread.mk +++ b/libc/thread/thread.mk @@ -8,8 +8,8 @@ LIBC_THREAD = $(LIBC_THREAD_A_DEPS) $(LIBC_THREAD_A) LIBC_THREAD_A = o/$(MODE)/libc/thread/thread.a LIBC_THREAD_A_FILES := $(wildcard libc/thread/*) LIBC_THREAD_A_HDRS = $(filter %.h,$(LIBC_THREAD_A_FILES)) -LIBC_THREAD_A_SRCS_C = $(filter %.c,$(LIBC_THREAD_A_FILES)) LIBC_THREAD_A_SRCS_S = $(filter %.S,$(LIBC_THREAD_A_FILES)) +LIBC_THREAD_A_SRCS_C = $(filter %.c,$(LIBC_THREAD_A_FILES)) LIBC_THREAD_A_SRCS = \ $(LIBC_THREAD_A_SRCS_S) \ @@ -24,17 +24,15 @@ LIBC_THREAD_A_CHECKS = \ $(LIBC_THREAD_A_HDRS:%=o/$(MODE)/%.ok) LIBC_THREAD_A_DIRECTDEPS = \ + LIBC_STUBS \ LIBC_CALLS \ LIBC_INTRIN \ LIBC_MEM \ - LIBC_NT_KERNEL32 \ LIBC_RUNTIME \ LIBC_STR \ - LIBC_STUBS \ LIBC_SYSV \ LIBC_SYSV_CALLS \ - LIBC_NEXGEN32E \ - THIRD_PARTY_NSYNC + LIBC_NEXGEN32E LIBC_THREAD_A_DEPS := \ $(call uniq,$(foreach x,$(LIBC_THREAD_A_DIRECTDEPS),$($(x)))) @@ -48,10 +46,9 @@ $(LIBC_THREAD_A).pkg: \ $(LIBC_THREAD_A_OBJS) \ $(foreach x,$(LIBC_THREAD_A_DIRECTDEPS),$($(x)_A).pkg) -$(LIBC_THREAD_A_OBJS): private \ - OVERRIDE_CCFLAGS += \ - -ffunction-sections \ - -fdata-sections +o/tinylinux/libc/thread/clone.o: private \ + OVERRIDE_CFLAGS += \ + -ffunction-sections LIBC_THREAD_LIBS = $(foreach x,$(LIBC_THREAD_ARTIFACTS),$($(x))) LIBC_THREAD_SRCS = $(foreach x,$(LIBC_THREAD_ARTIFACTS),$($(x)_SRCS)) diff --git a/libc/thread/tls.h b/libc/thread/tls.h deleted file mode 100644 index f5a3f422d..000000000 --- a/libc/thread/tls.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_THREAD_TLS_H_ -#define COSMOPOLITAN_LIBC_THREAD_TLS_H_ - -#define TLS_ALIGNMENT 64 - -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -struct CosmoFtrace { /* 16 */ - bool ft_once; /* 0 */ - bool ft_noreentry; /* 1 */ - int ft_skew; /* 4 */ - int64_t ft_lastaddr; /* 8 */ -}; - -struct CosmoTib { - struct CosmoTib *tib_self; /* 0x00 */ - struct CosmoFtrace tib_ftrace; /* 0x08 */ - void *tib_garbages; /* 0x18 */ - intptr_t tib_locale; /* 0x20 */ - intptr_t tib_pthread; /* 0x28 */ - struct CosmoTib *tib_self2; /* 0x30 */ - int32_t tib_tid; /* 0x38 */ - int32_t tib_errno; /* 0x3c */ -}; - -extern int __threaded; -extern bool __tls_enabled; -extern unsigned __tls_index; - -void __require_tls(void); - -#if defined(__GNUC__) && defined(__x86_64__) && !defined(__STRICT_ANSI__) -/** - * Returns location of thread information block. - * - * This can't be used in privileged functions. - */ -static inline struct CosmoTib *__get_tls(void) { - struct CosmoTib *_tib; - asm("mov\t%%fs:0,%0" : "=r"(_tib) : /* no inputs */ : "memory"); - return _tib; -} -#endif /* GNU x86-64 */ - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_LIBC_THREAD_TLS_H_ */ diff --git a/libc/thread/pthread_ungarbage.c b/libc/thread/ungarbage.c similarity index 93% rename from libc/thread/pthread_ungarbage.c rename to libc/thread/ungarbage.c index 0a33e7011..80de172cd 100644 --- a/libc/thread/pthread_ungarbage.c +++ b/libc/thread/ungarbage.c @@ -19,11 +19,12 @@ #include "libc/assert.h" #include "libc/mem/mem.h" #include "libc/nexgen32e/gc.internal.h" -#include "libc/thread/tls.h" +#include "libc/nexgen32e/gettls.h" +#include "libc/thread/thread.h" -void _pthread_ungarbage(void) { +void cthread_ungarbage(void) { struct Garbages *g; - if ((g = __get_tls()->tib_garbages)) { + if ((g = ((cthread_t)__get_tls())->garbages)) { // _pthread_exit() uses _gclongjmp() so if this assertion fails, // then the likely cause is the thread used gc() with longjmp(). assert(!g->i); diff --git a/libc/thread/pthread_zombies.c b/libc/thread/zombie.c similarity index 98% rename from libc/thread/pthread_zombies.c rename to libc/thread/zombie.c index 1fee44614..b93f9d67e 100644 --- a/libc/thread/pthread_zombies.c +++ b/libc/thread/zombie.c @@ -17,11 +17,11 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/intrin/atomic.h" +#include "libc/intrin/pthread.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" #include "libc/thread/posixthread.internal.h" #include "libc/thread/spawn.h" -#include "libc/thread/thread.h" /** * @fileoverview Memory collector for detached threads. diff --git a/libc/time/localtime.c b/libc/time/localtime.c index f52e4f2f1..407c10c5f 100644 --- a/libc/time/localtime.c +++ b/libc/time/localtime.c @@ -4,10 +4,10 @@ #define LOCALTIME_IMPLEMENTATION #include "libc/calls/calls.h" #include "libc/intrin/bits.h" -#include "libc/intrin/nopl.internal.h" -#include "libc/thread/thread.h" +#include "libc/intrin/nopl.h" +#include "libc/intrin/pthread.h" #include "libc/mem/mem.h" -#include "libc/thread/tls.h" +#include "libc/nexgen32e/threaded.h" #include "libc/runtime/gc.h" #include "libc/str/str.h" #include "libc/sysv/consts/o.h" diff --git a/libc/vga/readv-vga.c b/libc/vga/readv-vga.c deleted file mode 100644 index a9668723f..000000000 --- a/libc/vga/readv-vga.c +++ /dev/null @@ -1,49 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ This is free and unencumbered software released into the public domain. │ -│ │ -│ Anyone is free to copy, modify, publish, use, compile, sell, or │ -│ distribute this software, either in source code form or as a compiled │ -│ binary, for any purpose, commercial or non-commercial, and by any │ -│ means. │ -│ │ -│ In jurisdictions that recognize copyright laws, the author or authors │ -│ of this software dedicate any and all copyright interest in the │ -│ software to the public domain. We make this dedication for the benefit │ -│ of the public at large and to the detriment of our heirs and │ -│ successors. We intend this dedication to be an overt act of │ -│ relinquishment in perpetuity of all present and future rights to this │ -│ software under copyright law. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ -│ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ -│ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. │ -│ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR │ -│ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ -│ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │ -│ OTHER DEALINGS IN THE SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/struct/fd.internal.h" -#include "libc/calls/struct/iovec.h" -#include "libc/calls/struct/iovec.internal.h" -#include "libc/vga/vga.internal.h" - -ssize_t sys_readv_vga(struct Fd *fd, const struct iovec *iov, int iovlen) { - /* NOTE: this routine is always non-blocking. */ - size_t i, redd = 0; - ssize_t res = 0; - for (i = 0; i < iovlen; ++i) { - void *input = iov[i].iov_base; - size_t len = iov[i].iov_len; - res = _TtyRead(&_vga_tty, input, len); - if (res < 0) - break; - redd += res; - if (redd != len) - return redd; - } - if (!redd) - return res; - return redd; -} diff --git a/libc/vga/tty.c b/libc/vga/tty.c index b76129a55..76d6c7f96 100644 --- a/libc/vga/tty.c +++ b/libc/vga/tty.c @@ -93,7 +93,7 @@ static wchar_t *Wcs(struct Tty *tty) void _StartTty(struct Tty *tty, unsigned short yn, unsigned short xn, unsigned short starty, unsigned short startx, - unsigned char chr_ht, void *vccs, wchar_t *wcs) { + void *vccs, wchar_t *wcs) { struct VgaTextCharCell *ccs = vccs; memset(tty, 0, sizeof(struct Tty)); SetYn(tty, yn); @@ -103,11 +103,8 @@ void _StartTty(struct Tty *tty, unsigned short yn, unsigned short xn, starty = yn - 1; if (startx >= xn) startx = xn - 1; - if (chr_ht > 32) - chr_ht = 32; tty->y = starty; tty->x = startx; - tty->chr_ht = chr_ht; if (SetWcs(tty, wcs)) { size_t n = (size_t)yn * xn, i; for (i = 0; i < n; ++i) @@ -146,27 +143,66 @@ static wchar_t *GetXlatLineDrawing(void) { return xlat; } +static void XlatAlphabet(wchar_t xlat[128], int a, int b) { + unsigned i; + for (i = 0; i < 128; ++i) { + if ('a' <= i && i <= 'z') { + xlat[i] = i - 'a' + a; + } else if ('A' <= i && i <= 'Z') { + xlat[i] = i - 'A' + b; + } else { + xlat[i] = i; + } + } +} + static wchar_t *GetXlatItalic(void) { - /* Unimplemented. Simply output normal non-italicized characters for now. */ - return GetXlatAscii(); + static bool once; + static wchar_t xlat[128]; + if (!once) { + XlatAlphabet(xlat, L'𝑎', L'𝐴'); + once = true; + } + return xlat; } static wchar_t *GetXlatBoldItalic(void) { - /* - * Unimplemented. Simply output high-intensity non-italicized characters - * for now. - */ - return GetXlatAscii(); + static bool once; + static wchar_t xlat[128]; + if (!once) { + XlatAlphabet(xlat, L'𝒂', L'𝑨'); + once = true; + } + return xlat; } static wchar_t *GetXlatBoldFraktur(void) { - /* Unimplemented. */ - return GetXlatAscii(); + static bool once; + static wchar_t xlat[128]; + if (!once) { + XlatAlphabet(xlat, L'𝖆', L'𝕬'); + once = true; + } + return xlat; } static wchar_t *GetXlatFraktur(void) { - /* Unimplemented. */ - return GetXlatAscii(); + unsigned i; + static bool once; + static wchar_t xlat[128]; + if (!once) { + for (i = 0; i < ARRAYLEN(xlat); ++i) { + if ('A' <= i && i <= 'Z') { + xlat[i] = L"𝔄𝔅ℭ𝔇𝔈𝔉𝔊ℌℑ𝔍𝔎𝔏𝔐𝔑𝔒𝔓𝔔ℜ𝔖𝔗𝔘𝔙𝔚𝔛𝔜ℨ"[i - 'A']; + } else if ('a' <= i && i <= 'z') { + xlat[i] = i - 'a' + L'𝔞'; + } else { + xlat[i] = i; + } + } + once = true; + } + return xlat; } static wchar_t *GetXlatDoubleWidth(void) { @@ -222,33 +258,15 @@ static void TtySetCodepage(struct Tty *tty, char id) { } } -/** - * Map the currently active foreground & background colors & terminal - * configuration to a VGA text character attribute byte. - * - * @see VGA_USE_BLINK macro (libc/vga/vga.internal.h) - * @see drivers/tty/vt/vt.c in Linux 5.9.14 source code - */ static uint8_t TtyGetVgaAttr(struct Tty *tty) { - uint8_t attr; - if ((tty->pr & kTtyFlip) == 0) - attr = tty->fg | tty->bg << 4; - else - attr = tty->bg | tty->fg << 4; + uint8_t attr = tty->fg | tty->bg << 4; + if ((tty->pr & kTtyBold) != 0) + attr |= 0x08; #ifdef VGA_USE_BLINK - /* - * If blinking is enabled, we can only have the 8 dark background color - * codes (0 to 7). Simply map any bright background color (8 to 15) to - * its corresponding dark color, & call it a day. This is a bit more - * simplistic than what Linux does, but should be enough. - */ - attr &= ~0x80; if ((tty->pr & kTtyBlink) != 0) attr |= 0x80; #endif - if ((tty->pr & kTtyBold) != 0) - attr |= 0x08; return attr; } @@ -553,18 +571,15 @@ static void TtyDeleteLines(struct Tty *tty) { } static void TtyReportDeviceStatus(struct Tty *tty) { - static const char report[] = "\e[0n"; - _TtyWriteInput(tty, report, strlen(report)); + _TtyWriteInput(tty, "\e[0n", 4); } static void TtyReportPreferredVtType(struct Tty *tty) { - static const char report[] = "\e[?1;0c"; - _TtyWriteInput(tty, report, strlen(report)); + _TtyWriteInput(tty, "\e[?1;0c", 4); } static void TtyReportPreferredVtIdentity(struct Tty *tty) { - static const char report[] = "\e/Z"; - _TtyWriteInput(tty, report, strlen(report)); + _TtyWriteInput(tty, "\e/Z", 4); } static void TtyBell(struct Tty *tty) { @@ -624,23 +639,37 @@ static void TtyCsiN(struct Tty *tty) { /** * Map the given (R, G, B) triplet to one of the 16 basic foreground colors - * or one of the 16 background colors. + * or one of the 8 (or 16) background colors. * + * @see VGA_USE_BLINK macro (libc/vga/vga.internal.h) * @see drivers/tty/vt/vt.c in Linux 5.9.14 source code */ -static uint8_t TtyMapTrueColor(uint8_t r, uint8_t g, uint8_t b) +static uint8_t TtyMapTrueColor(uint8_t r, uint8_t g, uint8_t b, bool as_fg) { - uint8_t hue = 0, max = MAX(MAX(r, g), b); - if (r > max / 2) - hue |= 4; - if (g > max / 2) - hue |= 2; - if (b > max / 2) - hue |= 1; - if (hue == 7 && max <= 0x55) - hue = 8; - else if (max > 0xaa) - hue |= 8; + uint8_t hue = 0; +#ifdef VGA_USE_BLINK + if (!as_fg) { + if (r >= 128) + hue |= 4; + if (g >= 128) + hue |= 2; + if (b >= 128) + hue |= 1; + } else +#endif + { + uint8_t max = MAX(MAX(r, g), b); + if (r > max / 2) + hue |= 4; + if (g > max / 2) + hue |= 2; + if (b > max / 2) + hue |= 1; + if (hue == 7 && max <= 0x55) + hue = 8; + else if (max > 0xaa) + hue |= 8; + } return hue; } @@ -650,7 +679,7 @@ static uint8_t TtyMapTrueColor(uint8_t r, uint8_t g, uint8_t b) * * @see drivers/tty/vt/vt.c in Linux 5.9.14 source code */ -static uint8_t TtyMapXtermColor(uint8_t color) +static uint8_t TtyMapXtermColor(uint8_t color, bool as_fg) { uint8_t r, g, b; if (color < 8) { @@ -670,7 +699,7 @@ static uint8_t TtyMapXtermColor(uint8_t color) r = color * 0x55 / 2; } else r = g = b = (unsigned)color * 10 - 2312; - return TtyMapTrueColor(r, g, b); + return TtyMapTrueColor(r, g, b, as_fg); } static void TtySelectGraphicsRendition(struct Tty *tty) { @@ -793,7 +822,13 @@ static void TtySelectGraphicsRendition(struct Tty *tty) { break; case 100 ... 107: code[0] -= 100 - 40; +#ifndef VGA_USE_BLINK + /* + * If blinking is not enabled in VGA text mode, then we can + * use bright background colors. + */ code[0] += 8; +#endif /* fallthrough */ case 40 ... 47: tty->bg = code[0] - 40; @@ -820,7 +855,7 @@ static void TtySelectGraphicsRendition(struct Tty *tty) { if (++code[3] == 3) { code[3] = 0; t = kSgr; - tty->fg = TtyMapTrueColor(code[0], code[1], code[2]); + tty->fg = TtyMapTrueColor(code[0], code[1], code[2], true); tty->pr |= kTtyFg; tty->pr |= kTtyTrue; } @@ -829,20 +864,20 @@ static void TtySelectGraphicsRendition(struct Tty *tty) { if (++code[3] == 3) { code[3] = 0; t = kSgr; - tty->bg = TtyMapTrueColor(code[0], code[1], code[2]); + tty->bg = TtyMapTrueColor(code[0], code[1], code[2], false); tty->pr |= kTtyBg; tty->pr |= kTtyTrue; } break; case kSgrFgXterm: t = kSgr; - tty->fg = TtyMapXtermColor(code[0]); + tty->fg = TtyMapXtermColor(code[0], true); tty->pr |= kTtyFg; tty->pr &= ~kTtyTrue; break; case kSgrBgXterm: t = kSgr; - tty->bg = TtyMapXtermColor(code[0]); + tty->bg = TtyMapXtermColor(code[0], false); tty->pr |= kTtyBg; tty->pr &= ~kTtyTrue; break; @@ -1046,17 +1081,10 @@ static void TtyEscAppend(struct Tty *tty, char c) { } static void TtyUpdateHwCursor(struct Tty *tty) { - unsigned char start = tty->chr_ht - 2, end = tty->chr_ht - 1; unsigned short pos = tty->y * Xn(tty) + tty->x; - if ((tty->conf & kTtyNocursor)) - start |= 1 << 5; - outb(CRTPORT, 0x0A); - outb(CRTPORT + 1, start); - outb(CRTPORT, 0x0B); - outb(CRTPORT + 1, end); - outb(CRTPORT, 0x0E); + outb(CRTPORT, 0x0e); outb(CRTPORT + 1, (unsigned char)(pos >> 8)); - outb(CRTPORT, 0x0F); + outb(CRTPORT, 0x0f); outb(CRTPORT + 1, (unsigned char)pos); } @@ -1176,11 +1204,9 @@ ssize_t _TtyWriteInput(struct Tty *tty, const void *data, size_t n) { if (cr && i < sizeof tty->input.p) { p[i++] = '\n'; } -#ifdef VGA_PERSNICKETY_STATUS if (!(tty->conf & kTtyNoecho)) { _TtyWrite(tty, p + tty->input.i, i - tty->input.i); } -#endif tty->input.i = i; return n; } @@ -1189,7 +1215,6 @@ ssize_t _TtyRead(struct Tty *tty, void *buf, size_t size) { char *p; size_t n; n = MIN(size, tty->input.i); -#ifdef VGA_PERSNICKETY_STATUS if (!(tty->conf & kTtyNocanon)) { if ((p = memchr(tty->input.p, '\n', n))) { n = MIN(n, tty->input.p - p + 1); @@ -1197,7 +1222,6 @@ ssize_t _TtyRead(struct Tty *tty, void *buf, size_t size) { n = 0; } } -#endif memcpy(buf, tty->input.p, n); memcpy(tty->input.p, tty->input.p + n, tty->input.i - n); tty->input.i -= n; diff --git a/libc/vga/vga-init.S b/libc/vga/vga-init.S index 7e332a5d7..8b4c657cc 100644 --- a/libc/vga/vga-init.S +++ b/libc/vga/vga-init.S @@ -72,4 +72,3 @@ vga_console: .endobj vga_console,globl,hidden .previous .yoink sys_writev_vga - .yoink sys_readv_vga diff --git a/libc/vga/vga.internal.h b/libc/vga/vga.internal.h index bfc6488e1..134303139 100644 --- a/libc/vga/vga.internal.h +++ b/libc/vga/vga.internal.h @@ -60,14 +60,6 @@ * (https://www.delorie.com/djgpp/doc/rbinter/id/22/1.html) */ #undef VGA_USE_BLINK -/** - * If VGA_PERSNICKETY_STATUS is defined, then when deciding how to return - * status response codes (e.g. "\e[0n"), the tty code will pay attention to - * the terminal's termios mode (TODO). If undefined, the tty code will - * simply return any response strings immediately, & will not echo them — - * per the common use case. - */ -#undef VGA_PERSNICKETY_STATUS #define kTtyFg 0x0001 #define kTtyBg 0x0002 @@ -117,7 +109,7 @@ struct Tty { uint32_t u8; uint32_t n8; uint32_t pr; - uint8_t fg, bg, chr_ht; + uint8_t fg, bg; uint32_t conf; unsigned short savey, savex; struct VgaTextCharCell *ccs; @@ -142,8 +134,7 @@ struct Tty { }; void _StartTty(struct Tty *, unsigned short, unsigned short, - unsigned short, unsigned short, unsigned char, - void *, wchar_t *); + unsigned short, unsigned short, void *, wchar_t *); ssize_t _TtyRead(struct Tty *, void *, size_t); ssize_t _TtyWrite(struct Tty *, const void *, size_t); ssize_t _TtyWriteInput(struct Tty *, const void *, size_t); @@ -154,9 +145,6 @@ void _TtyErase(struct Tty *, size_t, size_t); void _TtySetY(struct Tty *, unsigned short); void _TtySetX(struct Tty *, unsigned short); -extern struct Tty _vga_tty; - -ssize_t sys_readv_vga(struct Fd *, const struct iovec *, int); ssize_t sys_writev_vga(struct Fd *, const struct iovec *, int); COSMOPOLITAN_C_END_ diff --git a/libc/vga/writev-vga.c b/libc/vga/writev-vga.c index d7a7afa1d..854596441 100644 --- a/libc/vga/writev-vga.c +++ b/libc/vga/writev-vga.c @@ -31,21 +31,20 @@ #include "libc/runtime/pc.internal.h" #include "libc/str/str.h" +static struct Tty vga_tty; #ifdef VGA_USE_WCS static wchar_t vga_wcs[VGA_TTY_HEIGHT * VGA_TTY_WIDTH]; #else static wchar_t * const vga_wcs = NULL; #endif -struct Tty _vga_tty; - ssize_t sys_writev_vga(struct Fd *fd, const struct iovec *iov, int iovlen) { size_t i, wrote = 0; ssize_t res = 0; for (i = 0; i < iovlen; ++i) { - void *output = iov[i].iov_base; + void *input = iov[i].iov_base; size_t len = iov[i].iov_len; - res = _TtyWrite(&_vga_tty, output, len); + res = _TtyWrite(&vga_tty, input, len); if (res < 0) break; wrote += res; @@ -59,23 +58,15 @@ ssize_t sys_writev_vga(struct Fd *fd, const struct iovec *iov, int iovlen) { __attribute__((__constructor__)) static textstartup void _vga_init(void) { void * const vid_buf = (void *)(BANE + 0xb8000ull); - /* - * Get the initial cursor position from the BIOS data area. Also get - * the height (in scan lines) of each character; this is used to set the - * cursor shape. - */ + /* Get the initial cursor position from the BIOS data area. */ typedef struct { unsigned char col, row; } bios_curs_pos_t; bios_curs_pos_t pos = *(bios_curs_pos_t *)(BANE + 0x0450ull); - uint8_t chr_ht = *(uint8_t *)(BANE + 0x0485ull), - chr_ht_hi = *(uint8_t *)(BANE + 0x0486ull); - if (chr_ht_hi != 0 || chr_ht > 32) - chr_ht = 32; /* - * Initialize our tty structure from the current screen contents, current - * cursor position, & character height. + * Initialize our tty structure from the current screen contents & current + * cursor position. */ - _StartTty(&_vga_tty, VGA_TTY_HEIGHT, VGA_TTY_WIDTH, pos.row, pos.col, - chr_ht, vid_buf, vga_wcs); + _StartTty(&vga_tty, VGA_TTY_HEIGHT, VGA_TTY_WIDTH, pos.row, pos.col, + vid_buf, vga_wcs); } diff --git a/libc/zipos/free.c b/libc/zipos/free.c index 512a71eba..5962dcc3a 100644 --- a/libc/zipos/free.c +++ b/libc/zipos/free.c @@ -23,6 +23,11 @@ #include "libc/str/str.h" #include "libc/zipos/zipos.internal.h" +/** + * Frees ZipOS handle. + * @asyncsignalsafe + * @threadsafe + */ void __zipos_free(struct Zipos *z, struct ZiposHandle *h) { if (IsAsan()) { __asan_poison((char *)h + sizeof(struct ZiposHandle), diff --git a/libc/zipos/get.c b/libc/zipos/get.c index 352b3475d..6511d5526 100644 --- a/libc/zipos/get.c +++ b/libc/zipos/get.c @@ -20,7 +20,7 @@ #include "libc/calls/strace.internal.h" #include "libc/intrin/cmpxchg.h" #include "libc/intrin/promises.internal.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" #include "libc/macros.internal.h" #include "libc/runtime/runtime.h" #include "libc/sysv/consts/map.h" diff --git a/libc/zipos/lock.c b/libc/zipos/lock.c index 464619308..1b86a9346 100644 --- a/libc/zipos/lock.c +++ b/libc/zipos/lock.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" #include "libc/zipos/zipos.internal.h" static pthread_mutex_t __zipos_lock_obj; diff --git a/libc/zipos/open.c b/libc/zipos/open.c index 44979f08e..ad6ad9918 100644 --- a/libc/zipos/open.c +++ b/libc/zipos/open.c @@ -18,7 +18,6 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" #include "libc/calls/calls.h" -#include "libc/calls/extend.internal.h" #include "libc/calls/internal.h" #include "libc/calls/state.internal.h" #include "libc/calls/struct/sigset.h" @@ -43,19 +42,47 @@ #include "libc/zip.h" #include "libc/zipos/zipos.internal.h" -static char *mapend; -static size_t maptotal; +static volatile size_t maptotal; + +static pureconst size_t __zipos_granularity(void) { + return FRAMESIZE * (IsAsan() ? 8 : 1); +} static void *__zipos_mmap(size_t mapsize) { - char *start; size_t offset; + struct DirectMap dm; + int rc, prot, flags; + uint64_t addr, addr2, addr3; assert(mapsize); - offset = maptotal; - maptotal += mapsize; - start = (char *)0x6fd000040000; - if (!mapend) mapend = start; - mapend = _extend(start, maptotal, mapend, 0x6fdfffff0000); - return start + offset; + do offset = maptotal; + while (!_cmpxchg(&maptotal, offset, maptotal + mapsize)); + if (offset + mapsize <= kMemtrackZiposSize) { + prot = PROT_READ | PROT_WRITE; + flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED; + addr = kMemtrackZiposStart + offset; + if ((dm = sys_mmap((void *)addr, mapsize, prot, flags, -1, 0)).addr != + MAP_FAILED) { + rc = TrackMemoryInterval(&_mmi, addr >> 16, (addr + mapsize - 1) >> 16, + dm.maphandle, prot, flags, false, false, 0, + mapsize); + assert(!rc); + if (IsAsan()) { + addr2 = (addr >> 3) + 0x7fff8000; + addr3 = ((addr + mapsize) >> 3) + 0x7fff8000; + assert(!(addr2 & (FRAMESIZE - 1))); + assert(!(addr3 & (FRAMESIZE - 1))); + dm = sys_mmap((void *)addr2, mapsize >> 3, prot, flags, -1, 0); + assert(dm.addr != MAP_FAILED); + rc = TrackMemoryInterval(&_mmi, addr2 >> 16, (addr3 >> 16) - 1, + dm.maphandle, prot, flags, false, false, 0, + mapsize >> 3); + assert(!rc); + } + return (void *)addr; + } + } + enomem(); + return 0; } static struct ZiposHandle *__zipos_alloc(struct Zipos *zipos, size_t size) { @@ -63,7 +90,7 @@ static struct ZiposHandle *__zipos_alloc(struct Zipos *zipos, size_t size) { struct ZiposHandle *h, **ph; __zipos_lock(); mapsize = sizeof(struct ZiposHandle) + size; - mapsize = ROUNDUP(mapsize, 4096); + mapsize = ROUNDUP(mapsize, __zipos_granularity()); StartOver: ph = &zipos->freelist; while ((h = *ph)) { diff --git a/libc/zipos/zipos.h b/libc/zipos/zipos.h deleted file mode 100755 index e69de29bb..000000000 diff --git a/libc/zipos/zipos.internal.h b/libc/zipos/zipos.internal.h index 764916634..6fb612e6a 100644 --- a/libc/zipos/zipos.internal.h +++ b/libc/zipos/zipos.internal.h @@ -1,7 +1,8 @@ #ifndef COSMOPOLITAN_LIBC_ZIPOS_ZIPOS_H_ #define COSMOPOLITAN_LIBC_ZIPOS_ZIPOS_H_ -#include "libc/intrin/nopl.internal.h" -#include "libc/thread/tls.h" +#include "libc/calls/calls.h" +#include "libc/intrin/nopl.h" +#include "libc/nexgen32e/threaded.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ diff --git a/test/libc/calls/fcntl_test.c b/test/libc/calls/fcntl_test.c index dee11c127..e4c4a4ce6 100644 --- a/test/libc/calls/fcntl_test.c +++ b/test/libc/calls/fcntl_test.c @@ -73,6 +73,58 @@ TEST(fcntl, getfd) { void OnSig(int sig) { } +TEST(posixAdvisoryLocks, oneProcess_unlockedFromOwnPerspectiveHuh) { + struct flock lock; + ASSERT_SYS(0, 3, open("foo", O_RDWR | O_CREAT | O_TRUNC, 0644)); + ASSERT_SYS(0, 5, write(3, "hello", 5)); + + // set lock + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_SET; + lock.l_start = 1; + lock.l_len = 3; + lock.l_pid = -2; + ASSERT_SYS(0, 0, fcntl(3, F_SETLK, &lock)); + EXPECT_EQ(F_WRLCK, lock.l_type); + EXPECT_EQ(SEEK_SET, lock.l_whence); + EXPECT_EQ(1, lock.l_start); + EXPECT_EQ(3, lock.l_len); + EXPECT_EQ(-2, lock.l_pid); + + ASSERT_SYS(0, 4, open("foo", O_RDWR)); + + // try lock + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 0; + lock.l_pid = -1; + ASSERT_SYS(0, 0, fcntl(4, F_SETLK, &lock)); + EXPECT_EQ(F_WRLCK, lock.l_type); + EXPECT_EQ(SEEK_SET, lock.l_whence); + EXPECT_EQ(0, lock.l_start); + EXPECT_EQ(0, lock.l_len); + EXPECT_EQ(-1, lock.l_pid); + + // get lock information + if (!IsWindows()) { + lock.l_type = F_RDLCK; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 0; + lock.l_pid = -7; + ASSERT_SYS(0, 0, fcntl(4, F_GETLK, &lock)); + EXPECT_EQ(F_UNLCK, lock.l_type); + EXPECT_EQ(SEEK_SET, lock.l_whence); + EXPECT_EQ(0, lock.l_start); + EXPECT_EQ(0, lock.l_len); + EXPECT_EQ(-7, lock.l_pid); // doesn't change due to F_UNLCK + } + + ASSERT_SYS(0, 0, close(4)); + ASSERT_SYS(0, 0, close(3)); +} + TEST(posixAdvisoryLocks, twoProcesses) { if (IsWindows()) return; // due to signals diff --git a/test/libc/calls/lock_test.c b/test/libc/calls/lock_test.c deleted file mode 100644 index 7858733b0..000000000 --- a/test/libc/calls/lock_test.c +++ /dev/null @@ -1,183 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2022 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/calls/calls.h" -#include "libc/calls/struct/flock.h" -#include "libc/dce.h" -#include "libc/errno.h" -#include "libc/sysv/consts/f.h" -#include "libc/sysv/consts/o.h" -#include "libc/testlib/testlib.h" -#include "libc/thread/thread.h" - -/** - * @fileoverview POSIX Advisory Locks Test - */ - -#define PROCESSES 8 -#define THREADS (IsWindows() ? 8 : 0) -#define RATIO 3 -#define ITERATIONS 10 - -char testlib_enable_tmp_setup_teardown; - -_Thread_local const char *kind; - -void Log(const char *fmt, ...) { -#if 0 - va_list va; - char b[512]; - int i, n = sizeof(b); - va_start(va, fmt); - i = ksnprintf(b, n, "%s pid=%d tid=%d ", kind, getpid(), gettid()); - i += kvsnprintf(b + i, MAX(0, n - i), fmt, va); - i += ksnprintf(b + i, MAX(0, n - i), "\n"); - write(2, b, MIN(i, n)); - va_end(va); -#endif -} - -void Lock(int fd, int type, long start, long len) { - int e; - struct flock lock = { - .l_type = type, - .l_whence = SEEK_SET, - .l_start = start, - .l_len = len, - }; - e = errno; - while (fcntl(fd, F_SETLK, &lock)) { - ASSERT_TRUE(errno == EAGAIN || errno == EACCES); - errno = e; - Log("flock spinning on %d", fd); - } -} - -void WriteLock(int fd, long start, long len) { - Lock(fd, F_WRLCK, start, len); - Log("acquired write lock on %d", fd); -} - -void ReadLock(int fd, long start, long len) { - Lock(fd, F_RDLCK, start, len); - Log("acquired read lock on %d", fd); -} - -void Unlock(int fd, long start, long len) { - Lock(fd, F_UNLCK, start, len); - Log("released lock on %d", fd); -} - -void *Reader(void *arg) { - int i, j, fd; - char buf[10]; - kind = arg; - ASSERT_NE(-1, (fd = open("db", O_RDONLY))); - for (j = 0; j < ITERATIONS; ++j) { - ReadLock(fd, 10, 10); - for (i = 0; i < 10; ++i) { - ASSERT_SYS(0, 1, pread(fd, buf + i, 1, 10 + i)); - ASSERT_EQ(buf[0], buf[i]); - } - Unlock(fd, 10, 10); - sched_yield(); - } - ASSERT_SYS(0, 0, close(fd)); - return 0; -} - -void *Writer(void *arg) { - int i, j, fd; - char buf[10]; - kind = arg; - ASSERT_NE(-1, (fd = open("db", O_RDWR))); - for (j = 0; j < ITERATIONS; ++j) { - WriteLock(fd, 10, 10); - for (i = 0; i < 10; ++i) { - ASSERT_SYS(0, 1, pread(fd, buf + i, 1, 10 + i)); - ASSERT_EQ(buf[0], buf[i]); - } - for (i = 0; i < 10; ++i) { - buf[i]++; - } - for (i = 0; i < 10; ++i) { - ASSERT_SYS(0, 1, pwrite(fd, buf + i, 1, 10 + i)); - } - Unlock(fd, 10, 10); - sched_yield(); - } - ASSERT_SYS(0, 0, close(fd)); - return 0; -} - -TEST(posixAdvisoryLocks, threadsAndProcessesFightingForLock) { - int i, rc, pid, fd, ws; - pthread_t th[THREADS + 1]; - - ASSERT_SYS(0, 3, creat("db", 0644)); - ASSERT_SYS(0, 0, ftruncate(3, 30)); - ASSERT_SYS(0, 0, close(3)); - - for (i = 0; i < THREADS; ++i) { - if (i % RATIO == 0) { - ASSERT_EQ(0, pthread_create(th + i, 0, Reader, "reader thread")); - } else { - ASSERT_EQ(0, pthread_create(th + i, 0, Writer, "writer thread")); - } - } - - for (i = 0; i < PROCESSES; ++i) { - ASSERT_NE(-1, (rc = fork())); - if (!rc) { - if (i % RATIO == 0) { - Writer("writer process"); - } else { - Reader("reader process"); - } - _Exit(0); - } - } - - ASSERT_NE(-1, (fd = open("db", O_RDWR))); - Lock(fd, F_WRLCK, 0, 10); - Lock(fd, F_WRLCK, 20, 10); - - for (i = 0; i < THREADS; ++i) { - ASSERT_EQ(0, pthread_join(th[i], 0)); - } - - kind = "main process"; - for (;;) { - int e = errno; - if ((pid = waitpid(0, &ws, 0)) != -1) { - if (WIFSIGNALED(ws)) { - Log("process %d terminated with %G", pid, WTERMSIG(ws)); - testlib_incrementfailed(); - } else if (WEXITSTATUS(ws)) { - Log("process %d exited with %d", pid, WEXITSTATUS(ws)); - testlib_incrementfailed(); - } - } else { - ASSERT_EQ(ECHILD, errno); - errno = e; - break; - } - } - - ASSERT_SYS(0, 0, close(fd)); -} diff --git a/test/libc/calls/pledge_test.c b/test/libc/calls/pledge_test.c index f777df43d..b037d50f8 100644 --- a/test/libc/calls/pledge_test.c +++ b/test/libc/calls/pledge_test.c @@ -36,7 +36,7 @@ #include "libc/runtime/runtime.h" #include "libc/sock/sock.h" #include "libc/sock/struct/sockaddr.h" -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" #include "libc/sysv/consts/af.h" #include "libc/sysv/consts/at.h" @@ -560,7 +560,7 @@ TEST(pledge_openbsd, bigSyscalls) { int LockWorker(void *arg, int tid) { flockfile(stdout); - ASSERT_EQ(gettid(), stdout->lock._lock & 0x000fffff); + ASSERT_EQ(gettid(), stdout->lock.lock); funlockfile(stdout); return 0; } diff --git a/test/libc/calls/preadv_test.c b/test/libc/calls/preadv_test.c deleted file mode 100644 index 334918e93..000000000 --- a/test/libc/calls/preadv_test.c +++ /dev/null @@ -1,88 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2022 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/calls/calls.h" -#include "libc/calls/struct/iovec.h" -#include "libc/errno.h" -#include "libc/sysv/consts/o.h" -#include "libc/testlib/testlib.h" - -char testlib_enable_tmp_setup_teardown; - -TEST(preadv, ebadf) { - EXPECT_SYS(EBADF, -1, preadv(-1, 0, 0, 0)); - EXPECT_SYS(EBADF, -1, pwritev(-1, 0, 0, 0)); -} - -TEST(preadv, testOneIovecWorks_itDelegatesToPread) { - char b1[2] = "hi"; - struct iovec v[1] = {{b1, 2}}; - ASSERT_SYS(0, 3, open("foo", O_RDWR | O_CREAT | O_TRUNC, 0644)); - EXPECT_SYS(0, 2, pwritev(3, v, 1, 0)); - b1[0] = 0; - b1[1] = 0; - EXPECT_SYS(0, 0, preadv(3, 0, 0, 0)); - EXPECT_SYS(0, 0, pwritev(3, 0, 0, 0)); - EXPECT_SYS(0, 2, preadv(3, v, 1, 0)); - ASSERT_EQ('h', b1[0]); - ASSERT_EQ('i', b1[1]); - b1[0] = 0; - b1[1] = 0x55; - EXPECT_SYS(0, 1, preadv(3, v, 1, 1)); - ASSERT_EQ('i', b1[0]); - ASSERT_EQ(0x55, b1[1]); - ASSERT_SYS(0, 0, close(3)); -} - -TEST(preadv, testTwoIovecWorks_itMustDoItsThing) { - char b1[1] = "h"; - char b2[1] = "i"; - struct iovec v[2] = {{b1, 1}, {b2, 1}}; - ASSERT_SYS(0, 3, open("foo", O_RDWR | O_CREAT | O_TRUNC, 0644)); - EXPECT_SYS(0, 2, pwritev(3, v, 2, 0)); - b1[0] = 0; - b2[0] = 0; - EXPECT_SYS(0, 2, preadv(3, v, 2, 0)); - ASSERT_EQ('h', b1[0]); - ASSERT_EQ('i', b2[0]); - b1[0] = 0; - b2[0] = 0x55; - EXPECT_SYS(0, 1, preadv(3, v, 2, 1)); - ASSERT_EQ('i', b1[0]); - ASSERT_EQ(0x55, b2[0]); - ASSERT_SYS(0, 0, close(3)); -} - -TEST(pwritev, writePastEof_zeroExtendsFile_alsoDoesntChangeFilePointer) { - char b[6] = {0x55, 0x55, 0x55, 0x55, 0x55, 0x55}; - char s[2] = "hi"; - struct iovec v[1] = {{s, 2}}; - ASSERT_SYS(0, 3, open("foo", O_RDWR | O_CREAT | O_TRUNC, 0644)); - ASSERT_SYS(EINVAL, -1, pwritev(3, v, 1, -999)); - ASSERT_SYS(0, 2, pwritev(3, v, 1, 0)); - ASSERT_SYS(0, 2, pwritev(3, v, 1, 4)); - ASSERT_SYS(0, 2, preadv(3, v, 1, 0)); - ASSERT_SYS(0, 6, read(3, b, 6)); - ASSERT_EQ('h', b[0]); - ASSERT_EQ('i', b[1]); - ASSERT_EQ(0, b[2]); - ASSERT_EQ(0, b[3]); - ASSERT_EQ('h', b[4]); - ASSERT_EQ('i', b[5]); - ASSERT_SYS(0, 0, close(3)); -} diff --git a/test/libc/calls/reservefd_test.c b/test/libc/calls/reservefd_test.c index 0d958625f..a513cb243 100644 --- a/test/libc/calls/reservefd_test.c +++ b/test/libc/calls/reservefd_test.c @@ -23,7 +23,9 @@ #include "libc/calls/struct/rlimit.h" #include "libc/calls/struct/sigaction.h" #include "libc/errno.h" +#include "libc/intrin/wait0.internal.h" #include "libc/macros.internal.h" +#include "libc/nexgen32e/threaded.h" #include "libc/runtime/internal.h" #include "libc/runtime/stack.h" #include "libc/stdio/rand.h" @@ -39,8 +41,6 @@ #include "libc/testlib/hyperion.h" #include "libc/testlib/testlib.h" #include "libc/thread/spawn.h" -#include "libc/thread/tls.h" -#include "libc/thread/wait0.internal.h" #include "libc/time/struct/tm.h" #include "libc/time/time.h" @@ -61,6 +61,7 @@ void PullSomeZipFilesIntoLinkage(void) { TEST(reservefd, testGrowthOfFdsDataStructure) { int i, n; struct rlimit rlim; + ASSERT_EQ(g_fds.n, OPEN_MAX); n = 1700; // pe '2**16/40' → 1638 (likely value of g_fds.n) if (!getrlimit(RLIMIT_NOFILE, &rlim)) n = MIN(n, rlim.rlim_cur - 3); for (i = 0; i < n; ++i) { diff --git a/test/libc/calls/test.mk b/test/libc/calls/test.mk index a4fbbe871..16c467504 100644 --- a/test/libc/calls/test.mk +++ b/test/libc/calls/test.mk @@ -103,9 +103,6 @@ o/$(MODE)/test/libc/calls/poll_test.com.runs: \ o/$(MODE)/test/libc/calls/fcntl_test.com.runs: \ private .PLEDGE = stdio rpath wpath cpath fattr proc flock -o/$(MODE)/test/libc/calls/lock_test.com.runs: \ - private .PLEDGE = stdio rpath wpath cpath fattr proc flock - o/$(MODE)/test/libc/calls/openbsd_test.com.runs: \ private .PLEDGE = stdio rpath wpath cpath fattr proc unveil diff --git a/test/libc/stdio/ecvt_test.c b/test/libc/fmt/fcvt_test.c similarity index 100% rename from test/libc/stdio/ecvt_test.c rename to test/libc/fmt/fcvt_test.c diff --git a/test/libc/thread/pthread_barrier_wait_test.c b/test/libc/intrin/pthread_barrier_wait_test.c similarity index 95% rename from test/libc/thread/pthread_barrier_wait_test.c rename to test/libc/intrin/pthread_barrier_wait_test.c index dd9942fed..baa0d4acb 100644 --- a/test/libc/thread/pthread_barrier_wait_test.c +++ b/test/libc/intrin/pthread_barrier_wait_test.c @@ -17,17 +17,16 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" -#include "libc/atomic.h" #include "libc/errno.h" #include "libc/intrin/atomic.h" +#include "libc/intrin/pthread.h" #include "libc/mem/mem.h" #include "libc/runtime/gc.internal.h" #include "libc/testlib/testlib.h" #include "libc/thread/spawn.h" -#include "libc/thread/thread.h" int i, n; -atomic_int p, w; +_Atomic(int) p, w; pthread_barrier_t barrier; int Worker(void *arg, int tid) { @@ -37,6 +36,9 @@ int Worker(void *arg, int tid) { ASSERT_GE(rc, 0); if (rc == PTHREAD_BARRIER_SERIAL_THREAD) { atomic_fetch_add(&p, 1); + ASSERT_EQ(0, barrier.popped); + ASSERT_EQ(0, barrier.waits); + ASSERT_EQ(n, barrier.count); } return 0; } diff --git a/test/libc/intrin/pthread_cond_broadcast_test.c b/test/libc/intrin/pthread_cond_broadcast_test.c new file mode 100644 index 000000000..9d944690d --- /dev/null +++ b/test/libc/intrin/pthread_cond_broadcast_test.c @@ -0,0 +1,123 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 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/calls/struct/timespec.h" +#include "libc/errno.h" +#include "libc/intrin/atomic.h" +#include "libc/intrin/kprintf.h" +#include "libc/intrin/pthread.h" +#include "libc/intrin/pthread2.h" +#include "libc/mem/mem.h" +#include "libc/runtime/gc.internal.h" +#include "libc/testlib/testlib.h" +#include "libc/thread/spawn.h" + +// TODO(jart): Re-enable me. +#if 0 +_Atomic(int) bReady; +pthread_cond_t bCond; +pthread_mutex_t bMutex; + +int BroadcastWorker(void *arg, int tid) { + ASSERT_EQ(0, pthread_mutex_lock(&bMutex)); + atomic_fetch_add(&bReady, 1); + ASSERT_EQ(0, pthread_cond_wait(&bCond, &bMutex)); + ASSERT_EQ(0, pthread_mutex_unlock(&bMutex)); + return 0; +} + +TEST(pthread_cond_broadcast, test) { + pthread_condattr_t cAttr; + pthread_mutexattr_t mAttr; + ASSERT_EQ(0, pthread_mutexattr_init(&mAttr)); + ASSERT_EQ(0, pthread_mutexattr_settype(&mAttr, PTHREAD_MUTEX_ERRORCHECK)); + ASSERT_EQ(0, pthread_mutex_init(&bMutex, &mAttr)); + ASSERT_EQ(0, pthread_condattr_init(&cAttr)); + ASSERT_EQ(0, pthread_condattr_setpshared(&cAttr, PTHREAD_PROCESS_PRIVATE)); + ASSERT_EQ(0, pthread_cond_init(&bCond, &cAttr)); + int i, n = 16; + struct spawn *t = gc(malloc(sizeof(struct spawn) * n)); + for (i = 0; i < n; ++i) { + ASSERT_SYS(0, 0, _spawn(BroadcastWorker, 0, t + i)); + } + for (;;) { + if (atomic_load_explicit(&bReady, memory_order_relaxed) == n) { + break; + } else { + pthread_yield(); + } + } + ASSERT_EQ(0, pthread_mutex_lock(&bMutex)); + ASSERT_EQ(0, pthread_cond_broadcast(&bCond)); + ASSERT_EQ(0, pthread_mutex_unlock(&bMutex)); + for (i = 0; i < n; ++i) { + EXPECT_SYS(0, 0, _join(t + i)); + } + ASSERT_EQ(0, pthread_mutex_destroy(&bMutex)); + ASSERT_EQ(0, pthread_cond_destroy(&bCond)); +} + +TEST(pthread_cond_timedwait, testTimeoutInPast_timesOutImmediately) { + struct timespec t = {100000}; + pthread_cond_t c = PTHREAD_COND_INITIALIZER; + pthread_mutex_t m = {PTHREAD_MUTEX_ERRORCHECK}; + ASSERT_EQ(0, pthread_mutex_lock(&m)); + ASSERT_EQ(ETIMEDOUT, pthread_cond_timedwait(&c, &m, &t)); + ASSERT_EQ(0, pthread_mutex_unlock(&m)); +} + +_Atomic(int) tReady; +pthread_cond_t tCond; +pthread_mutex_t tMutex; + +int TimedWorker(void *arg, int tid) { + struct timespec ts; + ASSERT_EQ(0, pthread_mutex_lock(&tMutex)); + atomic_fetch_add(&tReady, 1); + ts = _timespec_add(_timespec_mono(), _timespec_frommillis(30000)); + ASSERT_EQ(0, pthread_cond_timedwait(&tCond, &tMutex, &ts)); + ASSERT_EQ(0, pthread_mutex_unlock(&tMutex)); + return 0; +} + +TEST(pthread_cond_timedwait, testThirtySeconds_doesntTimeOut) { + pthread_condattr_t cAttr; + pthread_mutexattr_t mAttr; + ASSERT_EQ(0, pthread_mutexattr_init(&mAttr)); + ASSERT_EQ(0, pthread_mutexattr_settype(&mAttr, PTHREAD_MUTEX_ERRORCHECK)); + ASSERT_EQ(0, pthread_mutex_init(&tMutex, &mAttr)); + ASSERT_EQ(0, pthread_condattr_init(&cAttr)); + ASSERT_EQ(0, pthread_condattr_setpshared(&cAttr, PTHREAD_PROCESS_PRIVATE)); + ASSERT_EQ(0, pthread_cond_init(&tCond, &cAttr)); + struct spawn t; + ASSERT_SYS(0, 0, _spawn(TimedWorker, 0, &t)); + for (;;) { + if (atomic_load_explicit(&tReady, memory_order_relaxed)) { + break; + } else { + pthread_yield(); + } + } + ASSERT_EQ(0, pthread_mutex_lock(&tMutex)); + ASSERT_EQ(0, pthread_cond_signal(&tCond)); + ASSERT_EQ(0, pthread_mutex_unlock(&tMutex)); + EXPECT_SYS(0, 0, _join(&t)); + ASSERT_EQ(0, pthread_mutex_destroy(&tMutex)); + ASSERT_EQ(0, pthread_cond_destroy(&tCond)); +} +#endif diff --git a/test/libc/intrin/pthread_mutex_lock2_test.c b/test/libc/intrin/pthread_mutex_lock2_test.c index c582c6167..e8d8794f9 100644 --- a/test/libc/intrin/pthread_mutex_lock2_test.c +++ b/test/libc/intrin/pthread_mutex_lock2_test.c @@ -16,25 +16,23 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/atomic.h" #include "libc/calls/calls.h" #include "libc/calls/struct/timespec.h" #include "libc/intrin/atomic.h" +#include "libc/intrin/pthread.h" #include "libc/mem/mem.h" #include "libc/runtime/gc.internal.h" #include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" #include "libc/thread/posixthread.internal.h" #include "libc/thread/spawn.h" -#include "libc/thread/thread.h" -#include "third_party/nsync/mu.h" int THREADS = 16; int ITERATIONS = 100; int count; -atomic_int started; -atomic_int finished; +_Atomic(int) started; +_Atomic(int) finished; pthread_mutex_t lock; pthread_mutexattr_t attr; @@ -110,50 +108,29 @@ void BenchLockUnlock(pthread_mutex_t *m) { pthread_mutex_unlock(m); } -void BenchLockUnlockNsync(nsync_mu *m) { - nsync_mu_lock(m); - nsync_mu_unlock(m); -} - BENCH(pthread_mutex_lock, bench_uncontended) { { pthread_spinlock_t s = {0}; EZBENCH2("spin 1x", donothing, BenchSpinUnspin(&s)); } { - nsync_mu m = {0}; - EZBENCH2("nsync 1x", donothing, BenchLockUnlockNsync(&m)); - } - { - pthread_mutex_t m; - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); - pthread_mutex_init(&m, &attr); + pthread_mutex_t m = {PTHREAD_MUTEX_NORMAL}; EZBENCH2("normal 1x", donothing, BenchLockUnlock(&m)); } { - pthread_mutex_t m; - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&m, &attr); + pthread_mutex_t m = {PTHREAD_MUTEX_RECURSIVE}; EZBENCH2("recursive 1x", donothing, BenchLockUnlock(&m)); } { - pthread_mutex_t m; - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); - pthread_mutex_init(&m, &attr); + pthread_mutex_t m = {PTHREAD_MUTEX_ERRORCHECK}; EZBENCH2("errorcheck 1x", donothing, BenchLockUnlock(&m)); } } struct SpinContentionArgs { pthread_spinlock_t *spin; - atomic_char done; - atomic_char ready; + _Atomic(char) done; + _Atomic(char) ready; }; int SpinContentionWorker(void *arg, int tid) { @@ -168,8 +145,8 @@ int SpinContentionWorker(void *arg, int tid) { struct MutexContentionArgs { pthread_mutex_t *mutex; - atomic_char done; - atomic_char ready; + _Atomic(char) done; + _Atomic(char) ready; }; int MutexContentionWorker(void *arg, int tid) { @@ -182,22 +159,6 @@ int MutexContentionWorker(void *arg, int tid) { return 0; } -struct NsyncContentionArgs { - nsync_mu *nsync; - atomic_char done; - atomic_char ready; -}; - -int NsyncContentionWorker(void *arg, int tid) { - struct NsyncContentionArgs *a = arg; - while (!atomic_load_explicit(&a->done, memory_order_relaxed)) { - nsync_mu_lock(a->nsync); - atomic_store_explicit(&a->ready, 1, memory_order_relaxed); - nsync_mu_unlock(a->nsync); - } - return 0; -} - BENCH(pthread_mutex_lock, bench_contended) { struct spawn t; { @@ -210,20 +171,7 @@ BENCH(pthread_mutex_lock, bench_contended) { _join(&t); } { - nsync_mu m = {0}; - struct NsyncContentionArgs a = {&m}; - _spawn(NsyncContentionWorker, &a, &t); - while (!a.ready) sched_yield(); - EZBENCH2("nsync 2x", donothing, BenchLockUnlockNsync(&m)); - a.done = true; - _join(&t); - } - { - pthread_mutex_t m; - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); - pthread_mutex_init(&m, &attr); + pthread_mutex_t m = {PTHREAD_MUTEX_NORMAL}; struct MutexContentionArgs a = {&m}; _spawn(MutexContentionWorker, &a, &t); while (!a.ready) sched_yield(); @@ -232,11 +180,7 @@ BENCH(pthread_mutex_lock, bench_contended) { _join(&t); } { - pthread_mutex_t m; - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&m, &attr); + pthread_mutex_t m = {PTHREAD_MUTEX_RECURSIVE}; struct MutexContentionArgs a = {&m}; _spawn(MutexContentionWorker, &a, &t); while (!a.ready) sched_yield(); @@ -245,11 +189,7 @@ BENCH(pthread_mutex_lock, bench_contended) { _join(&t); } { - pthread_mutex_t m; - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); - pthread_mutex_init(&m, &attr); + pthread_mutex_t m = {PTHREAD_MUTEX_ERRORCHECK}; struct MutexContentionArgs a = {&m}; _spawn(MutexContentionWorker, &a, &t); while (!a.ready) sched_yield(); diff --git a/test/libc/intrin/pthread_mutex_lock_test.c b/test/libc/intrin/pthread_mutex_lock_test.c index 396bb9319..97e4f515a 100644 --- a/test/libc/intrin/pthread_mutex_lock_test.c +++ b/test/libc/intrin/pthread_mutex_lock_test.c @@ -17,15 +17,18 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" -#include "libc/atomic.h" #include "libc/calls/calls.h" #include "libc/calls/state.internal.h" #include "libc/calls/strace.internal.h" #include "libc/errno.h" +#include "libc/intrin/futex.internal.h" +#include "libc/intrin/pthread.h" +#include "libc/intrin/wait0.internal.h" #include "libc/log/check.h" #include "libc/macros.internal.h" #include "libc/math.h" #include "libc/mem/mem.h" +#include "libc/nexgen32e/threaded.h" #include "libc/runtime/gc.internal.h" #include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" @@ -38,15 +41,13 @@ #include "libc/testlib/testlib.h" #include "libc/thread/spawn.h" #include "libc/thread/thread.h" -#include "libc/thread/tls.h" -#include "libc/thread/wait0.internal.h" #define THREADS 8 #define ITERATIONS 512 int count; -atomic_int started; -atomic_int finished; +_Atomic(int) started; +_Atomic(int) finished; pthread_mutex_t mylock; pthread_spinlock_t slock; struct spawn th[THREADS]; diff --git a/test/libc/intrin/pthread_once_test.c b/test/libc/intrin/pthread_once_test.c index e51f7a574..9032724a8 100644 --- a/test/libc/intrin/pthread_once_test.c +++ b/test/libc/intrin/pthread_once_test.c @@ -16,19 +16,17 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/atomic.h" #include "libc/intrin/atomic.h" +#include "libc/intrin/pthread.h" #include "libc/mem/mem.h" #include "libc/runtime/gc.internal.h" #include "libc/testlib/testlib.h" #include "libc/thread/spawn.h" -#include "libc/thread/thread.h" int i, n; struct spawn *t; -atomic_int x, y; +_Atomic(int) x, y; pthread_barrier_t b; -static pthread_once_t once = PTHREAD_ONCE_INIT; void InitFactory(void) { ASSERT_EQ(0, atomic_load(&x)); @@ -36,6 +34,7 @@ void InitFactory(void) { } int Worker(void *arg, int tid) { + static pthread_once_t once = PTHREAD_ONCE_INIT; pthread_barrier_wait(&b); ASSERT_EQ(0, pthread_once(&once, InitFactory)); ASSERT_EQ(1, atomic_load(&y)); @@ -46,11 +45,10 @@ int Worker(void *arg, int tid) { TEST(pthread_once, test) { n = 32; x = y = 0; - ASSERT_EQ(0, pthread_barrier_init(&b, 0, n)); + pthread_barrier_init(&b, 0, n); t = gc(malloc(sizeof(struct spawn) * n)); for (i = 0; i < n; ++i) ASSERT_SYS(0, 0, _spawn(Worker, 0, t + i)); for (i = 0; i < n; ++i) EXPECT_SYS(0, 0, _join(t + i)); ASSERT_EQ(n, atomic_load(&x)); ASSERT_EQ(1, atomic_load(&y)); - ASSERT_EQ(0, pthread_barrier_destroy(&b)); } diff --git a/test/libc/thread/pthread_rwlock_rdlock_test.c b/test/libc/intrin/pthread_rwlock_rdlock_test.c similarity index 83% rename from test/libc/thread/pthread_rwlock_rdlock_test.c rename to test/libc/intrin/pthread_rwlock_rdlock_test.c index 28f4eab48..6eaf59a9a 100644 --- a/test/libc/thread/pthread_rwlock_rdlock_test.c +++ b/test/libc/intrin/pthread_rwlock_rdlock_test.c @@ -16,28 +16,27 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/atomic.h" +#include "libc/intrin/pthread.h" #include "libc/mem/mem.h" -#include "libc/runtime/gc.h" +#include "libc/runtime/gc.internal.h" #include "libc/testlib/testlib.h" #include "libc/thread/spawn.h" -#include "libc/thread/thread.h" #define ITERATIONS 50000 #define READERS 8 #define WRITERS 2 -atomic_int reads; -atomic_int writes; +_Atomic(int) reads; +_Atomic(int) writes; pthread_rwlock_t lock; pthread_barrier_t barrier; int Reader(void *arg, int tid) { pthread_barrier_wait(&barrier); for (int i = 0; i < ITERATIONS; ++i) { - ASSERT_EQ(0, pthread_rwlock_rdlock(&lock)); + pthread_rwlock_rdlock(&lock); ++reads; - ASSERT_EQ(0, pthread_rwlock_unlock(&lock)); + pthread_rwlock_unlock(&lock); } return 0; } @@ -45,17 +44,17 @@ int Reader(void *arg, int tid) { int Writer(void *arg, int tid) { pthread_barrier_wait(&barrier); for (int i = 0; i < ITERATIONS; ++i) { - ASSERT_EQ(0, pthread_rwlock_wrlock(&lock)); + pthread_rwlock_wrlock(&lock); ++writes; - ASSERT_EQ(0, pthread_rwlock_unlock(&lock)); + pthread_rwlock_unlock(&lock); } return 0; } TEST(pthread_rwlock_rdlock, test) { int i; - struct spawn *t = _gc(malloc(sizeof(struct spawn) * (READERS + WRITERS))); - ASSERT_EQ(0, pthread_barrier_init(&barrier, 0, READERS + WRITERS)); + struct spawn *t = gc(malloc(sizeof(struct spawn) * (READERS + WRITERS))); + pthread_barrier_init(&barrier, 0, READERS + WRITERS); for (i = 0; i < READERS + WRITERS; ++i) { ASSERT_SYS(0, 0, _spawn(i < READERS ? Reader : Writer, 0, t + i)); } @@ -64,5 +63,4 @@ TEST(pthread_rwlock_rdlock, test) { } EXPECT_EQ(READERS * ITERATIONS, reads); EXPECT_EQ(WRITERS * ITERATIONS, writes); - ASSERT_EQ(0, pthread_barrier_destroy(&barrier)); } diff --git a/test/libc/intrin/rand64_test.c b/test/libc/intrin/rand64_test.c index 0c6efa4fd..4251d7a43 100644 --- a/test/libc/intrin/rand64_test.c +++ b/test/libc/intrin/rand64_test.c @@ -25,6 +25,7 @@ #include "libc/limits.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" +#include "libc/nexgen32e/threaded.h" #include "libc/runtime/internal.h" #include "libc/runtime/stack.h" #include "libc/stdio/rand.h" @@ -43,7 +44,7 @@ #define ENTRIES 1024 volatile uint64_t A[THREADS * ENTRIES]; -pthread_barrier_t barrier; +pthread_barrier_t barrier = PTHREAD_BARRIER_INITIALIZER; void SetUpOnce(void) { __enable_threads(); @@ -93,7 +94,7 @@ TEST(rand64, testThreadSafety_doesntProduceIdenticalValues) { sigemptyset(&ss); sigaddset(&ss, SIGCHLD); EXPECT_EQ(0, sigprocmask(SIG_BLOCK, &ss, &oldss)); - ASSERT_EQ(0, pthread_barrier_init(&barrier, 0, THREADS)); + pthread_barrier_init(&barrier, 0, THREADS); for (i = 0; i < THREADS; ++i) { ASSERT_SYS(0, 0, _spawn(Thrasher, (void *)(intptr_t)i, th + i)); } @@ -109,5 +110,4 @@ TEST(rand64, testThreadSafety_doesntProduceIdenticalValues) { EXPECT_NE(A[i], A[j], "i=%d j=%d", i, j); } } - ASSERT_EQ(0, pthread_barrier_destroy(&barrier)); } diff --git a/test/libc/intrin/test.mk b/test/libc/intrin/test.mk index 48ce2837a..5eb021c64 100644 --- a/test/libc/intrin/test.mk +++ b/test/libc/intrin/test.mk @@ -39,8 +39,7 @@ TEST_LIBC_INTRIN_DIRECTDEPS = \ LIBC_TINYMATH \ LIBC_X \ TOOL_VIZ_LIB \ - THIRD_PARTY_COMPILER_RT \ - THIRD_PARTY_NSYNC + THIRD_PARTY_COMPILER_RT TEST_LIBC_INTRIN_DEPS := \ $(call uniq,$(foreach x,$(TEST_LIBC_INTRIN_DIRECTDEPS),$($(x)))) diff --git a/test/libc/nexgen32e/stackrw_test.c b/test/libc/nexgen32e/stackrw_test.c index 4f55e45f6..97f85d28c 100644 --- a/test/libc/nexgen32e/stackrw_test.c +++ b/test/libc/nexgen32e/stackrw_test.c @@ -17,8 +17,6 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/struct/sigaction.h" -#include "libc/calls/ucontext.h" -#include "libc/dce.h" #include "libc/runtime/runtime.h" #include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sig.h" @@ -30,16 +28,15 @@ jmp_buf jb; -void EscapeSegfault(int sig, struct siginfo *si, void *vctx) { +void EscapeSegfault(int sig) { longjmp(jb, 666); } TEST(xstack, test) { - if (IsXnu()) return; // TODO(jart): what's up with xnu in MODE=tiny? struct sigaction old[2]; struct sigaction sa = { - .sa_sigaction = EscapeSegfault, - .sa_flags = SA_SIGINFO, + .sa_handler = EscapeSegfault, + .sa_flags = SA_NODEFER, }; sigaction(SIGSEGV, &sa, old + 0); sigaction(SIGBUS, &sa, old + 1); diff --git a/test/libc/release/test.mk b/test/libc/release/test.mk index 23c3393e1..502f01fa2 100644 --- a/test/libc/release/test.mk +++ b/test/libc/release/test.mk @@ -1,7 +1,6 @@ #-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐ #───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘ -o/$(MODE)/test/libc/release/cosmopolitan.zip: .UNSANDBOXED = 1 o/$(MODE)/test/libc/release/cosmopolitan.zip: \ o/cosmopolitan.h \ o/$(MODE)/ape/public/ape.lds \ diff --git a/test/libc/runtime/arch_prctl_test.c b/test/libc/runtime/arch_prctl_test.c index a66123e20..6c1e9cda9 100644 --- a/test/libc/runtime/arch_prctl_test.c +++ b/test/libc/runtime/arch_prctl_test.c @@ -24,7 +24,7 @@ #include "libc/intrin/fsgsbase.h" #include "libc/intrin/kprintf.h" #include "libc/intrin/segmentation.h" -#include "libc/thread/tls.h" +#include "libc/nexgen32e/threaded.h" #include "libc/nt/version.h" #include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sig.h" diff --git a/test/libc/thread/clone_test.c b/test/libc/runtime/clone_test.c similarity index 86% rename from test/libc/thread/clone_test.c rename to test/libc/runtime/clone_test.c index 4457364fd..a6d9c2cce 100644 --- a/test/libc/thread/clone_test.c +++ b/test/libc/runtime/clone_test.c @@ -16,14 +16,16 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/atomic.h" #include "libc/calls/calls.h" #include "libc/calls/struct/timespec.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/intrin/futex.internal.h" +#include "libc/intrin/wait0.internal.h" #include "libc/log/backtrace.internal.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" +#include "libc/nexgen32e/gettls.h" #include "libc/nexgen32e/nexgen32e.h" #include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" @@ -40,19 +42,26 @@ #include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" #include "libc/thread/spawn.h" -#include "libc/thread/tls.h" -#include "libc/thread/tls2.h" -#include "libc/thread/wait0.internal.h" #include "libc/time/time.h" int x, me, tid; -atomic_int thechilde; +_Atomic(int) thechilde; void SetUpOnce(void) { __enable_threads(); ASSERT_SYS(0, 0, pledge("stdio rpath", 0)); } +void *__initialize_tls(char tib[64]) { + if (tib) { + *(intptr_t *)(tib + 0x00) = (intptr_t)tib; + *(intptr_t *)(tib + 0x30) = (intptr_t)tib; + *(int *)(tib + 0x38) = -1; // tid + *(int *)(tib + 0x3c) = 0; + } + return tib; +} + int Hog(void *arg, int tid) { return 0; } @@ -109,7 +118,7 @@ TEST(clone, test1) { //////////////////////////////////////////////////////////////////////////////// // TEST THREADS CAN ISSUE SYSTEM CALLS WITH INDEPENDENT ERRNOS -atomic_int sysbarrier; +_Atomic(int) sysbarrier; int CloneTestSys(void *arg, int tid) { int i, id = (intptr_t)arg; @@ -155,3 +164,14 @@ TEST(clone, tlsSystemCallsErrno_wontClobberMainThreadBecauseTls) { } ASSERT_EQ(0, errno); } + +//////////////////////////////////////////////////////////////////////////////// +// BENCHMARK + +BENCH(clone, bench) { + char *volatile tp; + errno_t *volatile ep; + EZBENCH2("__errno_location", donothing, (ep = __errno_location())); + EZBENCH2("__get_tls_priv", donothing, (tp = __get_tls_privileged())); + EZBENCH2("__get_tls", donothing, (tp = __get_tls())); +} diff --git a/test/libc/runtime/tls_test.c b/test/libc/runtime/tls_test.c index 69bf48225..59e2ad501 100644 --- a/test/libc/runtime/tls_test.c +++ b/test/libc/runtime/tls_test.c @@ -16,39 +16,12 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dce.h" -#include "libc/intrin/asan.internal.h" #include "libc/testlib/testlib.h" -#include "libc/thread/thread.h" -#include "libc/thread/tls.h" -#define A TLS_ALIGNMENT - -long z = 2; -pthread_t t; -_Thread_local long x; -_Thread_local long y[1] = {40}; -_Alignas(A) _Thread_local long a; - -noubsan void *Worker(void *arg) { - ASSERT_EQ(42, x + y[0] + z); - ASSERT_EQ(0, (intptr_t)&a & (A - 1)); - if (IsAsan()) { - ASSERT_EQ(kAsanProtected, __asan_check(y + 1, sizeof(long)).kind); - } - return 0; -} +_Thread_local int x; +_Thread_local int y = 40; +int z = 2; TEST(tls, test) { - ASSERT_EQ(A, _Alignof(a)); - ASSERT_EQ(0, sizeof(struct CosmoTib) % A); - ASSERT_EQ(0, (intptr_t)__get_tls() & (A - 1)); - EXPECT_EQ(42, x + y[0] + z); - y[0] = 666; - ASSERT_EQ(0, (intptr_t)&a & (A - 1)); - ASSERT_EQ(0, pthread_create(&t, 0, Worker, 0)); - ASSERT_EQ(0, pthread_join(t, 0)); - if (IsAsan()) { - ASSERT_EQ(kAsanProtected, __asan_check(y + 1, sizeof(long)).kind); - } + EXPECT_EQ(42, x + y + z); } diff --git a/test/libc/stdio/dtoa_test.c b/test/libc/stdio/dtoa_test.c index 0ecebcccc..7b2cc638f 100644 --- a/test/libc/stdio/dtoa_test.c +++ b/test/libc/stdio/dtoa_test.c @@ -20,6 +20,7 @@ #include "libc/calls/struct/sched_param.h" #include "libc/dce.h" #include "libc/fmt/fmt.h" +#include "libc/intrin/wait0.internal.h" #include "libc/macros.internal.h" #include "libc/math.h" #include "libc/mem/mem.h" @@ -34,7 +35,6 @@ #include "libc/sysv/consts/sched.h" #include "libc/testlib/testlib.h" #include "libc/thread/spawn.h" -#include "libc/thread/wait0.internal.h" #include "libc/x/x.h" #define DUB(i) (union Dub){i}.x diff --git a/test/libc/stdio/fputc_test.c b/test/libc/stdio/fputc_test.c index e11599b06..8b02a0de3 100644 --- a/test/libc/stdio/fputc_test.c +++ b/test/libc/stdio/fputc_test.c @@ -18,7 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/runtime/internal.h" -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/testlib/ezbench.h" diff --git a/test/libc/test.mk b/test/libc/test.mk index 4abd1a1e0..ef908621a 100644 --- a/test/libc/test.mk +++ b/test/libc/test.mk @@ -19,5 +19,4 @@ o/$(MODE)/test/libc: \ o/$(MODE)/test/libc/time \ o/$(MODE)/test/libc/tinymath \ o/$(MODE)/test/libc/x \ - o/$(MODE)/test/libc/zipos \ o/$(MODE)/test/libc/xed diff --git a/test/libc/thread/nsync_test.c b/test/libc/thread/nsync_test.c deleted file mode 100644 index 6c27d1ef7..000000000 --- a/test/libc/thread/nsync_test.c +++ /dev/null @@ -1,119 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2022 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/errno.h" -#include "libc/testlib/testlib.h" -#include "libc/thread/thread.h" -#include "third_party/nsync/cv.h" -#include "third_party/nsync/mu.h" - -int pos; -int count; -int limit; -long data[1000]; -nsync_mu mu; -nsync_cv non_full; -nsync_cv non_empty; - -int Put(long v, nsync_time abs_deadline) { - int err, added = 0, wake = 0; - nsync_mu_lock(&mu); - while (count == limit) { - if ((err = nsync_cv_wait_with_deadline(&non_full, &mu, abs_deadline, 0))) { - ASSERT_EQ(ETIMEDOUT, err); - ASSERT_NE(0, nsync_time_cmp(nsync_time_no_deadline, abs_deadline)); - } - } - if (count != limit) { - int i = pos + count; - if (limit <= i) i -= limit; - data[i] = v; - if (count == 0) wake = 1; - count++; - added = 1; - } - nsync_mu_unlock(&mu); - if (wake) nsync_cv_broadcast(&non_empty); - return added; -} - -long Get(nsync_time abs_deadline) { - long err, v = 0; - nsync_mu_lock(&mu); - while (!count) { - if ((err = nsync_cv_wait_with_deadline(&non_empty, &mu, abs_deadline, 0))) { - ASSERT_EQ(ETIMEDOUT, err); - ASSERT_NE(0, nsync_time_cmp(nsync_time_no_deadline, abs_deadline)); - } - } - if (count) { - v = data[pos]; - data[pos] = 0; - if (count == limit) { - nsync_cv_broadcast(&non_full); - } - pos++; - count--; - if (pos == limit) { - pos = 0; - } - } - nsync_mu_unlock(&mu); - return v; -} - -#define N 10000 - -void *Producer(void *arg) { - for (int i = 0; i < N; i++) { - ASSERT_EQ(1, Put((i + 1) * 3, nsync_time_no_deadline)); - } - return 0; -} - -void *Consumer(void *arg) { - for (int i = 0; i < N; i++) { - ASSERT_EQ((i + 1) * 3, Get(nsync_time_no_deadline)); - } - return 0; -} - -TEST(cond, test) { - pthread_t t; - nsync_mu_init(&mu); - - limit = 1; - ASSERT_EQ(0, pthread_create(&t, 0, Producer, 0)); - Consumer(0); - ASSERT_EQ(0, pthread_join(t, 0)); - - limit = 10; - ASSERT_EQ(0, pthread_create(&t, 0, Producer, 0)); - Consumer(0); - ASSERT_EQ(0, pthread_join(t, 0)); - - limit = 100; - ASSERT_EQ(0, pthread_create(&t, 0, Producer, 0)); - Consumer(0); - ASSERT_EQ(0, pthread_join(t, 0)); - - limit = 1000; - ASSERT_EQ(0, pthread_create(&t, 0, Producer, 0)); - Consumer(0); - ASSERT_EQ(0, pthread_join(t, 0)); -} diff --git a/test/libc/thread/pthread_cond_signal_test.c b/test/libc/thread/pthread_cond_signal_test.c deleted file mode 100644 index f72e77abd..000000000 --- a/test/libc/thread/pthread_cond_signal_test.c +++ /dev/null @@ -1,118 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2022 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/calls/struct/timespec.h" -#include "libc/errno.h" -#include "libc/testlib/testlib.h" -#include "libc/thread/thread.h" -#include "libc/thread/thread2.h" - -int pos; -int count; -int limit; -long data[1000]; -pthread_mutex_t mu; -pthread_cond_t non_full; -pthread_cond_t non_empty; - -int Put(long v, struct timespec *abs_deadline) { - int err, added = 0, wake = 0; - pthread_mutex_lock(&mu); - while (count == limit) { - if ((err = pthread_cond_timedwait(&non_full, &mu, abs_deadline))) { - ASSERT_EQ(ETIMEDOUT, err); - ASSERT_NE(NULL, abs_deadline); - } - } - if (count != limit) { - int i = pos + count; - if (limit <= i) i -= limit; - data[i] = v; - if (count == 0) wake = 1; - count++; - added = 1; - } - pthread_mutex_unlock(&mu); - if (wake) pthread_cond_broadcast(&non_empty); - return added; -} - -long Get(struct timespec *abs_deadline) { - long err, v = 0; - pthread_mutex_lock(&mu); - while (!count) { - if ((err = pthread_cond_timedwait(&non_empty, &mu, abs_deadline))) { - ASSERT_EQ(ETIMEDOUT, err); - ASSERT_NE(NULL, abs_deadline); - } - } - if (count) { - v = data[pos]; - data[pos] = 0; - if (count == limit) { - pthread_cond_broadcast(&non_full); - } - pos++; - count--; - if (pos == limit) { - pos = 0; - } - } - pthread_mutex_unlock(&mu); - return v; -} - -#define N 10000 - -void *Producer(void *arg) { - for (int i = 0; i < N; i++) { - ASSERT_EQ(1, Put((i + 1) * 3, 0)); - } - return 0; -} - -void *Consumer(void *arg) { - for (int i = 0; i < N; i++) { - ASSERT_EQ((i + 1) * 3, Get(0)); - } - return 0; -} - -TEST(cond, test) { - pthread_t t; - - limit = 1; - ASSERT_EQ(0, pthread_create(&t, 0, Producer, 0)); - Consumer(0); - ASSERT_EQ(0, pthread_join(t, 0)); - - limit = 10; - ASSERT_EQ(0, pthread_create(&t, 0, Producer, 0)); - Consumer(0); - ASSERT_EQ(0, pthread_join(t, 0)); - - limit = 100; - ASSERT_EQ(0, pthread_create(&t, 0, Producer, 0)); - Consumer(0); - ASSERT_EQ(0, pthread_join(t, 0)); - - limit = 1000; - ASSERT_EQ(0, pthread_create(&t, 0, Producer, 0)); - Consumer(0); - ASSERT_EQ(0, pthread_join(t, 0)); -} diff --git a/test/libc/thread/pthread_create_test.c b/test/libc/thread/pthread_create_test.c index 96566f673..421761208 100644 --- a/test/libc/thread/pthread_create_test.c +++ b/test/libc/thread/pthread_create_test.c @@ -22,9 +22,10 @@ #include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/kprintf.h" +#include "libc/intrin/pthread.h" +#include "libc/intrin/pthread2.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" -#include "libc/nexgen32e/nexgen32e.h" #include "libc/runtime/runtime.h" #include "libc/runtime/stack.h" #include "libc/sysv/consts/prot.h" @@ -35,21 +36,19 @@ #include "libc/testlib/subprocess.h" #include "libc/testlib/testlib.h" #include "libc/thread/thread.h" -#include "libc/thread/thread2.h" -void OnUsr1(int sig, struct siginfo *si, void *vctx) { +void OnTrap(int sig, struct siginfo *si, void *vctx) { struct ucontext *ctx = vctx; } void SetUp(void) { - struct sigaction sig = {.sa_sigaction = OnUsr1, .sa_flags = SA_SIGINFO}; - sigaction(SIGUSR1, &sig, 0); + struct sigaction sig = {.sa_sigaction = OnTrap, .sa_flags = SA_SIGINFO}; + sigaction(SIGTRAP, &sig, 0); } void TriggerSignal(void) { sched_yield(); - /* kprintf("raising at %p\n", __builtin_frame_address(0)); */ - raise(SIGUSR1); + DebugBreak(); sched_yield(); } @@ -68,7 +67,6 @@ TEST(pthread_create, testCreateReturnJoin) { } static void *IncExit(void *arg) { - CheckStackIsAligned(); TriggerSignal(); pthread_exit((void *)((uintptr_t)arg + 1)); } diff --git a/test/libc/thread/pthread_key_create_test.c b/test/libc/thread/pthread_key_create_test.c index ced6cf725..15cdc4bd2 100644 --- a/test/libc/thread/pthread_key_create_test.c +++ b/test/libc/thread/pthread_key_create_test.c @@ -16,7 +16,9 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/dce.h" #include "libc/mem/mem.h" +#include "libc/runtime/internal.h" #include "libc/testlib/testlib.h" #include "libc/thread/thread.h" diff --git a/test/libc/thread/pthread_setname_np_test.c b/test/libc/thread/pthread_setname_np_test.c index 16154b44d..e39226e75 100644 --- a/test/libc/thread/pthread_setname_np_test.c +++ b/test/libc/thread/pthread_setname_np_test.c @@ -16,9 +16,9 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/atomic.h" #include "libc/dce.h" #include "libc/intrin/atomic.h" +#include "libc/intrin/pthread.h" #include "libc/testlib/testlib.h" #include "libc/thread/thread.h" @@ -63,7 +63,7 @@ TEST(pthread_setname_np, GetDefaultName_IsEmptyString) { ASSERT_EQ(0, pthread_join(id, 0)); } -atomic_char sync1, sync2; +_Atomic(char) sync1, sync2; static void *GetNameOfOtherThreadWorker(void *arg) { pthread_setname_np(pthread_self(), "justine"); diff --git a/test/libc/thread/spawn_test.c b/test/libc/thread/spawn_test.c index 084f00341..88193d605 100644 --- a/test/libc/thread/spawn_test.c +++ b/test/libc/thread/spawn_test.c @@ -17,7 +17,6 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" -#include "libc/atomic.h" #include "libc/calls/calls.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/intrin/atomic.h" @@ -27,8 +26,9 @@ #include "libc/runtime/internal.h" #include "libc/testlib/testlib.h" #include "libc/thread/spawn.h" +#include "libc/thread/thread.h" -atomic_int itworked; +_Atomic(int) itworked; _Thread_local int var; void SetUpOnce(void) { diff --git a/test/libc/thread/test.mk b/test/libc/thread/test.mk index 962a66a96..bbd33da99 100644 --- a/test/libc/thread/test.mk +++ b/test/libc/thread/test.mk @@ -34,8 +34,7 @@ TEST_LIBC_THREAD_DIRECTDEPS = \ LIBC_SYSV \ LIBC_THREAD \ LIBC_TIME \ - LIBC_TESTLIB \ - THIRD_PARTY_NSYNC + LIBC_TESTLIB TEST_LIBC_THREAD_DEPS := \ $(call uniq,$(foreach x,$(TEST_LIBC_THREAD_DIRECTDEPS),$($(x)))) diff --git a/test/libc/x/makedirs_test.c b/test/libc/x/makedirs_test.c index 388006035..d5da76a09 100644 --- a/test/libc/x/makedirs_test.c +++ b/test/libc/x/makedirs_test.c @@ -16,11 +16,11 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/intrin/pthread.h" #include "libc/mem/mem.h" #include "libc/runtime/gc.internal.h" #include "libc/testlib/testlib.h" #include "libc/thread/spawn.h" -#include "libc/thread/thread.h" #include "libc/x/x.h" #define DIR \ @@ -42,5 +42,4 @@ TEST(makedirs, test) { ASSERT_EQ(0, pthread_barrier_init(&barrier, 0, n)); for (i = 0; i < n; ++i) ASSERT_SYS(0, 0, _spawn(Worker, 0, t + i)); for (i = 0; i < n; ++i) EXPECT_SYS(0, 0, _join(t + i)); - ASSERT_EQ(0, pthread_barrier_destroy(&barrier)); } diff --git a/test/libc/zipos/open_test.c b/test/libc/zipos/open_test.c deleted file mode 100644 index 28d97ab6e..000000000 --- a/test/libc/zipos/open_test.c +++ /dev/null @@ -1,56 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2022 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/calls/calls.h" -#include "libc/mem/mem.h" -#include "libc/runtime/gc.h" -#include "libc/runtime/runtime.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/o.h" -#include "libc/testlib/hyperion.h" -#include "libc/testlib/testlib.h" -#include "libc/thread/spawn.h" -#include "libc/zipos/zipos.h" - -STATIC_YOINK("zip_uri_support"); -STATIC_YOINK("libc/testlib/hyperion.txt"); -STATIC_YOINK("inflate"); -STATIC_YOINK("inflateInit2"); -STATIC_YOINK("inflateEnd"); - -int Worker(void *arg, int tid) { - int i, fd; - char *data; - for (i = 0; i < 20; ++i) { - ASSERT_NE(-1, (fd = open("/zip/libc/testlib/hyperion.txt", O_RDONLY))); - data = malloc(kHyperionSize); - ASSERT_EQ(kHyperionSize, read(fd, data, kHyperionSize)); - ASSERT_EQ(0, memcmp(data, kHyperion, kHyperionSize)); - ASSERT_SYS(0, 0, close(fd)); - free(data); - } - return 0; -} - -TEST(zipos, test) { - int i, n = 16; - struct spawn *t = _gc(malloc(sizeof(struct spawn) * n)); - for (i = 0; i < n; ++i) ASSERT_SYS(0, 0, _spawn(Worker, 0, t + i)); - for (i = 0; i < n; ++i) EXPECT_SYS(0, 0, _join(t + i)); - /* __print_maps(); */ -} diff --git a/test/libc/zipos/test.mk b/test/libc/zipos/test.mk deleted file mode 100644 index 086867e33..000000000 --- a/test/libc/zipos/test.mk +++ /dev/null @@ -1,63 +0,0 @@ -#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐ -#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘ - -PKGS += TEST_LIBC_ZIPOS - -TEST_LIBC_ZIPOS_SRCS := $(wildcard test/libc/zipos/*.c) -TEST_LIBC_ZIPOS_SRCS_TEST = $(filter %_test.c,$(TEST_LIBC_ZIPOS_SRCS)) - -TEST_LIBC_ZIPOS_OBJS = \ - $(TEST_LIBC_ZIPOS_SRCS:%.c=o/$(MODE)/%.o) - -TEST_LIBC_ZIPOS_COMS = \ - $(TEST_LIBC_ZIPOS_SRCS:%.c=o/$(MODE)/%.com) - -TEST_LIBC_ZIPOS_BINS = \ - $(TEST_LIBC_ZIPOS_COMS) \ - $(TEST_LIBC_ZIPOS_COMS:%=%.dbg) - -TEST_LIBC_ZIPOS_TESTS = \ - $(TEST_LIBC_ZIPOS_SRCS_TEST:%.c=o/$(MODE)/%.com.ok) - -TEST_LIBC_ZIPOS_CHECKS = \ - $(TEST_LIBC_ZIPOS_SRCS_TEST:%.c=o/$(MODE)/%.com.runs) - -TEST_LIBC_ZIPOS_DIRECTDEPS = \ - LIBC_CALLS \ - LIBC_FMT \ - LIBC_INTRIN \ - LIBC_MEM \ - LIBC_NEXGEN32E \ - LIBC_RUNTIME \ - LIBC_STR \ - LIBC_STUBS \ - LIBC_THREAD \ - LIBC_SYSV \ - LIBC_ZIPOS \ - LIBC_TIME \ - LIBC_TESTLIB \ - THIRD_PARTY_ZLIB - -TEST_LIBC_ZIPOS_DEPS := \ - $(call uniq,$(foreach x,$(TEST_LIBC_ZIPOS_DIRECTDEPS),$($(x)))) - -o/$(MODE)/test/libc/zipos/zipos.pkg: \ - $(TEST_LIBC_ZIPOS_OBJS) \ - $(foreach x,$(TEST_LIBC_ZIPOS_DIRECTDEPS),$($(x)_A).pkg) - -#o/$(MODE)/libc/testlib/hyperion.txt.zip.o: private ZIPOBJ_FLAGS += -0 - -o/$(MODE)/test/libc/zipos/%.com.dbg: \ - $(TEST_LIBC_ZIPOS_DEPS) \ - o/$(MODE)/test/libc/zipos/%.o \ - o/$(MODE)/test/libc/zipos/zipos.pkg \ - o/$(MODE)/libc/testlib/hyperion.txt.zip.o \ - $(LIBC_TESTMAIN) \ - $(CRT) \ - $(APE_NO_MODIFY_SELF) - @$(APELINK) - -.PHONY: o/$(MODE)/test/libc/zipos -o/$(MODE)/test/libc/zipos: \ - $(TEST_LIBC_ZIPOS_BINS) \ - $(TEST_LIBC_ZIPOS_CHECKS) diff --git a/test/tool/plinko/plinko_test.c b/test/tool/plinko/plinko_test.c index 75fda9923..dfd438767 100644 --- a/test/tool/plinko/plinko_test.c +++ b/test/tool/plinko/plinko_test.c @@ -53,7 +53,6 @@ static const char *const kSauces[] = { char testlib_enable_tmp_setup_teardown_once; void SetUpOnce(void) { - exit(0); // TODO(jart): How can we safely disable TLS with *NSYNC? int fdin, fdout; ASSERT_NE(-1, mkdir("bin", 0755)); ASSERT_NE(-1, (fdin = open("/zip/plinko.com", O_RDONLY))); diff --git a/third_party/awk/awkgram.tab.c b/third_party/awk/awkgram.tab.c index 1acd78ab9..96b3555eb 100644 --- a/third_party/awk/awkgram.tab.c +++ b/third_party/awk/awkgram.tab.c @@ -21,7 +21,7 @@ #line 26 "awkgram.y" #include "libc/calls/calls.h" #include "libc/fmt/fmt.h" -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" #include "libc/stdio/temp.h" #include "libc/mem/alg.h" diff --git a/third_party/awk/awkgram.y b/third_party/awk/awkgram.y index 7c449cabe..04104b35d 100644 --- a/third_party/awk/awkgram.y +++ b/third_party/awk/awkgram.y @@ -25,7 +25,7 @@ THIS SOFTWARE. %{ #include "libc/calls/calls.h" #include "libc/fmt/fmt.h" -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" #include "libc/stdio/temp.h" #include "libc/mem/alg.h" diff --git a/third_party/awk/lex.c b/third_party/awk/lex.c index 4bfa01d83..92616986e 100644 --- a/third_party/awk/lex.c +++ b/third_party/awk/lex.c @@ -31,7 +31,7 @@ #include "libc/mem/alg.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/rand.h" #include "libc/stdio/stdio.h" #include "libc/stdio/temp.h" diff --git a/third_party/awk/lib.c b/third_party/awk/lib.c index 287619ec8..08cfef0de 100644 --- a/third_party/awk/lib.c +++ b/third_party/awk/lib.c @@ -36,7 +36,7 @@ #include "libc/mem/mem.h" #include "libc/nexgen32e/ffs.h" #include "libc/runtime/runtime.h" -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/rand.h" #include "libc/stdio/stdio.h" #include "libc/stdio/temp.h" diff --git a/third_party/dlmalloc/dlmalloc.mk b/third_party/dlmalloc/dlmalloc.mk index 3a5c2e600..a4e1c34fd 100644 --- a/third_party/dlmalloc/dlmalloc.mk +++ b/third_party/dlmalloc/dlmalloc.mk @@ -34,8 +34,7 @@ THIRD_PARTY_DLMALLOC_A_DIRECTDEPS = \ LIBC_STUBS \ LIBC_SYSV \ LIBC_SYSV_CALLS \ - THIRD_PARTY_COMPILER_RT \ - THIRD_PARTY_NSYNC + THIRD_PARTY_COMPILER_RT THIRD_PARTY_DLMALLOC_A_DEPS := \ $(call uniq,$(foreach x,$(THIRD_PARTY_DLMALLOC_A_DIRECTDEPS),$($(x)))) diff --git a/third_party/dlmalloc/locks.inc b/third_party/dlmalloc/locks.inc index 5084a5d38..e3b722b27 100644 --- a/third_party/dlmalloc/locks.inc +++ b/third_party/dlmalloc/locks.inc @@ -1,6 +1,7 @@ // clang-format off -#include "third_party/nsync/mu.h" -#include "libc/thread/tls.h" +#include "libc/calls/calls.h" +#include "libc/intrin/pthread.h" +#include "libc/nexgen32e/threaded.h" /* --------------------------- Lock preliminaries ------------------------ */ @@ -38,20 +39,233 @@ #define DESTROY_LOCK(l) (0) #define ACQUIRE_MALLOC_GLOBAL_LOCK() #define RELEASE_MALLOC_GLOBAL_LOCK() + #else -#define MLOCK_T nsync_mu -#define ACQUIRE_LOCK(lk) (__threaded && (nsync_mu_lock(lk), 0)) -#define RELEASE_LOCK(lk) (__threaded && (nsync_mu_unlock(lk), 0)) -#define TRY_LOCK(lk) (__threaded ? nsync_mu_trylock(lk) : 1) -#define INITIAL_LOCK(lk) memset(lk, 0, sizeof(*lk)) -#define DESTROY_LOCK(lk) memset(lk, -1, sizeof(*lk)) -#define ACQUIRE_MALLOC_GLOBAL_LOCK() ACQUIRE_LOCK(&malloc_global_mutex); -#define RELEASE_MALLOC_GLOBAL_LOCK() RELEASE_LOCK(&malloc_global_mutex); +#if USE_LOCKS > 1 +/* ----------------------- User-defined locks ------------------------ */ +/* Define your own lock implementation here */ +/* #define INITIAL_LOCK(lk) ... */ +/* #define DESTROY_LOCK(lk) ... */ +/* #define ACQUIRE_LOCK(lk) ... */ +/* #define RELEASE_LOCK(lk) ... */ +/* #define TRY_LOCK(lk) ... */ +/* static MLOCK_T malloc_global_mutex = ... */ + +#define MLOCK_T pthread_mutex_t +#define ACQUIRE_LOCK(lk) (__threaded && pthread_mutex_lock(lk), 0) +#define RELEASE_LOCK(lk) (__threaded && pthread_mutex_unlock(lk), 0) +#define TRY_LOCK(lk) (__threaded ? !pthread_mutex_trylock(lk) : 1) +#define INITIAL_LOCK(lk) pthread_mutex_init(lk, 0) +#define DESTROY_LOCK(lk) pthread_mutex_destroy(lk) + static MLOCK_T malloc_global_mutex; + +#elif USE_SPIN_LOCKS + +/* First, define CAS_LOCK and CLEAR_LOCK on ints */ +/* Note CAS_LOCK defined to return 0 on success */ + +#if defined(__GNUC__)&& (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) +#define CAS_LOCK(sl) __sync_lock_test_and_set(sl, 1) +#define CLEAR_LOCK(sl) __sync_lock_release(sl) + +#elif (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))) +/* Custom spin locks for older gcc on x86 */ +FORCEINLINE int x86_cas_lock(int *sl) { + int ret; + int val = 1; + int cmp = 0; + __asm__ __volatile__ ("lock; cmpxchgl %1, %2" + : "=a" (ret) + : "r" (val), "m" (*(sl)), "0"(cmp) + : "memory", "cc"); + return ret; +} + +FORCEINLINE void x86_clear_lock(int* sl) { + assert(*sl != 0); + int prev = 0; + int ret; + __asm__ __volatile__ ("lock; xchgl %0, %1" + : "=r" (ret) + : "m" (*(sl)), "0"(prev) + : "memory"); +} + +#define CAS_LOCK(sl) x86_cas_lock(sl) +#define CLEAR_LOCK(sl) x86_clear_lock(sl) + +#else /* Win32 MSC */ +#define CAS_LOCK(sl) interlockedexchange(sl, (LONG)1) +#define CLEAR_LOCK(sl) interlockedexchange (sl, (LONG)0) + +#endif /* ... gcc spins locks ... */ + +#if !defined(USE_RECURSIVE_LOCKS) || USE_RECURSIVE_LOCKS == 0 +/* Plain spin locks use single word (embedded in malloc_states) */ +static dontinline int spin_acquire_lock(int *sl) { + while (*(volatile int *)sl != 0 || CAS_LOCK(sl)) { + sched_yield(); + } + return 0; +} + +#define MLOCK_T int +#define TRY_LOCK(sl) !CAS_LOCK(sl) +#define RELEASE_LOCK(sl) (__threaded && (CLEAR_LOCK(sl), 0)) +#define ACQUIRE_LOCK(sl) (__threaded && CAS_LOCK(sl) ? spin_acquire_lock(sl) : 0) +#define INITIAL_LOCK(sl) (*sl = 0) +#define DESTROY_LOCK(sl) (0) +static MLOCK_T malloc_global_mutex = 0; + +#else /* USE_RECURSIVE_LOCKS */ +/* types for lock owners */ +#ifdef WIN32 +#define THREAD_ID_T DWORD +#define CURRENT_THREAD GetCurrentThreadId() +#define EQ_OWNER(X,Y) ((X) == (Y)) +#else +/* + Note: the following assume that pthread_t is a type that can be + initialized to (casted) zero. If this is not the case, you will need to + somehow redefine these or not use spin locks. +*/ +#define THREAD_ID_T pthread_t +#define CURRENT_THREAD pthread_self() +#define EQ_OWNER(X,Y) pthread_equal(X, Y) #endif +struct malloc_recursive_lock { + int sl; + unsigned int c; + THREAD_ID_T threadid; +}; + +#define MLOCK_T struct malloc_recursive_lock +static MLOCK_T malloc_global_mutex = { 0, 0, (THREAD_ID_T)0}; + +FORCEINLINE void recursive_release_lock(MLOCK_T *lk) { + assert(lk->sl != 0); + if (--lk->c == 0) { + CLEAR_LOCK(&lk->sl); + } +} + +FORCEINLINE int recursive_acquire_lock(MLOCK_T *lk) { + THREAD_ID_T mythreadid = CURRENT_THREAD; + for (;;) { + if (*((volatile int *)(&lk->sl)) == 0) { + if (!CAS_LOCK(&lk->sl)) { + lk->threadid = mythreadid; + lk->c = 1; + return 0; + } + } + else if (EQ_OWNER(lk->threadid, mythreadid)) { + ++lk->c; + return 0; + } + sched_yield(); + } +} + +FORCEINLINE int recursive_try_lock(MLOCK_T *lk) { + THREAD_ID_T mythreadid = CURRENT_THREAD; + if (*((volatile int *)(&lk->sl)) == 0) { + if (!CAS_LOCK(&lk->sl)) { + lk->threadid = mythreadid; + lk->c = 1; + return 1; + } + } + else if (EQ_OWNER(lk->threadid, mythreadid)) { + ++lk->c; + return 1; + } + return 0; +} + +#define RELEASE_LOCK(lk) recursive_release_lock(lk) +#define TRY_LOCK(lk) recursive_try_lock(lk) +#define ACQUIRE_LOCK(lk) recursive_acquire_lock(lk) +#define INITIAL_LOCK(lk) ((lk)->threadid = (THREAD_ID_T)0, (lk)->sl = 0, (lk)->c = 0) +#define DESTROY_LOCK(lk) (0) +#endif /* USE_RECURSIVE_LOCKS */ + +#elif defined(WIN32) /* Win32 critical sections */ +#define MLOCK_T CRITICAL_SECTION +#define ACQUIRE_LOCK(lk) (EnterCriticalSection(lk), 0) +#define RELEASE_LOCK(lk) LeaveCriticalSection(lk) +#define TRY_LOCK(lk) TryEnterCriticalSection(lk) +#define INITIAL_LOCK(lk) (!InitializeCriticalSectionAndSpinCount((lk), 0x80000000|4000)) +#define DESTROY_LOCK(lk) (DeleteCriticalSection(lk), 0) +#define NEED_GLOBAL_LOCK_INIT + +static MLOCK_T malloc_global_mutex; +static volatile LONG malloc_global_mutex_status; + +/* Use spin loop to initialize global lock */ +static void init_malloc_global_mutex() { + for (;;) { + long stat = malloc_global_mutex_status; + if (stat > 0) + return; + /* transition to < 0 while initializing, then to > 0) */ + if (stat == 0 && + interlockedcompareexchange(&malloc_global_mutex_status, (LONG)-1, (LONG)0) == 0) { + InitializeCriticalSection(&malloc_global_mutex); + interlockedexchange(&malloc_global_mutex_status, (LONG)1); + return; + } + SleepEx(0, FALSE); + } +} + +#else /* pthreads-based locks */ +#define MLOCK_T pthread_mutex_t +#define ACQUIRE_LOCK(lk) pthread_mutex_lock(lk) +#define RELEASE_LOCK(lk) pthread_mutex_unlock(lk) +#define TRY_LOCK(lk) (!pthread_mutex_trylock(lk)) +#define INITIAL_LOCK(lk) pthread_init_lock(lk) +#define DESTROY_LOCK(lk) pthread_mutex_destroy(lk) + +#if defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0 && defined(linux) && !defined(PTHREAD_MUTEX_RECURSIVE) +/* Cope with old-style linux recursive lock initialization by adding */ +/* skipped internal declaration from pthread.h */ +extern int pthread_mutexattr_setkind_np __P ((pthread_mutexattr_t *__attr, + int __kind)); +#define PTHREAD_MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE_NP +#define pthread_mutexattr_settype(x,y) pthread_mutexattr_setkind_np(x,y) +#endif /* USE_RECURSIVE_LOCKS ... */ + +static MLOCK_T malloc_global_mutex = PTHREAD_MUTEX_INITIALIZER; + +static int pthread_init_lock (MLOCK_T *lk) { + pthread_mutexattr_t attr; + if (pthread_mutexattr_init(&attr)) return 1; +#if defined(USE_RECURSIVE_LOCKS) && USE_RECURSIVE_LOCKS != 0 + if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) return 1; +#endif + if (pthread_mutex_init(lk, &attr)) return 1; + if (pthread_mutexattr_destroy(&attr)) return 1; + return 0; +} + +#endif /* ... lock types ... */ + +/* Common code for all lock types */ #define USE_LOCK_BIT (2U) +#ifndef ACQUIRE_MALLOC_GLOBAL_LOCK +#define ACQUIRE_MALLOC_GLOBAL_LOCK() ACQUIRE_LOCK(&malloc_global_mutex); +#endif + +#ifndef RELEASE_MALLOC_GLOBAL_LOCK +#define RELEASE_MALLOC_GLOBAL_LOCK() RELEASE_LOCK(&malloc_global_mutex); +#endif + +#endif /* USE_LOCKS */ + struct malloc_chunk { size_t prev_foot; /* Size of previous chunk (if free). */ size_t head; /* Size and inuse bits. */ diff --git a/third_party/gdtoa/lock.c b/third_party/gdtoa/lock.c index 6297114c7..18290c86a 100644 --- a/third_party/gdtoa/lock.c +++ b/third_party/gdtoa/lock.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" #include "third_party/gdtoa/lock.h" static pthread_mutex_t __gdtoa_lock_obj; diff --git a/third_party/gdtoa/lock.h b/third_party/gdtoa/lock.h index 107bc3358..e3b281f0c 100644 --- a/third_party/gdtoa/lock.h +++ b/third_party/gdtoa/lock.h @@ -1,7 +1,7 @@ #ifndef COSMOPOLITAN_THIRD_PARTY_GDTOA_LOCK_H_ #define COSMOPOLITAN_THIRD_PARTY_GDTOA_LOCK_H_ -#include "libc/intrin/nopl.internal.h" -#include "libc/thread/tls.h" +#include "libc/intrin/nopl.h" +#include "libc/nexgen32e/threaded.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ diff --git a/third_party/libcxx/__threading_support b/third_party/libcxx/__threading_support index b67666d84..d7a0b2114 100644 --- a/third_party/libcxx/__threading_support +++ b/third_party/libcxx/__threading_support @@ -13,7 +13,7 @@ #include "third_party/libcxx/__config" #include "third_party/libcxx/chrono" #include "third_party/libcxx/iosfwd" -#include "libc/thread/thread2.h" +#include "libc/intrin/pthread2.h" #include "third_party/libcxx/errno.h" #ifndef _LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER @@ -25,7 +25,7 @@ #elif !defined(_LIBCPP_HAS_NO_THREADS) #if defined(_LIBCPP_HAS_THREAD_API_PTHREAD) -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" #include "libc/calls/calls.h" #include "libc/calls/struct/sched_param.h" #include "libc/sysv/consts/sched.h" diff --git a/third_party/lua/liolib.c b/third_party/lua/liolib.c index 6eced3349..89d6fa9ea 100644 --- a/third_party/lua/liolib.c +++ b/third_party/lua/liolib.c @@ -30,7 +30,7 @@ #include "libc/calls/calls.h" #include "libc/calls/weirdtypes.h" #include "libc/errno.h" -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" #include "libc/stdio/temp.h" #include "libc/str/str.h" diff --git a/third_party/lua/lrepl.c b/third_party/lua/lrepl.c index 1a47d135c..d99a5998c 100644 --- a/third_party/lua/lrepl.c +++ b/third_party/lua/lrepl.c @@ -31,7 +31,7 @@ #include "libc/calls/struct/sigaction.h" #include "libc/errno.h" #include "libc/intrin/nomultics.internal.h" -#include "libc/thread/thread.h" +#include "libc/intrin/pthread.h" #include "libc/log/check.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" diff --git a/third_party/lua/lrepl.h b/third_party/lua/lrepl.h index 199f696a2..f61240bcc 100644 --- a/third_party/lua/lrepl.h +++ b/third_party/lua/lrepl.h @@ -1,6 +1,6 @@ #ifndef COSMOPOLITAN_THIRD_PARTY_LUA_LREPL_H_ #define COSMOPOLITAN_THIRD_PARTY_LUA_LREPL_H_ -#include "libc/intrin/nopl.internal.h" +#include "libc/intrin/nopl.h" #include "third_party/linenoise/linenoise.h" #include "third_party/lua/lauxlib.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) diff --git a/third_party/nsync/LICENSE.txt b/third_party/nsync/LICENSE.txt deleted file mode 100644 index d64569567..000000000 --- a/third_party/nsync/LICENSE.txt +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/third_party/nsync/README.md b/third_party/nsync/README.md deleted file mode 100644 index 12dadf03b..000000000 --- a/third_party/nsync/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# *NSYNC - -The `THIRD_PARTY_NSYNC` and `LIBC_THREAD` packages include source code -from *NSYNC. Here's the latest upstream synchronization point: - - git@github.com:google/nsync - ac5489682760393fe21bd2a8e038b528442412a7 (1.25.0) - Author: Mike Burrows - Date: Wed Jun 1 16:47:52 2022 -0700 - -NSYNC uses the Apache 2.0 license. We made the following local changes: - - - Write custom `nsync_malloc_()` so `malloc()` can use *NSYNC. - - - Rewrite `futex()` wrapper to support old Linux kernels and OpenBSD. - - - Normalize sources to Cosmopolitan style conventions; *NSYNC upstream - supports dozens of compilers and operating systems, at compile-time. - Since Cosmo solves portability at runtime instead, most of the build - config toil has been removed, in order to help the NSYNC source code - be more readable and hackable. diff --git a/third_party/nsync/atomic.h b/third_party/nsync/atomic.h deleted file mode 100644 index eeaae199a..000000000 --- a/third_party/nsync/atomic.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef NSYNC_ATOMIC_H_ -#define NSYNC_ATOMIC_H_ -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -typedef uint32_t nsync_atomic_uint32_; - -#define NSYNC_ATOMIC_UINT32_INIT_ 0 -#define NSYNC_ATOMIC_UINT32_LOAD_(p) (*(p)) -#define NSYNC_ATOMIC_UINT32_STORE_(p, v) (*(p) = (v)) -#define NSYNC_ATOMIC_UINT32_PTR_(p) (p) - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* NSYNC_ATOMIC_H_ */ diff --git a/third_party/nsync/atomic.internal.h b/third_party/nsync/atomic.internal.h deleted file mode 100644 index 9aa4f87df..000000000 --- a/third_party/nsync/atomic.internal.h +++ /dev/null @@ -1,113 +0,0 @@ -#ifndef NSYNC_ATOMIC_INTERNAL_H_ -#define NSYNC_ATOMIC_INTERNAL_H_ -#include "libc/intrin/atomic.h" -#include "third_party/nsync/atomic.h" -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -/* Atomic operations on nsync_atomic_uint32_ quantities - CAS, load, and store. - - Normally, these are used only on nsync_atomic_uint32_ values, but on - Linux they may be invoked on int values, because futexes operate on - int values. A compile-time check in the futex code ensures that both - int and nsync_atomic_uint32_ are 32 bits. - - Memory barriers: - - Operations with the suffixes _ACQ and _RELACQ ensure that the - operation appears to complete before other memory operations - subsequently performed by the same thread, as seen by other - threads. (In the case of ATM_CAS_ACQ, this applies only if - the operation returns a non-zero value.) - - Operations with the suffixes _REL and _RELACQ ensure that the - operation appears to complete after other memory operations - previously performed by the same thread, as seen by other - threads. (In the case of ATM_CAS_REL, this applies only if - the operation returns a non-zero value.) - - // Atomically, - // int ATM_CAS (nsync_atomic_uint32_ *p, - // uint32_t old_value, uint32_t new_value) { - // if (*p == old_value) { - // *p = new_value; - // return (some-non-zero-value); - // } else { - // return (0); - // } - // } - // *_ACQ, *_REL, *_RELACQ variants are available, - // with the barrier semantics described above. - int ATM_CAS (nsync_atomic_uint32_ *p, uint32_t old_value, - uint32_t new_value); - - // Atomically, - // uint32_t ATM_LOAD (nsync_atomic_uint32_ *p) { return (*p); } - // A *_ACQ variant is available, - // with the barrier semantics described above. - uint32_t ATM_LOAD (nsync_atomic_uint32_ *p); - - // Atomically, - // void ATM_STORE (nsync_atomic_uint32_ *p, uint32_t value) { - // *p = value; - // } - // A *_REL variant is available, - // with the barrier semantics described above. - void ATM_STORE (nsync_atomic_uint32_ *p, uint32_t value); - */ - -static inline int atm_cas_nomb_u32_(nsync_atomic_uint32_ *p, uint32_t o, - uint32_t n) { - return atomic_compare_exchange_strong_explicit(NSYNC_ATOMIC_UINT32_PTR_(p), - &o, n, memory_order_relaxed, - memory_order_relaxed); -} - -static inline int atm_cas_acq_u32_(nsync_atomic_uint32_ *p, uint32_t o, - uint32_t n) { - return atomic_compare_exchange_strong_explicit(NSYNC_ATOMIC_UINT32_PTR_(p), - &o, n, memory_order_acquire, - memory_order_relaxed); -} - -static inline int atm_cas_rel_u32_(nsync_atomic_uint32_ *p, uint32_t o, - uint32_t n) { - return atomic_compare_exchange_strong_explicit(NSYNC_ATOMIC_UINT32_PTR_(p), - &o, n, memory_order_release, - memory_order_relaxed); -} - -static inline int atm_cas_relacq_u32_(nsync_atomic_uint32_ *p, uint32_t o, - uint32_t n) { - return atomic_compare_exchange_strong_explicit(NSYNC_ATOMIC_UINT32_PTR_(p), - &o, n, memory_order_acq_rel, - memory_order_relaxed); -} - -#define ATM_CAS_HELPER_(barrier, p, o, n) \ - (atm_cas_##barrier##_u32_((p), (o), (n))) - -#define ATM_CAS(p, o, n) ATM_CAS_HELPER_(nomb, (p), (o), (n)) -#define ATM_CAS_ACQ(p, o, n) ATM_CAS_HELPER_(acq, (p), (o), (n)) -#define ATM_CAS_REL(p, o, n) ATM_CAS_HELPER_(rel, (p), (o), (n)) -#define ATM_CAS_RELACQ(p, o, n) ATM_CAS_HELPER_(relacq, (p), (o), (n)) - -/* Need a cast to remove "const" from some uses. */ -#define ATM_LOAD(p) \ - (atomic_load_explicit((nsync_atomic_uint32_ *)NSYNC_ATOMIC_UINT32_PTR_(p), \ - memory_order_relaxed)) -#define ATM_LOAD_ACQ(p) \ - (atomic_load_explicit((nsync_atomic_uint32_ *)NSYNC_ATOMIC_UINT32_PTR_(p), \ - memory_order_acquire)) - -#define ATM_STORE(p, v) \ - (atomic_store_explicit(NSYNC_ATOMIC_UINT32_PTR_(p), (v), \ - memory_order_relaxed)) -#define ATM_STORE_REL(p, v) \ - (atomic_store_explicit(NSYNC_ATOMIC_UINT32_PTR_(p), (v), \ - memory_order_release)) - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* NSYNC_ATOMIC_INTERNAL_H_ */ diff --git a/third_party/nsync/common.c b/third_party/nsync/common.c deleted file mode 100644 index c722fb967..000000000 --- a/third_party/nsync/common.c +++ /dev/null @@ -1,246 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ -│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2016 Google Inc. │ -│ │ -│ Licensed under the Apache License, Version 2.0 (the "License"); │ -│ you may not use this file except in compliance with the License. │ -│ You may obtain a copy of the License at │ -│ │ -│ http://www.apache.org/licenses/LICENSE-2.0 │ -│ │ -│ Unless required by applicable law or agreed to in writing, software │ -│ distributed under the License is distributed on an "AS IS" BASIS, │ -│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │ -│ See the License for the specific language governing permissions and │ -│ limitations under the License. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/mem/mem.h" -#include "libc/runtime/runtime.h" -#include "libc/thread/thread.h" -#include "third_party/nsync/atomic.h" -#include "third_party/nsync/atomic.internal.h" -#include "third_party/nsync/common.internal.h" -#include "third_party/nsync/dll.h" -#include "third_party/nsync/malloc.internal.h" -#include "third_party/nsync/mu_semaphore.h" -#include "third_party/nsync/races.internal.h" -#include "third_party/nsync/wait_s.internal.h" - -asm(".ident\t\"\\n\\n\ -*NSYNC (Apache 2.0)\\n\ -Copyright 2016 Google, Inc.\\n\ -https://github.com/google/nsync\""); -// clang-format off - -/* This package provides a mutex nsync_mu and a Mesa-style condition - * variable nsync_cv. */ - -/* Implementation notes - - The implementations of nsync_mu and nsync_cv both use spinlocks to protect - their waiter queues. The spinlocks are implemented with atomic operations - and a delay loop found below. They could use pthread_mutex_t, but I wished - to have an implementation independent of pthread mutexes and condition - variables. - - nsync_mu and nsync_cv use the same type of doubly-linked list of waiters - (see waiter.c). This allows waiters to be transferred from the cv queue to - the mu queue when a thread is logically woken from the cv but would - immediately go to sleep on the mu. See the wake_waiters() call. - - In mu, the "designated waker" is a thread that was waiting on mu, has been - woken up, but as yet has neither acquired nor gone back to waiting. The - presence of such a thread is indicated by the MU_DESIG_WAKER bit in the mu - word. This bit allows the nsync_mu_unlock() code to avoid waking a second - waiter when there's already one that will wake the next thread when the time - comes. This speeds things up when the lock is heavily contended, and the - critical sections are small. - - The weasel words "with high probability" in the specification of - nsync_mu_trylock() and nsync_mu_rtrylock() prevent clients from believing - that they can determine with certainty whether another thread has given up a - lock yet. This, together with the requirement that a thread that acquired a - mutex must release it (rather than it being released by another thread), - prohibits clients from using mu as a sort of semaphore. The intent is that - it be used only for traditional mutual exclusion, and that clients that need - a semaphore should use one. This leaves room for certain future - optimizations, and make it easier to apply detection of potential races via - candidate lock-set algorithms, should that ever be desired. - - The nsync_mu_wait_with_deadline() and nsync_mu_wait_with_deadline() calls use an - absolute rather than a relative timeout. This is less error prone, as - described in the comment on nsync_cv_wait_with_deadline(). Alas, relative - timeouts are seductive in trivial examples (such as tests). These are the - first things that people try, so they are likely to be requested. If enough - people complain we could give them that particular piece of rope. - - Excessive evaluations of the same wait condition are avoided by maintaining - waiter.same_condition as a doubly-linked list of waiters with the same - non-NULL wait condition that are also adjacent in the waiter list. This does - well even with large numbers of threads if there is at most one - wait condition that can be false at any given time (such as in a - producer/consumer queue, which cannot be both empty and full - simultaneously). One could imagine a queueing mechanism that would - guarantee to evaluate each condition at most once per wakeup, but that would - be substantially more complex, and would still degrade if the number of - distinct wakeup conditions were high. So clients are advised to resort to - condition variables if they have many distinct wakeup conditions. */ - -/* Used in spinloops to delay resumption of the loop. - Usage: - unsigned attempts = 0; - while (try_something) { - attempts = nsync_spin_delay_ (attempts); - } */ -unsigned nsync_spin_delay_ (unsigned attempts) { - if (attempts < 7) { - volatile int i; - for (i = 0; i != 1 << attempts; i++) { - } - attempts++; - } else { - nsync_yield_ (); - } - return (attempts); -} - -/* Spin until (*w & test) == 0, then atomically perform *w = ((*w | set) & - ~clear), perform an acquire barrier, and return the previous value of *w. - */ -uint32_t nsync_spin_test_and_set_ (nsync_atomic_uint32_ *w, uint32_t test, - uint32_t set, uint32_t clear) { - unsigned attempts = 0; /* CV_SPINLOCK retry count */ - uint32_t old = ATM_LOAD (w); - while ((old & test) != 0 || !ATM_CAS_ACQ (w, old, (old | set) & ~clear)) { - attempts = nsync_spin_delay_ (attempts); - old = ATM_LOAD (w); - } - return (old); -} - -/* ====================================================================================== */ - -struct nsync_waiter_s *nsync_dll_nsync_waiter_ (nsync_dll_element_ *e) { - struct nsync_waiter_s *nw = (struct nsync_waiter_s *) e->container; - ASSERT (nw->tag == NSYNC_WAITER_TAG); - ASSERT (e == &nw->q); - return (nw); -} -waiter *nsync_dll_waiter_ (nsync_dll_element_ *e) { - struct nsync_waiter_s *nw = DLL_NSYNC_WAITER (e); - waiter *w = CONTAINER (waiter, nw, nw); - ASSERT ((nw->flags & NSYNC_WAITER_FLAG_MUCV) != 0); - ASSERT (w->tag == WAITER_TAG); - ASSERT (e == &w->nw.q); - return (w); -} - -waiter *nsync_dll_waiter_samecond_ (nsync_dll_element_ *e) { - waiter *w = (waiter *) e->container; - ASSERT (w->tag == WAITER_TAG); - ASSERT (e == &w->same_condition); - return (w); -} - -/* -------------------------------- */ - -static nsync_dll_list_ free_waiters = NULL; - -/* free_waiters points to a doubly-linked list of free waiter structs. */ -static nsync_atomic_uint32_ free_waiters_mu; /* spinlock; protects free_waiters */ - -static _Thread_local waiter *waiter_for_thread; - -static void waiter_destroy (void *v) { - waiter *w = (waiter *) v; - /* Reset waiter_for_thread in case another thread-local variable reuses - the waiter in its destructor while the waiter is taken by the other - thread from free_waiters. This can happen as the destruction order - of thread-local variables can be arbitrary in some platform e.g. - POSIX. */ - waiter_for_thread = NULL; - IGNORE_RACES_START (); - ASSERT ((w->flags & (WAITER_RESERVED|WAITER_IN_USE)) == WAITER_RESERVED); - w->flags &= ~WAITER_RESERVED; - nsync_spin_test_and_set_ (&free_waiters_mu, 1, 1, 0); - free_waiters = nsync_dll_make_first_in_list_ (free_waiters, &w->nw.q); - ATM_STORE_REL (&free_waiters_mu, 0); /* release store */ - IGNORE_RACES_END (); -} - -/* Return a pointer to an unused waiter struct. - Ensures that the enclosed timer is stopped and its channel drained. */ -waiter *nsync_waiter_new_ (void) { - nsync_dll_element_ *q; - waiter *tw; - waiter *w; - tw = waiter_for_thread; - w = tw; - if (w == NULL || (w->flags & (WAITER_RESERVED|WAITER_IN_USE)) != WAITER_RESERVED) { - w = NULL; - nsync_spin_test_and_set_ (&free_waiters_mu, 1, 1, 0); - q = nsync_dll_first_ (free_waiters); - if (q != NULL) { /* If free list is non-empty, dequeue an item. */ - free_waiters = nsync_dll_remove_ (free_waiters, q); - w = DLL_WAITER (q); - } - ATM_STORE_REL (&free_waiters_mu, 0); /* release store */ - if (w == NULL) { /* If free list was empty, allocate an item. */ - w = (waiter *) nsync_malloc_ (sizeof (*w)); - w->tag = WAITER_TAG; - w->nw.tag = NSYNC_WAITER_TAG; - nsync_mu_semaphore_init (&w->sem); - w->nw.sem = &w->sem; - nsync_dll_init_ (&w->nw.q, &w->nw); - NSYNC_ATOMIC_UINT32_STORE_ (&w->nw.waiting, 0); - w->nw.flags = NSYNC_WAITER_FLAG_MUCV; - ATM_STORE (&w->remove_count, 0); - nsync_dll_init_ (&w->same_condition, w); - w->flags = 0; - } - if (tw == NULL) { - w->flags |= WAITER_RESERVED; - nsync_set_per_thread_waiter_ (w, &waiter_destroy); - waiter_for_thread = w; - } - } - w->flags |= WAITER_IN_USE; - return (w); -} - -/* Return an unused waiter struct *w to the free pool. */ -void nsync_waiter_free_ (waiter *w) { - ASSERT ((w->flags & WAITER_IN_USE) != 0); - w->flags &= ~WAITER_IN_USE; - if ((w->flags & WAITER_RESERVED) == 0) { - nsync_spin_test_and_set_ (&free_waiters_mu, 1, 1, 0); - free_waiters = nsync_dll_make_first_in_list_ (free_waiters, &w->nw.q); - ATM_STORE_REL (&free_waiters_mu, 0); /* release store */ - } -} - -/* ====================================================================================== */ - -/* writer_type points to a lock_type that describes how to manipulate a mu for a writer. */ -static lock_type Xwriter_type = { - MU_WZERO_TO_ACQUIRE, - MU_WADD_TO_ACQUIRE, - MU_WHELD_IF_NON_ZERO, - MU_WSET_WHEN_WAITING, - MU_WCLEAR_ON_ACQUIRE, - MU_WCLEAR_ON_UNCONTENDED_RELEASE -}; -lock_type *nsync_writer_type_ = &Xwriter_type; - - -/* reader_type points to a lock_type that describes how to manipulate a mu for a reader. */ -static lock_type Xreader_type = { - MU_RZERO_TO_ACQUIRE, - MU_RADD_TO_ACQUIRE, - MU_RHELD_IF_NON_ZERO, - MU_RSET_WHEN_WAITING, - MU_RCLEAR_ON_ACQUIRE, - MU_RCLEAR_ON_UNCONTENDED_RELEASE -}; -lock_type *nsync_reader_type_ = &Xreader_type; diff --git a/third_party/nsync/common.internal.h b/third_party/nsync/common.internal.h deleted file mode 100644 index 91553e49c..000000000 --- a/third_party/nsync/common.internal.h +++ /dev/null @@ -1,290 +0,0 @@ -#ifndef NSYNC_COMMON_H_ -#define NSYNC_COMMON_H_ -#include "third_party/nsync/atomic.h" -#include "third_party/nsync/atomic.internal.h" -#include "third_party/nsync/cv.h" -#include "third_party/nsync/dll.h" -#include "third_party/nsync/mu.h" -#include "third_party/nsync/mu_semaphore.h" -#include "third_party/nsync/note.h" -#include "third_party/nsync/time.h" -#include "third_party/nsync/wait_s.internal.h" -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -#ifndef NSYNC_DEBUG -#define NSYNC_DEBUG 0 -#endif - -/* Yield the CPU. Platform specific. */ -void nsync_yield_(void); - -/* Retrieve the per-thread cache of the waiter object. Platform specific. */ -void *nsync_per_thread_waiter_(void (*dest)(void *)); - -/* Set the per-thread cache of the waiter object. Platform specific. */ -void nsync_set_per_thread_waiter_(void *v, void (*dest)(void *)); - -/* Used in spinloops to delay resumption of the loop. - Usage: - unsigned attempts = 0; - while (try_something) { - attempts = nsync_spin_delay_ (attempts); - } */ -unsigned nsync_spin_delay_(unsigned attempts); - -/* Spin until (*w & test) == 0, then atomically perform *w = ((*w | set) & - ~clear), perform an acquire barrier, and return the previous value of *w. - */ -uint32_t nsync_spin_test_and_set_(nsync_atomic_uint32_ *w, uint32_t test, - uint32_t set, uint32_t clear); - -/* Abort after printing the nul-temrinated string s[]. */ -void nsync_panic_(const char *s); - -/* ---------- */ - -#define MIN_(a_, b_) ((a_) < (b_) ? (a_) : (b_)) -#define MAX_(a_, b_) ((a_) > (b_) ? (a_) : (b_)) - -/* ---------- */ - -/* Fields in nsync_mu.word. - - - At least one of the MU_WLOCK or MU_RLOCK_FIELD fields must be zero. - - MU_WLOCK indicates that a write lock is held. - - MU_RLOCK_FIELD is a count of readers with read locks. - - - MU_SPINLOCK represents a spinlock that must be held when manipulating the - waiter queue. - - - MU_DESIG_WAKER indicates that a former waiter has been woken, but has - neither acquired the lock nor gone back to sleep. Legal to fail to set it; - illegal to set it when no such waiter exists. - - - MU_WAITING indicates whether the waiter queue is non-empty. - The following bits should be zero if MU_WAITING is zero. - - MU_CONDITION indicates that some waiter may have an associated condition - (from nsync_mu_wait, etc.). Legal to set it with no such waiter exists, - but illegal to fail to set it with such a waiter. - - MU_WRITER_WAITING indicates that a reader that has not yet blocked - at least once should not acquire in order not to starve waiting writers. - It set when a writer blocks or a reader is woken with a writer waiting. - It is reset when a writer acquires, but set again when that writer - releases if it wakes readers and there is a waiting writer. - - MU_LONG_WAIT indicates that a waiter has been woken many times but - repeatedly failed to acquire when competing for the lock. This is used - only to prevent long-term starvation by writers. The thread that sets it - clears it when if acquires. - - MU_ALL_FALSE indicates that a complete scan of the waiter list found no - waiters with true conditions, and the lock has not been acquired by a - writer since then. This allows a reader lock to be released without - testing conditions again. It is legal to fail to set this, but illegal - to set it inappropriately. - */ -#define MU_WLOCK ((uint32_t)(1 << 0)) /* writer lock is held. */ -#define MU_SPINLOCK \ - ((uint32_t)(1 << 1)) /* spinlock is held (protects waiters). */ -#define MU_WAITING ((uint32_t)(1 << 2)) /* waiter list is non-empty. */ -#define MU_DESIG_WAKER \ - ((uint32_t)(1 << 3)) /* a former waiter awoke, and hasn't yet acquired or \ - slept anew */ -#define MU_CONDITION \ - ((uint32_t)(1 << 4)) /* the wait list contains some conditional waiters. */ -#define MU_WRITER_WAITING ((uint32_t)(1 << 5)) /* there is a writer waiting */ -#define MU_LONG_WAIT \ - ((uint32_t)(1 << 6)) /* the waiter at the head of the queue has been waiting \ - a long time */ -#define MU_ALL_FALSE \ - ((uint32_t)(1 << 7)) /* all waiter conditions are false \ - */ -#define MU_RLOCK \ - ((uint32_t)( \ - 1 << 8)) /* low-order bit of reader count, which uses rest of word */ - -/* The constants below are derived from those above. */ -#define MU_RLOCK_FIELD \ - (~(uint32_t)(MU_RLOCK - 1)) /* mask of reader count field */ - -#define MU_ANY_LOCK (MU_WLOCK | MU_RLOCK_FIELD) /* mask for any lock held */ - -#define MU_WZERO_TO_ACQUIRE \ - (MU_ANY_LOCK | MU_LONG_WAIT) /* bits to be zero to acquire write lock */ -#define MU_WADD_TO_ACQUIRE (MU_WLOCK) /* add to acquire a write lock */ -#define MU_WHELD_IF_NON_ZERO \ - (MU_WLOCK) /* if any of these bits are set, write lock is held */ -#define MU_WSET_WHEN_WAITING \ - (MU_WAITING | MU_WRITER_WAITING) /* a writer is waiting */ -#define MU_WCLEAR_ON_ACQUIRE \ - (MU_WRITER_WAITING) /* clear MU_WRITER_WAITING when a writer acquires */ -#define MU_WCLEAR_ON_UNCONTENDED_RELEASE \ - (MU_ALL_FALSE) /* clear if a writer releases w/o waking */ - -/* bits to be zero to acquire read lock */ -#define MU_RZERO_TO_ACQUIRE (MU_WLOCK | MU_WRITER_WAITING | MU_LONG_WAIT) -#define MU_RADD_TO_ACQUIRE (MU_RLOCK) /* add to acquire a read lock */ -#define MU_RHELD_IF_NON_ZERO \ - (MU_RLOCK_FIELD) /* if any of these bits are set, read lock is held */ -#define MU_RSET_WHEN_WAITING \ - (MU_WAITING) /* indicate that some thread is waiting */ -#define MU_RCLEAR_ON_ACQUIRE \ - ((uint32_t)0) /* nothing to clear when a read acquires */ -#define MU_RCLEAR_ON_UNCONTENDED_RELEASE \ - ((uint32_t)0) /* nothing to clear when a read releases */ - -/* A lock_type holds the values needed to manipulate a mu in some mode (read or - write). This allows some of the code to be generic, and parameterized by - the lock type. */ -typedef struct lock_type_s { - uint32_t zero_to_acquire; /* bits that must be zero to acquire */ - uint32_t add_to_acquire; /* constant to add to acquire */ - uint32_t - held_if_non_zero; /* if any of these bits are set, the lock is held */ - uint32_t set_when_waiting; /* set when thread waits */ - uint32_t clear_on_acquire; /* clear when thread acquires */ - uint32_t clear_on_uncontended_release; /* clear when thread releases without - waking */ -} lock_type; - -/* writer_type points to a lock_type that describes how to manipulate a mu for a - * writer. */ -extern lock_type *nsync_writer_type_; - -/* reader_type points to a lock_type that describes how to manipulate a mu for a - * reader. */ -extern lock_type *nsync_reader_type_; - -/* ---------- */ - -/* Bits in nsync_cv.word */ - -#define CV_SPINLOCK ((uint32_t)(1 << 0)) /* protects waiters */ -#define CV_NON_EMPTY ((uint32_t)(1 << 1)) /* waiters list is non-empty */ - -/* ---------- */ - -/* Hold a pair of condition function and its argument. */ -struct wait_condition_s { - int (*f)(const void *v); - const void *v; - int (*eq)(const void *a, const void *b); -}; - -/* Return whether wait conditions *a_ and *b_ are equal and non-null. */ -#define WAIT_CONDITION_EQ(a_, b_) \ - ((a_)->f != NULL && (a_)->f == (b_)->f && \ - ((a_)->v == (b_)->v || \ - ((a_)->eq != NULL && (*(a_)->eq)((a_)->v, (b_)->v)))) - -/* If a waiter has waited this many times, it may set the MU_LONG_WAIT bit. */ -#define LONG_WAIT_THRESHOLD 30 - -/* ---------- */ - -#define NOTIFIED_TIME(n_) \ - (ATM_LOAD_ACQ(&(n_)->notified) != 0 ? nsync_time_zero \ - : (n_)->expiry_time_valid ? (n_)->expiry_time \ - : nsync_time_no_deadline) - -/* A waiter represents a single waiter on a cv or a mu. - - To wait: - Allocate a waiter struct *w with new_waiter(), set w.waiting=1, and - w.cv_mu=nil or to the associated mu if waiting on a condition variable, then - queue w.nsync_dll on some queue, and then wait using: - while (ATM_LOAD_ACQ (&w.waiting) != 0) { nsync_mu_semaphore_p (&w.sem); } - Return *w to the freepool by calling free_waiter (w). - - To wakeup: - Remove *w from the relevant queue then: - ATM_STORE_REL (&w.waiting, 0); - nsync_mu_semaphore_v (&w.sem); */ -typedef struct { - uint32_t tag; /* debug DLL_NSYNC_WAITER, DLL_WAITER, DLL_WAITER_SAMECOND */ - nsync_semaphore sem; /* Thread waits on this semaphore. */ - struct nsync_waiter_s nw; /* An embedded nsync_waiter_s. */ - struct nsync_mu_s_ *cv_mu; /* pointer to nsync_mu associated with a cv wait */ - lock_type - *l_type; /* Lock type of the mu, or nil if not associated with a mu. */ - nsync_atomic_uint32_ remove_count; /* count of removals from queue */ - struct wait_condition_s cond; /* A condition on which to acquire a mu. */ - nsync_dll_element_ same_condition; /* Links neighbours in nw.q with same - non-nil condition. */ - int flags; /* see WAITER_* bits below */ -} waiter; -static const uint32_t WAITER_TAG = 0x0590239f; -static const uint32_t NSYNC_WAITER_TAG = 0x726d2ba9; - -#define WAITER_RESERVED \ - 0x1 /* waiter reserved by a thread, even when not in use */ -#define WAITER_IN_USE 0x2 /* waiter in use by a thread */ - -#define CONTAINER(t_, f_, p_) ((t_ *)(((char *)(p_)) - offsetof(t_, f_))) -#define ASSERT(x) \ - do { \ - if (!(x)) { \ - *(volatile int *)0 = 0; \ - } \ - } while (0) - -/* Return a pointer to the nsync_waiter_s containing nsync_dll_element_ *e. */ -#define DLL_NSYNC_WAITER(e) \ - (NSYNC_DEBUG ? nsync_dll_nsync_waiter_(e) \ - : ((struct nsync_waiter_s *)((e)->container))) -struct nsync_waiter_s *nsync_dll_nsync_waiter_(nsync_dll_element_ *e); - -/* Return a pointer to the waiter struct that *e is embedded in, where *e is an - * nw.q field. */ -#define DLL_WAITER(e) \ - (NSYNC_DEBUG ? nsync_dll_waiter_(e) \ - : CONTAINER(waiter, nw, DLL_NSYNC_WAITER(e))) -waiter *nsync_dll_waiter_(nsync_dll_element_ *e); - -/* Return a pointer to the waiter struct that *e is embedded in, where *e is a - same_condition field. */ -#define DLL_WAITER_SAMECOND(e) \ - (NSYNC_DEBUG ? nsync_dll_waiter_samecond_(e) : ((waiter *)((e)->container))) -waiter *nsync_dll_waiter_samecond_(nsync_dll_element_ *e); - -/* Return a pointer to an unused waiter struct. - Ensures that the enclosed timer is stopped and its channel drained. */ -waiter *nsync_waiter_new_(void); - -/* Return an unused waiter struct *w to the free pool. */ -void nsync_waiter_free_(waiter *w); - -/* ---------- */ - -/* The internals of an nync_note. See internal/note.c for details of locking - discipline. */ -struct nsync_note_s_ { - nsync_dll_element_ - parent_child_link; /* parent's children, under parent->note_mu */ - int expiry_time_valid; /* whether expiry_time is valid; r/o after init */ - nsync_time - expiry_time; /* expiry time, if expiry_time_valid != 0; r/o after init */ - nsync_mu note_mu; /* protects fields below except "notified" */ - nsync_cv no_children_cv; /* signalled when children becomes empty */ - uint32_t disconnecting; /* non-zero => node is being disconnected */ - nsync_atomic_uint32_ notified; /* non-zero if the note has been notified */ - struct nsync_note_s_ *parent; /* points to parent, if any */ - nsync_dll_element_ *children; /* list of children */ - nsync_dll_element_ *waiters; /* list of waiters */ -}; - -/* ---------- */ - -void nsync_mu_lock_slow_(nsync_mu *mu, waiter *w, uint32_t clear, - lock_type *l_type); -void nsync_mu_unlock_slow_(nsync_mu *mu, lock_type *l_type); -nsync_dll_list_ nsync_remove_from_mu_queue_(nsync_dll_list_ mu_queue, - nsync_dll_element_ *e); -void nsync_maybe_merge_conditions_(nsync_dll_element_ *p, - nsync_dll_element_ *n); -nsync_time nsync_note_notified_deadline_(nsync_note n); -int nsync_sem_wait_with_cancel_(waiter *w, nsync_time abs_deadline, - nsync_note cancel_note); - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* NSYNC_COMMON_H_ */ diff --git a/third_party/nsync/counter.h b/third_party/nsync/counter.h deleted file mode 100644 index 7828553e9..000000000 --- a/third_party/nsync/counter.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef NSYNC_COUNTER_H_ -#define NSYNC_COUNTER_H_ -#include "third_party/nsync/atomic.h" -#include "third_party/nsync/time.h" -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -struct nsync_dll_element_s_; - -/* An nsync_counter represents an unsigned integer that can count up and down, - and wake waiters when zero. */ -typedef struct nsync_counter_s_ *nsync_counter; - -/* Return a freshly allocated nsync_counter with the specified value, - of NULL if an nsync_counter cannot be created. - - Any non-NULL returned value should be passed to nsync_counter_free() when no - longer needed. */ -nsync_counter nsync_counter_new(uint32_t value); - -/* Free resources associated with c. Requires that c was allocated by - nsync_counter_new(), and no concurrent or future operations are applied to - c. */ -void nsync_counter_free(nsync_counter c); - -/* Add delta to c, and return its new value. It is a checkable runtime error - to decrement c below 0, or to increment c (i.e., apply a delta > 0) after a - waiter has waited. */ -uint32_t nsync_counter_add(nsync_counter c, int32_t delta); - -/* Return the current value of c. */ -uint32_t nsync_counter_value(nsync_counter c); - -/* Wait until c has value 0, or until abs_deadline, then return - the value of c. It is a checkable runtime error to increment c after - a waiter may have been woken due to the counter reaching zero. - If abs_deadline==nsync_time_no_deadline, the deadline - is far in the future. */ -uint32_t nsync_counter_wait(nsync_counter c, nsync_time abs_deadline); - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* NSYNC_COUNTER_H_ */ diff --git a/third_party/nsync/cv.h b/third_party/nsync/cv.h deleted file mode 100644 index bd6b5ad91..000000000 --- a/third_party/nsync/cv.h +++ /dev/null @@ -1,157 +0,0 @@ -#ifndef NSYNC_CV_H_ -#define NSYNC_CV_H_ -#include "third_party/nsync/mu.h" -#include "third_party/nsync/time.h" -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -#define NSYNC_CV_INIT \ - { NSYNC_ATOMIC_UINT32_INIT_, 0 } - -struct nsync_dll_element_s_; -struct nsync_note_s_; - -/* An nsync_cv is a condition variable in the style of Mesa, Java, - POSIX, and Go's sync.Cond. It allows a thread to wait for a condition - on state protected by a mutex, and to proceed with the mutex held and - the condition true. - - See also nsync_mu_wait() and nsync_mu_wait_with_deadline(), which - implement conditional critical sections. In many cases, they are - easier to use than condition variables. - - Usage - - After making the desired predicate true, call: - - nsync_cv_signal (&cv); // If at most one thread can make use - // of the predicate becoming true. - - or - - nsync_cv_broadcast (&cv); // If multiple threads can make use - // of the predicate becoming true. - - To wait for a predicate with no deadline (assuming - nsync_cv_broadcast() or nsync_cv_signal() is called whenever the - predicate becomes true): - - nsync_mu_lock (μ) - while (!some_predicate_protected_by_mu) { // while-loop required - nsync_cv_wait (&cv, &mu); - } - // predicate is now true - nsync_mu_unlock (&mu); - - To wait for a predicate with a deadline (assuming nsync_cv_broadcast() or - nsync_cv_signal() is called whenever the predicate becomes true): - - nsync_mu_lock (&mu); - while (!some_predicate_protected_by_mu && - nsync_cv_wait_with_deadline (&cv, &mu, abs_deadline, - cancel_note) == 0) { - } - if (some_predicate_protected_by_mu) { // predicate is true - } else { - // predicate is false, and deadline expired, or - // cancel_note was notified. - } - nsync_mu_unlock (&mu); - - or, if the predicate is complex and you wish to write it just once - and inline, you could use the following instead of the for-loop - above: - - nsync_mu_lock (&mu); - int pred_is_true = 0; - int outcome = 0; - while (!(pred_is_true = some_predicate_protected_by_mu) && - outcome == 0) { - outcome = nsync_cv_wait_with_deadline (&cv, &mu, abs_deadline, - cancel_note); - } - if (pred_is_true) { // predicate is true - } else { - // predicate is false, and deadline expired, or - // cancel_note was notified. - } - nsync_mu_unlock (&mu); - - As the examples show, Mesa-style condition variables require that - waits use a loop that tests the predicate anew after each wait. It - may be surprising that these are preferred over the precise wakeups - offered by the condition variables in Hoare monitors. Imprecise - wakeups make more efficient use of the critical section, because - threads can enter it while a woken thread is still emerging from the - scheduler, which may take thousands of cycles. Further, they make the - programme easier to read and debug by making the predicate explicit - locally at the wait, where the predicate is about to be assumed; the - reader does not have to infer the predicate by examining all the - places where wakeups may occur. */ -typedef struct nsync_cv_s_ { - /* see bits below */ - nsync_atomic_uint32_ word; - /* points to tail of list of waiters; under mu. */ - struct nsync_dll_element_s_ *waiters; -} nsync_cv; - -/* An nsync_cv should be zeroed to initialize, which can be accomplished - by initializing with static initializer NSYNC_CV_INIT, or by setting - the entire struct to 0, or using nsync_cv_init(). */ -void nsync_cv_init(nsync_cv *cv); - -/* Wake at least one thread if any are currently blocked on *cv. If the - chosen thread is a reader on an nsync_mu, wake all readers and, if - possible, a writer. */ -void nsync_cv_signal(nsync_cv *cv); - -/* Wake all threads currently blocked on *cv. */ -void nsync_cv_broadcast(nsync_cv *cv); - -/* Atomically release "mu" (which must be held on entry) and block the - caller on *cv. Wait until awakened by a call to nsync_cv_signal() or - nsync_cv_broadcast(), or a spurious wakeup; then reacquire "mu", and - return. Equivalent to a call to nsync_mu_wait_with_deadline() with - abs_deadline==nsync_time_no_deadline, and cancel_note==NULL. Callers - should use nsync_cv_wait() in a loop, as with all standard Mesa-style - condition variables. See examples above. */ -void nsync_cv_wait(nsync_cv *cv, nsync_mu *mu); - -/* Atomically release "mu" (which must be held on entry) and block the - calling thread on *cv. It then waits until awakened by a call to - nsync_cv_signal() or nsync_cv_broadcast() (or a spurious wakeup), or - by the time reaching abs_deadline, or by cancel_note being notified. - In all cases, it reacquires "mu", and returns the reason for the call - returned (0, ETIMEDOUT, or ECANCELED). Use - abs_deadline==nsync_time_no_deadline for no deadline, and - cancel_note==NULL for no cancellation. wait_with_deadline() should be - used in a loop, as with all Mesa-style condition variables. See - examples above. - - There are two reasons for using an absolute deadline, rather than a - relative timeout---these are why pthread_cond_timedwait() also uses - an absolute deadline. First, condition variable waits have to be used - in a loop; with an absolute times, the deadline does not have to be - recomputed on each iteration. Second, in most real programmes, some - activity (such as an RPC to a server, or when guaranteeing response - time in a UI), there is a deadline imposed by the specification or - the caller/user; relative delays can shift arbitrarily with - scheduling delays, and so after multiple waits might extend beyond - the expected deadline. Relative delays tend to be more convenient - mostly in tests and trivial examples than they are in real - programmes. */ -int nsync_cv_wait_with_deadline(nsync_cv *cv, nsync_mu *mu, - nsync_time abs_deadline, - struct nsync_note_s_ *cancel_note); - -/* Like nsync_cv_wait_with_deadline(), but allow an arbitrary lock *v to be - used, given its (*lock)(mu) and (*unlock)(mu) routines. */ -int nsync_cv_wait_with_deadline_generic(nsync_cv *cv, void *mu, - void (*lock)(void *), - void (*unlock)(void *), - nsync_time abs_deadline, - struct nsync_note_s_ *cancel_note); - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* NSYNC_CV_H_ */ diff --git a/third_party/nsync/debug.h b/third_party/nsync/debug.h deleted file mode 100644 index 44d59618f..000000000 --- a/third_party/nsync/debug.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef NSYNC_DEBUG_H_ -#define NSYNC_DEBUG_H_ -#include "third_party/nsync/cv.h" -#include "third_party/nsync/mu.h" -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -/* Debugging operations for mutexes and condition variables. - - These operations should not be relied upon for normal functionality. - The implementation may be slow, output formats may change, and the - implementation is free to yield the empty string. */ - -/* Place in buf[0,..,n-1] a nul-terminated, human readable string - indicative of some of the internal state of the mutex or condition - variable, and return buf. If n>=4, buffer overflow is indicated by - placing the characters "..." at the end of the string. - - The *_and_waiters() variants attempt to output the waiter lists in - addition to the basic state. These variants may acquire internal - locks and follow internal pointers. Thus, they are riskier if invoked - in an address space whose overall health is uncertain. */ -char *nsync_mu_debug_state(nsync_mu *mu, char *buf, int n); -char *nsync_cv_debug_state(nsync_cv *cv, char *buf, int n); -char *nsync_mu_debug_state_and_waiters(nsync_mu *mu, char *buf, int n); -char *nsync_cv_debug_state_and_waiters(nsync_cv *cv, char *buf, int n); - -/* Like nsync_*_debug_state_and_waiters(), but ignoring all locking and - safety considerations, and using an internal, possibly static buffer - that may be overwritten by subsequent or concurrent calls to these - routines. These variants should be used only from an interactive - debugger, when all other threads are stopped; the debugger is - expected to recover from errors. */ -char *nsync_mu_debugger(nsync_mu *mu); -char *nsync_cv_debugger(nsync_cv *cv); - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* NSYNC_DEBUG_H_ */ diff --git a/third_party/nsync/dll.c b/third_party/nsync/dll.c deleted file mode 100644 index d5ff5e555..000000000 --- a/third_party/nsync/dll.c +++ /dev/null @@ -1,143 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ -│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2016 Google Inc. │ -│ │ -│ Licensed under the Apache License, Version 2.0 (the "License"); │ -│ you may not use this file except in compliance with the License. │ -│ You may obtain a copy of the License at │ -│ │ -│ http://www.apache.org/licenses/LICENSE-2.0 │ -│ │ -│ Unless required by applicable law or agreed to in writing, software │ -│ distributed under the License is distributed on an "AS IS" BASIS, │ -│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │ -│ See the License for the specific language governing permissions and │ -│ limitations under the License. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "third_party/nsync/dll.h" - -asm(".ident\t\"\\n\\n\ -*NSYNC (Apache 2.0)\\n\ -Copyright 2016 Google, Inc.\\n\ -https://github.com/google/nsync\""); -// clang-format off - -/* Initialize *e. */ -void nsync_dll_init_ (nsync_dll_element_ *e, void *container) { - e->next = e; - e->prev = e; - e->container = container; -} - -/* Return whether list is empty. */ -int nsync_dll_is_empty_ (nsync_dll_list_ list) { - return (list == NULL); -} - -/* Remove *e from list, and returns the new list. */ -nsync_dll_list_ nsync_dll_remove_ (nsync_dll_list_ list, nsync_dll_element_ *e) { - if (list == e) { /* removing tail of list */ - if (list->prev == list) { - list = NULL; /* removing only element of list */ - } else { - list = list->prev; - } - } - e->next->prev = e->prev; - e->prev->next = e->next; - e->next = e; - e->prev = e; - return (list); -} - -/* Cause element *n and its successors to come after element *p. - Requires n and p are non-NULL and do not point at elements of the same list. - - Unlike the other operations in this API, this operation acts on - two circular lists of elements, rather than on a "head" location that points - to such a circular list. - - If the two lists are p->p_2nd->p_mid->p_last->p and n->n_2nd->n_mid->n_last->n, - then after nsync_dll_splice_after_ (p, n), the p list would be: - p->n->n_2nd->n_mid->n_last->p_2nd->p_mid->p_last->p. */ -void nsync_dll_splice_after_ (nsync_dll_element_ *p, nsync_dll_element_ *n) { - nsync_dll_element_ *p_2nd = p->next; - nsync_dll_element_ *n_last = n->prev; - p->next = n; /* n follows p */ - n->prev = p; - n_last->next = p_2nd; /* remainder of p-list follows last of n-list */ - p_2nd->prev = n_last; -} - -/* Make element *e the first element of list, and return - the list. The resulting list will have *e as its first element, followed by - any elements in the same list as *e, followed by the elements that were - previously in list. Requires that *e not be in list. If e==NULL, list is - returned unchanged. - - Suppose the e list is e->e_2nd->e_mid->e_last->e. - Recall that a head "list" points to the last element of its list. - If list is initially null, then the outcome is: - result = e_last->e->e_2nd->e_mid->e_last - If list is initially list->list_last->list_1st->list_mid->list_last, - then the outcome is: - result = list_last->e->e_2nd->e_mid->e_last->list_1st->list_mid->list_last - */ -nsync_dll_list_ nsync_dll_make_first_in_list_ (nsync_dll_list_ list, nsync_dll_element_ *e) { - if (e != NULL) { - if (list == NULL) { - list = e->prev; /*e->prev is e_last*/ - } else { - nsync_dll_splice_after_ (list, e); - } - } - return (list); -} - -/* Make element *e the last element of list, and return - the list. The resulting list will have *e as its last element, preceded by - any elements in the same list as *e, preceded by the elements that were - previously in list. Requires that *e not be in list. If e==NULL, list is - returned unchanged. */ -nsync_dll_list_ nsync_dll_make_last_in_list_ (nsync_dll_list_ list, nsync_dll_element_ *e) { - if (e != NULL) { - nsync_dll_make_first_in_list_ (list, e->next); - list = e; - } - return (list); -} - -/* Return a pointer to the first element of list, or NULL if list is empty. */ -nsync_dll_element_ *nsync_dll_first_ (nsync_dll_list_ list) { - nsync_dll_element_ *first = NULL; - if (list != NULL) { - first = list->next; - } - return (first); -} - -/* Return a pointer to the last element of list, or NULL if list is empty. */ -nsync_dll_element_ *nsync_dll_last_ (nsync_dll_list_ list) { - return (list); -} - -/* Return a pointer to the next element of list following *e, - or NULL if there is no such element. */ -nsync_dll_element_ *nsync_dll_next_ (nsync_dll_list_ list, nsync_dll_element_ *e) { - nsync_dll_element_ *next = NULL; - if (e != list) { - next = e->next; - } - return (next); -} - -/* Return a pointer to the previous element of list following *e, - or NULL if there is no such element. */ -nsync_dll_element_ *nsync_dll_prev_ (nsync_dll_list_ list, nsync_dll_element_ *e) { - nsync_dll_element_ *prev = NULL; - if (e != list->next) { - prev = e->prev; - } - return (prev); -} diff --git a/third_party/nsync/dll.h b/third_party/nsync/dll.h deleted file mode 100644 index a1345d386..000000000 --- a/third_party/nsync/dll.h +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef NSYNC_DLL_H_ -#define NSYNC_DLL_H_ -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -/* A nsync_dll_element_ represents an element of a doubly-linked list of - waiters. */ -typedef struct nsync_dll_element_s_ { - struct nsync_dll_element_s_ *next; - struct nsync_dll_element_s_ *prev; - /* points to the struct this nsync_dll struct is embedded in. */ - void *container; -} nsync_dll_element_; - -/* A nsync_dll_list_ represents a list of nsync_dll_elements_. */ -typedef nsync_dll_element_ *nsync_dll_list_; /* last elem of circular list; nil - => empty; first is x.next. */ - -/* Initialize *e. */ -void nsync_dll_init_(nsync_dll_element_ *e, void *container); - -/* Return whether list is empty. */ -int nsync_dll_is_empty_(nsync_dll_list_ list); - -/* Remove *e from list, and returns the new list. */ -nsync_dll_list_ nsync_dll_remove_(nsync_dll_list_ list, nsync_dll_element_ *e); - -/* Cause element *n and its successors to come after element *p. - Requires n and p are non-NULL and do not point at elements of the - same list. */ -void nsync_dll_splice_after_(nsync_dll_element_ *p, nsync_dll_element_ *n); - -/* Make element *e the first element of list, and return the list. The - resulting list will have *e as its first element, followed by any - elements in the same list as *e, followed by the elements that were - previously in list. Requires that *e not be in list. If e==NULL, list - is returned unchanged. */ -nsync_dll_list_ nsync_dll_make_first_in_list_(nsync_dll_list_ list, - nsync_dll_element_ *e); - -/* Make element *e the last element of list, and return the list. The - resulting list will have *e as its last element, preceded by any - elements in the same list as *e, preceded by the elements that were - previously in list. Requires that *e not be in list. If e==NULL, list - is returned unchanged. */ -nsync_dll_list_ nsync_dll_make_last_in_list_(nsync_dll_list_ list, - nsync_dll_element_ *e); - -/* Return a pointer to the first element of list, or NULL if list is - * empty. */ -nsync_dll_element_ *nsync_dll_first_(nsync_dll_list_ list); - -/* Return a pointer to the last element of list, or NULL if list is - * empty. */ -nsync_dll_element_ *nsync_dll_last_(nsync_dll_list_ list); - -/* Return a pointer to the next element of list following *e, or NULL if - there is no such element. */ -nsync_dll_element_ *nsync_dll_next_(nsync_dll_list_ list, - nsync_dll_element_ *e); - -/* Return a pointer to the previous element of list following *e, or - NULL if there is no such element. */ -nsync_dll_element_ *nsync_dll_prev_(nsync_dll_list_ list, - nsync_dll_element_ *e); - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* NSYNC_DLL_H_ */ diff --git a/third_party/nsync/futex.c b/third_party/nsync/futex.c deleted file mode 100644 index cc875abbe..000000000 --- a/third_party/nsync/futex.c +++ /dev/null @@ -1,124 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ -│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2022 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/calls/strace.internal.h" -#include "libc/calls/struct/timespec.internal.h" -#include "libc/dce.h" -#include "libc/errno.h" -#include "libc/intrin/describeflags.internal.h" -#include "libc/sysv/consts/futex.h" -#include "libc/thread/thread.h" -#include "third_party/nsync/common.internal.h" -#include "third_party/nsync/futex.internal.h" -// clang-format off - -/* futex() polyfill w/ sched_yield() fallback */ - -#define FUTEX_WAIT_BITS_ FUTEX_BITSET_MATCH_ANY - -int _futex (int *, int, int, const struct timespec *, int *, int); - -static int FUTEX_WAIT_; -static int FUTEX_WAKE_; -static int FUTEX_PRIVATE_FLAG_; -static bool FUTEX_IS_SUPPORTED; -bool FUTEX_TIMEOUT_IS_ABSOLUTE; - -__attribute__((__constructor__)) static void sync_futex_init_ (void) { - int x = 0; - - FUTEX_WAKE_ = FUTEX_WAKE; - - if (IsLinux () && - _futex (&x, FUTEX_WAIT_BITSET, 1, 0, 0, - FUTEX_BITSET_MATCH_ANY) == -EAGAIN) { - FUTEX_WAIT_ = FUTEX_WAIT_BITSET; - FUTEX_TIMEOUT_IS_ABSOLUTE = true; - } else { - FUTEX_WAIT_ = FUTEX_WAIT; - } - - if (IsOpenbsd () || - (IsLinux () && - !_futex (&x, FUTEX_WAKE_PRIVATE, 1, 0, 0, 0))) { - FUTEX_PRIVATE_FLAG_ = FUTEX_PRIVATE_FLAG; - } - - // In our testing, we found that the monotonic clock on various - // popular systems (such as Linux, and some BSD variants) was no - // better behaved than the realtime clock, and routinely took - // large steps backwards, especially on multiprocessors. Given - // that "monotonic" doesn't seem to mean what it says, - // implementers of nsync_time might consider retaining the - // simplicity of a single epoch within an address space, by - // configuring any time synchronization mechanism (like ntp) to - // adjust for leap seconds by adjusting the rate, rather than - // with a backwards step. - if (IsLinux () && - _futex (&x, FUTEX_WAIT_BITSET | FUTEX_CLOCK_REALTIME, - 1, 0, 0, FUTEX_BITSET_MATCH_ANY) == -EAGAIN) { - FUTEX_WAIT_ |= FUTEX_CLOCK_REALTIME; - } - - FUTEX_IS_SUPPORTED = IsLinux() || IsOpenbsd(); -} - -int nsync_futex_wait_ (int *p, int expect, char pshare, struct timespec *timeout) { - int rc, op; - if (FUTEX_IS_SUPPORTED) { - op = FUTEX_WAIT_; - if (pshare == PTHREAD_PROCESS_PRIVATE) { - op |= FUTEX_PRIVATE_FLAG_; - } - rc = _futex (p, op, expect, timeout, 0, FUTEX_WAIT_BITS_); - if (IsOpenbsd() && rc > 0) { - // [jart] openbsd does this without setting carry flag - rc = -rc; - } - STRACE("futex(%t, %s, %d, %s) → %s", - p, DescribeFutexOp(op), expect, - DescribeTimespec(0, timeout), DescribeFutexResult(rc)); - } else { - nsync_yield_ (); - if (timeout) { - rc = -ETIMEDOUT; - } else { - rc = 0; - } - } - return rc; -} - -int nsync_futex_wake_ (int *p, int count, char pshare) { - int rc, op; - int wake (void *, int, int) asm ("_futex"); - if (FUTEX_IS_SUPPORTED) { - op = FUTEX_WAKE_; - if (pshare == PTHREAD_PROCESS_PRIVATE) { - op |= FUTEX_PRIVATE_FLAG_; - } - rc = wake (p, op, count); - STRACE("futex(%t, %s, %d) → %s", p, - DescribeFutexOp(op), - count, DescribeFutexResult(rc)); - } else { - nsync_yield_ (); - rc = 0; - } - return rc; -} diff --git a/third_party/nsync/futex.internal.h b/third_party/nsync/futex.internal.h deleted file mode 100644 index 5fc8f4998..000000000 --- a/third_party/nsync/futex.internal.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef NSYNC_FUTEX_INTERNAL_H_ -#define NSYNC_FUTEX_INTERNAL_H_ -#include "libc/calls/struct/timespec.h" -#include "libc/dce.h" -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -extern bool FUTEX_TIMEOUT_IS_ABSOLUTE; - -int nsync_futex_wake_(int *, int, char); -int nsync_futex_wait_(int *, int, char, struct timespec *); - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* NSYNC_FUTEX_INTERNAL_H_ */ diff --git a/third_party/nsync/malloc.c b/third_party/nsync/malloc.c deleted file mode 100644 index 65708ddfe..000000000 --- a/third_party/nsync/malloc.c +++ /dev/null @@ -1,49 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ -│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2022 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/atomic.h" -#include "libc/calls/extend.internal.h" -#include "libc/intrin/atomic.h" -#include "libc/macros.internal.h" -#include "third_party/nsync/common.internal.h" -#include "third_party/nsync/malloc.internal.h" -// clang-format off - -static char *nsync_malloc_endptr_; -static size_t nsync_malloc_total_; -static atomic_char nsync_malloc_lock_; - -/* nsync_malloc_() is a malloc-like routine used by mutex and condition - variable code to allocate waiter structs. This allows *NSYNC mutexes - to be used by malloc(), by providing another, simpler allocator here. - The intent is that the implicit NULL value here can be overridden by - a client declaration that uses an initializer. */ -void *nsync_malloc_ (size_t size) { - char *start; - size_t offset; - size = ROUNDUP (size, __BIGGEST_ALIGNMENT__); - while (atomic_exchange (&nsync_malloc_lock_, 1)) nsync_yield_ (); - offset = nsync_malloc_total_; - nsync_malloc_total_ += size; - start = (char *) 0x6fc000040000; - if (!nsync_malloc_endptr_) nsync_malloc_endptr_ = start; - nsync_malloc_endptr_ = _extend (start, nsync_malloc_total_, - nsync_malloc_endptr_, 0x6fcfffff0000); - atomic_store_explicit (&nsync_malloc_lock_, 0, memory_order_relaxed); - return start + offset; -} diff --git a/third_party/nsync/malloc.internal.h b/third_party/nsync/malloc.internal.h deleted file mode 100644 index e4453e410..000000000 --- a/third_party/nsync/malloc.internal.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef NSYNC_MALLOC_INTERNAL_H_ -#define NSYNC_MALLOC_INTERNAL_H_ -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -void *nsync_malloc_(size_t); - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* NSYNC_MALLOC_INTERNAL_H_ */ diff --git a/third_party/nsync/mu.c b/third_party/nsync/mu.c deleted file mode 100644 index bda08494d..000000000 --- a/third_party/nsync/mu.c +++ /dev/null @@ -1,548 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ -│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2016 Google Inc. │ -│ │ -│ Licensed under the Apache License, Version 2.0 (the "License"); │ -│ you may not use this file except in compliance with the License. │ -│ You may obtain a copy of the License at │ -│ │ -│ http://www.apache.org/licenses/LICENSE-2.0 │ -│ │ -│ Unless required by applicable law or agreed to in writing, software │ -│ distributed under the License is distributed on an "AS IS" BASIS, │ -│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │ -│ See the License for the specific language governing permissions and │ -│ limitations under the License. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/str/str.h" -#include "third_party/nsync/atomic.h" -#include "third_party/nsync/common.internal.h" -#include "third_party/nsync/dll.h" -#include "third_party/nsync/mu_semaphore.h" -#include "third_party/nsync/races.internal.h" -#include "third_party/nsync/wait_s.internal.h" - -asm(".ident\t\"\\n\\n\ -*NSYNC (Apache 2.0)\\n\ -Copyright 2016 Google, Inc.\\n\ -https://github.com/google/nsync\""); -// clang-format off - -/* Initialize *mu. */ -void nsync_mu_init (nsync_mu *mu) { - memset ((void *) mu, 0, sizeof (*mu)); -} - -/* Release the mutex spinlock. */ -static void mu_release_spinlock (nsync_mu *mu) { - uint32_t old_word = ATM_LOAD (&mu->word); - while (!ATM_CAS_REL (&mu->word, old_word, old_word & ~MU_SPINLOCK)) { - old_word = ATM_LOAD (&mu->word); - } -} - -/* Lock *mu using the specified lock_type, waiting on *w if necessary. - "clear" should be zero if the thread has not previously slept on *mu, and - MU_DESIG_WAKER if it has; this represents bits that nsync_mu_lock_slow_() must clear when - it either acquires or sleeps on *mu. The caller owns *w on return; it is in a valid - state to be returned to the free pool. */ -void nsync_mu_lock_slow_ (nsync_mu *mu, waiter *w, uint32_t clear, lock_type *l_type) { - uint32_t zero_to_acquire; - uint32_t wait_count; - uint32_t long_wait; - unsigned attempts = 0; /* attempt count; used for spinloop backoff */ - w->cv_mu = NULL; /* not a cv wait */ - w->cond.f = NULL; /* Not using a conditional critical section. */ - w->cond.v = NULL; - w->cond.eq = NULL; - w->l_type = l_type; - zero_to_acquire = l_type->zero_to_acquire; - if (clear != 0) { - /* Only the constraints of mutual exclusion should stop a designated waker. */ - zero_to_acquire &= ~(MU_WRITER_WAITING | MU_LONG_WAIT); - } - wait_count = 0; /* number of times we waited, and were woken. */ - long_wait = 0; /* set to MU_LONG_WAIT when wait_count gets large */ - for (;;) { - uint32_t old_word = ATM_LOAD (&mu->word); - if ((old_word & zero_to_acquire) == 0) { - /* lock can be acquired; try to acquire, possibly - clearing MU_DESIG_WAKER and MU_LONG_WAIT. */ - if (ATM_CAS_ACQ (&mu->word, old_word, - (old_word+l_type->add_to_acquire) & - ~(clear|long_wait|l_type->clear_on_acquire))) { - return; - } - } else if ((old_word&MU_SPINLOCK) == 0 && - ATM_CAS_ACQ (&mu->word, old_word, - (old_word|MU_SPINLOCK|long_wait| - l_type->set_when_waiting) & ~(clear | MU_ALL_FALSE))) { - - /* Spinlock is now held, and lock is held by someone - else; MU_WAITING has also been set; queue ourselves. - There's no need to adjust same_condition here, - because w.condition==NULL. */ - ATM_STORE (&w->nw.waiting, 1); - if (wait_count == 0) { - /* first wait goes to end of queue */ - mu->waiters = nsync_dll_make_last_in_list_ (mu->waiters, - &w->nw.q); - } else { - /* subsequent waits go to front of queue */ - mu->waiters = nsync_dll_make_first_in_list_ (mu->waiters, - &w->nw.q); - } - - /* Release spinlock. Cannot use a store here, because - the current thread does not hold the mutex. If - another thread were a designated waker, the mutex - holder could be concurrently unlocking, even though - we hold the spinlock. */ - mu_release_spinlock (mu); - - /* wait until awoken. */ - while (ATM_LOAD_ACQ (&w->nw.waiting) != 0) { /* acquire load */ - nsync_mu_semaphore_p (&w->sem); - } - wait_count++; - /* If the thread has been woken more than this many - times, and still not acquired, it sets the - MU_LONG_WAIT bit to prevent thread that have not - waited from acquiring. This is the starvation - avoidance mechanism. The number is fairly high so - that we continue to benefit from the throughput of - not having running threads wait unless absolutely - necessary. */ - if (wait_count == LONG_WAIT_THRESHOLD) { /* repeatedly woken */ - long_wait = MU_LONG_WAIT; /* force others to wait at least once */ - } - - attempts = 0; - clear = MU_DESIG_WAKER; - /* Threads that have been woken at least once don't care - about waiting writers or long waiters. */ - zero_to_acquire &= ~(MU_WRITER_WAITING | MU_LONG_WAIT); - } - attempts = nsync_spin_delay_ (attempts); - } -} - -/* Attempt to acquire *mu in writer mode without blocking, and return non-zero - iff successful. Return non-zero with high probability if *mu was free on - entry. */ -int nsync_mu_trylock (nsync_mu *mu) { - int result; - IGNORE_RACES_START (); - if (ATM_CAS_ACQ (&mu->word, 0, MU_WADD_TO_ACQUIRE)) { /* acquire CAS */ - result = 1; - } else { - uint32_t old_word = ATM_LOAD (&mu->word); - result = ((old_word & MU_WZERO_TO_ACQUIRE) == 0 && - ATM_CAS_ACQ (&mu->word, old_word, - (old_word + MU_WADD_TO_ACQUIRE) & ~MU_WCLEAR_ON_ACQUIRE)); - } - IGNORE_RACES_END (); - return (result); -} - -/* Block until *mu is free and then acquire it in writer mode. */ -void nsync_mu_lock (nsync_mu *mu) { - IGNORE_RACES_START (); - if (!ATM_CAS_ACQ (&mu->word, 0, MU_WADD_TO_ACQUIRE)) { /* acquire CAS */ - uint32_t old_word = ATM_LOAD (&mu->word); - if ((old_word&MU_WZERO_TO_ACQUIRE) != 0 || - !ATM_CAS_ACQ (&mu->word, old_word, - (old_word+MU_WADD_TO_ACQUIRE) & ~MU_WCLEAR_ON_ACQUIRE)) { - waiter *w = nsync_waiter_new_ (); - nsync_mu_lock_slow_ (mu, w, 0, nsync_writer_type_); - nsync_waiter_free_ (w); - } - } - IGNORE_RACES_END (); -} - -/* Attempt to acquire *mu in reader mode without blocking, and return non-zero - iff successful. Returns non-zero with high probability if *mu was free on - entry. It may fail to acquire if a writer is waiting, to avoid starvation. - */ -int nsync_mu_rtrylock (nsync_mu *mu) { - int result; - IGNORE_RACES_START (); - if (ATM_CAS_ACQ (&mu->word, 0, MU_RADD_TO_ACQUIRE)) { /* acquire CAS */ - result = 1; - } else { - uint32_t old_word = ATM_LOAD (&mu->word); - result = ((old_word&MU_RZERO_TO_ACQUIRE) == 0 && - ATM_CAS_ACQ (&mu->word, old_word, - (old_word+MU_RADD_TO_ACQUIRE) & ~MU_RCLEAR_ON_ACQUIRE)); - } - IGNORE_RACES_END (); - return (result); -} - -/* Block until *mu can be acquired in reader mode and then acquire it. */ -void nsync_mu_rlock (nsync_mu *mu) { - IGNORE_RACES_START (); - if (!ATM_CAS_ACQ (&mu->word, 0, MU_RADD_TO_ACQUIRE)) { /* acquire CAS */ - uint32_t old_word = ATM_LOAD (&mu->word); - if ((old_word&MU_RZERO_TO_ACQUIRE) != 0 || - !ATM_CAS_ACQ (&mu->word, old_word, - (old_word+MU_RADD_TO_ACQUIRE) & ~MU_RCLEAR_ON_ACQUIRE)) { - waiter *w = nsync_waiter_new_ (); - nsync_mu_lock_slow_ (mu, w, 0, nsync_reader_type_); - nsync_waiter_free_ (w); - } - } - IGNORE_RACES_END (); -} - -/* Invoke the condition associated with *p, which is an element of - a "waiter" list. */ -static int condition_true (nsync_dll_element_ *p) { - return ((*DLL_WAITER (p)->cond.f) (DLL_WAITER (p)->cond.v)); -} - -/* If *p is an element of waiter_list (a list of "waiter" structs(, return a - pointer to the next element of the list that has a different condition. */ -static nsync_dll_element_ *skip_past_same_condition ( - nsync_dll_list_ waiter_list, nsync_dll_element_ *p) { - nsync_dll_element_ *next; - nsync_dll_element_ *last_with_same_condition = - &DLL_WAITER_SAMECOND (DLL_WAITER (p)->same_condition.prev)->nw.q; - if (last_with_same_condition != p && last_with_same_condition != p->prev) { - /* First in set with same condition, so skip to end. */ - next = nsync_dll_next_ (waiter_list, last_with_same_condition); - } else { - next = nsync_dll_next_ (waiter_list, p); - } - return (next); -} - -/* Merge the same_condition lists of *p and *n if they have the same non-NULL - condition. */ -void nsync_maybe_merge_conditions_ (nsync_dll_element_ *p, nsync_dll_element_ *n) { - if (p != NULL && n != NULL && - WAIT_CONDITION_EQ (&DLL_WAITER (p)->cond, &DLL_WAITER (n)->cond)) { - nsync_dll_splice_after_ (&DLL_WAITER (p)->same_condition, - &DLL_WAITER (n)->same_condition); - } -} - -/* Remove element *e from nsync_mu waiter queue mu_queue, fixing - up the same_condition list by merging the lists on either side if possible. - Also increment the waiter's remove_count. */ -nsync_dll_list_ nsync_remove_from_mu_queue_ (nsync_dll_list_ mu_queue, nsync_dll_element_ *e) { - /* Record previous and next elements in the original queue. */ - nsync_dll_element_ *prev = e->prev; - nsync_dll_element_ *next = e->next; - uint32_t old_value; - /* Remove. */ - mu_queue = nsync_dll_remove_ (mu_queue, e); - do { - old_value = ATM_LOAD (&DLL_WAITER (e)->remove_count); - } while (!ATM_CAS (&DLL_WAITER (e)->remove_count, old_value, old_value+1)); - if (!nsync_dll_is_empty_ (mu_queue)) { - /* Fix up same_condition. */ - nsync_dll_element_ *e_same_condition = &DLL_WAITER (e)->same_condition; - - if (e_same_condition->next != e_same_condition) { - /* *e is linked to a same_condition neighbour---just remove it. */ - e_same_condition->next->prev = e_same_condition->prev; - e_same_condition->prev->next = e_same_condition->next; - e_same_condition->next = e_same_condition; - e_same_condition->prev = e_same_condition; - } else if (prev != nsync_dll_last_ (mu_queue)) { - /* Merge the new neighbours together if we can. */ - nsync_maybe_merge_conditions_ (prev, next); - } - } - return (mu_queue); -} - -/* Unlock *mu and wake one or more waiters as appropriate after an unlock. - It is called with *mu held in mode l_type. */ -void nsync_mu_unlock_slow_ (nsync_mu *mu, lock_type *l_type) { - unsigned attempts = 0; /* attempt count; used for backoff */ - for (;;) { - uint32_t old_word = ATM_LOAD (&mu->word); - int testing_conditions = ((old_word & MU_CONDITION) != 0); - uint32_t early_release_mu = l_type->add_to_acquire; - uint32_t late_release_mu = 0; - if (testing_conditions) { - /* Convert to a writer lock, and release later. - - A writer lock is currently needed to test conditions - because exclusive access is needed to the list to - allow modification. The spinlock cannot be used - to achieve that, because an internal lock should not - be held when calling the external predicates. - - We must test conditions even though a reader region - cannot have made any new ones true because some - might have been true before the reader region started. - The MU_ALL_FALSE test below shortcuts the case where - the conditions are known all to be false. */ - early_release_mu = l_type->add_to_acquire - MU_WLOCK; - late_release_mu = MU_WLOCK; - } - if ((old_word&MU_WAITING) == 0 || (old_word&MU_DESIG_WAKER) != 0 || - (old_word & MU_RLOCK_FIELD) > MU_RLOCK || - (old_word & (MU_RLOCK|MU_ALL_FALSE)) == (MU_RLOCK|MU_ALL_FALSE)) { - /* no one to wake, there's a designated waker waking - up, there are still readers, or it's a reader and all waiters - have false conditions */ - if (ATM_CAS_REL (&mu->word, old_word, - (old_word - l_type->add_to_acquire) & - ~l_type->clear_on_uncontended_release)) { - return; - } - } else if ((old_word&MU_SPINLOCK) == 0 && - ATM_CAS_ACQ (&mu->word, old_word, - (old_word-early_release_mu)|MU_SPINLOCK|MU_DESIG_WAKER)) { - nsync_dll_list_ wake; - lock_type *wake_type; - uint32_t clear_on_release; - uint32_t set_on_release; - /* The spinlock is now held, and we've set the - designated wake flag, since we're likely to wake a - thread that will become that designated waker. If - there are conditions to check, the mutex itself is - still held. */ - - nsync_dll_element_ *p = NULL; - nsync_dll_element_ *next = NULL; - - /* Swap the entire mu->waiters list into the local - "new_waiters" list. This gives us exclusive access - to the list, even if we unlock the spinlock, which - we may do if checking conditions. The loop below - will grab more new waiters that arrived while we - were checking conditions, and terminates only if no - new waiters arrive in one loop iteration. */ - nsync_dll_list_ waiters = NULL; - nsync_dll_list_ new_waiters = mu->waiters; - mu->waiters = NULL; - - /* Remove a waiter from the queue, if possible. */ - wake = NULL; /* waiters to wake. */ - wake_type = NULL; /* type of waiter(s) on wake, or NULL if wake is empty. */ - clear_on_release = MU_SPINLOCK; - set_on_release = MU_ALL_FALSE; - while (!nsync_dll_is_empty_ (new_waiters)) { /* some new waiters to consider */ - p = nsync_dll_first_ (new_waiters); - if (testing_conditions) { - /* Should we continue to test conditions? */ - if (wake_type == nsync_writer_type_) { - /* No, because we're already waking a writer, - and need wake no others.*/ - testing_conditions = 0; - } else if (wake_type == NULL && - DLL_WAITER (p)->l_type != nsync_reader_type_ && - DLL_WAITER (p)->cond.f == NULL) { - /* No, because we've woken no one, but the - first waiter is a writer with no condition, - so we will certainly wake it, and need wake - no others. */ - testing_conditions = 0; - } - } - /* If testing waiters' conditions, release the - spinlock while still holding the write lock. - This is so that the spinlock is not held - while the conditions are evaluated. */ - if (testing_conditions) { - mu_release_spinlock (mu); - } - - /* Process the new waiters picked up in this iteration of the - "while (!nsync_dll_is_empty_ (new_waiters))" loop, - and stop looking when we run out of waiters, or we find - a writer to wake up. */ - while (p != NULL && wake_type != nsync_writer_type_) { - int p_has_condition; - next = nsync_dll_next_ (new_waiters, p); - p_has_condition = (DLL_WAITER (p)->cond.f != NULL); - if (p_has_condition && !testing_conditions) { - nsync_panic_ ("checking a waiter condition " - "while unlocked\n"); - } - if (p_has_condition && !condition_true (p)) { - /* condition is false */ - /* skip to the end of the same_condition group. */ - next = skip_past_same_condition (new_waiters, p); - } else if (wake_type == NULL || - DLL_WAITER (p)->l_type == nsync_reader_type_) { - /* Wake this thread. */ - new_waiters = nsync_remove_from_mu_queue_ ( - new_waiters, p); - wake = nsync_dll_make_last_in_list_ (wake, p); - wake_type = DLL_WAITER (p)->l_type; - } else { - /* Failing to wake a writer - that could acquire if it - were first. */ - set_on_release |= MU_WRITER_WAITING; - set_on_release &= ~MU_ALL_FALSE; - } - p = next; - } - - if (p != NULL) { - /* Didn't search to end of list, so can't be sure - all conditions are false. */ - set_on_release &= ~MU_ALL_FALSE; - } - - /* If testing waiters' conditions, reacquire the spinlock - released above. */ - if (testing_conditions) { - nsync_spin_test_and_set_ (&mu->word, MU_SPINLOCK, - MU_SPINLOCK, 0); - } - - /* add the new_waiters to the last of the waiters. */ - nsync_maybe_merge_conditions_ (nsync_dll_last_ (waiters), - nsync_dll_first_ (new_waiters)); - waiters = nsync_dll_make_last_in_list_ (waiters, - nsync_dll_last_ (new_waiters)); - /* Pick up the next set of new waiters. */ - new_waiters = mu->waiters; - mu->waiters = NULL; - } - - /* Return the local waiter list to *mu. */ - mu->waiters = waiters; - - if (nsync_dll_is_empty_ (wake)) { - /* not waking a waiter => no designated waker */ - clear_on_release |= MU_DESIG_WAKER; - } - - if ((set_on_release & MU_ALL_FALSE) == 0) { - /* If not explicitly setting MU_ALL_FALSE, clear it. */ - clear_on_release |= MU_ALL_FALSE; - } - - if (nsync_dll_is_empty_ (mu->waiters)) { - /* no waiters left */ - clear_on_release |= MU_WAITING | MU_WRITER_WAITING | - MU_CONDITION | MU_ALL_FALSE; - } - - /* Release the spinlock, and possibly the lock if - late_release_mu is non-zero. Other bits are set or - cleared according to whether we woke any threads, - whether any waiters remain, and whether any of them - are writers. */ - old_word = ATM_LOAD (&mu->word); - while (!ATM_CAS_REL (&mu->word, old_word, - ((old_word-late_release_mu)|set_on_release) & - ~clear_on_release)) { /* release CAS */ - old_word = ATM_LOAD (&mu->word); - } - /* Wake the waiters. */ - for (p = nsync_dll_first_ (wake); p != NULL; p = next) { - next = nsync_dll_next_ (wake, p); - wake = nsync_dll_remove_ (wake, p); - ATM_STORE_REL (&DLL_NSYNC_WAITER (p)->waiting, 0); - nsync_mu_semaphore_v (&DLL_WAITER (p)->sem); - } - return; - } - attempts = nsync_spin_delay_ (attempts); - } -} - -/* Unlock *mu, which must be held in write mode, and wake waiters, if appropriate. */ -void nsync_mu_unlock (nsync_mu *mu) { - IGNORE_RACES_START (); - /* C is not a garbage-collected language, so we cannot release until we - can be sure that we will not have to touch the mutex again to wake a - waiter. Another thread could acquire, decrement a reference count - and deallocate the mutex before the current thread touched the mutex - word again. */ - if (!ATM_CAS_REL (&mu->word, MU_WLOCK, 0)) { - uint32_t old_word = ATM_LOAD (&mu->word); - /* Clear MU_ALL_FALSE because the critical section we're just - leaving may have made some conditions true. */ - uint32_t new_word = (old_word - MU_WLOCK) & ~MU_ALL_FALSE; - /* Sanity check: mutex must be held in write mode, and there - must be no readers. */ - if ((new_word & (MU_RLOCK_FIELD | MU_WLOCK)) != 0) { - if ((old_word & MU_RLOCK_FIELD) != 0) { - nsync_panic_ ("attempt to nsync_mu_unlock() an nsync_mu " - "held in read mode\n"); - } else { - nsync_panic_ ("attempt to nsync_mu_unlock() an nsync_mu " - "not held in write mode\n"); - } - } else if ((old_word & (MU_WAITING|MU_DESIG_WAKER)) == MU_WAITING || - !ATM_CAS_REL (&mu->word, old_word, new_word)) { - /* There are waiters and no designated waker, or - our initial CAS attempt failed, to use slow path. */ - nsync_mu_unlock_slow_ (mu, nsync_writer_type_); - } - } - IGNORE_RACES_END (); -} - -/* Unlock *mu, which must be held in read mode, and wake waiters, if appropriate. */ -void nsync_mu_runlock (nsync_mu *mu) { - IGNORE_RACES_START (); - /* See comment in nsync_mu_unlock(). */ - if (!ATM_CAS_REL (&mu->word, MU_RLOCK, 0)) { - uint32_t old_word = ATM_LOAD (&mu->word); - /* Sanity check: mutex must not be held in write mode and - reader count must not be 0. */ - if (((old_word ^ MU_WLOCK) & (MU_WLOCK | MU_RLOCK_FIELD)) == 0) { - if ((old_word & MU_WLOCK) != 0) { - nsync_panic_ ("attempt to nsync_mu_runlock() an nsync_mu " - "held in write mode\n"); - } else { - nsync_panic_ ("attempt to nsync_mu_runlock() an nsync_mu " - "not held in read mode\n"); - } - } else if ((old_word & (MU_WAITING | MU_DESIG_WAKER)) == MU_WAITING && - (old_word & (MU_RLOCK_FIELD|MU_ALL_FALSE)) == MU_RLOCK) { - /* There are waiters and no designated waker, the last - reader is unlocking, and not all waiters have a - false condition. So we must take the slow path to - attempt to wake a waiter. */ - nsync_mu_unlock_slow_ (mu, nsync_reader_type_); - } else if (!ATM_CAS_REL (&mu->word, old_word, old_word - MU_RLOCK)) { - /* CAS attempt failed, so take slow path. */ - nsync_mu_unlock_slow_ (mu, nsync_reader_type_); - } - } - IGNORE_RACES_END (); -} - -/* Abort if *mu is not held in write mode. */ -void nsync_mu_assert_held (const nsync_mu *mu) { - IGNORE_RACES_START (); - if ((ATM_LOAD (&mu->word) & MU_WHELD_IF_NON_ZERO) == 0) { - nsync_panic_ ("nsync_mu not held in write mode\n"); - } - IGNORE_RACES_END (); -} - -/* Abort if *mu is not held in read or write mode. */ -void nsync_mu_rassert_held (const nsync_mu *mu) { - IGNORE_RACES_START (); - if ((ATM_LOAD (&mu->word) & MU_ANY_LOCK) == 0) { - nsync_panic_ ("nsync_mu not held in some mode\n"); - } - IGNORE_RACES_END (); -} - -/* Return whether *mu is held in read mode. - Requires that *mu is held in some mode. */ -int nsync_mu_is_reader (const nsync_mu *mu) { - uint32_t word; - IGNORE_RACES_START (); - word = ATM_LOAD (&mu->word); - if ((word & MU_ANY_LOCK) == 0) { - nsync_panic_ ("nsync_mu not held in some mode\n"); - } - IGNORE_RACES_END (); - return ((word & MU_WLOCK) == 0); -} diff --git a/third_party/nsync/mu.h b/third_party/nsync/mu.h deleted file mode 100644 index 8d72fea6c..000000000 --- a/third_party/nsync/mu.h +++ /dev/null @@ -1,103 +0,0 @@ -#ifndef NSYNC_MU_H_ -#define NSYNC_MU_H_ -#include "third_party/nsync/atomic.h" -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -struct nsync_dll_element_s_; - -/* An nsync_mu is a lock. If initialized to zero, it's valid and unlocked. - - An nsync_mu can be "free", held by a single thread (aka fiber, - goroutine) in "write" (exclusive) mode, or by many threads in "read" - (shared) mode. A thread that acquires it should eventually release - it. It is illegal to acquire an nsync_mu in one thread and release it - in another. It is illegal for a thread to reacquire an nsync_mu while - holding it (even a second share of a "read" lock). - - Example usage: - - static struct foo { - nsync_mu mu; // protects invariant a+b==0 on fields below. - int a; - int b; - } p = { NSYNC_MU_INIT, 0, 0 }; - // .... - nsync_mu_lock (&p.mu); - // The current thread now has exclusive access to p.a and p.b; - // invariant assumed true. - p.a++; - p.b--; // restore invariant p.a+p.b==0 before releasing p.mu - nsync_mu_unlock (&p.mu) - - Mutexes can be used with condition variables; see nsync_cv.h. - - nsync_mu_wait() and nsync_mu_wait_with_deadline() can be used instead - of condition variables. See nsync_mu_wait.h for more details. Example - use of nsync_mu_wait() to wait for p.a==0, using definition above: - - int a_is_zero (const void *condition_arg) { - return (((const struct foo *)condition_arg)->a == 0); - } - ... - nsync_mu_lock (&p.mu); - nsync_mu_wait (&p.mu, &a_is_zero, &p, NULL); - // The current thread now has exclusive access to - // p.a and p.b, and p.a==0. - ... - nsync_mu_unlock (&p.mu); - -*/ -typedef struct nsync_mu_s_ { - nsync_atomic_uint32_ word; /* internal use only */ - struct nsync_dll_element_s_ *waiters; /* internal use only */ -} nsync_mu; - -/* An nsync_mu should be zeroed to initialize, which can be accomplished - by initializing with static initializer NSYNC_MU_INIT, or by setting - the entire structure to all zeroes, or using nsync_mu_init(). */ -#define NSYNC_MU_INIT \ - { NSYNC_ATOMIC_UINT32_INIT_, 0 } -void nsync_mu_init(nsync_mu *mu); - -/* Block until *mu is free and then acquire it in writer mode. Requires - that the calling thread not already hold *mu in any mode. */ -void nsync_mu_lock(nsync_mu *mu); - -/* Unlock *mu, which must have been acquired in write mode by the - calling thread, and wake waiters, if appropriate. */ -void nsync_mu_unlock(nsync_mu *mu); - -/* Attempt to acquire *mu in writer mode without blocking, and return - non-zero iff successful. Return non-zero with high probability if *mu - was free on entry. */ -int nsync_mu_trylock(nsync_mu *mu); - -/* Block until *mu can be acquired in reader mode and then acquire it. - Requires that the calling thread not already hold *mu in any mode. */ -void nsync_mu_rlock(nsync_mu *mu); - -/* Unlock *mu, which must have been acquired in read mode by the calling - thread, and wake waiters, if appropriate. */ -void nsync_mu_runlock(nsync_mu *mu); - -/* Attempt to acquire *mu in reader mode without blocking, and return - non-zero iff successful. Return non-zero with high probability if *mu - was free on entry. Perhaps fail to acquire if a writer is waiting, to - avoid starvation. */ -int nsync_mu_rtrylock(nsync_mu *mu); - -/* May abort if *mu is not held in write mode by the calling thread. */ -void nsync_mu_assert_held(const nsync_mu *mu); - -/* May abort if *mu is not held in read or write mode - by the calling thread. */ -void nsync_mu_rassert_held(const nsync_mu *mu); - -/* Return whether *mu is held in read mode. - Requires that the calling thread holds *mu in some mode. */ -int nsync_mu_is_reader(const nsync_mu *mu); - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* NSYNC_MU_H_ */ diff --git a/third_party/nsync/mu_semaphore.c b/third_party/nsync/mu_semaphore.c deleted file mode 100644 index a30ef4873..000000000 --- a/third_party/nsync/mu_semaphore.c +++ /dev/null @@ -1,56 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ -│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2022 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/dce.h" -#include "third_party/nsync/mu_semaphore.h" -#include "third_party/nsync/mu_semaphore.internal.h" -// clang-format off - -/* Initialize *s; the initial value is 0. */ -void nsync_mu_semaphore_init (nsync_semaphore *s) { - if (!IsWindows ()) - nsync_mu_semaphore_init_futex (s); - else - nsync_mu_semaphore_init_win32 (s); -} - -/* Wait until the count of *s exceeds 0, and decrement it. */ -void nsync_mu_semaphore_p (nsync_semaphore *s) { - if (!IsWindows ()) - nsync_mu_semaphore_p_futex (s); - else - nsync_mu_semaphore_p_win32 (s); -} - -/* Wait until one of: - the count of *s is non-zero, in which case decrement *s and return 0; - or abs_deadline expires, in which case return ETIMEDOUT. */ -int nsync_mu_semaphore_p_with_deadline (nsync_semaphore *s, nsync_time abs_deadline) { - if (!IsWindows ()) - return nsync_mu_semaphore_p_with_deadline_futex (s, abs_deadline); - else - return nsync_mu_semaphore_p_with_deadline_win32 (s, abs_deadline); -} - -/* Ensure that the count of *s is at least 1. */ -void nsync_mu_semaphore_v (nsync_semaphore *s) { - if (!IsWindows ()) - nsync_mu_semaphore_v_futex (s); - else - nsync_mu_semaphore_v_win32 (s); -} diff --git a/third_party/nsync/mu_semaphore.h b/third_party/nsync/mu_semaphore.h deleted file mode 100644 index d57e1b770..000000000 --- a/third_party/nsync/mu_semaphore.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef NSYNC_SEM_H_ -#define NSYNC_SEM_H_ -#include "third_party/nsync/time.h" -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -typedef struct nsync_semaphore_s_ { - void *sem_space[32]; /* space used by implementation */ -} nsync_semaphore; - -/* Initialize *s; the initial value is 0. */ -void nsync_mu_semaphore_init(nsync_semaphore *s); - -/* Wait until the count of *s exceeds 0, and decrement it. */ -void nsync_mu_semaphore_p(nsync_semaphore *s); - -/* Wait until one of: the count of *s is non-zero, in which case - decrement *s and return 0; or abs_deadline expires, in which case - return ETIMEDOUT. */ -int nsync_mu_semaphore_p_with_deadline(nsync_semaphore *s, - nsync_time abs_deadline); - -/* Ensure that the count of *s is at least 1. */ -void nsync_mu_semaphore_v(nsync_semaphore *s); - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* NSYNC_SEM_H_ */ diff --git a/third_party/nsync/mu_semaphore.internal.h b/third_party/nsync/mu_semaphore.internal.h deleted file mode 100644 index 8fac00aa0..000000000 --- a/third_party/nsync/mu_semaphore.internal.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef NSYNC_MU_SEMAPHORE_INTERNAL_H_ -#define NSYNC_MU_SEMAPHORE_INTERNAL_H_ -#include "third_party/nsync/mu_semaphore.h" -#include "third_party/nsync/time.h" -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -void nsync_mu_semaphore_init_futex(nsync_semaphore *); -void nsync_mu_semaphore_p_futex(nsync_semaphore *); -int nsync_mu_semaphore_p_with_deadline_futex(nsync_semaphore *, nsync_time); -void nsync_mu_semaphore_v_futex(nsync_semaphore *); - -void nsync_mu_semaphore_init_win32(nsync_semaphore *); -void nsync_mu_semaphore_p_win32(nsync_semaphore *); -int nsync_mu_semaphore_p_with_deadline_win32(nsync_semaphore *, nsync_time); -void nsync_mu_semaphore_v_win32(nsync_semaphore *); - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* NSYNC_MU_SEMAPHORE_INTERNAL_H_ */ diff --git a/third_party/nsync/mu_semaphore_futex.c b/third_party/nsync/mu_semaphore_futex.c deleted file mode 100644 index 237f83ed2..000000000 --- a/third_party/nsync/mu_semaphore_futex.c +++ /dev/null @@ -1,124 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ -│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2016 Google Inc. │ -│ │ -│ Licensed under the Apache License, Version 2.0 (the "License"); │ -│ you may not use this file except in compliance with the License. │ -│ You may obtain a copy of the License at │ -│ │ -│ http://www.apache.org/licenses/LICENSE-2.0 │ -│ │ -│ Unless required by applicable law or agreed to in writing, software │ -│ distributed under the License is distributed on an "AS IS" BASIS, │ -│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │ -│ See the License for the specific language governing permissions and │ -│ limitations under the License. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/errno.h" -#include "libc/str/str.h" -#include "libc/thread/thread.h" -#include "third_party/nsync/atomic.h" -#include "third_party/nsync/atomic.internal.h" -#include "third_party/nsync/futex.internal.h" -#include "third_party/nsync/mu_semaphore.h" -#include "third_party/nsync/mu_semaphore.internal.h" - -asm(".ident\t\"\\n\\n\ -*NSYNC (Apache 2.0)\\n\ -Copyright 2016 Google, Inc.\\n\ -https://github.com/google/nsync\""); -// clang-format off - -/* Check that atomic operations on nsync_atomic_uint32_ can be applied to int. */ -static const int assert_int_size = 1 / - (sizeof (assert_int_size) == sizeof (uint32_t) && - sizeof (nsync_atomic_uint32_) == sizeof (uint32_t)); - -#define ASSERT(x) do { if (!(x)) { *(volatile int *)0 = 0; } } while (0) - -struct futex { - int i; /* lo half=count; hi half=waiter count */ -}; - -static nsync_semaphore *sem_big_enough_for_futex = (nsync_semaphore *) (uintptr_t)(1 / - (sizeof (struct futex) <= sizeof (*sem_big_enough_for_futex))); - -/* Initialize *s; the initial value is 0. */ -void nsync_mu_semaphore_init_futex (nsync_semaphore *s) { - struct futex *f = (struct futex *) s; - f->i = 0; -} - -/* Wait until the count of *s exceeds 0, and decrement it. */ -void nsync_mu_semaphore_p_futex (nsync_semaphore *s) { - struct futex *f = (struct futex *) s; - int i; - do { - i = ATM_LOAD ((nsync_atomic_uint32_ *) &f->i); - if (i == 0) { - int futex_result = nsync_futex_wait_ (&f->i, i, PTHREAD_PROCESS_PRIVATE, NULL); - ASSERT (futex_result == 0 || - futex_result == -EINTR || - futex_result == -EWOULDBLOCK); - } - } while (i == 0 || !ATM_CAS_ACQ ((nsync_atomic_uint32_ *) &f->i, i, i-1)); -} - -/* Wait until one of: - the count of *s is non-zero, in which case decrement *s and return 0; - or abs_deadline expires, in which case return ETIMEDOUT. */ -int nsync_mu_semaphore_p_with_deadline_futex (nsync_semaphore *s, nsync_time abs_deadline) { - struct futex *f = (struct futex *)s; - int i; - int result = 0; - do { - i = ATM_LOAD ((nsync_atomic_uint32_ *) &f->i); - if (i == 0) { - int futex_result; - struct timespec ts_buf; - const struct timespec *ts = NULL; - if (nsync_time_cmp (abs_deadline, nsync_time_no_deadline) != 0) { - memset (&ts_buf, 0, sizeof (ts_buf)); - if (FUTEX_TIMEOUT_IS_ABSOLUTE) { - ts_buf.tv_sec = NSYNC_TIME_SEC (abs_deadline); - ts_buf.tv_nsec = NSYNC_TIME_NSEC (abs_deadline); - } else { - nsync_time now; - now = nsync_time_now (); - if (nsync_time_cmp (now, abs_deadline) > 0) { - ts_buf.tv_sec = 0; - ts_buf.tv_nsec = 0; - } else { - nsync_time rel_deadline; - rel_deadline = nsync_time_sub (abs_deadline, now); - ts_buf.tv_sec = NSYNC_TIME_SEC (rel_deadline); - ts_buf.tv_nsec = NSYNC_TIME_NSEC (rel_deadline); - } - } - ts = &ts_buf; - } - futex_result = nsync_futex_wait_ (&f->i, i, PTHREAD_PROCESS_PRIVATE, ts); - ASSERT (futex_result == 0 || - futex_result == -EINTR || - futex_result == -ETIMEDOUT || - futex_result == -EWOULDBLOCK); - /* Some systems don't wait as long as they are told. */ - if (futex_result == -ETIMEDOUT && - nsync_time_cmp (abs_deadline, nsync_time_now ()) <= 0) { - result = ETIMEDOUT; - } - } - } while (result == 0 && (i == 0 || !ATM_CAS_ACQ ((nsync_atomic_uint32_ *) &f->i, i, i - 1))); - return (result); -} - -/* Ensure that the count of *s is at least 1. */ -void nsync_mu_semaphore_v_futex (nsync_semaphore *s) { - struct futex *f = (struct futex *) s; - uint32_t old_value; - do { - old_value = ATM_LOAD ((nsync_atomic_uint32_ *) &f->i); - } while (!ATM_CAS_REL ((nsync_atomic_uint32_ *) &f->i, old_value, old_value+1)); - ASSERT (nsync_futex_wake_ (&f->i, 1, PTHREAD_PROCESS_PRIVATE) >= 0); -} diff --git a/third_party/nsync/mu_semaphore_win32.c b/third_party/nsync/mu_semaphore_win32.c deleted file mode 100644 index 9dc8e2927..000000000 --- a/third_party/nsync/mu_semaphore_win32.c +++ /dev/null @@ -1,84 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ -│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2016 Google Inc. │ -│ │ -│ Licensed under the Apache License, Version 2.0 (the "License"); │ -│ you may not use this file except in compliance with the License. │ -│ You may obtain a copy of the License at │ -│ │ -│ http://www.apache.org/licenses/LICENSE-2.0 │ -│ │ -│ Unless required by applicable law or agreed to in writing, software │ -│ distributed under the License is distributed on an "AS IS" BASIS, │ -│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │ -│ See the License for the specific language governing permissions and │ -│ limitations under the License. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/errno.h" -#include "libc/nt/enum/wait.h" -#include "libc/nt/synchronization.h" -#include "libc/runtime/runtime.h" -#include "third_party/nsync/mu_semaphore.h" -#include "third_party/nsync/mu_semaphore.internal.h" -#include "third_party/nsync/time.h" - -asm(".ident\t\"\\n\\n\ -*NSYNC (Apache 2.0)\\n\ -Copyright 2016 Google, Inc.\\n\ -https://github.com/google/nsync\""); -// clang-format off - -/* Initialize *s; the initial value is 0. */ -void nsync_mu_semaphore_init_win32 (nsync_semaphore *s) { - int64_t *h = (int64_t *) s; - *h = CreateSemaphore(NULL, 0, 1, NULL); - if (!*h) notpossible; -} - -/* Wait until the count of *s exceeds 0, and decrement it. */ -void nsync_mu_semaphore_p_win32 (nsync_semaphore *s) { - int64_t *h = (int64_t *) s; - WaitForSingleObject(*h, -1u); -} - -/* Wait until one of: - the count of *s is non-zero, in which case decrement *s and return 0; - or abs_deadline expires, in which case return ETIMEDOUT. */ -int nsync_mu_semaphore_p_with_deadline_win32 (nsync_semaphore *s, nsync_time abs_deadline) { - int64_t *h = (int64_t *) s; - int result; - - if (nsync_time_cmp (abs_deadline, nsync_time_no_deadline) == 0) { - result = WaitForSingleObject(*h, -1u); - } else { - nsync_time now; - now = nsync_time_now (); - do { - if (nsync_time_cmp (abs_deadline, now) <= 0) { - result = WaitForSingleObject (*h, 0); - } else { - nsync_time delay; - delay = nsync_time_sub (abs_deadline, now); - if (NSYNC_TIME_SEC (delay) > 1000*1000) { - result = WaitForSingleObject (*h, 1000*1000); - } else { - result = WaitForSingleObject (*h, - (unsigned) (NSYNC_TIME_SEC (delay) * 1000 + - (NSYNC_TIME_NSEC (delay) + 999999) / (1000 * 1000))); - } - } - if (result == kNtWaitTimeout) { - now = nsync_time_now (); - } - } while (result == kNtWaitTimeout && /* Windows generates early wakeups. */ - nsync_time_cmp (abs_deadline, now) > 0); - } - return (result == kNtWaitTimeout ? ETIMEDOUT : 0); -} - -/* Ensure that the count of *s is at least 1. */ -void nsync_mu_semaphore_v_win32 (nsync_semaphore *s) { - int64_t *h = (int64_t *) s; - ReleaseSemaphore(*h, 1, NULL); -} diff --git a/third_party/nsync/mu_wait.h b/third_party/nsync/mu_wait.h deleted file mode 100644 index 7a5a3478b..000000000 --- a/third_party/nsync/mu_wait.h +++ /dev/null @@ -1,118 +0,0 @@ -#ifndef NSYNC_MU_WAIT_H_ -#define NSYNC_MU_WAIT_H_ -#include "third_party/nsync/mu.h" -#include "third_party/nsync/time.h" -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -/* nsync_mu_wait() and nsync_mu_wait_with_deadline() can be used instead - of condition variables. In many straightforward situations they are - of equivalent performance and are somewhat easier to use, because - unlike condition variables, they do not require that the waits be - placed in a loop, and they do not require explicit wakeup calls. - Example: - - Definitions: - - static nsync_mu mu = NSYNC_MU_INIT; - static int i = 0; // protected by mu - // Condition for use with nsync_mu_wait(). - static int int_is_zero (const void *v) { - return (*(const int *)v == 0); - } - - Waiter: - - nsync_mu_lock (&mu); - // Wait until i is zero. - nsync_mu_wait (&mu, &int_is_zero, &i, NULL); - // i is known to be zero here. - // ... - nsync_mu_unlock (&mu); - - Thread potentially making i zero: - - nsync_mu_lock (&mu); - i--; - // No need to signal that i may have become zero. The unlock call - // below will evaluate waiters' conditions to decide which to wake. - nsync_mu_unlock (&mu); - - It is legal to use conditional critical sections and condition - variables on the same mutex. - - -------------- - - The implementation benefits from determining whether waiters are - waiting for the same condition; it may then evaluate a condition once - on behalf of several waiters. Two waiters have equal condition if - their "condition" pointers are equal, and either: - - - their "condition_arg" pointers are equal, or - - - "condition_arg_eq" is non-null and (*condition_arg_eq) - (condition_arg0, condition_arg1) returns non-zero. - - *condition_arg_eq will not be invoked unless the "condition" pointers - are equal, and the "condition_arg" pointers are unequal. - - If many waiters wait for distinct conditions simultaneously, - condition variables may be faster. - */ - -struct nsync_note_s_; /* forward declaration for an nsync_note */ - -/* Return when (*condition) (condition_arg) is true. Perhaps unlock and - relock *mu while blocked waiting for the condition to become true. - nsync_mu_wait() is equivalent to nsync_mu_wait_with_deadline() with - abs_deadline==nsync_time_no_deadline, and cancel_note==NULL. - - Requires that *mu be held on entry. See nsync_mu_wait_with_deadline() - for more details on *condition and *condition_arg_eq. */ -void nsync_mu_wait(nsync_mu *mu, int (*condition)(const void *condition_arg), - const void *condition_arg, - int (*condition_arg_eq)(const void *a, const void *b)); - -/* Return when at least one of: (*condition) (condition_arg) is true, - the deadline expires, or *cancel_note is notified. Perhaps unlock and - relock *mu while blocked waiting for one of these events, but always - return with *mu held. Return 0 iff the (*condition) (condition_arg) - is true on return, and otherwise either ETIMEDOUT or ECANCELED, - depending on why the call returned early. Callers should use - abs_deadline==nsync_time_no_deadline for no deadline, and - cancel_note==NULL for no cancellation. - - Requires that *mu be held on entry. - - The implementation may call *condition from any thread using the - mutex, and while holding *mu in either read or write mode; it - guarantees that any thread calling *condition will hold *mu in some - mode. Requires that (*condition) (condition_arg) neither modify state - protected by *mu, nor return a value dependent on state not protected - by *mu. To depend on time, use the abs_deadline parameter. - (Conventional use of condition variables have the same restrictions - on the conditions tested by the while-loop.) If non-null, - condition_arg_eq should return whether two condition_arg calls with - the same "condition" pointer are considered equivalent; it should - have no side-effects. */ -int nsync_mu_wait_with_deadline( - nsync_mu *mu, int (*condition)(const void *condition_arg), - const void *condition_arg, - int (*condition_arg_eq)(const void *a, const void *b), - nsync_time abs_deadline, struct nsync_note_s_ *cancel_note); - -/* Unlock *mu, which must be held in write mode, and wake waiters, if - appropriate. Unlike nsync_mu_unlock(), this call is not required to - wake nsync_mu_wait/nsync_mu_wait_with_deadline calls on conditions - that were false before this thread acquired the lock. This call - should be used only at the end of critical sections for which: - - nsync_mu_wait and/or nsync_mu_wait_with_deadline are in use on the same - mutex, - - this critical section cannot make the condition true for any of those - nsync_mu_wait/nsync_mu_wait_with_deadline waits, and - - when performance is significantly improved by using this call. */ -void nsync_mu_unlock_without_wakeup(nsync_mu *mu); - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* NSYNC_MU_WAIT_H_ */ diff --git a/third_party/nsync/note.h b/third_party/nsync/note.h deleted file mode 100644 index 213a2d70a..000000000 --- a/third_party/nsync/note.h +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef NSYNC_NOTE_H_ -#define NSYNC_NOTE_H_ -#include "third_party/nsync/time.h" -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -/* An nsync_note represents a single bit that can transition from 0 to 1 - at most once. When 1, the note is said to be notified. There are - operations to wait for the transition, which can be triggered either - by an explicit call, or timer expiry. Notes can have parent notes; a - note becomes notified if its parent becomes notified. */ -typedef struct nsync_note_s_ *nsync_note; - -/* Return a freshly allocated nsync_note, or NULL if an nsync_note - cannot be created. - - If parent!=NULL, the allocated nsync_note's parent will be parent. - The newaly allocated note will be automatically notified at - abs_deadline, and is notified at initialization if - abs_deadline==nsync_zero_time. - - nsync_notes should be passed to nsync_note_free() when no longer needed. */ -nsync_note nsync_note_new(nsync_note parent, nsync_time abs_deadline); - -/* Free resources associated with n. Requires that n was allocated by - nsync_note_new(), and no concurrent or future operations are applied - to n directly. - - It is legal to call nsync_note_free() on a node even if it has a - parent or children that are in use; if n has both a parent and - children, n's parent adopts its children. */ -void nsync_note_free(nsync_note n); - -/* Notify n and all its descendants. */ -void nsync_note_notify(nsync_note n); - -/* Return whether n has been notified. */ -int nsync_note_is_notified(nsync_note n); - -/* Wait until n has been notified or abs_deadline is reached, and return - whether n has been notified. If abs_deadline==nsync_time_no_deadline, - the deadline is far in the future. */ -int nsync_note_wait(nsync_note n, nsync_time abs_deadline); - -/* Return the expiry time associated with n. This is the minimum of the - abs_deadline passed on creation and that of any of its ancestors. */ -nsync_time nsync_note_expiry(nsync_note n); - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* NSYNC_NOTE_H_ */ diff --git a/third_party/nsync/nsync.mk b/third_party/nsync/nsync.mk deleted file mode 100644 index 89e03a84a..000000000 --- a/third_party/nsync/nsync.mk +++ /dev/null @@ -1,58 +0,0 @@ -#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐ -#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘ - -PKGS += THIRD_PARTY_NSYNC - -THIRD_PARTY_NSYNC_SRCS = $(THIRD_PARTY_NSYNC_A_SRCS) -THIRD_PARTY_NSYNC_HDRS = $(THIRD_PARTY_NSYNC_A_HDRS) - -THIRD_PARTY_NSYNC_ARTIFACTS += THIRD_PARTY_NSYNC_A -THIRD_PARTY_NSYNC = $(THIRD_PARTY_NSYNC_A_DEPS) $(THIRD_PARTY_NSYNC_A) -THIRD_PARTY_NSYNC_A = o/$(MODE)/third_party/nsync/nsync.a -THIRD_PARTY_NSYNC_A_FILES := $(wildcard third_party/nsync/*) -THIRD_PARTY_NSYNC_A_HDRS = $(filter %.h,$(THIRD_PARTY_NSYNC_A_FILES)) -THIRD_PARTY_NSYNC_A_SRCS = $(filter %.c,$(THIRD_PARTY_NSYNC_A_FILES)) - -THIRD_PARTY_NSYNC_A_OBJS = \ - $(THIRD_PARTY_NSYNC_A_SRCS:%.c=o/$(MODE)/%.o) - -THIRD_PARTY_NSYNC_A_DIRECTDEPS = \ - LIBC_CALLS \ - LIBC_INTRIN \ - LIBC_NEXGEN32E \ - LIBC_NT_KERNEL32 \ - LIBC_STR \ - LIBC_STUBS \ - LIBC_SYSV \ - LIBC_SYSV_CALLS - -THIRD_PARTY_NSYNC_A_DEPS := \ - $(call uniq,$(foreach x,$(THIRD_PARTY_NSYNC_A_DIRECTDEPS),$($(x)))) - -THIRD_PARTY_NSYNC_A_CHECKS = \ - $(THIRD_PARTY_NSYNC_A).pkg \ - $(THIRD_PARTY_NSYNC_A_HDRS:%=o/$(MODE)/%.ok) - -$(THIRD_PARTY_NSYNC_A): \ - third_party/nsync/ \ - $(THIRD_PARTY_NSYNC_A).pkg \ - $(THIRD_PARTY_NSYNC_A_OBJS) - -$(THIRD_PARTY_NSYNC_A).pkg: \ - $(THIRD_PARTY_NSYNC_A_OBJS) \ - $(foreach x,$(THIRD_PARTY_NSYNC_A_DIRECTDEPS),$($(x)_A).pkg) - -$(THIRD_PARTY_NSYNC_A_OBJS): private \ - OVERRIDE_CCFLAGS += \ - -ffunction-sections \ - -fdata-sections - -THIRD_PARTY_NSYNC_LIBS = $(foreach x,$(THIRD_PARTY_NSYNC_ARTIFACTS),$($(x))) -THIRD_PARTY_NSYNC_SRCS = $(foreach x,$(THIRD_PARTY_NSYNC_ARTIFACTS),$($(x)_SRCS)) -THIRD_PARTY_NSYNC_CHECKS = $(foreach x,$(THIRD_PARTY_NSYNC_ARTIFACTS),$($(x)_CHECKS)) -THIRD_PARTY_NSYNC_OBJS = $(foreach x,$(THIRD_PARTY_NSYNC_ARTIFACTS),$($(x)_OBJS)) -$(THIRD_PARTY_NSYNC_OBJS): third_party/nsync/nsync.mk - -.PHONY: o/$(MODE)/third_party/nsync -o/$(MODE)/third_party/nsync: $(THIRD_PARTY_NSYNC_CHECKS) - diff --git a/third_party/nsync/once.h b/third_party/nsync/once.h deleted file mode 100644 index 4144df129..000000000 --- a/third_party/nsync/once.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef NSYNC_ONCE_H_ -#define NSYNC_ONCE_H_ -#include "third_party/nsync/atomic.h" -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -/* An nsync_once allows a function to be called exactly once, when first - referenced. */ -typedef nsync_atomic_uint32_ nsync_once; - -/* An initializer for nsync_once; it is guaranteed to be all zeroes. */ -#define NSYNC_ONCE_INIT NSYNC_ATOMIC_UINT32_INIT_ - -/* The first time nsync_run_once() or nsync_run_once_arg() is applied to - *once, the supplied function is run (with argument, in the case of - nsync_run_once_arg()). Other callers will wait until the run of the - function is complete, and then return without running the function - again. */ -void nsync_run_once(nsync_once *once, void (*f)(void)); -void nsync_run_once_arg(nsync_once *once, void (*farg)(void *arg), void *arg); - -/* Same as nsync_run_once()/nsync_run_once_arg() but uses a spinloop. - Can be used on the same nsync_once as - nsync_run_once/nsync_run_once_arg(). - - These *_spin variants should be used only in contexts where normal - blocking is disallowed, such as within user-space schedulers, when - the runtime is not fully initialized, etc. They provide no - significant performance benefit, and they should be avoided in normal - code. */ -void nsync_run_once_spin(nsync_once *once, void (*f)(void)); -void nsync_run_once_arg_spin(nsync_once *once, void (*farg)(void *arg), - void *arg); - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* NSYNC_ONCE_H_ */ diff --git a/third_party/nsync/panic.c b/third_party/nsync/panic.c deleted file mode 100644 index 6606ab06e..000000000 --- a/third_party/nsync/panic.c +++ /dev/null @@ -1,30 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ -│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2022 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/calls/calls.h" -#include "third_party/nsync/common.internal.h" -// clang-format off - -/* Aborts after printing the nul-terminated string s[]. */ -void nsync_panic_ (const char *s) { - size_t n = 0; - while (s[n]) ++n; - write (2, "panic: ", 7); - write (2, s, n); - notpossible; -} diff --git a/third_party/nsync/races.internal.h b/third_party/nsync/races.internal.h deleted file mode 100644 index 84ff665f5..000000000 --- a/third_party/nsync/races.internal.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef COSMOPOLITAN_THIRD_PARTY_NSYNC_RACES_INTERNAL_H_ -#define COSMOPOLITAN_THIRD_PARTY_NSYNC_RACES_INTERNAL_H_ -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -/* Annotations for race detectors. */ -#if defined(__has_feature) && !defined(__SANITIZE_THREAD__) -#if __has_feature(thread_sanitizer) /* used by clang */ -#define __SANITIZE_THREAD__ 1 /* GCC uses this; fake it in clang */ -#endif -#endif -#if defined(__SANITIZE_THREAD__) -NSYNC_C_START_ -void AnnotateIgnoreWritesBegin(const char *file, int line); -void AnnotateIgnoreWritesEnd(const char *file, int line); -void AnnotateIgnoreReadsBegin(const char *file, int line); -void AnnotateIgnoreReadsEnd(const char *file, int line); -NSYNC_C_END_ -#define IGNORE_RACES_START() \ - do { \ - AnnotateIgnoreReadsBegin(__FILE__, __LINE__); \ - AnnotateIgnoreWritesBegin(__FILE__, __LINE__); \ - } while (0) -#define IGNORE_RACES_END() \ - do { \ - AnnotateIgnoreWritesEnd(__FILE__, __LINE__); \ - AnnotateIgnoreReadsEnd(__FILE__, __LINE__); \ - } while (0) -#else -#define IGNORE_RACES_START() -#define IGNORE_RACES_END() -#endif - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_THIRD_PARTY_NSYNC_RACES_INTERNAL_H_ */ diff --git a/third_party/nsync/time.c b/third_party/nsync/time.c deleted file mode 100644 index d401c23cc..000000000 --- a/third_party/nsync/time.c +++ /dev/null @@ -1,54 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ -│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2016 Google Inc. │ -│ │ -│ Licensed under the Apache License, Version 2.0 (the "License"); │ -│ you may not use this file except in compliance with the License. │ -│ You may obtain a copy of the License at │ -│ │ -│ http://www.apache.org/licenses/LICENSE-2.0 │ -│ │ -│ Unless required by applicable law or agreed to in writing, software │ -│ distributed under the License is distributed on an "AS IS" BASIS, │ -│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │ -│ See the License for the specific language governing permissions and │ -│ limitations under the License. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/struct/timespec.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/clock.h" -#include "third_party/nsync/time.h" - -asm(".ident\t\"\\n\\n\ -*NSYNC (Apache 2.0)\\n\ -Copyright 2016 Google, Inc.\\n\ -https://github.com/google/nsync\""); -// clang-format off - -#define NSYNC_NS_IN_S_ (1000 * 1000 * 1000) - -/* Return the maximum t, assuming it's an integral - type, and the representation is not too strange. */ -#define MAX_INT_TYPE(t) (((t)~(t)0) > 1? /*is t unsigned?*/ \ - (t)~(t)0 : /*unsigned*/ \ - (t) ((((uintmax_t)1) << (sizeof (t) * CHAR_BIT - 1)) - 1)) /*signed*/ - -const nsync_time nsync_time_no_deadline = - NSYNC_TIME_STATIC_INIT (MAX_INT_TYPE (int64_t), NSYNC_NS_IN_S_ - 1); - -const nsync_time nsync_time_zero = NSYNC_TIME_STATIC_INIT (0, 0); - -nsync_time nsync_time_sleep (nsync_time delay) { - struct timespec ts; - struct timespec remain; - memset (&ts, 0, sizeof (ts)); - ts.tv_sec = NSYNC_TIME_SEC (delay); - ts.tv_nsec = NSYNC_TIME_NSEC (delay); - if (nanosleep (&ts, &remain) == 0) { - /* nanosleep() is not required to fill in "remain" - if it returns 0. */ - memset (&remain, 0, sizeof (remain)); - } - return (remain); -} diff --git a/third_party/nsync/time.h b/third_party/nsync/time.h deleted file mode 100644 index 2529d5d87..000000000 --- a/third_party/nsync/time.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef NSYNC_TIME_H_ -#define NSYNC_TIME_H_ -#include "libc/calls/struct/timespec.h" -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -#define NSYNC_TIME_SEC(t) ((t).tv_sec) -#define NSYNC_TIME_NSEC(t) ((t).tv_nsec) -#define NSYNC_TIME_STATIC_INIT(t, ns) \ - { (t), (ns) } - -/* The type nsync_time represents the interval elapsed between two - moments in time. Often the first such moment is an address-space-wide - epoch, such as the Unix epoch, but clients should not rely on the - epoch in one address space being the same as that in another. - Intervals relative to the epoch are known as absolute times. */ -typedef struct timespec nsync_time; - -/* A deadline infinitely far in the future. */ -extern const nsync_time nsync_time_no_deadline; - -/* The zero delay, or an expired deadline. */ -extern const nsync_time nsync_time_zero; - -/* Return the current time since the epoch. */ -#define nsync_time_now() _timespec_real() - -/* Sleep for the specified delay. Returns the unslept time which may be - non-zero if the call was interrupted. */ -nsync_time nsync_time_sleep(nsync_time delay); - -/* Return a+b */ -#define nsync_time_add(a, b) _timespec_add(a, b) - -/* Return a-b */ -#define nsync_time_sub(a, b) _timespec_sub(a, b) - -/* Return +ve, 0, or -ve according to whether a>b, a==b, or a the waiter is waiting */ - struct nsync_semaphore_s_ *sem; /* *sem will be Ved when waiter is woken */ - uint32_t flags; /* see below */ -}; - -/* set if waiter is embedded in Mu/CV's internal structures */ -#define NSYNC_WAITER_FLAG_MUCV 0x1 - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_LIBC_THREAD_WAIT_INTERNAL_H_ */ diff --git a/third_party/nsync/waiter.h b/third_party/nsync/waiter.h deleted file mode 100644 index 057591f13..000000000 --- a/third_party/nsync/waiter.h +++ /dev/null @@ -1,138 +0,0 @@ -#ifndef NSYNC_WAITER_H_ -#define NSYNC_WAITER_H_ -#include "third_party/nsync/time.h" -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -/* nsync_wait_n() allows the client to wait on multiple objects - (condition variables, nsync_notes, nsync_counters, etc.) until at - least one of them becomes ready, or a deadline expires. - - It can be thought of as rather like Unix's select() or poll(), except - the the objects being waited for are synchronization data structures, - rather than file descriptors. - - The client can construct new objects that can be waited for by - implementing three routines. - - Examples: - - To wait on two nsync_notes n0, n1, and a nsync_counter c0, with a - deadline of abs_deadline: - - // Form an array of struct nsync_waitable_s, identifying the - // objects and the corresponding descriptors. (Static - // initialization syntax is used for brevity.) - static struct nsync_waitable_s w[] = { - { &n0, &nsync_note_waitable_funcs }, - { &n1, &nsync_note_waitable_funcs }, - { &c0, &nsync_counter_waitable_funcs } - }; - static struct nsync_waitable_s *pw[] = { &w[0], &w[1], &w[2] }; - int n = sizeof (w) / sizeof (w[0]); - - // Wait. The mu, lock, and unlock arguments are NULL because no - // condition variables are invovled. - int i = nsync_wait_n (NULL, NULL, NULL, abs_deadline, n, pw); - if (i == n) { - // timeout - } else { - // w[i].v became ready. - } - - To wait on multiple condition variables, the mu/lock/unlock - parameters are used. Imagine cv0 and cv1 are signalled when - predicates pred0() (under lock mu0) and pred1() (under lock mu1) - become true respectively. Assume that mu0 is acquired before mu1. - - static void lock2 (void *v) { // lock two mutexes in order - nsync_mu **mu = (nsync_mu **) v; - nsync_mu_lock (mu[0]); - nsync_mu_lock (mu[1]); - } - static void unlock2 (void *v) { // unlock two mutexes. - nsync_mu **mu = (nsync_mu **) v; - nsync_mu_unlock (mu[1]); - nsync_mu_unlock (mu[0]); - } - - // Describe the condition variables and the locks. - static struct nsync_waitable_s w[] = { - { &cv0, &nsync_cv_waitable_funcs }, - { &cv1, &nsync_cv_waitable_funcs } - }; - static struct nsync_waitable_s *pw[] = { &w[0], &w[1] }; - nsync_mu *lock_list[] = { &mu0, &mu1 }; - int n = sizeof (w) / sizeof (w[0]); - - lock2 (list_list); - while (!pred0 () && !pred1 ()) { - // Wait for one of the condition variables to be signalled, - // with no timeout. - nsync_wait_n (lock_list, &lock2, &unlock2, - nsync_time_no_deadline, n, pw); - } - if (pred0 ()) { ... } - if (pred1 ()) { ... } - unlock2 (list_list); - - */ - -/* forward declaration of struct that contains type dependent wait - operations */ -struct nsync_waitable_funcs_s; - -/* Clients wait on objects by forming an array of struct - nsync_waitable_s. Each each element points to one object and its - type-dependent functions. */ -struct nsync_waitable_s { - /* pointer to object */ - void *v; - /* pointer to type-dependent functions. Use - &nsync_note_waitable_funcs for an nsync_note, - &nsync_counternote_waitable_funcs for an nsync_counter, - &nsync_cv_waitable_funcs for an nsync_cv. */ - const struct nsync_waitable_funcs_s *funcs; -}; - -/* Wait until at least one of *waitable[0,..,count-1] is has been - notified, or abs_deadline is reached. Return the index of the - notified element of waitable[], or count if no such element exists. - If mu!=NULL, (*unlock)(mu) is called after the thread is queued on - the various waiters, and (*lock)(mu) is called before return; - mu/lock/unlock are used to acquire and release the relevant locks - whan waiting on condition variables. */ -int nsync_wait_n(void *mu, void (*lock)(void *), void (*unlock)(void *), - nsync_time abs_deadline, int count, - struct nsync_waitable_s *waitable[]); - -/* A "struct nsync_waitable_s" implementation must implement these - functions. Clients should ignore the internals. */ -struct nsync_waiter_s; -struct nsync_waitable_funcs_s { - - /* Return the time when *v will be ready (max time if unknown), or 0 - if it is already ready. The parameter nw may be passed as NULL, in - which case the result should indicate whether the thread would - block if it were to wait on *v. All calls with the same *v must - report the same result until the object becomes ready, from which - point calls must report 0. */ - nsync_time (*ready_time)(void *v, struct nsync_waiter_s *nw); - - /* If *v is ready, return zero; otherwise enqueue *nw on *v and return - non-zero. */ - int (*enqueue)(void *v, struct nsync_waiter_s *nw); - - /* If nw has been previously dequeued, return zero; otherwise dequeue - *nw from *v and return non-zero. */ - int (*dequeue)(void *v, struct nsync_waiter_s *nw); -}; - -/* The "struct nsync_waitable_s" for nsync_note, nsync_counter, and nsync_cv. */ -extern const struct nsync_waitable_funcs_s nsync_note_waitable_funcs; -extern const struct nsync_waitable_funcs_s nsync_counter_waitable_funcs; -extern const struct nsync_waitable_funcs_s nsync_cv_waitable_funcs; - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* NSYNC_WAITER_H_ */ diff --git a/third_party/nsync/waiter_per_thread.c b/third_party/nsync/waiter_per_thread.c deleted file mode 100644 index 3ac2772f1..000000000 --- a/third_party/nsync/waiter_per_thread.c +++ /dev/null @@ -1,56 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ -│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2016 Google Inc. │ -│ │ -│ Licensed under the Apache License, Version 2.0 (the "License"); │ -│ you may not use this file except in compliance with the License. │ -│ You may obtain a copy of the License at │ -│ │ -│ http://www.apache.org/licenses/LICENSE-2.0 │ -│ │ -│ Unless required by applicable law or agreed to in writing, software │ -│ distributed under the License is distributed on an "AS IS" BASIS, │ -│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │ -│ See the License for the specific language governing permissions and │ -│ limitations under the License. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "third_party/nsync/atomic.h" -#include "third_party/nsync/atomic.internal.h" -#include "third_party/nsync/common.internal.h" -#include "libc/thread/thread.h" - -asm(".ident\t\"\\n\\n\ -*NSYNC (Apache 2.0)\\n\ -Copyright 2016 Google, Inc.\\n\ -https://github.com/google/nsync\""); -// clang-format off - -static pthread_key_t waiter_key; -static nsync_atomic_uint32_ pt_once; - -static void do_once (nsync_atomic_uint32_ *ponce, void (*dest) (void *)) { - uint32_t o = ATM_LOAD_ACQ (ponce); - if (o != 2) { - while (o == 0 && !ATM_CAS_ACQ (ponce, 0, 1)) { - o = ATM_LOAD (ponce); - } - if (o == 0) { - pthread_key_create (&waiter_key, dest); - ATM_STORE_REL (ponce, 2); - } - while (ATM_LOAD_ACQ (ponce) != 2) { - nsync_yield_ (); - } - } -} - -void *nsync_per_thread_waiter_ (void (*dest) (void *)) { - do_once (&pt_once, dest); - return (pthread_getspecific (waiter_key)); -} - -void nsync_set_per_thread_waiter_ (void *v, void (*dest) (void *)) { - do_once (&pt_once, dest); - pthread_setspecific (waiter_key, v); -} diff --git a/third_party/nsync/yield.c b/third_party/nsync/yield.c deleted file mode 100644 index b6183a85c..000000000 --- a/third_party/nsync/yield.c +++ /dev/null @@ -1,27 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│ -│vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2022 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/calls/calls.h" -#include "libc/calls/strace.internal.h" -#include "third_party/nsync/common.internal.h" -// clang-format off - -void nsync_yield_ (void) { - sched_yield (); - STRACE ("nsync_yield_()"); -} diff --git a/third_party/python/Objects/fileobject.c b/third_party/python/Objects/fileobject.c index 4f591d0f9..5f3186f27 100644 --- a/third_party/python/Objects/fileobject.c +++ b/third_party/python/Objects/fileobject.c @@ -7,7 +7,7 @@ #define PY_SSIZE_T_CLEAN #include "libc/calls/calls.h" #include "libc/errno.h" -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "third_party/python/Include/abstract.h" #include "third_party/python/Include/boolobject.h" #include "third_party/python/Include/bytesobject.h" diff --git a/third_party/sed/compile.c b/third_party/sed/compile.c index fe9fa8f11..aaa7a950a 100644 --- a/third_party/sed/compile.c +++ b/third_party/sed/compile.c @@ -63,7 +63,7 @@ #include "third_party/regex/regex.h" #include "libc/calls/calls.h" #include "libc/fmt/fmt.h" -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" #include "libc/stdio/temp.h" #include "libc/mem/alg.h" diff --git a/third_party/sed/main.c b/third_party/sed/main.c index e525ebb5f..cf0b97318 100644 --- a/third_party/sed/main.c +++ b/third_party/sed/main.c @@ -91,7 +91,7 @@ #define _WITH_GETLINE #include "libc/calls/calls.h" #include "libc/fmt/fmt.h" -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" #include "libc/stdio/temp.h" #include "libc/mem/alg.h" diff --git a/third_party/sed/misc.c b/third_party/sed/misc.c index 52b676c2a..1548bfdc9 100644 --- a/third_party/sed/misc.c +++ b/third_party/sed/misc.c @@ -48,7 +48,7 @@ #include "third_party/regex/regex.h" #include "libc/calls/calls.h" #include "libc/fmt/fmt.h" -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" #include "libc/stdio/temp.h" #include "libc/mem/alg.h" diff --git a/third_party/sed/process.c b/third_party/sed/process.c index 7b1d3d750..e5199a72c 100644 --- a/third_party/sed/process.c +++ b/third_party/sed/process.c @@ -74,7 +74,7 @@ #include "third_party/regex/regex.h" #include "libc/calls/calls.h" #include "libc/fmt/fmt.h" -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "libc/stdio/stdio.h" #include "libc/stdio/temp.h" #include "libc/mem/alg.h" diff --git a/third_party/sqlite3/main.c b/third_party/sqlite3/main.c index 02e7fe6a8..31673e193 100644 --- a/third_party/sqlite3/main.c +++ b/third_party/sqlite3/main.c @@ -3105,10 +3105,6 @@ static int openDatabase( char *zErrMsg = 0; /* Error message from sqlite3ParseUri() */ int i; /* Loop counter */ - // TODO(jart): Fix SQLite. - extern bool __force_sqlite_to_work_until_we_can_fix_it; - __force_sqlite_to_work_until_we_can_fix_it = true; - #ifdef SQLITE_ENABLE_API_ARMOR if( ppDb==0 ) return SQLITE_MISUSE_BKPT; #endif diff --git a/third_party/third_party.mk b/third_party/third_party.mk index e42a207c0..c7f65bec0 100644 --- a/third_party/third_party.mk +++ b/third_party/third_party.mk @@ -20,13 +20,12 @@ o/$(MODE)/third_party: \ o/$(MODE)/third_party/maxmind \ o/$(MODE)/third_party/mbedtls \ o/$(MODE)/third_party/musl \ - o/$(MODE)/third_party/nsync \ o/$(MODE)/third_party/python \ o/$(MODE)/third_party/quickjs \ o/$(MODE)/third_party/regex \ - o/$(MODE)/third_party/sed \ o/$(MODE)/third_party/smallz4 \ o/$(MODE)/third_party/sqlite3 \ + o/$(MODE)/third_party/sed \ o/$(MODE)/third_party/stb \ o/$(MODE)/third_party/tidy \ o/$(MODE)/third_party/unzip \ diff --git a/third_party/zip/zipfile.c b/third_party/zip/zipfile.c index 47c0b4231..9a85e5b1d 100644 --- a/third_party/zip/zipfile.c +++ b/third_party/zip/zipfile.c @@ -17,7 +17,7 @@ #include "third_party/zip/zip.h" #include "third_party/zip/revision.h" #ifdef UNICODE_SUPPORT -#include "libc/stdio/lock.internal.h" +#include "libc/stdio/lock.h" #include "third_party/zip/crc32.h" #endif diff --git a/tool/build/lib/pty.c b/tool/build/lib/pty.c index 32d63cccc..264bb0ecc 100644 --- a/tool/build/lib/pty.c +++ b/tool/build/lib/pty.c @@ -641,18 +641,15 @@ static void PtyDeleteLines(struct Pty *pty) { } static void PtyReportDeviceStatus(struct Pty *pty) { - static const char report[] = "\e[0n"; - PtyWriteInput(pty, report, strlen(report)); + PtyWriteInput(pty, "\e[0n", 4); } static void PtyReportPreferredVtType(struct Pty *pty) { - static const char report[] = "\e[?1;0c"; - PtyWriteInput(pty, report, strlen(report)); + PtyWriteInput(pty, "\e[?1;0c", 4); } static void PtyReportPreferredVtIdentity(struct Pty *pty) { - static const char report[] = "\e/Z"; - PtyWriteInput(pty, report, strlen(report)); + PtyWriteInput(pty, "\e/Z", 4); } static void PtyBell(struct Pty *pty) { diff --git a/tool/build/mkdeps.c b/tool/build/mkdeps.c index b55bc74b2..e2876b925 100644 --- a/tool/build/mkdeps.c +++ b/tool/build/mkdeps.c @@ -24,7 +24,9 @@ #include "libc/fmt/fmt.h" #include "libc/intrin/bits.h" #include "libc/intrin/kprintf.h" +#include "libc/intrin/pthread.h" #include "libc/intrin/safemacros.internal.h" +#include "libc/intrin/wait0.internal.h" #include "libc/log/check.h" #include "libc/log/log.h" #include "libc/macros.internal.h" @@ -35,6 +37,7 @@ #include "libc/mem/bisectcarleft.internal.h" #include "libc/mem/mem.h" #include "libc/nexgen32e/crc32.h" +#include "libc/nexgen32e/threaded.h" #include "libc/runtime/ezmap.internal.h" #include "libc/runtime/gc.internal.h" #include "libc/runtime/runtime.h" @@ -49,9 +52,6 @@ #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/prot.h" #include "libc/thread/spawn.h" -#include "libc/thread/thread.h" -#include "libc/thread/tls.h" -#include "libc/thread/wait0.internal.h" #include "libc/time/time.h" #include "libc/x/x.h" #include "third_party/getopt/getopt.h" diff --git a/tool/build/pstrace.c b/tool/build/pstrace.c index 3ba54a7ac..78bb2c572 100644 --- a/tool/build/pstrace.c +++ b/tool/build/pstrace.c @@ -16,12 +16,12 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/intrin/bits.h" #include "libc/calls/calls.h" #include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/sigset.h" #include "libc/errno.h" #include "libc/fmt/conv.h" -#include "libc/intrin/bits.h" #include "libc/log/check.h" #include "libc/log/log.h" #include "libc/mem/mem.h" @@ -115,7 +115,7 @@ struct Trace { EK_KILLED, // ret is signal code } kind; unsigned char arity; - unsigned char syscall_; + unsigned char syscall; bool is_interrupted; int us; int elap; @@ -675,7 +675,7 @@ static void Parse(struct Trace *t, const char *line, long lineno) { return; } else if (isalpha(*p) && (q = strchr(p, '('))) { t->events.p[event].kind = EK_CALL; - CHECK_NE(-1, (t->events.p[event].syscall_ = GetSyscall(p, q - p)), DEBUG); + CHECK_NE(-1, (t->events.p[event].syscall = GetSyscall(p, q - p)), DEBUG); p = q + 1; } } @@ -825,8 +825,8 @@ static void PrintEvent(FILE *f, struct Trace *t, long ev) { fprintf(f, "%d", t->events.p[ev].ret); break; case EK_CALL: - CHECK_LT(t->events.p[ev].syscall_, ARRAYLEN(kSyscalls)); - fprintf(f, "(b%`'s,%ld,", kSyscalls[t->events.p[ev].syscall_].name, + CHECK_LT(t->events.p[ev].syscall, ARRAYLEN(kSyscalls)); + fprintf(f, "(b%`'s,%ld,", kSyscalls[t->events.p[ev].syscall].name, t->events.p[ev].ret); fprintf(f, "%c", t->events.p[ev].arity && t->events.p[ev].arg[0].name != -1 ? '{' diff --git a/tool/emacs/cosmo-stuff.el b/tool/emacs/cosmo-stuff.el index d7241f477..a10d4b269 100644 --- a/tool/emacs/cosmo-stuff.el +++ b/tool/emacs/cosmo-stuff.el @@ -637,7 +637,7 @@ (let ((default-directory root)) (save-buffer) (cond ((memq major-mode '(c-mode c++-mode asm-mode fortran-mode)) - (let* ((mode (cosmo--make-mode arg "fastbuild")) + (let* ((mode (cosmo--make-mode arg "")) (compile-command (cosmo--compile-command this root 'test mode "" "" ".ok"))) (compile compile-command))) ('t @@ -653,7 +653,7 @@ (let ((default-directory root)) (save-buffer) (cond ((memq major-mode '(c-mode c++-mode asm-mode fortran-mode)) - (let* ((mode (cosmo--make-mode arg "fastbuild")) + (let* ((mode (cosmo--make-mode arg "")) (compile-command (cosmo--compile-command this root 'run-win10 mode "" "" ""))) (compile compile-command))) ('t diff --git a/tool/net/help.txt b/tool/net/help.txt index 3d1971059..4a4f32491 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -792,7 +792,7 @@ FUNCTIONS The following options may be used: - useoutput: (bool=false) encodes the result directly to the - output buffer and returns `true` value. This option is + output buffer and returns `nil` value. This option is ignored if used outside of request handling code. - sorted: (bool=true) Lua uses hash tables so the order of @@ -801,13 +801,13 @@ FUNCTIONS don't care about ordering then setting `sorted=false` should yield a performance boost in serialization. - - pretty: (bool=false) Setting this option to `true` will + - pretty: (bool=false) Setting this option to true will cause tables with more than one entry to be formatted across multiple lines for readability. - indent: (str=" ") This option controls the indentation of pretty formatting. This field is ignored if `pretty` isn't - `true`. + true. - maxdepth: (int=64) This option controls the maximum amount of recursion the serializer is allowed to perform. The max @@ -863,7 +863,7 @@ FUNCTIONS The following options may be used: - useoutput: (bool=false) encodes the result directly to the - output buffer and returns `true` value. This option is + output buffer and returns `nil` value. This option is ignored if used outside of request handling code. - sorted: (bool=true) Lua uses hash tables so the order of @@ -872,13 +872,13 @@ FUNCTIONS don't care about ordering then setting `sorted=false` should yield a performance boost in serialization. - - pretty: (bool=false) Setting this option to `true` will + - pretty: (bool=false) Setting this option to true will cause tables with more than one entry to be formatted across multiple lines for readability. - indent: (str=" ") This option controls the indentation of pretty formatting. This field is ignored if `pretty` isn't - `true`. + true. - maxdepth: (int=64) This option controls the maximum amount of recursion the serializer is allowed to perform. The max diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 15775acff..890ec1321 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -16,7 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/atomic.h" #include "libc/calls/calls.h" #include "libc/calls/ioctl.h" #include "libc/calls/pledge.h" @@ -37,6 +36,7 @@ #include "libc/intrin/atomic.h" #include "libc/intrin/likely.h" #include "libc/intrin/nomultics.internal.h" +#include "libc/intrin/pthread.h" #include "libc/intrin/safemacros.internal.h" #include "libc/log/check.h" #include "libc/log/log.h" @@ -48,6 +48,7 @@ #include "libc/nexgen32e/crc32.h" #include "libc/nexgen32e/nt2sysv.h" #include "libc/nexgen32e/rdtsc.h" +#include "libc/nexgen32e/threaded.h" #include "libc/nexgen32e/x86feature.h" #include "libc/nt/enum/fileflagandattributes.h" #include "libc/nt/thread.h" @@ -90,8 +91,6 @@ #include "libc/sysv/consts/w.h" #include "libc/sysv/errfuns.h" #include "libc/thread/spawn.h" -#include "libc/thread/thread.h" -#include "libc/thread/tls.h" #include "libc/x/x.h" #include "libc/zip.h" #include "net/http/escape.h" @@ -419,8 +418,8 @@ static bool hasonprocessdestroy; static bool loggednetworkorigin; static bool ishandlingconnection; static bool hasonclientconnection; -static atomic_bool terminatemonitor; static bool evadedragnetsurveillance; +static _Atomic(bool) terminatemonitor; static int zfd; static int gmtoff; diff --git a/tool/plinko/plinko.c b/tool/plinko/plinko.c index 6a75105c4..7a6120a16 100644 --- a/tool/plinko/plinko.c +++ b/tool/plinko/plinko.c @@ -16,7 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/tls.h" +#include "libc/nexgen32e/threaded.h" #include "tool/plinko/lib/plinko.h" STATIC_YOINK("__zipos_get"); diff --git a/tool/scripts/check-includes.py b/tool/scripts/check-includes.py new file mode 100755 index 000000000..eed104417 --- /dev/null +++ b/tool/scripts/check-includes.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python.com + +import os +import sys + +def CheckFile(path): + if path.endswith(('.png', '.ico')): + return + sys.stderr.write('%s\n' % (path)) + with open(path) as f: + data = f.read() + assert '#include' not in data[65530:], "late include in %s" % (path) + +for arg in sys.argv[1:]: + if os.path.isdir(arg): + for dirpath, dirs, files in os.walk(arg): + for filepath in files: + CheckFile(os.path.join(dirpath, filepath)) + else: + CheckFile(arg)