mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-06 11:18:30 +00:00
Support non-blocking i/o across platforms
This change introduces new tests for `O_NONBLOCK` and `SOCK_NONBLOCK` to confirm that non-blocking i/o is now working on all supported platforms, including Windows. For example, you can now say on Windows, MacOS, etc.: socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP); To create a non-blocking IPv4 TCP socket. Or you can enable non-blocking i/o on an existing socket / pipe / etc. file descriptor by calling fcntl fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); This functionality is polyfilled on older Linux kernels too, e.g. RHEL5. Now that fcntl() support is much better the FIOCLEX / FIONCLEX polyfills for ioctl() have been removed since they're ugly non-POSIX diameond APIs This change fixes a weakness in kprintf() that was causing Windows trace tools to frequently crash.
This commit is contained in:
parent
5c9e03e3e0
commit
1d4eb08fa1
102 changed files with 678 additions and 331 deletions
|
@ -36,10 +36,13 @@
|
|||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/byhandlefileinformation.h"
|
||||
#include "libc/nt/struct/overlapped.h"
|
||||
#include "libc/nt/winsock.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/stdckdint.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
#include "libc/sysv/consts/fd.h"
|
||||
#include "libc/sysv/consts/fio.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
@ -307,21 +310,49 @@ static textwindows int sys_fcntl_nt_dupfd(int fd, int cmd, int start) {
|
|||
return sys_dup_nt(fd, -1, (cmd == F_DUPFD_CLOEXEC ? O_CLOEXEC : 0), start);
|
||||
}
|
||||
|
||||
static textwindows int sys_fcntl_nt_setfl(int fd, unsigned *flags, unsigned arg,
|
||||
unsigned supported) {
|
||||
unsigned old, neu, changed, other, allowed;
|
||||
old = *flags & supported;
|
||||
other = *flags & ~supported;
|
||||
neu = arg & supported;
|
||||
changed = old ^ neu;
|
||||
// you may change the following access mode flags:
|
||||
//
|
||||
// - O_NONBLOCK make read() raise EAGAIN
|
||||
// - O_NDELAY same thing as O_NONBLOCK
|
||||
//
|
||||
allowed = O_NONBLOCK;
|
||||
if (changed & ~allowed) {
|
||||
// the following access mode flags are supported, but it's currently
|
||||
// not possible to change them on windows.
|
||||
//
|
||||
// - O_APPEND tried to support but failed
|
||||
// - O_RANDOM use posix_fadvise() instead
|
||||
// - O_SEQUENTIAL use posix_fadvise() instead
|
||||
// - O_DIRECT possibly in future?
|
||||
// - O_DSYNC possibly in future?
|
||||
// - O_RSYNC possibly in future?
|
||||
// - O_SYNC possibly in future?
|
||||
//
|
||||
return enotsup();
|
||||
}
|
||||
// 1. ignore flags that aren't access mode flags
|
||||
// 2. return zero if nothing's changed
|
||||
*flags = other | neu;
|
||||
return 0;
|
||||
}
|
||||
|
||||
textwindows int sys_fcntl_nt(int fd, int cmd, uintptr_t arg) {
|
||||
int rc;
|
||||
uint32_t flags;
|
||||
int access_mode_flags = O_ACCMODE | O_APPEND | O_ASYNC | O_DIRECT |
|
||||
O_NOATIME | O_NONBLOCK | O_RANDOM | O_SEQUENTIAL;
|
||||
if (__isfdkind(fd, kFdFile) || __isfdkind(fd, kFdSocket)) {
|
||||
if (cmd == F_GETFL) {
|
||||
rc = g_fds.p[fd].flags &
|
||||
(O_ACCMODE | O_APPEND | O_ASYNC | O_DIRECT | O_NOATIME | O_NONBLOCK |
|
||||
O_RANDOM | O_SEQUENTIAL);
|
||||
rc = g_fds.p[fd].flags & access_mode_flags;
|
||||
} else if (cmd == F_SETFL) {
|
||||
// O_APPEND doesn't appear to be tunable at cursory glance
|
||||
// O_NONBLOCK might require we start doing all i/o in threads
|
||||
// O_DSYNC / O_RSYNC / O_SYNC maybe if we fsync() everything
|
||||
// O_DIRECT | O_RANDOM | O_SEQUENTIAL | O_NDELAY possible but
|
||||
// not worth it.
|
||||
rc = enosys();
|
||||
rc = sys_fcntl_nt_setfl(fd, &g_fds.p[fd].flags, arg, access_mode_flags);
|
||||
} else if (cmd == F_GETFD) {
|
||||
if (g_fds.p[fd].flags & O_CLOEXEC) {
|
||||
rc = FD_CLOEXEC;
|
||||
|
|
|
@ -26,9 +26,15 @@
|
|||
// Applies file descriptor fixups on XNU or old Linux.
|
||||
// See __fixupnewsockfd() for socket file descriptors.
|
||||
int __fixupnewfd(int fd, int flags) {
|
||||
int file_mode;
|
||||
if (fd != -1) {
|
||||
if (flags & O_CLOEXEC) {
|
||||
_npassert(!__sys_fcntl(fd, F_SETFD, FD_CLOEXEC));
|
||||
_unassert((file_mode = __sys_fcntl(fd, F_GETFD)) != -1);
|
||||
_unassert(!__sys_fcntl(fd, F_SETFD, file_mode | FD_CLOEXEC));
|
||||
}
|
||||
if (flags & O_NONBLOCK) {
|
||||
_unassert((file_mode = __sys_fcntl(fd, F_GETFL)) != -1);
|
||||
_unassert(!__sys_fcntl(fd, F_SETFL, file_mode | O_NONBLOCK));
|
||||
}
|
||||
}
|
||||
return fd;
|
||||
|
|
|
@ -76,6 +76,6 @@ int getdomainname(char *name, size_t len) {
|
|||
if (!rc && len && !strcmp(name, "(none)")) {
|
||||
name[0] = 0;
|
||||
}
|
||||
STRACE("getdomainname([%#.*s], %'zu) → %d% m", len, name, len, rc);
|
||||
STRACE("getdomainname([%#.*s], %'zu) → %d% m", (int)len, name, len, rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -71,6 +71,6 @@ int gethostname(char *name, size_t len) {
|
|||
} else {
|
||||
rc = enosys();
|
||||
}
|
||||
STRACE("gethostname([%#.*s], %'zu) → %d% m", len, name, len, rc);
|
||||
STRACE("gethostname([%#.*s], %'zu) → %d% m", (int)len, name, len, rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -558,34 +558,6 @@ static int ioctl_siocgifflags(int fd, void *arg) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets "close on exec" on file descriptor the fast way.
|
||||
*
|
||||
* @see ioctl(fd, FIOCLEX, 0) dispatches here
|
||||
*/
|
||||
static int ioctl_fioclex(int fd, int req) {
|
||||
int rc;
|
||||
if (fd >= 0) {
|
||||
if (IsWindows() || (fd < g_fds.n && g_fds.p[fd].kind == kFdZip)) {
|
||||
if (__isfdopen(fd)) {
|
||||
if (req == FIOCLEX) {
|
||||
g_fds.p[fd].flags |= O_CLOEXEC;
|
||||
} else {
|
||||
g_fds.p[fd].flags &= ~O_CLOEXEC;
|
||||
}
|
||||
rc = 0;
|
||||
} else {
|
||||
rc = ebadf();
|
||||
}
|
||||
} else {
|
||||
rc = sys_ioctl(fd, req);
|
||||
}
|
||||
} else {
|
||||
rc = einval();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs special i/o operation on file descriptor.
|
||||
*
|
||||
|
@ -618,17 +590,6 @@ static int ioctl_fioclex(int fd, int req) {
|
|||
* - `TIOCNXCL` to give up exclusive mode on terminal. Only
|
||||
* available on UNIX.
|
||||
*
|
||||
* - `FIOCLEX` sets the `O_CLOEXEC` state (no arg) noting that this
|
||||
* polyfill may be removed in the future, and code should migrate
|
||||
* to the equivalent fcntl() api.
|
||||
*
|
||||
* - `FIONBIO` sets the `O_NONBLOCK` state (arg is `int *enabled`)
|
||||
* which is supported on Windows for sockets.
|
||||
*
|
||||
* - `FIONCLEX` clears the `O_CLOEXEC` state (no arg) noting that
|
||||
* this polyfill may be removed in the future, and code should
|
||||
* migrate to the equivalent fcntl() api.
|
||||
*
|
||||
* - `SIOCGIFCONF` takes an struct ifconf object of a given size,
|
||||
* whose arg is `struct ifconf *`. It implements the Linux style
|
||||
* and modifies the following:
|
||||
|
@ -647,6 +608,9 @@ static int ioctl_fioclex(int fd, int req) {
|
|||
* network broadcast addr. This data structure should be obtained
|
||||
* by calling `SIOCGIFCONF`.
|
||||
*
|
||||
* - `FIONBIO` isn't polyfilled; use `fcntl(F_SETFL, O_NONBLOCK)`
|
||||
* - `FIOCLEX` isn't polyfilled; use `fcntl(F_SETFD, FD_CLOEXEC)`
|
||||
* - `FIONCLEX` isn't polyfilled; use `fcntl(F_SETFD, 0)`
|
||||
* - `TCGETS` isn't polyfilled; use tcgetattr()
|
||||
* - `TCSETS` isn't polyfilled; use tcsetattr()
|
||||
* - `TCSETSW` isn't polyfilled; use tcsetattr()
|
||||
|
@ -673,10 +637,6 @@ int ioctl(int fd, unsigned long request, ...) {
|
|||
va_end(va);
|
||||
if (request == FIONBIO) {
|
||||
rc = ioctl_default(fd, request, arg);
|
||||
} else if (request == FIOCLEX) {
|
||||
rc = ioctl_fioclex(fd, request);
|
||||
} else if (request == FIONCLEX) {
|
||||
rc = ioctl_fioclex(fd, request);
|
||||
} else if (request == TIOCGWINSZ) {
|
||||
rc = tcgetwinsize(fd, arg);
|
||||
} else if (request == TIOCSWINSZ) {
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/overlapped.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
static textwindows ssize_t sys_read_nt_impl(struct Fd *fd, void *data,
|
||||
|
@ -51,6 +52,9 @@ static textwindows ssize_t sys_read_nt_impl(struct Fd *fd, void *data,
|
|||
if (SleepEx(__SIG_POLLING_INTERVAL_MS, true) == kNtWaitIoCompletion) {
|
||||
POLLTRACE("IOCP EINTR");
|
||||
}
|
||||
if (fd->flags & O_NONBLOCK) {
|
||||
return eagain();
|
||||
}
|
||||
if (_check_interrupts(true, g_fds.p)) {
|
||||
POLLTRACE("sys_read_nt interrupted");
|
||||
return -1;
|
||||
|
|
|
@ -56,6 +56,6 @@ ssize_t readlinkat(int dirfd, const char *path, char *buf, size_t bufsiz) {
|
|||
bytes = sys_readlinkat_nt(dirfd, path, buf, bufsiz);
|
||||
}
|
||||
STRACE("readlinkat(%s, %#s, [%#.*s]) → %d% m", DescribeDirfd(dirfd), path,
|
||||
MAX(0, bytes), buf, bytes);
|
||||
(int)MAX(0, bytes), buf, bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ unsigned __wincrash(struct NtExceptionPointers *ep) {
|
|||
ucontext_t ctx;
|
||||
struct CosmoTib *tib;
|
||||
static bool noreentry;
|
||||
noreentry = true;
|
||||
|
||||
if ((tib = __tls_enabled ? __get_tls_privileged() : 0)) {
|
||||
if (~tib->tib_flags & TIB_FLAG_WINCRASHING) {
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/errno.h"
|
||||
#include "libc/nt/winsock.h"
|
||||
#include "libc/sock/internal.h"
|
||||
|
||||
/**
|
||||
* Error return path for winsock wrappers.
|
||||
*/
|
||||
textwindows int64_t __winsockerr(void) {
|
||||
errno = __dos2errno(WSAGetLastError());
|
||||
return -1;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue