From 134ffee51900a4d70e8e68b598374856ceec8355 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Thu, 15 Sep 2022 03:49:34 -0700 Subject: [PATCH] Change support vector to Windows 8+ Doing this makes binaries tinier, since we don't need to have all the extra code for supporting a 32-bit address space. It also benefits us because we're able to use WIN32 futexes, which makes locking simpler. b69f3d2488dbaf9dcc541e699f5b7c09fbf046e0 is what officially ended our Windows 7 support. This change is merely a formalization. You can use old versions of Cosmo now and forevermore if you need Windows 7 since our repository is hermetic and vendors all its dependencies. Won't fix #617 --- Makefile | 1 + README.md | 4 +- ape/idata.internal.h | 66 +++++++------- libc/calls/getcpucount.c | 15 +--- libc/intrin/intrin.h | 0 .../WaitOnAddress.s | 12 +++ .../WakeByAddressAll.s | 15 ++++ .../WakeByAddressSingle.s | 15 ++++ libc/nt/errors.h | 2 +- libc/nt/master.sh | 7 ++ libc/nt/nt.mk | 21 +++++ libc/nt/synchronization.h | 5 ++ libc/runtime/memtrack32.txt | 83 ----------------- libc/sysv/consts.sh | 4 +- libc/sysv/consts/ETIMEDOUT.s | 2 +- libc/sysv/consts/FUTEX_WAKE.s | 2 +- libc/sysv/dos2errno.sh | 3 +- libc/sysv/dos2errno/ETIMEDOUT.S | 1 + test/libc/intrin/lockscale_test.c | 87 ++++++++++++++++++ third_party/nsync/futex.c | 89 ++++++++++++++----- third_party/nsync/mu_semaphore.c | 8 +- third_party/nsync/mu_semaphore.internal.h | 5 ++ third_party/nsync/mu_semaphore_win32.c | 7 +- third_party/nsync/nsync.mk | 1 + tool/scripts/man2txt | 8 +- 25 files changed, 296 insertions(+), 167 deletions(-) create mode 100755 libc/intrin/intrin.h create mode 100644 libc/nt/API-MS-Win-Core-Synch-l1-2-0/WaitOnAddress.s create mode 100644 libc/nt/API-MS-Win-Core-Synch-l1-2-0/WakeByAddressAll.s create mode 100644 libc/nt/API-MS-Win-Core-Synch-l1-2-0/WakeByAddressSingle.s delete mode 100644 libc/runtime/memtrack32.txt create mode 100644 test/libc/intrin/lockscale_test.c diff --git a/Makefile b/Makefile index f653b1aa0..1b334601d 100644 --- a/Makefile +++ b/Makefile @@ -325,6 +325,7 @@ COSMOPOLITAN_OBJECTS = \ LIBC_NT_USER32 \ LIBC_NT_NTDLL \ LIBC_NT_ADVAPI32 \ + LIBC_NT_SYNCHRONIZATION \ LIBC_FMT \ THIRD_PARTY_COMPILER_RT \ LIBC_TINYMATH \ diff --git a/README.md b/README.md index 0bbb27c34..dc9d2a28d 100644 --- a/README.md +++ b/README.md @@ -245,11 +245,11 @@ gdb foo.com -ex 'add-symbol-file foo.com.dbg 0x401000' | :--- | ---: | ---: | | AMD | K8 Venus | 2005 | | Intel | Core | 2006 | -| Windows | Vista | 2006 | | Linux | 2.6.18 | 2007 | +| Windows | 8 | 2012 | | Mac OS X | 15.6 | 2018 | -| FreeBSD | 13 | 2020 | | OpenBSD | 6.4 | 2018 | +| FreeBSD | 13 | 2020 | | NetBSD | 9.2 | 2021 | ## Special Thanks diff --git a/ape/idata.internal.h b/ape/idata.internal.h index 7a3369827..7ea542a96 100644 --- a/ape/idata.internal.h +++ b/ape/idata.internal.h @@ -32,23 +32,23 @@ // @see ape/ape.lds // @see winimp .macro .imp dll:req fn:req actual:req hint - .dll \dll - .section .piro.data.sort.iat.2.\dll\().2.\actual,"aw",@progbits + .dll "\dll" + .section ".piro.data.sort.iat.2.\dll\().2.\actual","aw",@progbits .type \fn,@object .align __SIZEOF_POINTER__ -\fn: .quad RVA((\dll\().\actual)) +\fn: .quad RVA(("\dll\().\actual")) .size \fn,.-\fn .globl \fn .hidden \fn .previous - .section .idata.ro.ilt.\dll\().2.\actual,"a",@progbits -.Lidata.ilt.\dll\().\actual: - .quad RVA((\dll\().\actual)) - .type .Lidata.ilt.\dll\().\actual,@object - .size .Lidata.ilt.\dll\().\actual,.-.Lidata.ilt.\dll\().\actual + .section ".idata.ro.ilt.\dll\().2.\actual","a",@progbits +".Lidata.ilt.\dll\().\actual": + .quad RVA("\dll\().\actual") + .type ".Lidata.ilt.\dll\().\actual",@object + .size ".Lidata.ilt.\dll\().\actual",.-".Lidata.ilt.\dll\().\actual" .previous - .section .idata.ro.hnt.\dll\().2.\actual,"a",@progbits -\dll\().\actual: + .section ".idata.ro.hnt.\dll\().2.\actual","a",@progbits +"\dll\().\actual": .ifnb \hint # hint i.e. guess function ordinal .short \hint .else @@ -56,55 +56,55 @@ .endif .asciz "\actual" .align 2 # documented requirement - .globl \dll\().\actual - .hidden \dll\().\actual - .type \dll\().\actual,@object - .size \dll\().\actual,.-\dll\().\actual + .globl "\dll\().\actual" + .hidden "\dll\().\actual" + .type "\dll\().\actual",@object + .size "\dll\().\actual",.-"\dll\().\actual" .previous .endm // Defines DLL import. // @note this is an implementation detail of .imp .macro .dll name:req - .section .idata.ro.idt.2.\name,"aG",@progbits,\name,comdat - .equ .Lidata.idt.\name,. - .long RVA(idata.ilt.\name) # ImportLookupTable + .section ".idata.ro.idt.2.\name","aG",@progbits,"\name",comdat + .equ ".Lidata.idt.\name",. + .long RVA("idata.ilt.\name") # ImportLookupTable .long 0 # TimeDateStamp .long 0 # ForwarderChain - .long RVA(.Lidata.str.\name) # DllNameRva - .long RVA(idata.iat.\name) # ImportAddressTable - .type .Lidata.idt.\name,@object - .size .Lidata.idt.\name,.-.Lidata.idt.\name + .long RVA(".Lidata.str.\name") # DllNameRva + .long RVA("idata.iat.\name") # ImportAddressTable + .type ".Lidata.idt.\name",@object + .size ".Lidata.idt.\name",.-".Lidata.idt.\name" .previous - .section .idata.ro.ilt.\name\().1,"aG",@progbits,\name,comdat + .section ".idata.ro.ilt.\name\().1","aG",@progbits,"\name",comdat .align __SIZEOF_POINTER__ - .type idata.ilt.\name,@object -idata.ilt.\name: + .type "idata.ilt.\name",@object +"idata.ilt.\name": .previous/* ... decentralized content ... - */.section .idata.ro.ilt.\name\().3,"aG",@progbits,\name,comdat + */.section ".idata.ro.ilt.\name\().3","aG",@progbits,"\name",comdat .quad 0 .previous - .section .idata.ro.hnt.\name\().1,"aG",@progbits,\name,comdat + .section ".idata.ro.hnt.\name\().1","aG",@progbits,"\name",comdat .align __SIZEOF_POINTER__ - .type idata.hnt.\name,@object - .equ idata.hnt.\name,. + .type "idata.hnt.\name",@object + .equ "idata.hnt.\name",. .previous - .section .piro.data.sort.iat.2.\name\().1,"awG",@progbits,\name,comdat + .section ".piro.data.sort.iat.2.\name\().1","awG",@progbits,"\name",comdat .align __SIZEOF_POINTER__ - .type idata.iat.\name,@object -idata.iat.\name: + .type "idata.iat.\name",@object +"idata.iat.\name": .previous/* ... decentralized content ... - */.section .piro.data.sort.iat.2.\name\().3,"awG",@progbits,\name,comdat + */.section ".piro.data.sort.iat.2.\name\().3","awG",@progbits,"\name",comdat .quad 0 .previous .section .rodata.str1.1,"aSM",@progbits,1 -.Lidata.str.\name: +".Lidata.str.\name": .asciz "\name\().dll" .previous .endm diff --git a/libc/calls/getcpucount.c b/libc/calls/getcpucount.c index 82b8e2c24..3f0640322 100644 --- a/libc/calls/getcpucount.c +++ b/libc/calls/getcpucount.c @@ -22,6 +22,7 @@ #include "libc/calls/weirdtypes.h" #include "libc/dce.h" #include "libc/macros.internal.h" +#include "libc/nt/accounting.h" #include "libc/nt/dll.h" #include "libc/nt/struct/systeminfo.h" #include "libc/nt/systeminfo.h" @@ -61,18 +62,6 @@ static unsigned GetCpuCountBsd(void) { } } -static textwindows unsigned GetCpuCountWindows(void) { - struct NtSystemInfo si; - uint32_t (*f)(uint16_t); - if ((f = GetProcAddress(GetModuleHandle("KERNEL32"), - "GetMaximumProcessorCount"))) { - return f(ALL_PROCESSOR_GROUPS); - } else { - GetSystemInfo(&si); - return si.dwNumberOfProcessors; - } -} - static unsigned GetCpuCountImpl(void) { if (!IsWindows()) { if (!IsBsd()) { @@ -81,7 +70,7 @@ static unsigned GetCpuCountImpl(void) { return GetCpuCountBsd(); } } else { - return GetCpuCountWindows(); + return GetMaximumProcessorCount(ALL_PROCESSOR_GROUPS); } } diff --git a/libc/intrin/intrin.h b/libc/intrin/intrin.h new file mode 100755 index 000000000..e69de29bb diff --git a/libc/nt/API-MS-Win-Core-Synch-l1-2-0/WaitOnAddress.s b/libc/nt/API-MS-Win-Core-Synch-l1-2-0/WaitOnAddress.s new file mode 100644 index 000000000..d6bd213b9 --- /dev/null +++ b/libc/nt/API-MS-Win-Core-Synch-l1-2-0/WaitOnAddress.s @@ -0,0 +1,12 @@ +.include "o/libc/nt/codegen.inc" +.imp API-MS-Win-Core-Synch-l1-2-0,__imp_WaitOnAddress,WaitOnAddress,111 + + .text.windows +WaitOnAddress: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_WaitOnAddress(%rip),%rax + jmp __sysv2nt + .endfn WaitOnAddress,globl + .previous diff --git a/libc/nt/API-MS-Win-Core-Synch-l1-2-0/WakeByAddressAll.s b/libc/nt/API-MS-Win-Core-Synch-l1-2-0/WakeByAddressAll.s new file mode 100644 index 000000000..4d5ef8e38 --- /dev/null +++ b/libc/nt/API-MS-Win-Core-Synch-l1-2-0/WakeByAddressAll.s @@ -0,0 +1,15 @@ +.include "o/libc/nt/codegen.inc" +.imp API-MS-Win-Core-Synch-l1-2-0,__imp_WakeByAddressAll,WakeByAddressAll,113 + + .text.windows +WakeByAddressAll: + push %rbp + mov %rsp,%rbp + .profilable + mov %rdi,%rcx + sub $32,%rsp + call *__imp_WakeByAddressAll(%rip) + leave + ret + .endfn WakeByAddressAll,globl + .previous diff --git a/libc/nt/API-MS-Win-Core-Synch-l1-2-0/WakeByAddressSingle.s b/libc/nt/API-MS-Win-Core-Synch-l1-2-0/WakeByAddressSingle.s new file mode 100644 index 000000000..2c3eb8089 --- /dev/null +++ b/libc/nt/API-MS-Win-Core-Synch-l1-2-0/WakeByAddressSingle.s @@ -0,0 +1,15 @@ +.include "o/libc/nt/codegen.inc" +.imp API-MS-Win-Core-Synch-l1-2-0,__imp_WakeByAddressSingle,WakeByAddressSingle,116 + + .text.windows +WakeByAddressSingle: + push %rbp + mov %rsp,%rbp + .profilable + mov %rdi,%rcx + sub $32,%rsp + call *__imp_WakeByAddressSingle(%rip) + leave + ret + .endfn WakeByAddressSingle,globl + .previous diff --git a/libc/nt/errors.h b/libc/nt/errors.h index 51018e957..9e1d1a17b 100644 --- a/libc/nt/errors.h +++ b/libc/nt/errors.h @@ -992,7 +992,7 @@ #define kNtErrorInvalidKeyboardHandle 1457 #define kNtErrorHookTypeNotAllowed 1458 #define kNtErrorRequiresInteractiveWindowstation 1459 -#define kNtErrorTimeout 1460 +#define kNtErrorTimeout 1460 /* ETIMEDOUT */ #define kNtErrorInvalidMonitorHandle 1461 #define kNtErrorIncorrectSize 1462 #define kNtErrorSymlinkClassDisabled 1463 diff --git a/libc/nt/master.sh b/libc/nt/master.sh index 415068d57..e064899e6 100755 --- a/libc/nt/master.sh +++ b/libc/nt/master.sh @@ -3554,6 +3554,13 @@ imp 'InetIsOffline' InetIsOffline url 106 imp 'MIMEAssociationDialog' MIMEAssociationDialogW url 108 imp 'MailToProtocolHandler' MailToProtocolHandler url 109 +# API-MS-Win-Core-Synch-l1-2-0.dll (Windows 8+) +# +# Name Actual DLL Hint Arity +imp 'WaitOnAddress' WaitOnAddress API-MS-Win-Core-Synch-l1-2-0 111 4 +imp 'WakeByAddressAll' WakeByAddressAll API-MS-Win-Core-Synch-l1-2-0 113 1 +imp 'WakeByAddressSingle' WakeByAddressSingle API-MS-Win-Core-Synch-l1-2-0 116 1 + # NTDLL.DLL # BEYOND THE PALE # diff --git a/libc/nt/nt.mk b/libc/nt/nt.mk index 069137bec..9a17df51d 100644 --- a/libc/nt/nt.mk +++ b/libc/nt/nt.mk @@ -167,6 +167,27 @@ $(LIBC_NT_URL_A).pkg: \ #─────────────────────────────────────────────────────────────────────────────── +LIBC_NT_ARTIFACTS += LIBC_NT_SYNCHRONIZATION_A +LIBC_NT_SYNCHRONIZATION = $(LIBC_NT_SYNCHRONIZATION_A_DEPS) $(LIBC_NT_SYNCHRONIZATION_A) +LIBC_NT_SYNCHRONIZATION_A = o/$(MODE)/libc/nt/synchronization.a +LIBC_NT_SYNCHRONIZATION_A_SRCS := $(wildcard libc/nt/API-MS-Win-Core-Synch-l1-2-0/*.s) +LIBC_NT_SYNCHRONIZATION_A_OBJS = $(LIBC_NT_SYNCHRONIZATION_A_SRCS:%.s=o/$(MODE)/%.o) +LIBC_NT_SYNCHRONIZATION_A_CHECKS = $(LIBC_NT_SYNCHRONIZATION_A).pkg +LIBC_NT_SYNCHRONIZATION_A_DIRECTDEPS = LIBC_NT_KERNEL32 +LIBC_NT_SYNCHRONIZATION_A_DEPS := \ + $(call uniq,$(foreach x,$(LIBC_NT_SYNCHRONIZATION_A_DIRECTDEPS),$($(x)))) + +$(LIBC_NT_SYNCHRONIZATION_A): \ + libc/nt/API-MS-Win-Core-Synch-l1-2-0/ \ + $(LIBC_NT_SYNCHRONIZATION_A).pkg \ + $(LIBC_NT_SYNCHRONIZATION_A_OBJS) + +$(LIBC_NT_SYNCHRONIZATION_A).pkg: \ + $(LIBC_NT_SYNCHRONIZATION_A_OBJS) \ + $(foreach x,$(LIBC_NT_SYNCHRONIZATION_A_DIRECTDEPS),$($(x)_A).pkg) + +#─────────────────────────────────────────────────────────────────────────────── + LIBC_NT_ARTIFACTS += LIBC_NT_USER32_A LIBC_NT_USER32 = $(LIBC_NT_USER32_A_DEPS) $(LIBC_NT_USER32_A) LIBC_NT_USER32_A = o/$(MODE)/libc/nt/user32.a diff --git a/libc/nt/synchronization.h b/libc/nt/synchronization.h index 5c0fe55eb..491e3fe5b 100644 --- a/libc/nt/synchronization.h +++ b/libc/nt/synchronization.h @@ -54,6 +54,11 @@ typedef void (*NtTimerapcroutine)(void *lpArgToCompletionRoutine, typedef void (*NtWaitOrTimerCallback)(void *lpParameter, bool32 TimerOrWaitFired); +void WakeByAddressAll(void *Address); +void WakeByAddressSingle(void *Address); +bool32 WaitOnAddress(volatile void *Address, void *CompareAddress, + size_t AddressSize, uint32_t opt_dwMilliseconds); + void Sleep(uint32_t dwMilliseconds); uint32_t SleepEx(uint32_t dwMilliseconds, bool32 bAlertable); diff --git a/libc/runtime/memtrack32.txt b/libc/runtime/memtrack32.txt deleted file mode 100644 index ed9770b20..000000000 --- a/libc/runtime/memtrack32.txt +++ /dev/null @@ -1,83 +0,0 @@ -# -*- conf -*- -# Cosmopolitan Libc Legacy Memory Plan - -00000000-0000001f 2048kb guard -00000020-0000003f 2048kb loader -00000040-000000ff 12mb image -00000100-000003ff 48mb free -00000400-000007ff 64mb free -00000800-00000bff 64mb free -00000c00-00000fff 64mb free -00001000-000013ff 64mb automap -00001400-000017ff 64mb automap -00001800-00001bff 64mb automap -00001c00-00001fff 64mb automap -00002000-000023ff 64mb automap -00002400-000027ff 64mb automap -00002800-00002bff 64mb automap -00002c00-00002fff 64mb automap -00003000-000033ff 64mb automap -00003400-000037ff 64mb automap -00003800-00003bff 64mb automap -00003c00-00003fe7 63mb automap -00003fe4-00003ffb 1536kb memtrack -00003ffc-00003fff 256kb free -00004000-000043ff 64mb fixedmap -00004400-000047ff 64mb fixedmap -00004800-00004bff 64mb fixedmap -00004c00-00004fff 64mb fixedmap -00005000-000053ff 64mb fixedmap -00005400-000057ff 64mb fixedmap -00005800-00005bff 64mb fixedmap -00005c00-00005fff 64mb fixedmap -00006000-000063ff 64mb fixedmap -00006400-000067ff 64mb fixedmap -00006800-00006bff 64mb fixedmap -00006c00-00006fff 64mb fixedmap -00005000-000053ff 64mb arena -00005400-000057ff 64mb arena -00005800-00005bff 64mb arena -00005c00-00005fff 64mb arena -00006000-000063ff 64mb arena -00006400-000067ff 64mb arena -00006800-00006bff 64mb fds -00006c00-00006fff 64mb zipos -00007000-000073ff 64mb zipos -00007400-000077ff 64mb zipos -00007800-00007bff 64mb zipos -00007c00-00007ffd 64mb arena -00007ffe-00007fff 128kb free -00008000-000083ff 64mb free -00008400-000087ff 64mb free -00008800-00008bff 64mb free -00008c00-00008fff 64mb free -00009000-000093ff 64mb free -00009400-000097ff 64mb free -00009800-00009bff 64mb free -00009c00-00009fff 64mb free -0000a000-0000a3ff 64mb free -0000a400-0000a7ff 64mb free -0000a800-0000abff 64mb free -0000ac00-0000afff 64mb free -0000b000-0000b3ff 64mb free -0000b400-0000b7ff 64mb free -0000b800-0000bbff 64mb free -0000bc00-0000bfff 64mb free -0000c000-0000c3ff 64mb free -0000c400-0000c7ff 64mb free -0000c800-0000cbff 64mb free -0000cc00-0000cfff 64mb free -0000d000-0000d3ff 64mb free -0000d400-0000d7ff 64mb free -0000d800-0000dbff 64mb free -0000dc00-0000dfff 64mb free -0000e000-0000e3ff 64mb free -0000e400-0000e7ff 64mb free -0000e800-0000ebff 64mb free -0000ec00-0000efff 64mb free -0000f000-0000f3ff 64mb free -0000f400-0000f7ff 64mb free -0000f800-0000fbff 64mb free -0000fc00-0000fffb 64mb free -0000fffc-0000fffd 128kb winargs -0000fffe-0000ffff 128kb stack diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index 93cba7b4a..f1823bab8 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -94,7 +94,7 @@ syscon errno EISCONN 106 56 56 56 56 10056 # socket is connected syscon errno ENOTCONN 107 57 57 57 57 10057 # socket is not connected; bsd consensus; WSAENOTCONN; raised by getpeername(2), recv(2), send(2), shutdown(2), ip(7) syscon errno ESHUTDOWN 108 58 58 58 58 10058 # cannot send after transport endpoint shutdown; note that shutdown write is an EPIPE; bsd consensus; WSAESHUTDOWN syscon errno ETOOMANYREFS 109 59 59 59 59 10059 # too many references: cannot splice; bsd consensus; WSAETOOMANYREFS; raised by sendmsg(2), unix(7) -syscon errno ETIMEDOUT 110 60 60 60 60 10060 # connection timed out; bsd consensus; WSAETIMEDOUT; raised by connect(2), futex(2), keyctl(2), tcp(7) +syscon errno ETIMEDOUT 110 60 60 60 60 1460 # connection timed out; kNtErrorTimeout; bsd consensus; WSAETIMEDOUT; raised by connect(2), futex(2), keyctl(2), tcp(7) syscon errno ECONNREFUSED 111 61 61 61 61 10061 # bsd consensus; WSAECONNREFUSED; raised by connect(2), listen(2), recv(2), unix(7), udp(7)system-imposed limit on the number of threads was encountered. syscon errno EHOSTDOWN 112 64 64 64 64 10064 # bsd consensus; WSAEHOSTDOWN; raised by accept(2) syscon errno EHOSTUNREACH 113 65 65 65 65 10065 # bsd consensus; WSAEHOSTUNREACH; raised by accept(2), ip(7) @@ -1316,7 +1316,7 @@ 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_WAKE 1 0 0 2 0 1 syscon futex FUTEX_REQUEUE 3 0 0 3 0 0 syscon futex FUTEX_PRIVATE_FLAG 128 0 0 128 0 0 diff --git a/libc/sysv/consts/ETIMEDOUT.s b/libc/sysv/consts/ETIMEDOUT.s index 7bbeff37d..9c183daeb 100644 --- a/libc/sysv/consts/ETIMEDOUT.s +++ b/libc/sysv/consts/ETIMEDOUT.s @@ -1,3 +1,3 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon errno,ETIMEDOUT,110,60,60,60,60,10060 +.syscon errno,ETIMEDOUT,110,60,60,60,60,1460 .yoink kDos2Errno.ETIMEDOUT diff --git a/libc/sysv/consts/FUTEX_WAKE.s b/libc/sysv/consts/FUTEX_WAKE.s index acdc80afa..e9ed1e63e 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,0,1 diff --git a/libc/sysv/dos2errno.sh b/libc/sysv/dos2errno.sh index 2dea062fc..5781685f6 100755 --- a/libc/sysv/dos2errno.sh +++ b/libc/sysv/dos2errno.sh @@ -84,7 +84,7 @@ dir=libc/sysv/dos2errno # dos WSAENOTCONN ENOTCONN # in consts.sh # dos WSAESHUTDOWN ESHUTDOWN # in consts.sh # dos WSAETOOMANYREFS ETOOMANYREFS # in consts.sh -# dos WSAETIMEDOUT ETIMEDOUT # in consts.sh +# dos kNtErrorTimeout ETIMEDOUT # in consts.sh # dos WSAECONNREFUSED ECONNREFUSED # in consts.sh # dos WSAEHOSTDOWN EHOSTDOWN # in consts.sh # dos WSAEHOSTUNREACH EHOSTUNREACH # in consts.sh @@ -170,3 +170,4 @@ dos WSAEPROCLIM ENOMEM dos WSANOTINITIALISED ENETDOWN dos WSASYSNOTREADY ENETDOWN dos WSAVERNOTSUPPORTED ENOSYS +dos WSAETIMEDOUT ETIMEDOUT diff --git a/libc/sysv/dos2errno/ETIMEDOUT.S b/libc/sysv/dos2errno/ETIMEDOUT.S index 816f72a94..1b8a42cb5 100644 --- a/libc/sysv/dos2errno/ETIMEDOUT.S +++ b/libc/sysv/dos2errno/ETIMEDOUT.S @@ -9,3 +9,4 @@ .type kDos2Errno.ETIMEDOUT,@object kDos2Errno.ETIMEDOUT: .e kNtErrorSemTimeout,ETIMEDOUT + .e WSAETIMEDOUT,ETIMEDOUT diff --git a/test/libc/intrin/lockscale_test.c b/test/libc/intrin/lockscale_test.c new file mode 100644 index 000000000..16d58668c --- /dev/null +++ b/test/libc/intrin/lockscale_test.c @@ -0,0 +1,87 @@ +/*-*- 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/atomic.h" +#include "libc/calls/calls.h" +#include "libc/calls/struct/timespec.h" +#include "libc/intrin/intrin.h" +#include "libc/mem/gc.h" +#include "libc/mem/mem.h" +#include "libc/stdio/stdio.h" +#include "libc/testlib/testlib.h" +#include "libc/thread/thread.h" +#include "libc/time/time.h" + +/** + * @fileoverview Lock Waiter Scalability Test + * + * This test can be run as follows: + * + * make o//test/libc/intrin/lockscale_test.com.runs V=5 TESTARGS=-b + * + * It's intended to demonstrate the importance of futexes. On systems + * that don't have them, this test consumes orders of a magnitude more + * cpu time, because there's a lot of waiters and they need to wait a + * very long time. + */ + +#define WAITERS 100 +#define ITERATIONS 10000 + +atomic_int x; +pthread_t t[WAITERS]; +pthread_mutex_t lock; +pthread_barrier_t barrier; + +void Worker(void) { + pthread_mutex_lock(&lock); + pthread_barrier_wait(&barrier); + for (x = 0; x < ITERATIONS; ++x) { + sched_yield(); + } + pthread_mutex_unlock(&lock); +} + +void *Waiter(void *arg) { + pthread_barrier_wait(&barrier); + pthread_mutex_lock(&lock); + ASSERT_EQ(ITERATIONS, x); + pthread_mutex_unlock(&lock); + return 0; +} + +BENCH(lock, scalability) { + int i; + struct timespec t1, t2; + t1 = _timespec_real(); + pthread_mutex_init(&lock, 0); + pthread_barrier_init(&barrier, 0, WAITERS + 1); + for (i = 0; i < WAITERS; ++i) { + ASSERT_EQ(0, pthread_create(t + i, 0, Waiter, 0)); + } + Worker(); + for (i = 0; i < WAITERS; ++i) { + ASSERT_EQ(0, pthread_join(t[i], 0)); + } + pthread_barrier_destroy(&barrier); + pthread_mutex_destroy(&lock); + t2 = _timespec_real(); + printf("consumed %10g seconds monotonic time and %10g seconds cpu time\n", + _timespec_tonanos(_timespec_sub(t2, t1)) / 1000000000., + (double)clock() / CLOCKS_PER_SEC); +} diff --git a/third_party/nsync/futex.c b/third_party/nsync/futex.c index da7ed4da4..d7b300e52 100644 --- a/third_party/nsync/futex.c +++ b/third_party/nsync/futex.c @@ -17,14 +17,19 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/struct/timespec.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/describeflags.internal.h" #include "libc/intrin/strace.internal.h" +#include "libc/limits.h" +#include "libc/nt/runtime.h" +#include "libc/nt/synchronization.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" +#include "third_party/nsync/mu_semaphore.internal.h" // clang-format off /* futex() polyfill w/ sched_yield() fallback */ @@ -41,8 +46,18 @@ bool FUTEX_TIMEOUT_IS_ABSOLUTE; __attribute__((__constructor__)) static void nsync_futex_init_ (void) { int x = 0; - if (!(FUTEX_IS_SUPPORTED = IsLinux() || IsOpenbsd())) + if (NSYNC_FUTEX_WIN32 && IsWindows ()) { + FUTEX_IS_SUPPORTED = true; + FUTEX_WAIT_ = FUTEX_WAIT; return; + } + + if (!(FUTEX_IS_SUPPORTED = IsLinux () || IsOpenbsd ())) { + // we're using sched_yield() so let's + // avoid needless clock_gettime calls + FUTEX_TIMEOUT_IS_ABSOLUTE = true; + return; + } // In our testing, we found that the monotonic clock on various // popular systems (such as Linux, and some BSD variants) was no @@ -78,8 +93,30 @@ __attribute__((__constructor__)) static void nsync_futex_init_ (void) { int nsync_futex_wait_ (int *p, int expect, char pshare, struct timespec *timeout) { int rc, op; - if (FUTEX_IS_SUPPORTED) { - op = FUTEX_WAIT_; + uint32_t ms; + + if (!FUTEX_IS_SUPPORTED) { + nsync_yield_ (); + if (timeout) { + return -EINTR; + } else { + return 0; + } + } + + op = FUTEX_WAIT_; + if (NSYNC_FUTEX_WIN32 && IsWindows ()) { + if (timeout) { + ms = _timespec_tomillis (*timeout); + } else { + ms = -1; + } + if (WaitOnAddress (p, &expect, sizeof(int), ms)) { + rc = 0; + } else { + rc = -GetLastError (); + } + } else { if (pshare == PTHREAD_PROCESS_PRIVATE) { op |= FUTEX_PRIVATE_FLAG_; } @@ -88,35 +125,45 @@ int nsync_futex_wait_ (int *p, int expect, char pshare, struct timespec *timeout // [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; - } } + + STRACE("futex(%t, %s, %d, %s) → %s", + p, DescribeFutexOp (op), expect, + DescribeTimespec (0, timeout), + DescribeFutexResult (rc)); + 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; + + ASSERT (count == 1 || count == INT_MAX); + + if (!FUTEX_IS_SUPPORTED) { + nsync_yield_ (); + return 0; + } + + op = FUTEX_WAKE; + if (NSYNC_FUTEX_WIN32 && IsWindows ()) { + if (count == 1) { + WakeByAddressSingle (p); + } else { + WakeByAddressAll (p); + } + rc = 0; + } else { 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; } + + STRACE("futex(%t, %s, %d) → %s", p, + DescribeFutexOp(op), + count, DescribeFutexResult(rc)); + return rc; } diff --git a/third_party/nsync/mu_semaphore.c b/third_party/nsync/mu_semaphore.c index a30ef4873..6fe6014c6 100644 --- a/third_party/nsync/mu_semaphore.c +++ b/third_party/nsync/mu_semaphore.c @@ -23,7 +23,7 @@ /* Initialize *s; the initial value is 0. */ void nsync_mu_semaphore_init (nsync_semaphore *s) { - if (!IsWindows ()) + if (NSYNC_FUTEX_WIN32 || !IsWindows ()) nsync_mu_semaphore_init_futex (s); else nsync_mu_semaphore_init_win32 (s); @@ -31,7 +31,7 @@ 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) { - if (!IsWindows ()) + if (NSYNC_FUTEX_WIN32 || !IsWindows ()) nsync_mu_semaphore_p_futex (s); else nsync_mu_semaphore_p_win32 (s); @@ -41,7 +41,7 @@ void nsync_mu_semaphore_p (nsync_semaphore *s) { 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 ()) + if (NSYNC_FUTEX_WIN32 || !IsWindows ()) return nsync_mu_semaphore_p_with_deadline_futex (s, abs_deadline); else return nsync_mu_semaphore_p_with_deadline_win32 (s, abs_deadline); @@ -49,7 +49,7 @@ int nsync_mu_semaphore_p_with_deadline (nsync_semaphore *s, nsync_time abs_deadl /* Ensure that the count of *s is at least 1. */ void nsync_mu_semaphore_v (nsync_semaphore *s) { - if (!IsWindows ()) + if (NSYNC_FUTEX_WIN32 || !IsWindows ()) nsync_mu_semaphore_v_futex (s); else nsync_mu_semaphore_v_win32 (s); diff --git a/third_party/nsync/mu_semaphore.internal.h b/third_party/nsync/mu_semaphore.internal.h index 8fac00aa0..b636bfc3f 100644 --- a/third_party/nsync/mu_semaphore.internal.h +++ b/third_party/nsync/mu_semaphore.internal.h @@ -2,6 +2,11 @@ #define NSYNC_MU_SEMAPHORE_INTERNAL_H_ #include "third_party/nsync/mu_semaphore.h" #include "third_party/nsync/time.h" + +#ifndef NSYNC_FUTEX_WIN32 +#define NSYNC_FUTEX_WIN32 1 +#endif + #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ diff --git a/third_party/nsync/mu_semaphore_win32.c b/third_party/nsync/mu_semaphore_win32.c index 9dc8e2927..3d1cdc2de 100644 --- a/third_party/nsync/mu_semaphore_win32.c +++ b/third_party/nsync/mu_semaphore_win32.c @@ -15,6 +15,7 @@ │ See the License for the specific language governing permissions and │ │ limitations under the License. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/state.internal.h" #include "libc/errno.h" #include "libc/nt/enum/wait.h" #include "libc/nt/synchronization.h" @@ -32,14 +33,14 @@ https://github.com/google/nsync\""); /* 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); + *h = CreateSemaphore (&kNtIsInheritable, 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); + WaitForSingleObject (*h, -1u); } /* Wait until one of: @@ -50,7 +51,7 @@ int nsync_mu_semaphore_p_with_deadline_win32 (nsync_semaphore *s, nsync_time abs int result; if (nsync_time_cmp (abs_deadline, nsync_time_no_deadline) == 0) { - result = WaitForSingleObject(*h, -1u); + result = WaitForSingleObject (*h, -1u); } else { nsync_time now; now = nsync_time_now (); diff --git a/third_party/nsync/nsync.mk b/third_party/nsync/nsync.mk index efc01d64f..de04e9d85 100644 --- a/third_party/nsync/nsync.mk +++ b/third_party/nsync/nsync.mk @@ -27,6 +27,7 @@ THIRD_PARTY_NSYNC_A_DIRECTDEPS = \ LIBC_INTRIN \ LIBC_NEXGEN32E \ LIBC_NT_KERNEL32 \ + LIBC_NT_SYNCHRONIZATION \ LIBC_STR \ LIBC_STUBS \ LIBC_SYSV \ diff --git a/tool/scripts/man2txt b/tool/scripts/man2txt index 864e0c3e9..db4d71115 100755 --- a/tool/scripts/man2txt +++ b/tool/scripts/man2txt @@ -1,5 +1,9 @@ #!/bin/sh + +ASCII2UTF8=$(command -v ascii2utf8.com 2>/dev/null) || { + ASCII2UTF8=$(ls -1 o/*/tool/viz/ascii2utf8.com | head -n1) +} + for x; do - nroff -mandoc -rLL=80n -rLT=80n -Tutf8 <"$x" | - o//tool/viz/ascii2utf8.com + nroff -mandoc -rLL=80n -rLT=80n -Tutf8 <"$x" | $ASCII2UTF8 done