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.
This commit is contained in:
Justine Tunney 2023-10-12 18:53:17 -07:00
parent f7343319cc
commit 49b0eaa69f
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
43 changed files with 528 additions and 425 deletions

View file

@ -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 \

View file

@ -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;

View file

@ -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);

View file

@ -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 {

View file

@ -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 {

View file

@ -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();
}

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -5,7 +5,7 @@
COSMOPOLITAN_C_START_
struct NtOverlappedEntry {
uint32_t *lpCompletionKey;
uint64_t lpCompletionKey;
struct NtOverlapped *lpOverlapped;
uint32_t *Internal;
uint32_t dwNumberOfBytesTransferred;

View file

@ -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,

View file

@ -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;
}

View file

@ -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,

View file

@ -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();

View file

@ -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();

View file

@ -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;
}
} else {
__winsockerr();
}
WSACloseEvent(eh);
}
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 {
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();
}
}
textwindows int sys_connect_nt(struct Fd *f, const void *addr,
uint32_t addrsize) {
sigset_t mask = __sig_block();
int rc = sys_connect_nt_impl(f, addr, addrsize, mask);
__sig_unblock(mask);
return rc;
}
textwindows int sys_connect_nt(struct Fd *fd, 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);
}
#endif /* __x86_64__ */

View file

@ -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)) {

View file

@ -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 \

View file

@ -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 *);

View file

@ -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};

46
libc/sock/wsaid.c Normal file
View file

@ -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;
}

View file

@ -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_ */

View file

@ -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

View file

@ -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, ""); */
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, /* 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 = 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 = 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, /* 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 = 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 = 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 = 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 = 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"); */
/* } */
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 = 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, /* 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 = 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 = 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, /* 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,

View file

@ -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));
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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));

View file

@ -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) {

View file

@ -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"

View file

@ -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) */

View file

@ -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);
}

View file

@ -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,52 +30,48 @@
*/
static int malloc_lock(atomic_int *lk) {
if (!__threaded) return 0;
while (atomic_exchange_explicit(lk, 1, memory_order_acquire)) {
donothing;
}
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) {
atomic_store_explicit(lk, 0, memory_order_release);
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 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;
#ifdef TINY
#define MLOCK_T atomic_uint
#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);
static MLOCK_T malloc_global_mutex;
#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)) {
spin_yield();
}
#else
nsync_mu_lock(lk);
#endif
return 0;
}
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;
}
#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;
#define USE_LOCK_BIT (2U)
struct malloc_chunk {

View file

@ -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 (_weaken (pthread_testcancel_np) &&
_weaken (pthread_testcancel_np) ()) {
return -ETIMEDOUT;
}
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 (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) {

View file

@ -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\

View file

@ -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;

View file

@ -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);
}