Skip to content

Commit

Permalink
Rewrite Windows connect()
Browse files Browse the repository at this point in the history
Our old code wasn't working with projects like Qt that call connect() in
O_NONBLOCK mode multiple times. This change overhauls connect() to use a
simpler WSAConnect() API and follows the same pattern as cosmo accept().
This change also reduces the binary footprint of read(), which no longer
needs to depend on our enormous clock_gettime() function.
  • Loading branch information
jart committed Sep 13, 2024
1 parent 5469202 commit 694ba36
Show file tree
Hide file tree
Showing 25 changed files with 555 additions and 277 deletions.
26 changes: 26 additions & 0 deletions libc/calls/clock_gettime_monotonic_nt.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2024 Justine Alexandra Roberts Tunney │
│ │
│ Permission to use, copy, modify, and/or distribute this software for │
│ any purpose with or without fee is hereby granted, provided that the │
│ above copyright notice and this permission notice appear in all copies. │
│ │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/struct/timespec.internal.h"
#include "libc/nt/time.h"

textwindows struct timespec sys_clock_gettime_monotonic_nt(void) {
uint64_t hectons;
QueryUnbiasedInterruptTimePrecise(&hectons);
return timespec_fromnanos(hectons * 100);
}
11 changes: 3 additions & 8 deletions libc/calls/poll-nt.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/struct/timespec.h"
#include "libc/calls/struct/timespec.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/fds.h"
Expand Down Expand Up @@ -58,14 +59,8 @@
#define POLLPRI_ 0x0400 // MSDN unsupported
// </sync libc/sysv/consts.sh>

textwindows dontinline static struct timespec sys_poll_nt_now(void) {
uint64_t hectons;
QueryUnbiasedInterruptTimePrecise(&hectons);
return timespec_fromnanos(hectons * 100);
}

textwindows static uint32_t sys_poll_nt_waitms(struct timespec deadline) {
struct timespec now = sys_poll_nt_now();
struct timespec now = sys_clock_gettime_monotonic_nt();
if (timespec_cmp(now, deadline) < 0) {
struct timespec remain = timespec_sub(deadline, now);
int64_t millis = timespec_tomillis(remain);
Expand Down Expand Up @@ -340,7 +335,7 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint32_t *ms,
int rc;
struct timespec now, timeout, deadline;
BLOCK_SIGNALS;
now = ms ? sys_poll_nt_now() : timespec_zero;
now = ms ? sys_clock_gettime_monotonic_nt() : timespec_zero;
timeout = ms ? timespec_frommillis(*ms) : timespec_max;
deadline = timespec_add(now, timeout);
rc = sys_poll_nt_impl(fds, nfds, deadline, sigmask ? *sigmask : _SigMask);
Expand Down
6 changes: 4 additions & 2 deletions libc/calls/read-nt.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "libc/calls/struct/iovec.h"
#include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/struct/timespec.h"
#include "libc/calls/struct/timespec.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/cosmo.h"
#include "libc/ctype.h"
Expand Down Expand Up @@ -837,7 +838,8 @@ textwindows static int CountConsoleInputBytesBlockingImpl(uint32_t ms,
uint32_t wi;
struct timespec now, deadline;
InitConsole();
deadline = timespec_add(timespec_mono(), timespec_frommillis(ms));
deadline =
timespec_add(sys_clock_gettime_monotonic_nt(), timespec_frommillis(ms));
RestartOperation:
if (_check_cancel() == -1)
return -1;
Expand Down Expand Up @@ -870,7 +872,7 @@ textwindows static int CountConsoleInputBytesBlockingImpl(uint32_t ms,
// this can happen for multiple reasons. first our driver controls
// user interactions in canonical mode. secondly we could lose the
// race with another thread that's reading input.
now = timespec_mono();
now = sys_clock_gettime_monotonic_nt();
if (timespec_cmp(now, deadline) >= 0)
return etimedout();
ms = timespec_tomillis(timespec_sub(deadline, now));
Expand Down
1 change: 1 addition & 0 deletions libc/calls/struct/timespec.internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ int sys_sem_timedwait(int64_t, const struct timespec *);
int sys_utimensat(int, const char *, const struct timespec[2], int);
int sys_utimensat_nt(int, const char *, const struct timespec[2], int);
int sys_utimensat_old(int, const char *, const struct timespec[2], int);
struct timespec sys_clock_gettime_monotonic_nt(void);

const char *_DescribeTimespec(char[45], int, const struct timespec *);
#define DescribeTimespec(rc, ts) _DescribeTimespec(alloca(45), rc, ts)
Expand Down
2 changes: 1 addition & 1 deletion libc/intrin/fds.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ struct Cursor {
struct Fd {
char kind;
bool isbound;
char connecting;
unsigned flags;
unsigned mode;
long handle;
Expand All @@ -38,7 +39,6 @@ struct Fd {
unsigned sndtimeo; /* millis; 0 means wait forever */
void *connect_op;
struct Cursor *cursor;
struct sockaddr_storage peer;
};

struct Fds {
Expand Down
1 change: 1 addition & 0 deletions libc/sock/BUILD.mk
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ LIBC_SOCK_A_DIRECTDEPS = \
LIBC_NT_IPHLPAPI \
LIBC_NT_KERNEL32 \
LIBC_NT_NTDLL \
LIBC_NT_REALTIME \
LIBC_NT_WS2_32 \
LIBC_RUNTIME \
LIBC_STDIO \
Expand Down
15 changes: 1 addition & 14 deletions libc/sock/accept-nt.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,12 @@
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/internal.h"
#include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/errno.h"
#include "libc/intrin/kprintf.h"
#include "libc/nt/errors.h"
#include "libc/nt/struct/pollfd.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/sysv/consts/fio.h"
#include "libc/sysv/consts/o.h"
Expand All @@ -38,8 +35,6 @@

#define POLL_INTERVAL_MS 10

__msabi extern typeof(__sys_setsockopt_nt) *const __imp_setsockopt;
__msabi extern typeof(__sys_closesocket_nt) *const __imp_closesocket;
__msabi extern typeof(__sys_ioctlsocket_nt) *const __imp_ioctlsocket;

textwindows static int sys_accept_nt_impl(struct Fd *f,
Expand All @@ -61,7 +56,6 @@ textwindows static int sys_accept_nt_impl(struct Fd *f,
for (;;) {

// perform non-blocking accept
// we assume listen() put f->handle in non-blocking mode
int32_t addrsize = sizeof(*addr);
struct sockaddr *paddr = (struct sockaddr *)addr;
if ((handle = WSAAccept(f->handle, paddr, &addrsize, 0, 0)) != -1)
Expand All @@ -76,7 +70,7 @@ textwindows static int sys_accept_nt_impl(struct Fd *f,
return -1;
}

// we're done if user wants non-blocking
// check for non-blocking
if (f->flags & O_NONBLOCK)
return eagain();

Expand All @@ -91,13 +85,6 @@ textwindows static int sys_accept_nt_impl(struct Fd *f,
return __winsockerr();
}

// inherit properties of listening socket
// errors ignored as if f->handle was created before forking
// this fails with WSAENOTSOCK, see
// https://github.com/jart/cosmopolitan/issues/1174
__imp_setsockopt(handle, SOL_SOCKET, kNtSoUpdateAcceptContext, &f->handle,
sizeof(f->handle));

// create file descriptor for new socket
// don't inherit the file open mode bits
int oflags = 0;
Expand Down
3 changes: 3 additions & 0 deletions libc/sock/accept.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
/**
* Creates client socket file descriptor for incoming connection.
*
* On Windows, when this function blocks, there may be a 10 millisecond
* delay on the handling of signals or thread cancelation.
*
* @param fd is the server socket file descriptor
* @param opt_out_addr will receive the remote address
* @param opt_inout_addrsize provides and receives addr's byte length
Expand Down
11 changes: 11 additions & 0 deletions libc/sock/accept4.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,23 @@
/**
* Creates client socket file descriptor for incoming connection.
*
* When `fd` is in `O_NONBLOCK` mode, this function will raise `EAGAIN`
* when no client is available to accept. To wait until a client exists
* the poll() function may be called using `POLLIN`.
*
* On Linux, your `SO_RCVTIMEO` will timeout accept4(). Other OSes (i.e.
* Windows, MacOS, and BSDs) do not support this and will block forever.
*
* On Windows, when this function blocks, there may be a 10 millisecond
* delay on the handling of signals or thread cancelation.
*
* @param fd is the server socket file descriptor
* @param opt_out_addr will receive the remote address
* @param opt_inout_addrsize provides and receives out_addr's byte length
* @param flags can have SOCK_{CLOEXEC,NONBLOCK}, which may apply to
* both the newly created socket and the server one
* @return client fd which needs close(), or -1 w/ errno
* @raise EAGAIN if `O_NONBLOCK` and no clients pending
* @cancelationpoint
* @asyncsignalsafe
* @restartable (unless SO_RCVTIMEO)
Expand Down
5 changes: 0 additions & 5 deletions libc/sock/closesocket-nt.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
│ 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"
#include "libc/sock/internal.h"
Expand All @@ -32,9 +30,6 @@ __msabi extern typeof(__sys_closesocket_nt) *const __imp_closesocket;
* This function should only be called by close().
*/
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 {
Expand Down
Loading

0 comments on commit 694ba36

Please sign in to comment.