From 49b0eaa69f6f27f433fd17c064a7a048688c5c56 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Thu, 12 Oct 2023 18:53:17 -0700 Subject: [PATCH] Improve threading and i/o routines - On Windows connect() can now be interrupted by a signal; connect() w/ O_NONBLOCK will now raise EINPROGRESS; and connect() with SO_SNDTIMEO will raise ETIMEDOUT after the interval has elapsed. - We now get the AcceptEx(), ConnectEx(), and TransmitFile() functions from the WIN32 API the officially blessed way, using WSAIoctl(). - Do nothing on Windows when fsync() is called on a directory handle. This was raising EACCES earlier becaues GENERIC_WRITE is required on the handle. It's possible to FlushFileBuffers() a directory handle if it's opened with write access but MSDN doesn't document what it does. If you have any idea, please let us know! - Prefer manual reset event objects for read() and write() on Windows. - Do some code cleanup on our dlmalloc customizations. - Fix errno type error in Windows blocking routines. - Make the futex polyfill simpler and faster. --- Makefile | 1 - libc/calls/fdatasync-nt.c | 9 + libc/calls/readwrite-nt.c | 5 +- libc/calls/releasefd.c | 1 + libc/calls/struct/fd.internal.h | 10 +- libc/intrin/pthread_yield.c | 3 + libc/intrin/ulock.c | 2 +- libc/nt/MsWSock/AcceptEx.S | 18 -- libc/nt/MsWSock/DisconnectEx.S | 18 -- libc/nt/MsWSock/GetAcceptExSockaddrs.S | 18 -- libc/nt/MsWSock/TransmitFile.S | 18 -- libc/nt/MsWSock/WSARecvEx.S | 18 -- libc/nt/iocp.h | 8 +- libc/nt/master.sh | 10 +- libc/nt/nt.mk | 21 -- libc/nt/struct/overlappedentry.h | 2 +- libc/nt/winsock.h | 20 +- libc/runtime/clone.c | 5 - libc/sock/accept-nt.c | 35 ++- libc/sock/bind-nt.c | 10 +- libc/sock/closesocket-nt.c | 9 +- libc/sock/connect-nt.c | 142 +++++++++--- libc/sock/sendfile.c | 28 ++- libc/sock/sock.mk | 1 - libc/sock/syscall_fd.internal.h | 1 + libc/sock/winsockblock.c | 2 +- libc/sock/wsaid.c | 46 ++++ libc/sock/wsaid.internal.h | 11 + libc/sysv/consts.sh | 1 - test/libc/dns/servicestxt_test.c | 203 +++++++++--------- test/libc/sock/connect_test.c | 85 ++++++++ test/libc/sock/socket_test.c | 24 +-- test/libc/stdio/fwrite_test.c | 3 +- test/libc/stdio/memory_test.c | 6 +- test/libc/thread/pthread_kill_test.c | 3 +- third_party/dlmalloc/dlmalloc.c | 11 +- third_party/dlmalloc/dlmalloc.h | 2 +- ...dlmalloc_abort.greg.c => dlmalloc_abort.c} | 14 +- third_party/dlmalloc/locks.inc | 67 +++--- third_party/nsync/futex.c | 45 +--- third_party/nsync/mu_semaphore.c | 2 +- third_party/nsync/mu_semaphore_gcd.c | 4 +- third_party/nsync/panic.c | 11 +- 43 files changed, 528 insertions(+), 425 deletions(-) delete mode 100644 libc/nt/MsWSock/AcceptEx.S delete mode 100644 libc/nt/MsWSock/DisconnectEx.S delete mode 100644 libc/nt/MsWSock/GetAcceptExSockaddrs.S delete mode 100644 libc/nt/MsWSock/TransmitFile.S delete mode 100644 libc/nt/MsWSock/WSARecvEx.S create mode 100644 libc/sock/wsaid.c create mode 100644 libc/sock/wsaid.internal.h create mode 100644 test/libc/sock/connect_test.c rename third_party/dlmalloc/{dlmalloc_abort.greg.c => dlmalloc_abort.c} (87%) diff --git a/Makefile b/Makefile index 8f484a74f31..66c2dbb77be 100644 --- a/Makefile +++ b/Makefile @@ -322,7 +322,6 @@ COSMOPOLITAN_OBJECTS = \ LIBC_SOCK \ LIBC_NT_WS2_32 \ LIBC_NT_IPHLPAPI \ - LIBC_NT_MSWSOCK \ LIBC_X \ THIRD_PARTY_GETOPT \ LIBC_LOG \ diff --git a/libc/calls/fdatasync-nt.c b/libc/calls/fdatasync-nt.c index 196640f1534..6c83d398e1c 100644 --- a/libc/calls/fdatasync-nt.c +++ b/libc/calls/fdatasync-nt.c @@ -18,15 +18,24 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" #include "libc/calls/syscall_support-nt.internal.h" +#include "libc/nt/enum/fileflagandattributes.h" #include "libc/nt/enum/filetype.h" #include "libc/nt/files.h" +#include "libc/nt/struct/byhandlefileinformation.h" #include "libc/sysv/errfuns.h" #ifdef __x86_64__ textwindows int sys_fdatasync_nt(int fd, bool fake) { + struct NtByHandleFileInformation wst; if (!__isfdopen(fd)) return ebadf(); if (!__isfdkind(fd, kFdFile)) return einval(); if (GetFileType(g_fds.p[fd].handle) != kNtFileTypeDisk) return einval(); + if (!GetFileInformationByHandle(g_fds.p[fd].handle, &wst)) return __winerr(); + if (wst.dwFileAttributes & kNtFileAttributeDirectory) { + // Flushing a directory handle is possible, but it needs + // kNtGenericWrite access, and MSDN doesn't document it. + return 0; + } if (_check_cancel() == -1) return -1; if (_check_signal(false) == -1) return -1; if (fake) return 0; diff --git a/libc/calls/readwrite-nt.c b/libc/calls/readwrite-nt.c index adb8b7c11f5..bd6bc46bf48 100644 --- a/libc/calls/readwrite-nt.c +++ b/libc/calls/readwrite-nt.c @@ -33,6 +33,7 @@ #include "libc/nt/synchronization.h" #include "libc/nt/thread.h" #include "libc/stdio/sysparam.h" +#include "libc/str/str.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/errfuns.h" #include "libc/thread/posixthread.internal.h" @@ -67,10 +68,10 @@ sys_readwrite_nt(int fd, void *data, size_t size, ssize_t offset, bool32 ok; uint64_t m; uint32_t exchanged; + int olderror = errno; bool eagained = false; bool eintered = false; bool canceled = false; - bool olderror = errno; struct PosixThread *pt; struct Fd *f = g_fds.p + fd; @@ -124,7 +125,7 @@ sys_readwrite_nt(int fd, void *data, size_t size, ssize_t offset, // can only be returned by a single system call in a thread's life // another thing we do is check if any pending signals exist, then // running as many of them as possible before entering a wait call - struct NtOverlapped overlap = {.hEvent = CreateEvent(0, 0, 0, 0), + struct NtOverlapped overlap = {.hEvent = CreateEvent(0, 1, 0, 0), .Pointer = offset}; struct ReadwriteResources rwc = {handle, &overlap}; pthread_cleanup_push(UnwindReadwrite, &rwc); diff --git a/libc/calls/releasefd.c b/libc/calls/releasefd.c index 2efcf13c727..ea11080c39d 100644 --- a/libc/calls/releasefd.c +++ b/libc/calls/releasefd.c @@ -25,6 +25,7 @@ void __releasefd(int fd) { int f1, f2; if (!(0 <= fd && fd < g_fds.n)) return; + g_fds.p[fd].kind = kFdEmpty; bzero(g_fds.p + fd, sizeof(*g_fds.p)); f1 = atomic_load_explicit(&g_fds.f, memory_order_relaxed); do { diff --git a/libc/calls/struct/fd.internal.h b/libc/calls/struct/fd.internal.h index c464730be85..2e7405c55ab 100644 --- a/libc/calls/struct/fd.internal.h +++ b/libc/calls/struct/fd.internal.h @@ -14,15 +14,17 @@ COSMOPOLITAN_C_START_ struct Fd { char kind; + bool isbound; unsigned flags; unsigned mode; - int64_t handle; - int64_t pointer; + long handle; + long pointer; int family; int type; int protocol; - uint32_t rcvtimeo; - uint32_t sndtimeo; + unsigned rcvtimeo; /* millis; 0 means wait forever */ + unsigned sndtimeo; /* millis; 0 means wait forever */ + void *connect_op; }; struct Fds { diff --git a/libc/intrin/pthread_yield.c b/libc/intrin/pthread_yield.c index 8090c43ce91..20462fe79cc 100644 --- a/libc/intrin/pthread_yield.c +++ b/libc/intrin/pthread_yield.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/dce.h" +#include "libc/nexgen32e/yield.h" #include "libc/runtime/runtime.h" #include "libc/runtime/syslib.internal.h" #include "libc/thread/thread.h" @@ -30,6 +31,8 @@ int pthread_yield(void) { if (IsXnuSilicon()) { __syslib->__pthread_yield_np(); + } else if (IsOpenbsd()) { + spin_yield(); // sched_yield() is punishingly slow on OpenBSD } else { sched_yield(); } diff --git a/libc/intrin/ulock.c b/libc/intrin/ulock.c index c6d7437b58e..516ad5c3329 100644 --- a/libc/intrin/ulock.c +++ b/libc/intrin/ulock.c @@ -31,7 +31,7 @@ int sys_ulock_wait(uint32_t operation, void *addr, uint64_t value, uint32_t timeout_micros) asm("sys_futex_cp"); -// returns -1 w/ errno +// returns number of other waiters, or -1 w/ errno int ulock_wait(uint32_t operation, void *addr, uint64_t value, uint32_t timeout_micros) { int rc; diff --git a/libc/nt/MsWSock/AcceptEx.S b/libc/nt/MsWSock/AcceptEx.S deleted file mode 100644 index 1a8682c81e6..00000000000 --- a/libc/nt/MsWSock/AcceptEx.S +++ /dev/null @@ -1,18 +0,0 @@ -#include "libc/nt/codegen.h" -.imp MsWSock,__imp_AcceptEx,AcceptEx - - .text.windows - .ftrace1 -AcceptEx: - .ftrace2 -#ifdef __x86_64__ - push %rbp - mov %rsp,%rbp - mov __imp_AcceptEx(%rip),%rax - jmp __sysv2nt8 -#elif defined(__aarch64__) - mov x0,#0 - ret -#endif - .endfn AcceptEx,globl - .previous diff --git a/libc/nt/MsWSock/DisconnectEx.S b/libc/nt/MsWSock/DisconnectEx.S deleted file mode 100644 index 1f01c7f5094..00000000000 --- a/libc/nt/MsWSock/DisconnectEx.S +++ /dev/null @@ -1,18 +0,0 @@ -#include "libc/nt/codegen.h" -.imp MsWSock,__imp_DisconnectEx,DisconnectEx - - .text.windows - .ftrace1 -DisconnectEx: - .ftrace2 -#ifdef __x86_64__ - push %rbp - mov %rsp,%rbp - mov __imp_DisconnectEx(%rip),%rax - jmp __sysv2nt -#elif defined(__aarch64__) - mov x0,#0 - ret -#endif - .endfn DisconnectEx,globl - .previous diff --git a/libc/nt/MsWSock/GetAcceptExSockaddrs.S b/libc/nt/MsWSock/GetAcceptExSockaddrs.S deleted file mode 100644 index 573531c3aa0..00000000000 --- a/libc/nt/MsWSock/GetAcceptExSockaddrs.S +++ /dev/null @@ -1,18 +0,0 @@ -#include "libc/nt/codegen.h" -.imp MsWSock,__imp_GetAcceptExSockaddrs,GetAcceptExSockaddrs - - .text.windows - .ftrace1 -GetAcceptExSockaddrs: - .ftrace2 -#ifdef __x86_64__ - push %rbp - mov %rsp,%rbp - mov __imp_GetAcceptExSockaddrs(%rip),%rax - jmp __sysv2nt8 -#elif defined(__aarch64__) - mov x0,#0 - ret -#endif - .endfn GetAcceptExSockaddrs,globl - .previous diff --git a/libc/nt/MsWSock/TransmitFile.S b/libc/nt/MsWSock/TransmitFile.S deleted file mode 100644 index e80254f01f9..00000000000 --- a/libc/nt/MsWSock/TransmitFile.S +++ /dev/null @@ -1,18 +0,0 @@ -#include "libc/nt/codegen.h" -.imp MsWSock,__imp_TransmitFile,TransmitFile - - .text.windows - .ftrace1 -TransmitFile: - .ftrace2 -#ifdef __x86_64__ - push %rbp - mov %rsp,%rbp - mov __imp_TransmitFile(%rip),%rax - jmp __sysv2nt8 -#elif defined(__aarch64__) - mov x0,#0 - ret -#endif - .endfn TransmitFile,globl - .previous diff --git a/libc/nt/MsWSock/WSARecvEx.S b/libc/nt/MsWSock/WSARecvEx.S deleted file mode 100644 index a790dcfc5b0..00000000000 --- a/libc/nt/MsWSock/WSARecvEx.S +++ /dev/null @@ -1,18 +0,0 @@ -#include "libc/nt/codegen.h" -.imp MsWSock,__imp_WSARecvEx,WSARecvEx - - .text.windows - .ftrace1 -WSARecvEx: - .ftrace2 -#ifdef __x86_64__ - push %rbp - mov %rsp,%rbp - mov __imp_WSARecvEx(%rip),%rax - jmp __sysv2nt -#elif defined(__aarch64__) - mov x0,#0 - ret -#endif - .endfn WSARecvEx,globl - .previous diff --git a/libc/nt/iocp.h b/libc/nt/iocp.h index 3781bccbcd7..ec261d6c539 100644 --- a/libc/nt/iocp.h +++ b/libc/nt/iocp.h @@ -39,13 +39,13 @@ typedef void (*NtOverlappedCompletionRoutine)( int64_t CreateIoCompletionPort(int64_t FileHandleOrNeg1, int64_t opt_ExistingCompletionPortOrZero, - void *StatePointer, + uint64_t CompletionKey, uint32_t NumberOfConcurrentThreads); bool32 GetQueuedCompletionStatus(int64_t CompletionPort, uint32_t *lpNumberOfBytesTransferred, - void *StatePointerPointer, - struct NtOverlapped **lpOverlapped, + uint64_t *out_lpCompletionKey, + struct NtOverlapped **out_lpOverlapped, uint32_t dwMilliseconds); bool32 GetQueuedCompletionStatusEx( @@ -56,7 +56,7 @@ bool32 GetQueuedCompletionStatusEx( bool32 PostQueuedCompletionStatus(int64_t CompletionPort, uint32_t dwNumberOfBytesTransferred, - uint32_t *dwCompletionKey, + uint64_t dwCompletionKey, struct NtOverlapped *opt_lpOverlapped); bool32 SetFileCompletionNotificationModes(int64_t FileHandle, diff --git a/libc/nt/master.sh b/libc/nt/master.sh index c59c73bf590..45677c1d224 100755 --- a/libc/nt/master.sh +++ b/libc/nt/master.sh @@ -483,15 +483,6 @@ imp 'GetSaveFileName' GetSaveFileNameW comdlg32 1 imp 'PrintDlg' PrintDlgW comdlg32 1 imp 'ReplaceText' ReplaceTextW comdlg32 1 -# MSWSOCK.DLL -# -# Name Actual DLL Arity -imp 'AcceptEx' AcceptEx MsWSock 8 -imp 'DisconnectEx' DisconnectEx MsWSock 4 -imp 'GetAcceptExSockaddrs' GetAcceptExSockaddrs MsWSock 8 -imp 'TransmitFile' TransmitFile MsWSock 7 -imp 'WSARecvEx' WSARecvEx MsWSock 4 - # WS2_32.DLL # # Name Actual DLL Arity @@ -516,6 +507,7 @@ imp '' setsockopt ws2_32 5 imp '' shutdown ws2_32 2 imp '' socket ws2_32 3 imp '' socket ws2_32 3 +imp '' socket ws2_32 3 imp 'FreeAddrInfo' FreeAddrInfoW ws2_32 1 imp 'FreeAddrInfoEx' FreeAddrInfoExW ws2_32 1 imp 'GetAddrInfo' GetAddrInfoW ws2_32 4 diff --git a/libc/nt/nt.mk b/libc/nt/nt.mk index 2e0c318b7eb..f3879b4449b 100644 --- a/libc/nt/nt.mk +++ b/libc/nt/nt.mk @@ -221,27 +221,6 @@ $(LIBC_NT_IPHLPAPI_A).pkg: \ #─────────────────────────────────────────────────────────────────────────────── -LIBC_NT_ARTIFACTS += LIBC_NT_MSWSOCK_A -LIBC_NT_MSWSOCK = $(LIBC_NT_MSWSOCK_A_DEPS) $(LIBC_NT_MSWSOCK_A) -LIBC_NT_MSWSOCK_A = o/$(MODE)/libc/nt/MsWSock.a -LIBC_NT_MSWSOCK_A_SRCS := $(wildcard libc/nt/MsWSock/*.S) -LIBC_NT_MSWSOCK_A_OBJS = $(LIBC_NT_MSWSOCK_A_SRCS:%.S=o/$(MODE)/%.o) -LIBC_NT_MSWSOCK_A_CHECKS = $(LIBC_NT_MSWSOCK_A).pkg -LIBC_NT_MSWSOCK_A_DIRECTDEPS = LIBC_NT_KERNEL32 -LIBC_NT_MSWSOCK_A_DEPS := \ - $(call uniq,$(foreach x,$(LIBC_NT_MSWSOCK_A_DIRECTDEPS),$($(x)))) - -$(LIBC_NT_MSWSOCK_A): \ - libc/nt/MsWSock/ \ - $(LIBC_NT_MSWSOCK_A).pkg \ - $(LIBC_NT_MSWSOCK_A_OBJS) - -$(LIBC_NT_MSWSOCK_A).pkg: \ - $(LIBC_NT_MSWSOCK_A_OBJS) \ - $(foreach x,$(LIBC_NT_MSWSOCK_A_DIRECTDEPS),$($(x)_A).pkg) - -#─────────────────────────────────────────────────────────────────────────────── - LIBC_NT_ARTIFACTS += LIBC_NT_IPHLPAPI_A LIBC_NT_IPHLPAPI = $(LIBC_NT_IPHLPAPI_A_DEPS) $(LIBC_NT_IPHLPAPI_A) LIBC_NT_IPHLPAPI_A = o/$(MODE)/libc/nt/iphlpapi.a diff --git a/libc/nt/struct/overlappedentry.h b/libc/nt/struct/overlappedentry.h index 5fd5385e1c3..e567fa964fb 100644 --- a/libc/nt/struct/overlappedentry.h +++ b/libc/nt/struct/overlappedentry.h @@ -5,7 +5,7 @@ COSMOPOLITAN_C_START_ struct NtOverlappedEntry { - uint32_t *lpCompletionKey; + uint64_t lpCompletionKey; struct NtOverlapped *lpOverlapped; uint32_t *Internal; uint32_t dwNumberOfBytesTransferred; diff --git a/libc/nt/winsock.h b/libc/nt/winsock.h index 3ef42846bcb..b1a3adff15d 100644 --- a/libc/nt/winsock.h +++ b/libc/nt/winsock.h @@ -297,6 +297,7 @@ int WSACleanup(void); int WSAGetLastError(void) nosideeffect; void WSASetLastError(int); +int64_t __sys_socket_nt(int, int, int); int __sys_bind_nt(uint64_t, const void *, int); int __sys_closesocket_nt(uint64_t); int __sys_getpeername_nt(uint64_t, void *, uint32_t *); @@ -342,12 +343,6 @@ int64_t WSAAccept(uint64_t s, struct sockaddr *out_addr, const NtConditionProc opt_lpfnCondition, const uint32_t *opt_dwCallbackData) paramsnonnull((2)) __wur; -bool32 AcceptEx(int64_t sListenSocket, int64_t sAcceptSocket, - void *out_lpOutputBuffer /*[recvlen+local+remoteaddrlen]*/, - uint32_t dwReceiveDataLength, uint32_t dwLocalAddressLength, - uint32_t dwRemoteAddressLength, uint32_t *out_lpdwBytesReceived, - struct NtOverlapped *inout_lpOverlapped); - int WSASend(uint64_t s, const struct NtIovec *lpBuffers, uint32_t dwBufferCount, uint32_t *opt_out_lpNumberOfBytesSent, uint32_t dwFlags, struct NtOverlapped *opt_inout_lpOverlapped, @@ -494,19 +489,6 @@ int /* success==0 */ WSAGetServiceClassNameByClassId( const struct NtGuid *lpServiceClassId, char16_t *out_lpszServiceClassName, uint32_t *inout_lpdwBufferLength) paramsnonnull(); -bool32 TransmitFile(int64_t hSocket, int64_t hFile, - uint32_t opt_nNumberOfBytesToWrite, - uint32_t opt_nNumberOfBytesPerSend, - struct NtOverlapped *opt_inout_lpOverlapped, - const struct NtTransmitFileBuffers *opt_lpTransmitBuffers, - uint32_t dwReserved); - -bool32 AcceptEx(int64_t sListenSocket, int64_t sAcceptSocket, - void *out_lpOutputBuffer /*[recvlen+local+remoteaddrlen]*/, - uint32_t dwReceiveDataLength, uint32_t dwLocalAddressLength, - uint32_t dwRemoteAddressLength, uint32_t *out_lpdwBytesReceived, - struct NtOverlapped *inout_lpOverlapped); - void GetAcceptExSockaddrs( const void *lpOutputBuffer /*[recvsize+addrsize+addrlen]*/, uint32_t dwReceiveDataLength, uint32_t dwLocalAddressLength, diff --git a/libc/runtime/clone.c b/libc/runtime/clone.c index c40d149d39b..ca8884a9775 100644 --- a/libc/runtime/clone.c +++ b/libc/runtime/clone.c @@ -30,7 +30,6 @@ #include "libc/intrin/asan.internal.h" #include "libc/intrin/atomic.h" #include "libc/intrin/describeflags.internal.h" -#include "libc/intrin/strace.internal.h" #include "libc/intrin/ulock.h" #include "libc/intrin/weaken.h" #include "libc/limits.h" @@ -667,7 +666,6 @@ errno_t clone(void *func, void *stk, size_t stksz, int flags, void *arg, CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID)) != (CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_SYSVSEM)) { - STRACE("cosmo clone() is picky about flags, see clone.c"); rc = EINVAL; } else if (IsXnu()) { #ifdef __x86_64__ @@ -695,8 +693,5 @@ errno_t clone(void *func, void *stk, size_t stksz, int flags, void *arg, rc = EAGAIN; } - STRACE("clone(%t, %p, %'zu, %#x, %p, %p, %p, %p) → %s", func, stk, stksz, - flags, arg, ptid, tls, ctid, DescribeErrno(rc)); - return rc; } diff --git a/libc/sock/accept-nt.c b/libc/sock/accept-nt.c index d8cf13796fe..4039a972f5e 100644 --- a/libc/sock/accept-nt.c +++ b/libc/sock/accept-nt.c @@ -17,29 +17,26 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" +#include "libc/atomic.h" #include "libc/calls/internal.h" -#include "libc/calls/state.internal.h" #include "libc/calls/struct/fd.internal.h" #include "libc/calls/struct/sigset.internal.h" -#include "libc/errno.h" -#include "libc/intrin/strace.internal.h" -#include "libc/macros.internal.h" -#include "libc/mem/mem.h" +#include "libc/cosmo.h" +#include "libc/nt/enum/wsaid.h" #include "libc/nt/thunk/msabi.h" #include "libc/nt/winsock.h" #include "libc/sock/internal.h" #include "libc/sock/struct/sockaddr.h" -#include "libc/sock/syscall_fd.internal.h" +#include "libc/sock/wsaid.internal.h" #include "libc/str/str.h" -#include "libc/sysv/consts/af.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/sock.h" #include "libc/sysv/consts/sol.h" #include "libc/thread/thread.h" #ifdef __x86_64__ -__msabi extern typeof(__sys_closesocket_nt) *const __imp_closesocket; __msabi extern typeof(__sys_setsockopt_nt) *const __imp_setsockopt; +__msabi extern typeof(__sys_closesocket_nt) *const __imp_closesocket; union AcceptExAddr { struct sockaddr_storage addr; @@ -60,6 +57,21 @@ struct AcceptArgs { struct AcceptExBuffer *buffer; }; +static struct { + atomic_uint once; + bool32 (*__msabi lpAcceptEx)( + int64_t sListenSocket, int64_t sAcceptSocket, + void *out_lpOutputBuffer /*[recvlen+local+remoteaddrlen]*/, + uint32_t dwReceiveDataLength, uint32_t dwLocalAddressLength, + uint32_t dwRemoteAddressLength, uint32_t *out_lpdwBytesReceived, + struct NtOverlapped *inout_lpOverlapped); +} g_acceptex; + +static void acceptex_init(void) { + static struct NtGuid AcceptExGuid = WSAID_ACCEPTEX; + g_acceptex.lpAcceptEx = __get_wsaid(&AcceptExGuid); +} + static void sys_accept_nt_unwind(void *arg) { struct AcceptResources *resources = arg; if (resources->handle != -1) { @@ -70,9 +82,10 @@ static void sys_accept_nt_unwind(void *arg) { static int sys_accept_nt_start(int64_t handle, struct NtOverlapped *overlap, uint32_t *flags, void *arg) { struct AcceptArgs *args = arg; - if (AcceptEx(args->listensock, handle, args->buffer, 0, - sizeof(args->buffer->local), sizeof(args->buffer->remote), 0, - overlap)) { + cosmo_once(&g_acceptex.once, acceptex_init); + if (g_acceptex.lpAcceptEx(args->listensock, handle, args->buffer, 0, + sizeof(args->buffer->local), + sizeof(args->buffer->remote), 0, overlap)) { // inherit properties of listening socket unassert(!__imp_setsockopt(args->listensock, SOL_SOCKET, kNtSoUpdateAcceptContext, &handle, diff --git a/libc/sock/bind-nt.c b/libc/sock/bind-nt.c index a1d0b927748..44676d0ec53 100644 --- a/libc/sock/bind-nt.c +++ b/libc/sock/bind-nt.c @@ -16,20 +16,16 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" -#include "libc/calls/struct/fd.internal.h" #include "libc/nt/thunk/msabi.h" -#include "libc/nt/winsock.h" #include "libc/sock/internal.h" #include "libc/sock/syscall_fd.internal.h" #ifdef __x86_64__ __msabi extern typeof(__sys_bind_nt) *const __imp_bind; -textwindows int sys_bind_nt(struct Fd *fd, const void *addr, - uint32_t addrsize) { - unassert(fd->kind == kFdSocket); - if (__imp_bind(fd->handle, addr, addrsize) != -1) { +textwindows int sys_bind_nt(struct Fd *f, const void *addr, uint32_t addrsize) { + if (__imp_bind(f->handle, addr, addrsize) != -1) { + f->isbound = true; return 0; } else { return __winsockerr(); diff --git a/libc/sock/closesocket-nt.c b/libc/sock/closesocket-nt.c index 074d25282f8..d30240cccec 100644 --- a/libc/sock/closesocket-nt.c +++ b/libc/sock/closesocket-nt.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/intrin/weaken.h" #include "libc/mem/mem.h" #include "libc/nt/thunk/msabi.h" #include "libc/nt/winsock.h" @@ -30,9 +31,11 @@ __msabi extern typeof(__sys_closesocket_nt) *const __imp_closesocket; * * This function should only be called by close(). */ -textwindows int sys_closesocket_nt(struct Fd *fd) { - int rc = __imp_closesocket(fd->handle); - if (rc != -1) { +textwindows int sys_closesocket_nt(struct Fd *f) { + if (_weaken(sys_connect_nt_cleanup)) { + _weaken(sys_connect_nt_cleanup)(f, true); + } + if (!__imp_closesocket(f->handle)) { return 0; } else { return __winsockerr(); diff --git a/libc/sock/connect-nt.c b/libc/sock/connect-nt.c index 93ba8a45d7c..e1b48fb963d 100644 --- a/libc/sock/connect-nt.c +++ b/libc/sock/connect-nt.c @@ -17,49 +17,133 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" +#include "libc/atomic.h" +#include "libc/calls/struct/fd.internal.h" +#include "libc/calls/struct/sigset.internal.h" +#include "libc/cosmo.h" #include "libc/errno.h" -#include "libc/intrin/bsr.h" +#include "libc/mem/mem.h" +#include "libc/nt/enum/wsaid.h" +#include "libc/nt/errors.h" +#include "libc/nt/struct/guid.h" +#include "libc/nt/struct/overlapped.h" +#include "libc/nt/thread.h" +#include "libc/nt/thunk/msabi.h" #include "libc/nt/winsock.h" #include "libc/sock/internal.h" +#include "libc/sock/struct/sockaddr.h" #include "libc/sock/syscall_fd.internal.h" +#include "libc/sock/wsaid.internal.h" +#include "libc/sysv/consts/o.h" #include "libc/sysv/errfuns.h" + #ifdef __x86_64__ #include "libc/sock/yoink.inc" -static textwindows int64_t __connect_block(int64_t fh, unsigned eventbit, - int64_t rc, uint32_t timeout) { - int64_t eh; - struct NtWsaNetworkEvents ev; - if (rc != -1) return rc; - if (WSAGetLastError() != EWOULDBLOCK) return __winsockerr(); - eh = WSACreateEvent(); - bzero(&ev, sizeof(ev)); - /* The proper way to reset the state of an event object used with the - WSAEventSelect function is to pass the handle of the event object - to the WSAEnumNetworkEvents function in the hEventObject parameter. - This will reset the event object and adjust the status of active FD - events on the socket in an atomic fashion. -- MSDN */ - if (WSAEventSelect(fh, eh, 1u << eventbit) != -1 && - WSAEnumNetworkEvents(fh, eh, &ev) != -1) { - if (!ev.iErrorCode[eventbit]) { - rc = 0; - } else { - errno = ev.iErrorCode[eventbit]; +struct ConnectArgs { + const void *addr; + uint32_t addrsize; +}; + +static struct { + atomic_uint once; + bool32 (*__msabi lpConnectEx)(int64_t hSocket, const struct sockaddr *name, + int namelen, const void *opt_lpSendBuffer, + uint32_t dwSendDataLength, + uint32_t *opt_out_lpdwBytesSent, + struct NtOverlapped *lpOverlapped); +} g_connectex; + +static void connectex_init(void) { + static struct NtGuid ConnectExGuid = WSAID_CONNECTEX; + g_connectex.lpConnectEx = __get_wsaid(&ConnectExGuid); +} + +void sys_connect_nt_cleanup(struct Fd *f, bool cancel) { + struct NtOverlapped *overlap; + if ((overlap = f->connect_op)) { + uint32_t got, flags; + if (cancel) CancelIoEx(f->handle, overlap); + if (WSAGetOverlappedResult(f->handle, overlap, &got, cancel, &flags) || + WSAGetLastError() != kNtErrorIoIncomplete) { + WSACloseEvent(overlap->hEvent); + free(overlap); + f->connect_op = 0; } + } +} + +static int sys_connect_nt_start(int64_t hSocket, + struct NtOverlapped *lpOverlapped, + uint32_t *flags, void *arg) { + struct ConnectArgs *args = arg; + if (g_connectex.lpConnectEx(hSocket, args->addr, args->addrsize, 0, 0, 0, + lpOverlapped)) { + return 0; } else { - __winsockerr(); + return -1; + } +} + +static textwindows int sys_connect_nt_impl(struct Fd *f, const void *addr, + uint32_t addrsize, sigset_t mask) { + + // get connect function from winsock api + cosmo_once(&g_connectex.once, connectex_init); + + // fail if previous connect() is still in progress + if (f->connect_op) return ealready(); + + // ConnectEx() requires bind() be called beforehand + if (!f->isbound) { + struct sockaddr_storage ss = {0}; + ss.ss_family = ((struct sockaddr *)addr)->sa_family; + if (sys_bind_nt(f, &ss, sizeof(ss)) == -1) return -1; + } + + // perform normal connect + if (!(f->flags & O_NONBLOCK)) { + ssize_t rc = __winsock_block(f->handle, 0, false, f->sndtimeo, mask, + sys_connect_nt_start, + &(struct ConnectArgs){addr, addrsize}); + if (rc == -1 && errno == EAGAIN) { + // return ETIMEDOUT if SO_SNDTIMEO elapsed + // note that Linux will return EINPROGRESS + errno = etimedout(); + } + return rc; + } + + // perform nonblocking connect(), i.e. + // 1. connect(O_NONBLOCK) → EINPROGRESS + // 2. poll(POLLOUT) + bool32 ok; + struct NtOverlapped *overlap = calloc(1, sizeof(struct NtOverlapped)); + if (!overlap) return -1; + overlap->hEvent = WSACreateEvent(); + ok = g_connectex.lpConnectEx(f->handle, addr, addrsize, 0, 0, 0, overlap); + if (ok) { + uint32_t dwBytes, dwFlags; + ok = WSAGetOverlappedResult(f->handle, overlap, &dwBytes, false, &dwFlags); + WSACloseEvent(overlap->hEvent); + free(overlap); + return ok ? 0 : __winsockerr(); + } else if (WSAGetLastError() == kNtErrorIoPending) { + f->connect_op = overlap; + return einprogress(); + } else { + WSACloseEvent(overlap->hEvent); + free(overlap); + return __winsockerr(); } - WSACloseEvent(eh); - return rc; } -textwindows int sys_connect_nt(struct Fd *fd, const void *addr, +textwindows int sys_connect_nt(struct Fd *f, const void *addr, uint32_t addrsize) { - npassert(fd->kind == kFdSocket); - return __connect_block( - fd->handle, _bsr(kNtFdConnect), - WSAConnect(fd->handle, addr, addrsize, NULL, NULL, NULL, NULL), - fd->rcvtimeo); + sigset_t mask = __sig_block(); + int rc = sys_connect_nt_impl(f, addr, addrsize, mask); + __sig_unblock(mask); + return rc; } #endif /* __x86_64__ */ diff --git a/libc/sock/sendfile.c b/libc/sock/sendfile.c index d620995fef3..f6328e56bc2 100644 --- a/libc/sock/sendfile.c +++ b/libc/sock/sendfile.c @@ -17,27 +17,48 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" +#include "libc/atomic.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" -#include "libc/calls/struct/fd.internal.h" #include "libc/calls/struct/sigset.internal.h" #include "libc/calls/syscall-sysv.internal.h" +#include "libc/cosmo.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/describeflags.internal.h" #include "libc/intrin/strace.internal.h" +#include "libc/nt/enum/wsaid.h" #include "libc/nt/errors.h" +#include "libc/nt/events.h" #include "libc/nt/files.h" #include "libc/nt/struct/byhandlefileinformation.h" +#include "libc/nt/struct/guid.h" #include "libc/nt/struct/overlapped.h" +#include "libc/nt/thunk/msabi.h" #include "libc/nt/winsock.h" #include "libc/sock/internal.h" #include "libc/sock/sendfile.internal.h" -#include "libc/sock/sock.h" +#include "libc/sock/wsaid.internal.h" #include "libc/stdio/sysparam.h" #include "libc/sysv/errfuns.h" +static struct { + atomic_uint once; + errno_t err; + bool32 (*__msabi lpTransmitFile)( + int64_t hSocket, int64_t hFile, uint32_t opt_nNumberOfBytesToWrite, + uint32_t opt_nNumberOfBytesPerSend, + struct NtOverlapped *opt_inout_lpOverlapped, + const struct NtTransmitFileBuffers *opt_lpTransmitBuffers, + uint32_t dwReserved); +} g_transmitfile; + +static void transmitfile_init(void) { + static struct NtGuid TransmitfileGuid = WSAID_TRANSMITFILE; + g_transmitfile.lpTransmitFile = __get_wsaid(&TransmitfileGuid); +} + static dontinline textwindows ssize_t sys_sendfile_nt( int outfd, int infd, int64_t *opt_in_out_inoffset, uint32_t uptobytes) { ssize_t rc; @@ -64,7 +85,8 @@ static dontinline textwindows ssize_t sys_sendfile_nt( } BLOCK_SIGNALS; struct NtOverlapped ov = {.hEvent = WSACreateEvent(), .Pointer = offset}; - if (TransmitFile(oh, ih, uptobytes, 0, &ov, 0, 0) || + cosmo_once(&g_transmitfile.once, transmitfile_init); + if (g_transmitfile.lpTransmitFile(oh, ih, uptobytes, 0, &ov, 0, 0) || WSAGetLastError() == kNtErrorIoPending || WSAGetLastError() == WSAEINPROGRESS) { if (WSAGetOverlappedResult(oh, &ov, &uptobytes, true, &flags)) { diff --git a/libc/sock/sock.mk b/libc/sock/sock.mk index f95fbfce421..4f3777f4b4d 100644 --- a/libc/sock/sock.mk +++ b/libc/sock/sock.mk @@ -34,7 +34,6 @@ LIBC_SOCK_A_DIRECTDEPS = \ LIBC_NT_IPHLPAPI \ LIBC_NT_IPHLPAPI \ LIBC_NT_KERNEL32 \ - LIBC_NT_MSWSOCK \ LIBC_NT_NTDLL \ LIBC_NT_WS2_32 \ LIBC_RUNTIME \ diff --git a/libc/sock/syscall_fd.internal.h b/libc/sock/syscall_fd.internal.h index 2762d0457ee..4c40867e272 100644 --- a/libc/sock/syscall_fd.internal.h +++ b/libc/sock/syscall_fd.internal.h @@ -7,6 +7,7 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ +void sys_connect_nt_cleanup(struct Fd *, bool); int sys_accept_nt(struct Fd *, struct sockaddr_storage *, int); int sys_bind_nt(struct Fd *, const void *, uint32_t); int sys_closesocket_nt(struct Fd *); diff --git a/libc/sock/winsockblock.c b/libc/sock/winsockblock.c index 1c0e0f4b296..04f36e9f901 100644 --- a/libc/sock/winsockblock.c +++ b/libc/sock/winsockblock.c @@ -69,10 +69,10 @@ __winsock_block(int64_t handle, uint32_t flags, bool nonblock, uint64_t m; uint32_t status; uint32_t exchanged; + int olderror = errno; bool eagained = false; bool eintered = false; bool canceled = false; - bool olderror = errno; struct PosixThread *pt; struct NtOverlapped overlap = {.hEvent = WSACreateEvent()}; struct WinsockBlockResources wbr = {handle, &overlap}; diff --git a/libc/sock/wsaid.c b/libc/sock/wsaid.c new file mode 100644 index 00000000000..98179514e77 --- /dev/null +++ b/libc/sock/wsaid.c @@ -0,0 +1,46 @@ +/*-*- 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 2023 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ 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/nt/enum/sio.h" +#include "libc/nt/struct/guid.h" +#include "libc/nt/thunk/msabi.h" +#include "libc/nt/winsock.h" +#include "libc/sock/sock.h" +#include "libc/sock/wsaid.internal.h" +#include "libc/sysv/consts/af.h" +#include "libc/sysv/consts/ipproto.h" +#include "libc/sysv/consts/sock.h" + +__msabi extern typeof(__sys_closesocket_nt) *const __imp_closesocket; + +// returns address of winsock function where msdn says we must do this +// this should be called once, since WSAIoctl has ~2 microsec overhead +void *__get_wsaid(const struct NtGuid *lpFunctionGuid) { + int r; + int64_t h; + void *lpFunc; + uint32_t dwBytes; + h = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, + kNtWsaFlagOverlapped); + r = WSAIoctl(h, kNtSioGetExtensionFunctionPointer, lpFunctionGuid, + sizeof(struct NtGuid), &lpFunc, sizeof(lpFunc), &dwBytes, 0, 0); + unassert(r != -1); + __imp_closesocket(h); + return lpFunc; +} diff --git a/libc/sock/wsaid.internal.h b/libc/sock/wsaid.internal.h new file mode 100644 index 00000000000..6c41710de12 --- /dev/null +++ b/libc/sock/wsaid.internal.h @@ -0,0 +1,11 @@ +#ifndef COSMOPOLITAN_LIBC_SOCK_WSAID_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_SOCK_WSAID_INTERNAL_H_ +#include "libc/nt/struct/guid.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +void *__get_wsaid(const struct NtGuid *); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_SOCK_WSAID_INTERNAL_H_ */ diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index f1943816346..c5cd41e99dc 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -18,7 +18,6 @@ ╚────────────────────────────────────────────────────────────────'>/dev/null #*/ dir=libc/sysv/consts . libc/sysv/gen.sh -#include "libc/sysv/consts/ss.h" # The Fifth Bell System, Community Edition # » catalogue of carnage diff --git a/test/libc/dns/servicestxt_test.c b/test/libc/dns/servicestxt_test.c index c03422295f0..0eb753be5bb 100644 --- a/test/libc/dns/servicestxt_test.c +++ b/test/libc/dns/servicestxt_test.c @@ -51,77 +51,72 @@ ssh 22/tcp # SSH Remote Login Protocol"; ASSERT_NE(-1, close(fd)); } -/* TEST(LookupServicesByPort, GetNameWhenPortCorrect) { */ -/* char name[8]; /\* service names are of length 3 *\/ */ -/* char eitherproto[8]; /\* protocol names are of length 3 *\/ */ -/* char proto1[] = "tcp"; */ -/* char proto2[] = "udp"; */ -/* char* localproto; */ -/* strcpy(eitherproto, ""); */ -/* strcpy(name, ""); */ - -/* localproto = eitherproto; */ -/* ASSERT_EQ(-1, /\* non existent port *\/ */ -/* LookupServicesByPort(965, localproto, sizeof(eitherproto), name, - */ -/* sizeof(name), "services")); */ -/* ASSERT_EQ('\0', localproto[0]); */ - -/* localproto = eitherproto; */ -/* ASSERT_EQ(-1, /\* port in network byte order *\/ */ -/* LookupServicesByPort(htons(22), localproto, sizeof(eitherproto), - */ -/* name, sizeof(name), "services")); */ -/* ASSERT_EQ('\0', localproto[0]); */ - -/* localproto = proto2; */ -/* ASSERT_EQ(-1, /\* port ok but wrong protocol *\/ */ -/* LookupServicesByPort(22, localproto, sizeof(proto2), name, */ -/* sizeof(name), "services")); */ -/* ASSERT_STREQ(proto2, "udp"); */ - -/* localproto = proto1; */ -/* ASSERT_EQ( */ -/* -1, /\* protocol is non-NULL/length must be nonzero *\/ */ -/* LookupServicesByPort(22, localproto, 0, name, sizeof(name), - * "services")); */ -/* ASSERT_STREQ(proto1, "tcp"); */ - -/* localproto = proto1; */ -/* ASSERT_EQ(-1, /\* sizeof(name) insufficient, memccpy failure *\/ */ -/* LookupServicesByPort(22, localproto, sizeof(proto1), name, 1, */ -/* "services")); */ -/* ASSERT_STREQ(proto1, "tcp"); */ -/* ASSERT_STREQ(name, ""); /\* cleaned up after memccpy failed *\/ */ - -/* localproto = eitherproto; */ -/* ASSERT_EQ( */ -/* -1, /\* sizeof(proto) insufficient, memccpy failure *\/ */ -/* LookupServicesByPort(22, localproto, 1, name, sizeof(name), - * "services")); */ -/* ASSERT_STREQ(eitherproto, ""); /\* cleaned up after memccpy failed *\/ */ - -/* localproto = proto1; */ -/* ASSERT_EQ(0, LookupServicesByPort(22, localproto, sizeof(proto1), name, */ -/* sizeof(name), "services")); */ -/* ASSERT_STREQ(name, "ssh"); */ -/* ASSERT_STREQ(proto1, "tcp"); */ - -/* localproto = proto2; */ -/* ASSERT_EQ(0, LookupServicesByPort(19, localproto, sizeof(proto2), name, */ -/* sizeof(name), "services")); */ -/* ASSERT_STREQ(name, "chargen"); */ -/* ASSERT_STREQ(proto2, "udp"); */ - -/* localproto = eitherproto; */ -/* ASSERT_EQ(0, /\* pick first matching protocol *\/ */ -/* LookupServicesByPort(19, localproto, sizeof(eitherproto), name, - */ -/* sizeof(name), "services")); */ -/* ASSERT_STREQ(name, "chargen"); */ -/* ASSERT_NE('\0', localproto[0]); /\* buffer filled during the call *\/ */ -/* ASSERT_STREQ(eitherproto, "tcp"); */ -/* } */ +TEST(LookupServicesByPort, GetNameWhenPortCorrect) { + char name[8]; /* service names are of length 3 */ + char eitherproto[8]; /* protocol names are of length 3 */ + char proto1[] = "tcp"; + char proto2[] = "udp"; + char* localproto; + strcpy(eitherproto, ""); + strcpy(name, ""); + + localproto = eitherproto; + ASSERT_EQ(-1, /* non existent port */ + LookupServicesByPort(965, localproto, sizeof(eitherproto), name, + sizeof(name), "services")); + ASSERT_EQ('\0', localproto[0]); + + localproto = eitherproto; + ASSERT_EQ(-1, /* port in network byte order */ + LookupServicesByPort(htons(22), localproto, sizeof(eitherproto), + name, sizeof(name), "services")); + ASSERT_EQ('\0', localproto[0]); + + localproto = proto2; + ASSERT_EQ(-1, /* port ok but wrong protocol */ + LookupServicesByPort(22, localproto, sizeof(proto2), name, + sizeof(name), "services")); + ASSERT_STREQ(proto2, "udp"); + + localproto = proto1; + ASSERT_EQ( + -1, /* protocol is non-NULL/length must be nonzero */ + LookupServicesByPort(22, localproto, 0, name, sizeof(name), "services")); + ASSERT_STREQ(proto1, "tcp"); + + localproto = proto1; + ASSERT_EQ(-1, /* sizeof(name) insufficient, memccpy failure */ + LookupServicesByPort(22, localproto, sizeof(proto1), name, 1, + "services")); + ASSERT_STREQ(proto1, "tcp"); + ASSERT_STREQ(name, ""); /* cleaned up after memccpy failed */ + + localproto = eitherproto; + ASSERT_EQ( + -1, /* sizeof(proto) insufficient, memccpy failure */ + LookupServicesByPort(22, localproto, 1, name, sizeof(name), "services")); + ASSERT_STREQ(eitherproto, ""); /* cleaned up after memccpy failed */ + + localproto = proto1; + ASSERT_EQ(0, LookupServicesByPort(22, localproto, sizeof(proto1), name, + sizeof(name), "services")); + ASSERT_STREQ(name, "ssh"); + ASSERT_STREQ(proto1, "tcp"); + + localproto = proto2; + ASSERT_EQ(0, LookupServicesByPort(19, localproto, sizeof(proto2), name, + sizeof(name), "services")); + ASSERT_STREQ(name, "chargen"); + ASSERT_STREQ(proto2, "udp"); + + localproto = eitherproto; + ASSERT_EQ(0, /* pick first matching protocol */ + LookupServicesByPort(19, localproto, sizeof(eitherproto), name, + sizeof(name), "services")); + ASSERT_STREQ(name, "chargen"); + ASSERT_NE('\0', localproto[0]); /* buffer filled during the call */ + ASSERT_STREQ(eitherproto, "tcp"); +} TEST(LookupServicesByName, GetPortWhenNameOrAlias) { char name[8]; /* service names are of length 3 */ @@ -132,42 +127,36 @@ TEST(LookupServicesByName, GetPortWhenNameOrAlias) { strcpy(eitherproto, ""); strcpy(name, ""); - /* localproto = eitherproto; */ - /* ASSERT_EQ(-1, /\* non-existent name *\/ */ - /* LookupServicesByName("http", localproto, sizeof(eitherproto), - * name, */ - /* sizeof(name), "services")); */ - /* ASSERT_EQ('\0', localproto[0]); */ - - /* localproto = proto2; */ - /* ASSERT_EQ(-1, /\* name exists but wrong protocol *\/ */ - /* LookupServicesByName("ssh", localproto, sizeof(proto2), name, */ - /* sizeof(name), "services")); */ - /* ASSERT_STREQ(proto2, "udp"); */ - - /* localproto = proto2; */ - /* ASSERT_EQ(-1, /\* protocol is non-NULL/length must be nonzero *\/ */ - /* LookupServicesByName("ssh", localproto, sizeof(proto2), name, */ - /* sizeof(name), "services")); */ - /* ASSERT_STREQ(proto2, "udp"); */ - - /* localproto = proto1; */ - /* ASSERT_EQ(-1, /\* sizeof(name) insufficient, memccpy failure *\/ */ - /* LookupServicesByName("ssh", localproto, sizeof(proto1), name, 1, - */ - /* "services")); */ - /* ASSERT_STREQ(proto1, "tcp"); */ - /* ASSERT_STREQ(name, ""); /\* cleaned up after memccpy failed *\/ */ - - /* localproto = eitherproto; */ - /* ASSERT_EQ(-1, /\* sizeof(proto) insufficient, memccpy failure *\/ */ - /* LookupServicesByName("ssh", localproto, 1, name, sizeof(name), */ - /* "services")); */ - /* ASSERT_STREQ(eitherproto, ""); /\* cleaned up after memccpy failed *\/ */ - - ftrace_install(); - strace_enabled(+1); - ftrace_enabled(+1); + localproto = eitherproto; + ASSERT_EQ(-1, /* non-existent name */ + LookupServicesByName("http", localproto, sizeof(eitherproto), name, + sizeof(name), "services")); + ASSERT_EQ('\0', localproto[0]); + + localproto = proto2; + ASSERT_EQ(-1, /* name exists but wrong protocol */ + LookupServicesByName("ssh", localproto, sizeof(proto2), name, + sizeof(name), "services")); + ASSERT_STREQ(proto2, "udp"); + + localproto = proto2; + ASSERT_EQ(-1, /* protocol is non-NULL/length must be nonzero */ + LookupServicesByName("ssh", localproto, sizeof(proto2), name, + sizeof(name), "services")); + ASSERT_STREQ(proto2, "udp"); + + localproto = proto1; + ASSERT_EQ(-1, /* sizeof(name) insufficient, memccpy failure */ + LookupServicesByName("ssh", localproto, sizeof(proto1), name, 1, + "services")); + ASSERT_STREQ(proto1, "tcp"); + ASSERT_STREQ(name, ""); /* cleaned up after memccpy failed */ + + localproto = eitherproto; + ASSERT_EQ(-1, /* sizeof(proto) insufficient, memccpy failure */ + LookupServicesByName("ssh", localproto, 1, name, sizeof(name), + "services")); + ASSERT_STREQ(eitherproto, ""); /* cleaned up after memccpy failed */ localproto = proto1; ASSERT_EQ(22, LookupServicesByName("ssh", localproto, sizeof(proto1), name, diff --git a/test/libc/sock/connect_test.c b/test/libc/sock/connect_test.c new file mode 100644 index 00000000000..c772efd64a1 --- /dev/null +++ b/test/libc/sock/connect_test.c @@ -0,0 +1,85 @@ +/*-*- 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 2023 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ 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/dce.h" +#include "libc/errno.h" +#include "libc/intrin/kprintf.h" +#include "libc/runtime/runtime.h" +#include "libc/sock/sock.h" +#include "libc/sock/struct/pollfd.h" +#include "libc/sock/struct/sockaddr.h" +#include "libc/sysv/consts/af.h" +#include "libc/sysv/consts/ipproto.h" +#include "libc/sysv/consts/limits.h" +#include "libc/sysv/consts/poll.h" +#include "libc/sysv/consts/sock.h" +#include "libc/testlib/subprocess.h" +#include "libc/testlib/testlib.h" +#include "libc/thread/thread.h" + +TEST(connect, nonblocking) { + if (IsFreebsd()) return; // TODO(jart): why did this start flaking? + char buf[16] = {0}; + atomic_uint *sem = _mapshared(sizeof(unsigned)); + uint32_t addrsize = sizeof(struct sockaddr_in); + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = htonl(0x7f000001), + }; + ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); + ASSERT_SYS(0, 0, bind(3, (struct sockaddr *)&addr, sizeof(addr))); + ASSERT_SYS(0, 0, getsockname(3, (struct sockaddr *)&addr, &addrsize)); + ASSERT_SYS(0, 0, listen(3, SOMAXCONN)); + SPAWN(fork); + while (!*sem) pthread_yield(); + ASSERT_SYS(0, 4, accept(3, (struct sockaddr *)&addr, &addrsize)); + ASSERT_SYS(0, 2, read(4, buf, 16)); // hi + ASSERT_SYS(0, 5, write(4, "hello", 5)); + ASSERT_SYS(0, 3, read(4, buf, 16)); // bye + PARENT(); + ASSERT_SYS(0, 0, close(3)); + ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP)); + ASSERT_SYS(EINPROGRESS, -1, + connect(3, (struct sockaddr *)&addr, sizeof(addr))); + if (!(IsLinux() || IsNetbsd())) { + // this doens't work on rhel7 and netbsd + ASSERT_SYS(EALREADY, -1, + connect(3, (struct sockaddr *)&addr, sizeof(addr))); + } + ASSERT_SYS(EAGAIN, -1, read(3, buf, 16)); + *sem = 1; + { // wait until connected + struct pollfd pfd = {3, POLLOUT}; + ASSERT_SYS(0, 1, poll(&pfd, 1, -1)); + ASSERT_TRUE(!!(POLLOUT & pfd.revents)); + } + ASSERT_SYS(0, 2, write(3, "hi", 2)); + { // wait for other process to send us stuff + struct pollfd pfd = {3, POLLIN}; + ASSERT_SYS(0, 1, poll(&pfd, 1, -1)); + ASSERT_TRUE(!!(POLLIN & pfd.revents)); + } + ASSERT_SYS(0, 5, read(3, buf, 16)); + ASSERT_STREQ("hello", buf); + ASSERT_SYS(0, 3, write(3, "bye", 3)); + ASSERT_SYS(0, 0, close(3)); + WAIT(exit, 0); + munmap(sem, sizeof(unsigned)); +} diff --git a/test/libc/sock/socket_test.c b/test/libc/sock/socket_test.c index 589cea10858..e01c9467202 100644 --- a/test/libc/sock/socket_test.c +++ b/test/libc/sock/socket_test.c @@ -49,12 +49,12 @@ TEST(ipv4, test) { ASSERT_SYS(0, 4, accept(3, (struct sockaddr *)&addr, &addrsize)); ASSERT_SYS(0, 5, send(4, "hello", 5, 0)); PARENT(); - EXPECT_SYS(0, 0, close(3)); - EXPECT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); - EXPECT_SYS(0, 0, connect(3, (struct sockaddr *)&addr, sizeof(addr))); - EXPECT_SYS(0, 5, read(3, buf, 16)); - EXPECT_STREQ("hello", buf); - EXPECT_SYS(0, 0, close(3)); + ASSERT_SYS(0, 0, close(3)); + ASSERT_SYS(0, 3, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); + ASSERT_SYS(0, 0, connect(3, (struct sockaddr *)&addr, sizeof(addr))); + ASSERT_SYS(0, 5, read(3, buf, 16)); + ASSERT_STREQ("hello", buf); + ASSERT_SYS(0, 0, close(3)); WAIT(exit, 0); } @@ -77,12 +77,12 @@ TEST(ipv6, test) { ASSERT_SYS(0, 0, close(4)); ASSERT_SYS(0, 0, close(3)); PARENT(); - EXPECT_SYS(0, 0, close(3)); - EXPECT_SYS(0, 3, socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)); - EXPECT_SYS(0, 0, connect(3, (struct sockaddr *)&addr, sizeof(addr))); - EXPECT_SYS(0, 5, read(3, buf, 16)); - EXPECT_STREQ("hello", buf); - EXPECT_SYS(0, 0, close(3)); + ASSERT_SYS(0, 0, close(3)); + ASSERT_SYS(0, 3, socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)); + ASSERT_SYS(0, 0, connect(3, (struct sockaddr *)&addr, sizeof(addr))); + ASSERT_SYS(0, 5, read(3, buf, 16)); + ASSERT_STREQ("hello", buf); + ASSERT_SYS(0, 0, close(3)); WAIT(exit, 0); } diff --git a/test/libc/stdio/fwrite_test.c b/test/libc/stdio/fwrite_test.c index 11d5424ce14..d257d8298dc 100644 --- a/test/libc/stdio/fwrite_test.c +++ b/test/libc/stdio/fwrite_test.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/struct/sigaction.h" +#include "libc/calls/struct/sigset.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/mem/gc.internal.h" @@ -172,7 +173,7 @@ TEST(fwrite, signalStorm) { if (!pid) { do { ASSERT_NE(-1, kill(getppid(), SIGINT)); - usleep(25); + usleep(5000); } while (!gotsigint); _exit(0); } diff --git a/test/libc/stdio/memory_test.c b/test/libc/stdio/memory_test.c index b5eefd2d9b0..61d6140c8a7 100644 --- a/test/libc/stdio/memory_test.c +++ b/test/libc/stdio/memory_test.c @@ -28,12 +28,12 @@ void *Worker(void *arg) { int i; char *volatile p; char *volatile q; - for (i = 0; i < 256; ++i) { + for (i = 0; i < 3000; ++i) { p = malloc(17); free(p); p = malloc(17); q = malloc(17); - sched_yield(); + pthread_yield(); free(p); free(q); } @@ -45,7 +45,7 @@ void SetUpOnce(void) { } TEST(memory, test) { - int i, n = 32; + int i, n = 8; pthread_t *t = gc(malloc(sizeof(pthread_t) * n)); for (i = 0; i < n; ++i) { ASSERT_EQ(0, pthread_create(t + i, 0, Worker, 0)); diff --git a/test/libc/thread/pthread_kill_test.c b/test/libc/thread/pthread_kill_test.c index 679e4460f15..a74810606c5 100644 --- a/test/libc/thread/pthread_kill_test.c +++ b/test/libc/thread/pthread_kill_test.c @@ -21,6 +21,7 @@ #include "libc/calls/struct/sigaction.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/intrin/kprintf.h" #include "libc/runtime/runtime.h" #include "libc/sock/sock.h" #include "libc/sock/struct/sockaddr.h" @@ -61,7 +62,7 @@ void OnSig(int sig) { void WaitUntilReady(void) { while (!ready) pthread_yield(); ASSERT_EQ(0, errno); - ASSERT_SYS(0, 0, usleep(1000)); + ASSERT_SYS(0, 0, usleep(100000)); } void *SleepWorker(void *arg) { diff --git a/third_party/dlmalloc/dlmalloc.c b/third_party/dlmalloc/dlmalloc.c index fb940138fe7..bee028cfd2d 100644 --- a/third_party/dlmalloc/dlmalloc.c +++ b/third_party/dlmalloc/dlmalloc.c @@ -1,14 +1,17 @@ #include "third_party/dlmalloc/dlmalloc.h" #include "libc/assert.h" +#include "libc/atomic.h" #include "libc/calls/calls.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/intrin/atomic.h" #include "libc/intrin/bsr.h" #include "libc/intrin/likely.h" #include "libc/intrin/weaken.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" #include "libc/nexgen32e/rdtsc.h" +#include "libc/nexgen32e/yield.h" #include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" #include "libc/runtime/sysconf.h" @@ -19,7 +22,9 @@ #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/prot.h" #include "libc/thread/thread.h" +#include "libc/thread/tls.h" #include "third_party/dlmalloc/vespene.internal.h" +#include "third_party/nsync/mu.h" // clang-format off #define FOOTERS 0 @@ -45,7 +50,11 @@ #endif #undef assert -#define assert(x) npassert(x) +#if IsTiny() +#define assert(x) if(!(x)) __builtin_unreachable() +#else +#define assert(x) if(!(x)) ABORT +#endif #include "third_party/dlmalloc/platform.inc" #include "third_party/dlmalloc/locks.inc" diff --git a/third_party/dlmalloc/dlmalloc.h b/third_party/dlmalloc/dlmalloc.h index d8f366adf92..0669cf58a8a 100644 --- a/third_party/dlmalloc/dlmalloc.h +++ b/third_party/dlmalloc/dlmalloc.h @@ -507,7 +507,7 @@ void mspace_inspect_all(mspace msp, void* arg); void dlmalloc_atfork(void); -void dlmalloc_abort(void); +void dlmalloc_abort(void) relegated wontreturn; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/third_party/dlmalloc/dlmalloc_abort.greg.c b/third_party/dlmalloc/dlmalloc_abort.c similarity index 87% rename from third_party/dlmalloc/dlmalloc_abort.greg.c rename to third_party/dlmalloc/dlmalloc_abort.c index 64a6ab53f01..eeab8ddd8fd 100644 --- a/third_party/dlmalloc/dlmalloc_abort.greg.c +++ b/third_party/dlmalloc/dlmalloc_abort.c @@ -17,16 +17,16 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/intrin/weaken.h" -#include "libc/log/log.h" +#include "libc/errno.h" +#include "libc/intrin/describebacktrace.internal.h" #include "libc/runtime/runtime.h" -#include "libc/str/str.h" #include "third_party/dlmalloc/dlmalloc.h" -#define MESSAGE "dlmalloc_abort()\n" - void dlmalloc_abort(void) { - write(2, MESSAGE, strlen(MESSAGE)); - if (_weaken(__die)) _weaken(__die)(); + tinyprint(2, + "error: dlmalloc aborted\n" + "cosmoaddr2line ", + program_invocation_name, " ", + DescribeBacktrace(__builtin_frame_address(0)), "\n", NULL); _Exit(44); } diff --git a/third_party/dlmalloc/locks.inc b/third_party/dlmalloc/locks.inc index 5c601757b1e..ee73aa2e9e4 100644 --- a/third_party/dlmalloc/locks.inc +++ b/third_party/dlmalloc/locks.inc @@ -1,9 +1,4 @@ // clang-format off -#include "third_party/nsync/mu.h" -#include "libc/atomic.h" -#include "libc/intrin/atomic.h" -#include "libc/calls/calls.h" -#include "libc/thread/tls.h" /* --------------------------- Lock preliminaries ------------------------ */ @@ -35,51 +30,47 @@ */ -static int malloc_lock(atomic_int *lk) { +#ifdef TINY +#define MLOCK_T atomic_uint +#else +#define MLOCK_T nsync_mu +#endif + +static int malloc_wipe(MLOCK_T *lk) { + bzero(lk, sizeof(*lk)); + return 0; +} + +static int malloc_lock(MLOCK_T *lk) { if (!__threaded) return 0; +#ifdef TINY while (atomic_exchange_explicit(lk, 1, memory_order_acquire)) { - donothing; + spin_yield(); } +#else + nsync_mu_lock(lk); +#endif return 0; } -static int malloc_trylock(atomic_int *lk) { - if (!__threaded) return 1; - return !atomic_exchange_explicit(lk, 1, memory_order_acquire); -} - -static inline int malloc_unlock(atomic_int *lk) { +static int malloc_unlock(MLOCK_T *lk) { + if (!__threaded) return 0; +#ifdef TINY atomic_store_explicit(lk, 0, memory_order_release); +#else + nsync_mu_unlock(lk); +#endif return 0; } -#if !USE_LOCKS -#define USE_LOCK_BIT (0U) -#define INITIAL_LOCK(l) (0) -#define DESTROY_LOCK(l) (0) -#define ACQUIRE_MALLOC_GLOBAL_LOCK() -#define RELEASE_MALLOC_GLOBAL_LOCK() -#elif defined(TINY) -#define MLOCK_T atomic_int -#define ACQUIRE_LOCK(lk) malloc_lock(lk) -#define RELEASE_LOCK(lk) malloc_unlock(lk) -#define TRY_LOCK(lk) malloc_trylock(lk) -#define INITIAL_LOCK(lk) (atomic_store_explicit(lk, 0, memory_order_relaxed), 0) +#define ACQUIRE_LOCK(lk) malloc_lock(lk) +#define RELEASE_LOCK(lk) malloc_unlock(lk) +#define INITIAL_LOCK(lk) malloc_wipe(lk) #define DESTROY_LOCK(lk) -#define ACQUIRE_MALLOC_GLOBAL_LOCK() ACQUIRE_LOCK(&malloc_global_mutex); -#define RELEASE_MALLOC_GLOBAL_LOCK() RELEASE_LOCK(&malloc_global_mutex); -static MLOCK_T malloc_global_mutex; -#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); +#define ACQUIRE_MALLOC_GLOBAL_LOCK() ACQUIRE_LOCK(&malloc_global_mutex); +#define RELEASE_MALLOC_GLOBAL_LOCK() RELEASE_LOCK(&malloc_global_mutex); + static MLOCK_T malloc_global_mutex; -#endif #define USE_LOCK_BIT (2U) diff --git a/third_party/nsync/futex.c b/third_party/nsync/futex.c index 9e500e568bc..0bef7bcc883 100644 --- a/third_party/nsync/futex.c +++ b/third_party/nsync/futex.c @@ -38,6 +38,7 @@ #include "libc/nexgen32e/vendor.internal.h" #include "libc/nt/runtime.h" #include "libc/nt/synchronization.h" +#include "libc/runtime/clktck.h" #include "libc/sysv/consts/clock.h" #include "libc/sysv/consts/timer.h" #include "libc/sysv/errfuns.h" @@ -129,46 +130,19 @@ static void nsync_futex_init_ (void) { } static int nsync_futex_polyfill_ (atomic_int *w, int expect, struct timespec *abstime) { - int rc; - int64_t nanos, maxnanos; - struct timespec now, wait, remain, deadline; - - if (!abstime) { - deadline = timespec_max; - } else { - deadline = *abstime; - } - - nanos = 100; - maxnanos = __SIG_LOCK_INTERVAL_MS * 1000L * 1000; for (;;) { if (atomic_load_explicit (w, memory_order_acquire) != expect) { return 0; } - now = timespec_real (); - if (atomic_load_explicit (w, memory_order_acquire) != expect) { - return 0; - } - if (timespec_cmp (now, deadline) >= 0) { - break; - } - wait = timespec_fromnanos (nanos); - remain = timespec_sub (deadline, now); - if (timespec_cmp(wait, remain) > 0) { - wait = remain; - } - if ((rc = clock_nanosleep (CLOCK_REALTIME, 0, &wait, 0))) { - return -rc; + if (_weaken (pthread_testcancel_np) && + _weaken (pthread_testcancel_np) ()) { + return -ETIMEDOUT; } - if (nanos < maxnanos) { - nanos <<= 1; - if (nanos > maxnanos) { - nanos = maxnanos; - } + if (abstime && timespec_cmp (timespec_real (), *abstime) >= 0) { + return -ETIMEDOUT; } + pthread_yield (); } - - return -ETIMEDOUT; } static int nsync_futex_wait_win32_ (atomic_int *w, int expect, char pshare, @@ -189,7 +163,7 @@ static int nsync_futex_wait_win32_ (atomic_int *w, int expect, char pshare, return etimedout(); } remain = timespec_sub (deadline, now); - interval = timespec_frommillis (__SIG_LOCK_INTERVAL_MS); + interval = timespec_frommillis (5000); wait = timespec_cmp (remain, interval) > 0 ? interval : remain; if (atomic_load_explicit (w, memory_order_acquire) != expect) { return 0; @@ -274,7 +248,7 @@ int nsync_futex_wait_ (atomic_int *w, int expect, char pshare, const struct time us = -1u; } rc = ulock_wait (op, w, expect, us); - if (rc > 0) rc = 0; // TODO(jart): What does it mean? + if (rc > 0) rc = 0; // don't care about #waiters } else if (IsFreebsd ()) { rc = sys_umtx_timedwait_uint (w, expect, pshare, timeout); } else { @@ -356,6 +330,7 @@ int nsync_futex_wake_ (atomic_int *w, int count, char pshare) { op |= ULF_WAKE_ALL; } rc = ulock_wake (op, w, 0); + ASSERT (!rc || rc == -ENOENT); if (!rc) { rc = 1; } else if (rc == -ENOENT) { diff --git a/third_party/nsync/mu_semaphore.c b/third_party/nsync/mu_semaphore.c index e5e3ef29273..6d674a8313a 100644 --- a/third_party/nsync/mu_semaphore.c +++ b/third_party/nsync/mu_semaphore.c @@ -23,7 +23,7 @@ /* Apple's ulock (part by Cosmo futexes) is an internal API, but: 1. Unlike GCD it's cancellable, i.e. can be EINTR'd by signals 2. We currently always use ulock anyway for joining threads */ -#define PREFER_GCD_OVER_ULOCK 0 +#define PREFER_GCD_OVER_ULOCK 1 asm(".ident\t\"\\n\\n\ *NSYNC (Apache 2.0)\\n\ diff --git a/third_party/nsync/mu_semaphore_gcd.c b/third_party/nsync/mu_semaphore_gcd.c index a58d1758427..73db494cb88 100644 --- a/third_party/nsync/mu_semaphore_gcd.c +++ b/third_party/nsync/mu_semaphore_gcd.c @@ -20,6 +20,7 @@ #include "libc/errno.h" #include "libc/intrin/strace.internal.h" #include "libc/intrin/weaken.h" +#include "libc/runtime/clktck.h" #include "libc/runtime/syslib.internal.h" #include "libc/str/str.h" #include "libc/thread/posixthread.internal.h" @@ -116,8 +117,7 @@ errno_t nsync_mu_semaphore_p_with_deadline_gcd (nsync_semaphore *s, (pt->pt_flags & PT_NOCANCEL)) { result = nsync_dispatch_semaphore_wait (s, abs_deadline); } else { - struct timespec now, until, slice; - slice = timespec_frommillis (__SIG_LOCK_INTERVAL_MS); + struct timespec now, until, slice = {0, 1000000000 / CLK_TCK}; for (;;) { if (_weaken (pthread_testcancel_np) () == ECANCELED) { result = ECANCELED; diff --git a/third_party/nsync/panic.c b/third_party/nsync/panic.c index 8028a7b5c7a..f1fe972aeee 100644 --- a/third_party/nsync/panic.c +++ b/third_party/nsync/panic.c @@ -17,12 +17,17 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/str/str.h" +#include "libc/errno.h" +#include "libc/intrin/describebacktrace.internal.h" +#include "libc/runtime/runtime.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) { - tinyprint (2, "nsync panic: ", s, NULL); - notpossible; + tinyprint(2, "error: nsync panic: ", s, "\n", + "cosmoaddr2line ", program_invocation_name, " ", + DescribeBacktrace (__builtin_frame_address (0)), "\n", + NULL); + _Exit (44); }