mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-07 03:38:31 +00:00
Improve synchronization
- Fix bugs in kDos2Errno definition - malloc() should now be thread safe - Fix bug in rollup.com header generator - Fix open(O_APPEND) on the New Technology - Fix select() on the New Technology and test it - Work towards refactoring i/o for thread safety - Socket reads and writes on NT now poll for signals - Work towards i/o completion ports on the New Technology - Make read() and write() intermittently check for signals - Blinkenlights keyboard i/o so much better on NT w/ poll() - You can now poll() files and sockets at the same time on NT - Fix bug in appendr() that manifests with dlmalloc footers off
This commit is contained in:
parent
233144b19d
commit
933411ba99
266 changed files with 8761 additions and 4344 deletions
|
@ -17,6 +17,8 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/struct/pollfd.h"
|
||||
|
@ -38,9 +40,13 @@ textwindows int sys_accept_nt(struct Fd *fd, void *addr, uint32_t *addrsize,
|
|||
int client, oflags;
|
||||
struct SockFd *sockfd, *sockfd2;
|
||||
sockfd = (struct SockFd *)fd->extra;
|
||||
if (_check_interrupts(true, g_fds.p)) return eintr();
|
||||
for (;;) {
|
||||
if (!WSAPoll(&(struct sys_pollfd_nt){fd->handle, POLLIN}, 1, 1000))
|
||||
if (!WSAPoll(&(struct sys_pollfd_nt){fd->handle, POLLIN}, 1,
|
||||
__SIG_POLLING_INTERVAL_MS)) {
|
||||
if (_check_interrupts(true, g_fds.p)) return eintr();
|
||||
continue;
|
||||
}
|
||||
if ((h = WSAAccept(fd->handle, addr, (int32_t *)addrsize, 0, 0)) != -1) {
|
||||
oflags = 0;
|
||||
if (flags & SOCK_CLOEXEC) oflags |= O_CLOEXEC;
|
||||
|
@ -48,13 +54,13 @@ textwindows int sys_accept_nt(struct Fd *fd, void *addr, uint32_t *addrsize,
|
|||
if ((!(flags & SOCK_NONBLOCK) ||
|
||||
__sys_ioctlsocket_nt(h, FIONBIO, (uint32_t[]){1}) != -1) &&
|
||||
(sockfd2 = calloc(1, sizeof(struct SockFd)))) {
|
||||
if ((client = __reservefd()) != -1) {
|
||||
if ((client = __reservefd(-1)) != -1) {
|
||||
sockfd2->family = sockfd->family;
|
||||
sockfd2->type = sockfd->type;
|
||||
sockfd2->protocol = sockfd->protocol;
|
||||
sockfd2->event = WSACreateEvent();
|
||||
g_fds.p[client].kind = kFdSocket;
|
||||
g_fds.p[client].flags = oflags;
|
||||
g_fds.p[client].mode = 0140666;
|
||||
g_fds.p[client].handle = h;
|
||||
g_fds.p[client].extra = (uintptr_t)sockfd2;
|
||||
return client;
|
||||
|
|
|
@ -49,7 +49,7 @@ int accept4(int fd, void *out_addr, uint32_t *inout_addrsize, int flags) {
|
|||
} else {
|
||||
rc = ebadf();
|
||||
}
|
||||
STRACE("accept4(%d, [%s]) -> %d% m", fd,
|
||||
STRACE("accept4(%d, [%s]) -> %d% lm", fd,
|
||||
__describe_sockaddr(out_addr, inout_addrsize ? *inout_addrsize : 0),
|
||||
rc);
|
||||
return rc;
|
||||
|
|
61
libc/sock/basesocket.c
Normal file
61
libc/sock/basesocket.c
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/nt/enum/sio.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/winsock.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/sock/sock.h"
|
||||
|
||||
static textwindows int64_t GetNtBspSocket(int64_t socket, uint32_t ioctl) {
|
||||
uint32_t bytes;
|
||||
int64_t bsp_socket;
|
||||
if (WSAIoctl(socket, ioctl, NULL, 0, &bsp_socket, sizeof(bsp_socket), &bytes,
|
||||
NULL, NULL) != -1) {
|
||||
return bsp_socket;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
textwindows int64_t GetNtBaseSocket(int64_t socket) {
|
||||
int64_t base_socket;
|
||||
for (;;) {
|
||||
base_socket = GetNtBspSocket(socket, kNtSioBaseHandle);
|
||||
if (base_socket != -1) return base_socket;
|
||||
if (WSAGetLastError() == WSAENOTSOCK) return __winsockerr();
|
||||
/*
|
||||
* Even though Microsoft documentation clearly states that Layered
|
||||
* Spyware Providers must never ever intercept the SIO_BASE_HANDLE
|
||||
* ioctl, Komodia LSPs (that Lenovo got sued for preinstalling) do
|
||||
* so anyway in order to redirect decrypted https requests through
|
||||
* some foreign proxy and inject ads which breaks high-performance
|
||||
* network event io. However it doesn't handle SIO_BSP_HANDLE_POLL
|
||||
* which will at least let us obtain the socket associated with the
|
||||
* next winsock protocol chain entry. If this succeeds, loop around
|
||||
* and call SIO_BASE_HANDLE again with the returned BSP socket, to
|
||||
* make sure we unwrap all layers and retrieve the real base socket.
|
||||
*/
|
||||
base_socket = GetNtBspSocket(socket, kNtSioBspHandlePoll);
|
||||
if (base_socket != -1 && base_socket != socket) {
|
||||
socket = base_socket;
|
||||
} else {
|
||||
return __winsockerr();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -62,6 +62,6 @@ int bind(int fd, const void *addr, uint32_t addrsize) {
|
|||
} else {
|
||||
rc = einval();
|
||||
}
|
||||
STRACE("bind(%d, %s) -> %d% m", fd, __describe_sockaddr(addr, addrsize), rc);
|
||||
STRACE("bind(%d, %s) -> %d% lm", fd, __describe_sockaddr(addr, addrsize), rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
textwindows int sys_closesocket_nt(struct Fd *fd) {
|
||||
struct SockFd *sockfd;
|
||||
sockfd = (struct SockFd *)fd->extra;
|
||||
WSACloseEvent(sockfd->event);
|
||||
free(sockfd);
|
||||
if (__sys_closesocket_nt(fd->handle) != -1) {
|
||||
return 0;
|
||||
|
|
|
@ -48,7 +48,7 @@ int connect(int fd, const void *addr, uint32_t addrsize) {
|
|||
} else {
|
||||
rc = efault();
|
||||
}
|
||||
STRACE("connect(%d, %s) -> %d% m", fd, __describe_sockaddr(addr, addrsize),
|
||||
STRACE("connect(%d, %s) -> %d% lm", fd, __describe_sockaddr(addr, addrsize),
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@ textwindows struct SockFd *_dupsockfd(struct SockFd *sockfd) {
|
|||
newsf->family = sockfd->family;
|
||||
newsf->type = sockfd->type;
|
||||
newsf->protocol = sockfd->protocol;
|
||||
newsf->event = WSACreateEvent();
|
||||
}
|
||||
return newsf;
|
||||
}
|
||||
|
|
|
@ -1324,7 +1324,7 @@ static textwindows dontinline int sys_epoll_create1_nt(uint32_t flags) {
|
|||
struct PortState *port_state;
|
||||
struct TsTreeNode *tree_node;
|
||||
if (wepoll_init() < 0) return -1;
|
||||
if ((fd = __reservefd()) == -1) return -1;
|
||||
if ((fd = __reservefd(-1)) == -1) return -1;
|
||||
port_state = port_new(&ephnd);
|
||||
if (!port_state) {
|
||||
__releasefd(fd);
|
||||
|
@ -1341,6 +1341,7 @@ static textwindows dontinline int sys_epoll_create1_nt(uint32_t flags) {
|
|||
g_fds.p[fd].kind = kFdEpoll;
|
||||
g_fds.p[fd].handle = ephnd;
|
||||
g_fds.p[fd].flags = flags;
|
||||
g_fds.p[fd].mode = 0140666;
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ int getpeername(int fd, void *out_addr, uint32_t *out_addrsize) {
|
|||
} else {
|
||||
rc = ebadf();
|
||||
}
|
||||
STRACE("getpeername(%d, [%s]) -> %d% m", fd,
|
||||
STRACE("getpeername(%d, [%s]) -> %d% lm", fd,
|
||||
__describe_sockaddr(out_addr, out_addrsize ? *out_addrsize : 0), rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ int getsockname(int fd, void *out_addr, uint32_t *out_addrsize) {
|
|||
} else {
|
||||
rc = ebadf();
|
||||
}
|
||||
STRACE("getsockname(%d, [%s]) -> %d% m", fd,
|
||||
STRACE("getsockname(%d, [%s]) -> %d% lm", fd,
|
||||
__describe_sockaddr(out_addr, out_addrsize ? *out_addrsize : 0), rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ int getsockopt(int fd, int level, int optname, void *out_opt_optval,
|
|||
} else {
|
||||
rc = ebadf();
|
||||
}
|
||||
STRACE("getsockopt(%d, %#x, %#x, %p, %p) → %d% m", fd, level, optname,
|
||||
STRACE("getsockopt(%d, %#x, %#x, %p, %p) → %d% lm", fd, level, optname,
|
||||
out_opt_optval, out_optlen, rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_SOCK_INTERNAL_H_
|
||||
#define COSMOPOLITAN_LIBC_SOCK_INTERNAL_H_
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/nt/struct/overlapped.h"
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
#include "libc/nt/winsock.h"
|
||||
#include "libc/sock/select.h"
|
||||
|
@ -54,23 +55,26 @@ struct sockaddr_un_bsd {
|
|||
|
||||
/* ------------------------------------------------------------------------------------*/
|
||||
|
||||
#define SOCKFD_OVERLAP_BUFSIZ 128
|
||||
|
||||
struct SockFd {
|
||||
int family;
|
||||
int type;
|
||||
int protocol;
|
||||
int64_t event;
|
||||
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) __msabi;
|
||||
bool32 (*__msabi ConnectEx)(int64_t s, const struct sockaddr *name,
|
||||
int namelen, const void *opt_lpSendBuffer,
|
||||
uint32_t dwSendDataLength,
|
||||
uint32_t *out_lpdwBytesSent,
|
||||
struct NtOverlapped *inout_lpOverlapped);
|
||||
bool32 (*__msabi 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);
|
||||
};
|
||||
|
||||
hidden extern int64_t __iocp;
|
||||
|
||||
errno_t __dos2errno(uint32_t);
|
||||
errno_t __dos2errno(uint32_t) hidden;
|
||||
|
||||
void _firewall(const void *, uint32_t) hidden;
|
||||
|
||||
|
@ -109,7 +113,7 @@ int32_t sys_epoll_ctl(int32_t, int32_t, int32_t, void *) hidden;
|
|||
int32_t sys_epoll_wait(int32_t, void *, int32_t, int32_t) hidden;
|
||||
int sys_poll_metal(struct pollfd *, size_t, unsigned);
|
||||
|
||||
int sys_poll_nt(struct pollfd *, uint64_t, uint64_t) hidden;
|
||||
int sys_poll_nt(struct pollfd *, uint64_t, uint64_t *) hidden;
|
||||
int sys_getsockopt_nt(struct Fd *, int, int, void *, uint32_t *) hidden;
|
||||
int sys_getsockname_nt(struct Fd *, void *, uint32_t *) hidden;
|
||||
int sys_getpeername_nt(struct Fd *, void *, uint32_t *) hidden;
|
||||
|
@ -128,6 +132,8 @@ int sys_select_nt(int, fd_set *, fd_set *, fd_set *, struct timeval *) hidden;
|
|||
int sys_shutdown_nt(struct Fd *, int) hidden;
|
||||
int sys_setsockopt_nt(struct Fd *, int, int, const void *, uint32_t) hidden;
|
||||
|
||||
ssize_t sys_send_nt(int, const struct iovec *, size_t, uint32_t) hidden;
|
||||
ssize_t sys_recv_nt(struct Fd *, const struct iovec *, size_t, uint32_t) hidden;
|
||||
size_t __iovec2nt(struct NtIovec[hasatleast 16], const struct iovec *,
|
||||
size_t) hidden;
|
||||
ssize_t sys_sendto_nt(int, const struct iovec *, size_t, uint32_t, void *,
|
||||
|
@ -140,7 +146,7 @@ int64_t __winsockerr(void) nocallback hidden;
|
|||
int __fixupnewsockfd(int, int) hidden;
|
||||
int64_t __winsockblock(int64_t, unsigned, int64_t) hidden;
|
||||
struct SockFd *_dupsockfd(struct SockFd *) hidden;
|
||||
|
||||
int64_t GetNtBaseSocket(int64_t) hidden;
|
||||
int sys_close_epoll(int) hidden;
|
||||
|
||||
/**
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/weaken.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/mem/mem.h"
|
||||
|
@ -37,17 +38,16 @@
|
|||
hidden struct NtWsaData kNtWsaData;
|
||||
|
||||
static textwindows void WinSockCleanup(void) {
|
||||
size_t i;
|
||||
STRACE("WSACleanup()");
|
||||
WSACleanup();
|
||||
for (i = 0; i < g_fds.n; ++i) {
|
||||
int i, rc;
|
||||
STRACE("WinSockCleanup()");
|
||||
for (i = g_fds.n; i--;) {
|
||||
if (g_fds.p[i].kind == kFdSocket) {
|
||||
if (weaken(free)) {
|
||||
weaken(free)((struct SockFd *)g_fds.p[i].extra);
|
||||
g_fds.p[i].extra = 0;
|
||||
}
|
||||
close(i);
|
||||
}
|
||||
}
|
||||
// TODO(jart): Check WSACleanup() result code
|
||||
rc = WSACleanup();
|
||||
STRACE("WSACleanup() → %d% lm", rc);
|
||||
}
|
||||
|
||||
textwindows noasan void WinSockInit(void) {
|
||||
|
|
|
@ -43,6 +43,6 @@ int listen(int fd, int backlog) {
|
|||
} else {
|
||||
rc = ebadf();
|
||||
}
|
||||
STRACE("listen(%d, %d) → %d% m", fd, backlog, rc);
|
||||
STRACE("listen(%d, %d) → %d% lm", fd, backlog, rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
158
libc/sock/ntstdin.greg.c
Normal file
158
libc/sock/ntstdin.greg.c
Normal file
|
@ -0,0 +1,158 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#define ShouldUseMsabiAttribute() 1
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nexgen32e/nt2sysv.h"
|
||||
#include "libc/nt/createfile.h"
|
||||
#include "libc/nt/enum/accessmask.h"
|
||||
#include "libc/nt/enum/creationdisposition.h"
|
||||
#include "libc/nt/enum/fileflagandattributes.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/events.h"
|
||||
#include "libc/nt/ipc.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
#include "libc/sock/ntstdin.internal.h"
|
||||
|
||||
/**
|
||||
* @fileoverview Pollable Standard Input for the New Technology.
|
||||
*/
|
||||
|
||||
__msabi extern typeof(CloseHandle) *const __imp_CloseHandle;
|
||||
|
||||
static textwindows bool IsEof(bool ok, uint32_t got) {
|
||||
return (ok && !got) || (!ok && (__imp_GetLastError() == kNtErrorHandleEof ||
|
||||
__imp_GetLastError() == kNtErrorBrokenPipe));
|
||||
}
|
||||
|
||||
static textwindows uint32_t StdinWorkerThread(void *arg) {
|
||||
char buf[512];
|
||||
bool32 ok = true;
|
||||
uint32_t i, rc, got, err, wrote;
|
||||
struct NtStdinWorker w, *wp = arg;
|
||||
STRACE("StdinWorkerThread(%ld → %ld → %ld) pid %d tid %d", wp->reader,
|
||||
wp->writer, wp->consumer, getpid(), gettid());
|
||||
__sync_lock_release(&wp->sync);
|
||||
w = *wp;
|
||||
do {
|
||||
ok = __imp_ReadFile(w.reader, buf, sizeof(buf), &got, 0);
|
||||
/* When writing to a non-blocking, byte-mode pipe handle with
|
||||
insufficient buffer space, WriteFile returns TRUE with
|
||||
*lpNumberOfBytesWritten < nNumberOfBytesToWrite.
|
||||
──Quoth MSDN WriteFile() */
|
||||
for (i = 0; ok && i < got; i += wrote) {
|
||||
ok = __imp_WriteFile(w.writer, buf + i, got - i, &wrote, 0);
|
||||
}
|
||||
} while (ok && got);
|
||||
if (!ok) {
|
||||
err = __imp_GetLastError();
|
||||
if (err == kNtErrorHandleEof || err == kNtErrorBrokenPipe ||
|
||||
err == kNtErrorNoData) {
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
STRACE("StdinWorkerThread(%ld → %ld → %ld) → %hhhd %d", w.reader, w.writer,
|
||||
w.consumer, __imp_GetLastError());
|
||||
return !ok;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts read-only file descriptor to pollable named pipe.
|
||||
*
|
||||
* @param fd is open file descriptor to convert
|
||||
* @return new object on success, or 0 w/ errno
|
||||
*/
|
||||
textwindows struct NtStdinWorker *NewNtStdinWorker(int fd) {
|
||||
struct NtStdinWorker *w;
|
||||
STRACE("LaunchNtStdinWorker(%d) pid %d tid %d", fd, getpid(), gettid());
|
||||
assert(!g_fds.p[fd].worker);
|
||||
assert(__isfdopen(fd));
|
||||
if (!(w = calloc(1, sizeof(struct NtStdinWorker)))) return 0;
|
||||
w->refs = 1;
|
||||
w->sync = 1;
|
||||
w->reader = g_fds.p[fd].handle;
|
||||
if ((w->consumer = CreateNamedPipe(
|
||||
CreatePipeName(w->name),
|
||||
kNtPipeAccessInbound | kNtFileFlagOverlapped,
|
||||
kNtPipeTypeByte | kNtPipeReadmodeByte | kNtPipeRejectRemoteClients,
|
||||
1, 512, 512, 0, 0)) != -1) {
|
||||
if ((w->writer = CreateFile(w->name, kNtGenericWrite, 0, 0, kNtOpenExisting,
|
||||
kNtFileFlagOverlapped, 0)) != -1) {
|
||||
if ((w->worker = CreateThread(0, 0, NT2SYSV(StdinWorkerThread), w, 0,
|
||||
&w->tid)) != -1) {
|
||||
while (__sync_lock_test_and_set(&w->sync, __ATOMIC_CONSUME)) {
|
||||
__builtin_ia32_pause();
|
||||
}
|
||||
g_fds.p[fd].handle = w->consumer;
|
||||
g_fds.p[fd].worker = w;
|
||||
return w;
|
||||
}
|
||||
CloseHandle(w->writer);
|
||||
}
|
||||
CloseHandle(w->consumer);
|
||||
}
|
||||
free(w);
|
||||
return w;
|
||||
}
|
||||
|
||||
/**
|
||||
* References stdin worker on the New Technology.
|
||||
* @param w is non-null worker object
|
||||
* @return worker object for new fd
|
||||
*/
|
||||
textwindows struct NtStdinWorker *RefNtStdinWorker(struct NtStdinWorker *w) {
|
||||
__atomic_fetch_add(&w->refs, 1, __ATOMIC_RELAXED);
|
||||
return w;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dereferences stdin worker on the New Technology.
|
||||
* @param w is non-null worker object
|
||||
* @return true if ok otherwise false
|
||||
*/
|
||||
textwindows bool UnrefNtStdinWorker(struct NtStdinWorker *w) {
|
||||
bool ok = true;
|
||||
if (__atomic_sub_fetch(&w->refs, 1, __ATOMIC_SEQ_CST)) return true;
|
||||
// w->consumer is freed by close_nt()
|
||||
if (!CloseHandle(w->writer)) ok = false;
|
||||
if (!CloseHandle(w->reader)) ok = false;
|
||||
if (!CloseHandle(w->worker)) ok = false;
|
||||
free(w);
|
||||
return ok;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs post fork for stdin workers on the New Technology.
|
||||
*/
|
||||
textwindows void ForkNtStdinWorker(void) {
|
||||
for (int i = 0; i < g_fds.n; ++i) {
|
||||
if (g_fds.p[i].kind && g_fds.p[i].worker) {
|
||||
g_fds.p[i].handle = g_fds.p[i].worker->reader;
|
||||
free(g_fds.p[i].worker);
|
||||
g_fds.p[i].worker = 0;
|
||||
}
|
||||
}
|
||||
}
|
24
libc/sock/ntstdin.internal.h
Normal file
24
libc/sock/ntstdin.internal.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_SOCK_NTSTDIN_INTERNAL_H_
|
||||
#define COSMOPOLITAN_LIBC_SOCK_NTSTDIN_INTERNAL_H_
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
struct NtStdinWorker { /* non-inherited */
|
||||
volatile char sync; /* spin sync start */
|
||||
int refs; /* reference count */
|
||||
uint32_t tid; /* of the worker */
|
||||
int64_t reader; /* the real handle */
|
||||
int64_t writer; /* for the worker */
|
||||
int64_t worker; /* thread handle */
|
||||
int64_t consumer; /* same as Fd::handle */
|
||||
char16_t name[64]; /* for named pipe */
|
||||
};
|
||||
|
||||
struct NtStdinWorker *NewNtStdinWorker(int) hidden;
|
||||
struct NtStdinWorker *RefNtStdinWorker(struct NtStdinWorker *) hidden;
|
||||
bool UnrefNtStdinWorker(struct NtStdinWorker *) hidden;
|
||||
void ForkNtStdinWorker(void) hidden;
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_SOCK_NTSTDIN_INTERNAL_H_ */
|
|
@ -73,7 +73,7 @@ int sys_poll_metal(struct pollfd *fds, size_t nfds, unsigned timeout_ms) {
|
|||
if (rc || !blocking || unsignedsubtract(rdtsc(), start) >= timeout) {
|
||||
break;
|
||||
} else {
|
||||
asm("pause");
|
||||
__builtin_ia32_pause();
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
|
|
|
@ -18,42 +18,189 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/bits.h"
|
||||
#include "libc/bits/weaken.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/sigbits.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/spinlock.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/ipc.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/pollfd.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/nt/winsock.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/sock/ntstdin.internal.h"
|
||||
#include "libc/sock/yoink.inc"
|
||||
#include "libc/sysv/consts/poll.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t ms) {
|
||||
int i, got, waitfor;
|
||||
struct sys_pollfd_nt ntfds[64];
|
||||
if (nfds >= ARRAYLEN(ntfds)) return einval();
|
||||
for (i = 0; i < nfds; ++i) {
|
||||
if (fds[i].fd >= 0) {
|
||||
if (!__isfdkind(fds[i].fd, kFdSocket)) return enotsock();
|
||||
ntfds[i].handle = g_fds.p[fds[i].fd].handle;
|
||||
ntfds[i].events = fds[i].events & (POLLPRI | POLLIN | POLLOUT);
|
||||
#undef STRACE // too verbosen
|
||||
#define STRACE(...) // but don't want to delete
|
||||
|
||||
_Alignas(64) static char poll_lock;
|
||||
|
||||
/**
|
||||
* Polls on the New Technology.
|
||||
*
|
||||
* This function is used to implement poll() and select(). You may poll
|
||||
* on both sockets and files at the same time. We also poll for signals
|
||||
* while poll is polling.
|
||||
*/
|
||||
textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms) {
|
||||
bool ok;
|
||||
uint32_t avail;
|
||||
struct sys_pollfd_nt pipefds[8];
|
||||
struct sys_pollfd_nt sockfds[64];
|
||||
int pipeindices[ARRAYLEN(pipefds)];
|
||||
int sockindices[ARRAYLEN(sockfds)];
|
||||
int i, sn, pn, failed, gotpipes, gotsocks, waitfor;
|
||||
|
||||
// check for interrupts early before doing work
|
||||
if (_check_interrupts(false, g_fds.p)) return eintr();
|
||||
|
||||
// do the planning
|
||||
// we need to read static variables
|
||||
// we might need to spawn threads and open pipes
|
||||
_spinlock(&poll_lock);
|
||||
for (failed = sn = pn = i = 0; i < nfds; ++i) {
|
||||
if (fds[i].fd < 0) continue;
|
||||
if (__isfdopen(fds[i].fd)) {
|
||||
if (__isfdkind(fds[i].fd, kFdSocket)) {
|
||||
if (sn < ARRAYLEN(sockfds)) {
|
||||
sockindices[sn] = i;
|
||||
sockfds[sn].handle = g_fds.p[fds[i].fd].handle;
|
||||
sockfds[sn].events = fds[i].events & (POLLPRI | POLLIN | POLLOUT);
|
||||
sn += 1;
|
||||
} else {
|
||||
// too many socket fds
|
||||
failed = enomem();
|
||||
break;
|
||||
}
|
||||
} else if (fds[i].events & POLLIN) {
|
||||
if (!g_fds.p[fds[i].fd].worker) {
|
||||
if (!(g_fds.p[fds[i].fd].worker = NewNtStdinWorker(fds[i].fd))) {
|
||||
// failed to launch stdin worker
|
||||
failed = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pn < ARRAYLEN(pipefds)) {
|
||||
pipeindices[pn] = i;
|
||||
pipefds[pn].handle = g_fds.p[fds[i].fd].handle;
|
||||
pipefds[pn].events = fds[i].events & (POLLPRI | POLLIN | POLLOUT);
|
||||
pn += 1;
|
||||
} else {
|
||||
// too many non-socket fds
|
||||
failed = enomem();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// non-sock w/o pollin
|
||||
failed = enotsock();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ntfds[i].handle = -1;
|
||||
ntfds[i].events = POLLIN;
|
||||
// non-open file descriptor
|
||||
failed = einval();
|
||||
break;
|
||||
}
|
||||
}
|
||||
_spunlock(&poll_lock);
|
||||
if (failed) {
|
||||
// failed to create a polling solution
|
||||
return failed;
|
||||
}
|
||||
|
||||
// perform the i/o and sleeping and looping
|
||||
for (;;) {
|
||||
if (_check_interrupts(false, g_fds.p)) return eintr();
|
||||
waitfor = MIN(__SIG_POLLING_INTERVAL_MS, ms); /* for ctrl+c */
|
||||
if ((got = WSAPoll(ntfds, nfds, waitfor)) != -1) {
|
||||
if (!got && (ms -= waitfor) > 0) continue;
|
||||
for (i = 0; i < nfds; ++i) {
|
||||
fds[i].revents = ntfds[i].handle < 0 ? 0 : ntfds[i].revents;
|
||||
// see if input is available on non-sockets
|
||||
for (gotpipes = i = 0; i < pn; ++i) {
|
||||
ok = PeekNamedPipe(pipefds[i].handle, 0, 0, 0, &avail, 0);
|
||||
STRACE("PeekNamedPipe(%ld, 0, 0, 0, [%'u], 0) → %hhhd% m",
|
||||
pipefds[i].handle, avail, ok);
|
||||
if (ok) {
|
||||
if (avail) {
|
||||
pipefds[i].revents = POLLIN;
|
||||
gotpipes += 1;
|
||||
} else {
|
||||
pipefds[i].revents = 0;
|
||||
}
|
||||
} else {
|
||||
pipefds[i].revents = POLLERR;
|
||||
gotpipes += 1;
|
||||
}
|
||||
return got;
|
||||
}
|
||||
// if we haven't found any good results yet then here we
|
||||
// compute a small time slice we don't mind sleeping for
|
||||
waitfor = gotpipes ? 0 : MIN(__SIG_POLLING_INTERVAL_MS, *ms);
|
||||
if (sn) {
|
||||
// we need to poll the socket handles separately because
|
||||
// microsoft certainly loves to challenge us with coding
|
||||
// please note that winsock will fail if we pass zero fd
|
||||
STRACE("WSAPoll(%p, %u, %'d) out of %'lu", sockfds, sn, waitfor, *ms);
|
||||
if ((gotsocks = WSAPoll(sockfds, sn, waitfor)) == -1) {
|
||||
return __winsockerr();
|
||||
}
|
||||
*ms -= waitfor;
|
||||
} else {
|
||||
return __winsockerr();
|
||||
gotsocks = 0;
|
||||
if (!gotpipes && waitfor) {
|
||||
// if we've only got pipes and none of them are ready
|
||||
// then we'll just explicitly sleep for the time left
|
||||
STRACE("SleepEx(%'d, false) out of %'lu", waitfor, *ms);
|
||||
if (SleepEx(waitfor, true) == kNtWaitIoCompletion) {
|
||||
STRACE("IOCP TRIGGERED EINTR");
|
||||
return eintr();
|
||||
}
|
||||
*ms -= waitfor;
|
||||
}
|
||||
}
|
||||
// we gave all the sockets and all the named pipes a shot
|
||||
// if we found anything at all then it's time to end work
|
||||
if (gotpipes || gotsocks || *ms <= 0) {
|
||||
break;
|
||||
}
|
||||
// otherwise loop limitlessly for timeout to elapse while
|
||||
// checking for signal delivery interrupts, along the way
|
||||
if (_check_interrupts(false, g_fds.p)) {
|
||||
return eintr();
|
||||
}
|
||||
}
|
||||
|
||||
// we got some
|
||||
// assemble the result
|
||||
for (i = 0; i < pn; ++i) {
|
||||
fds[pipeindices[i]].revents =
|
||||
pipefds[i].handle < 0 ? 0 : pipefds[i].revents;
|
||||
}
|
||||
for (i = 0; i < sn; ++i) {
|
||||
fds[sockindices[i]].revents =
|
||||
sockfds[i].handle < 0 ? 0 : sockfds[i].revents;
|
||||
}
|
||||
return gotpipes + gotsocks;
|
||||
}
|
||||
|
||||
static textexit void __freefds_workers(void) {
|
||||
int i;
|
||||
STRACE("__freefds_workers()");
|
||||
for (i = g_fds.n; i--;) {
|
||||
if (g_fds.p[i].kind && g_fds.p[i].worker) {
|
||||
close(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static textstartup void __freefds_workers_init(void) {
|
||||
atexit(__freefds_workers);
|
||||
}
|
||||
|
||||
const void *const __freefds_workers_ctor[] initarray = {
|
||||
__freefds_workers_init,
|
||||
};
|
||||
|
|
|
@ -28,19 +28,20 @@
|
|||
/**
|
||||
* Waits for something to happen on multiple file descriptors at once.
|
||||
*
|
||||
* @param fds[𝑖].fd should have been created with SOCK_NONBLOCK passed
|
||||
* to socket() or accept4()
|
||||
* @param fds[𝑖].events flags can have POLL{IN,OUT,PRI}
|
||||
* @param fds[𝑖].fd should be a socket, input pipe, or conosle input
|
||||
* @param fds[𝑖].events flags can have POLLIN, POLLOUT, and POLLPRI
|
||||
* @param timeout_ms if 0 means don't wait and -1 means wait forever
|
||||
* @return number of items fds whose revents field has been set to
|
||||
* nonzero to describe its events, or -1 w/ errno
|
||||
* @return fds[𝑖].revents flags can have:
|
||||
* (fds[𝑖].events & POLL{IN,OUT,PRI,HUP,ERR,NVAL})
|
||||
* @asyncsignalsafe
|
||||
* @threadsafe
|
||||
* @norestart
|
||||
*/
|
||||
int poll(struct pollfd *fds, uint64_t nfds, int32_t timeout_ms) {
|
||||
int poll(struct pollfd *fds, size_t nfds, int timeout_ms) {
|
||||
int rc;
|
||||
uint64_t millis;
|
||||
if (IsAsan() && !__asan_is_valid(fds, nfds * sizeof(struct pollfd))) {
|
||||
rc = efault();
|
||||
} else if (!IsWindows()) {
|
||||
|
@ -50,8 +51,9 @@ int poll(struct pollfd *fds, uint64_t nfds, int32_t timeout_ms) {
|
|||
rc = sys_poll_metal(fds, nfds, timeout_ms);
|
||||
}
|
||||
} else {
|
||||
rc = sys_poll_nt(fds, nfds, timeout_ms);
|
||||
millis = timeout_ms;
|
||||
rc = sys_poll_nt(fds, nfds, &millis);
|
||||
}
|
||||
STRACE("poll(%p, %'lu, %'d) → %d% m", fds, nfds, timeout_ms, rc);
|
||||
STRACE("poll(%p, %'lu, %'d) → %d% lm", fds, nfds, timeout_ms, rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
90
libc/sock/recv-nt.c
Normal file
90
libc/sock/recv-nt.c
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*-*- 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/assert.h"
|
||||
#include "libc/bits/weaken.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/log/backtrace.internal.h"
|
||||
#include "libc/nt/enum/wait.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/struct/overlapped.h"
|
||||
#include "libc/nt/winsock.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/sock/yoink.inc"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Performs stream socket receive on New Technology.
|
||||
*
|
||||
* @param fd must be a socket
|
||||
* @return number of bytes received, or -1 w/ errno
|
||||
*/
|
||||
textwindows ssize_t sys_recv_nt(struct Fd *fd, const struct iovec *iov,
|
||||
size_t iovlen, uint32_t flags) {
|
||||
ssize_t rc;
|
||||
uint32_t i, got = 0;
|
||||
struct NtIovec iovnt[16];
|
||||
struct NtOverlapped overlapped = {.hEvent = WSACreateEvent()};
|
||||
|
||||
if (_check_interrupts(true, g_fds.p)) return eintr();
|
||||
|
||||
if (!WSARecv(fd->handle, iovnt, __iovec2nt(iovnt, iov, iovlen), &got, &flags,
|
||||
&overlapped, NULL)) {
|
||||
rc = got;
|
||||
goto Finished;
|
||||
}
|
||||
|
||||
if (WSAGetLastError() != kNtErrorIoPending) {
|
||||
STRACE("WSARecv failed %lm");
|
||||
rc = __winsockerr();
|
||||
goto Finished;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
i = WSAWaitForMultipleEvents(1, &overlapped.hEvent, true,
|
||||
__SIG_POLLING_INTERVAL_MS, true);
|
||||
if (i == kNtWaitFailed) {
|
||||
STRACE("WSAWaitForMultipleEvents failed %lm");
|
||||
rc = __winsockerr();
|
||||
goto Finished;
|
||||
} else if (i == kNtWaitTimeout) {
|
||||
if (_check_interrupts(true, g_fds.p)) {
|
||||
rc = eintr();
|
||||
goto Finished;
|
||||
}
|
||||
} else if (i == kNtWaitIoCompletion) {
|
||||
STRACE("IOCP TRIGGERED EINTR");
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!WSAGetOverlappedResult(fd->handle, &overlapped, &got, false, &flags)) {
|
||||
STRACE("WSAGetOverlappedResult failed %lm");
|
||||
rc = __winsockerr();
|
||||
goto Finished;
|
||||
}
|
||||
|
||||
rc = got;
|
||||
Finished:
|
||||
WSACloseEvent(overlapped.hEvent);
|
||||
return rc;
|
||||
}
|
|
@ -16,7 +16,13 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/sock/sock.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Receives data from network socket.
|
||||
|
@ -32,5 +38,27 @@
|
|||
* @restartable (unless SO_RCVTIMEO)
|
||||
*/
|
||||
ssize_t recv(int fd, void *buf, size_t size, int flags) {
|
||||
return recvfrom(fd, buf, size, flags, NULL, 0);
|
||||
ssize_t rc, got;
|
||||
if (IsAsan() && !__asan_is_valid(buf, size)) {
|
||||
rc = efault();
|
||||
} else if (!IsWindows()) {
|
||||
rc = sys_recvfrom(fd, buf, size, flags, 0, 0);
|
||||
} else if (__isfdopen(fd)) {
|
||||
if (__isfdkind(fd, kFdSocket)) {
|
||||
rc = sys_recv_nt(&g_fds.p[fd], (struct iovec[]){{buf, size}}, 1, flags);
|
||||
} else if (__isfdkind(fd, kFdFile)) {
|
||||
if (flags) {
|
||||
rc = einval();
|
||||
} else {
|
||||
rc = sys_read_nt(&g_fds.p[fd], (struct iovec[]){{buf, size}}, 1, -1);
|
||||
}
|
||||
} else {
|
||||
rc = enotsock();
|
||||
}
|
||||
} else {
|
||||
rc = ebadf();
|
||||
}
|
||||
STRACE("recv(%d, [%#.*hhs%s], %'zu, %#x) → %'ld% lm", fd, MAX(0, MIN(40, rc)),
|
||||
buf, rc > 40 ? "..." : "", size, flags);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*-*- 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│
|
||||
/*-*- 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 │
|
||||
│ │
|
||||
|
@ -16,13 +16,17 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/nt/enum/wait.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/struct/overlapped.h"
|
||||
#include "libc/nt/winsock.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/sock/yoink.inc"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Performs recv(), recvfrom(), or readv() on Windows NT.
|
||||
* Performs datagram receive on New Technology.
|
||||
*
|
||||
* @param fd must be a socket
|
||||
* @return number of bytes received, or -1 w/ errno
|
||||
|
@ -31,14 +35,53 @@ textwindows ssize_t sys_recvfrom_nt(struct Fd *fd, const struct iovec *iov,
|
|||
size_t iovlen, uint32_t flags,
|
||||
void *opt_out_srcaddr,
|
||||
uint32_t *opt_inout_srcaddrsize) {
|
||||
uint32_t got;
|
||||
ssize_t rc;
|
||||
uint32_t i, got = 0;
|
||||
struct NtIovec iovnt[16];
|
||||
got = 0;
|
||||
if (WSARecvFrom(fd->handle, iovnt, __iovec2nt(iovnt, iov, iovlen), &got,
|
||||
&flags, opt_out_srcaddr, opt_inout_srcaddrsize, NULL,
|
||||
NULL) != -1) {
|
||||
return got;
|
||||
} else {
|
||||
return __winsockerr();
|
||||
struct NtOverlapped overlapped = {.hEvent = WSACreateEvent()};
|
||||
|
||||
if (_check_interrupts(true, g_fds.p)) return eintr();
|
||||
|
||||
if (!WSARecvFrom(fd->handle, iovnt, __iovec2nt(iovnt, iov, iovlen), &got,
|
||||
&flags, opt_out_srcaddr, opt_inout_srcaddrsize, &overlapped,
|
||||
NULL)) {
|
||||
rc = got;
|
||||
goto Finished;
|
||||
}
|
||||
|
||||
if (WSAGetLastError() != kNtErrorIoPending) {
|
||||
STRACE("WSARecvFrom failed %lm");
|
||||
rc = __winsockerr();
|
||||
goto Finished;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
i = WSAWaitForMultipleEvents(1, &overlapped.hEvent, true,
|
||||
__SIG_POLLING_INTERVAL_MS, true);
|
||||
if (i == kNtWaitFailed) {
|
||||
STRACE("WSAWaitForMultipleEvents failed %lm");
|
||||
rc = __winsockerr();
|
||||
goto Finished;
|
||||
} else if (i == kNtWaitTimeout) {
|
||||
if (_check_interrupts(true, g_fds.p)) {
|
||||
rc = eintr();
|
||||
goto Finished;
|
||||
}
|
||||
} else if (i == kNtWaitIoCompletion) {
|
||||
STRACE("IOCP TRIGGERED EINTR");
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!WSAGetOverlappedResult(fd->handle, &overlapped, &got, false, &flags)) {
|
||||
STRACE("WSAGetOverlappedResult failed %lm");
|
||||
rc = __winsockerr();
|
||||
goto Finished;
|
||||
}
|
||||
|
||||
rc = got;
|
||||
Finished:
|
||||
WSACloseEvent(overlapped.hEvent);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -51,8 +51,6 @@ ssize_t recvfrom(int fd, void *buf, size_t size, uint32_t flags,
|
|||
(opt_out_srcaddr &&
|
||||
!__asan_is_valid(opt_out_srcaddr, *opt_inout_srcaddrsize)))) {
|
||||
rc = efault();
|
||||
} else if (IsWindows() && _check_interrupts(false, g_fds.p)) {
|
||||
rc = eintr();
|
||||
} else if (!IsWindows()) {
|
||||
got = sys_recvfrom(fd, buf, size, flags, opt_out_srcaddr,
|
||||
opt_inout_srcaddrsize);
|
||||
|
@ -60,22 +58,23 @@ ssize_t recvfrom(int fd, void *buf, size_t size, uint32_t flags,
|
|||
sockaddr2linux(opt_out_srcaddr);
|
||||
}
|
||||
rc = got;
|
||||
} else {
|
||||
if (__isfdopen(fd)) {
|
||||
if (__isfdkind(fd, kFdSocket)) {
|
||||
rc = sys_recvfrom_nt(&g_fds.p[fd], (struct iovec[]){{buf, size}}, 1,
|
||||
flags, opt_out_srcaddr, opt_inout_srcaddrsize);
|
||||
} else if (__isfdkind(fd, kFdFile) && !opt_out_srcaddr) { /* socketpair */
|
||||
if (flags) rc = einval();
|
||||
rc = sys_read_nt(&g_fds.p[fd], (struct iovec[]){{buf, size}}, 1, -1);
|
||||
} else if (__isfdopen(fd)) {
|
||||
if (__isfdkind(fd, kFdSocket)) {
|
||||
rc = sys_recvfrom_nt(&g_fds.p[fd], (struct iovec[]){{buf, size}}, 1,
|
||||
flags, opt_out_srcaddr, opt_inout_srcaddrsize);
|
||||
} else if (__isfdkind(fd, kFdFile) && !opt_out_srcaddr) { /* socketpair */
|
||||
if (flags) {
|
||||
rc = einval();
|
||||
} else {
|
||||
rc = enotsock();
|
||||
rc = sys_read_nt(&g_fds.p[fd], (struct iovec[]){{buf, size}}, 1, -1);
|
||||
}
|
||||
} else {
|
||||
rc = ebadf();
|
||||
rc = enotsock();
|
||||
}
|
||||
} else {
|
||||
rc = ebadf();
|
||||
}
|
||||
STRACE("recvfrom(%d, %#.*hhs, %'zu, %#x) → %'ld% m", fd, size, buf, size,
|
||||
flags, rc);
|
||||
STRACE("recvfrom(%d, [%#.*hhs%s], %'zu, %#x) → %'ld% lm", fd,
|
||||
MAX(0, MIN(40, rc)), buf, rc > 40 ? "..." : "", size, flags, rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -16,117 +16,66 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/popcnt.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nt/winsock.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/sock/yoink.inc"
|
||||
#include "libc/sock/select.h"
|
||||
#include "libc/sock/sock.h"
|
||||
#include "libc/sysv/consts/poll.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
static int GetFdsPopcnt(int nfds, fd_set *fds) {
|
||||
int i, n = 0;
|
||||
if (fds) {
|
||||
for (i = 0; i < nfds; ++i) {
|
||||
n += popcnt(fds->fds_bits[i]);
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static int FindFdByHandle(int nfds, int64_t h) {
|
||||
int i, n;
|
||||
n = MIN(nfds << 3, g_fds.n);
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (h == g_fds.p[i].handle && g_fds.p[i].kind != kFdEmpty) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static struct NtFdSet *FdSetToNtFdSet(int nfds, fd_set *fds) {
|
||||
int i, j, k, n, m, fd;
|
||||
struct NtFdSet *ntfds;
|
||||
if (fds && (n = GetFdsPopcnt(nfds, fds))) {
|
||||
m = MIN(n, ARRAYLEN(ntfds->fd_array));
|
||||
ntfds = malloc(sizeof(struct NtFdSet));
|
||||
for (k = i = 0; i < nfds; ++i) {
|
||||
if (fds->fds_bits[i]) {
|
||||
for (j = 0; j < 64 && k < m; ++j) {
|
||||
if ((fds->fds_bits[i] & (1ul << j)) && i * 8 + j < g_fds.n) {
|
||||
ntfds->fd_array[k++] = g_fds.p[i * 8 + j].handle;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ntfds->fd_count = m;
|
||||
return ntfds;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void NtFdSetToFdSet(int nfds, fd_set *fds, struct NtFdSet *ntfds) {
|
||||
int i, fd;
|
||||
if (ntfds) {
|
||||
for (i = 0; i < nfds; ++i) {
|
||||
fds->fds_bits[i] = 0;
|
||||
}
|
||||
for (i = 0; i < ntfds->fd_count; ++i) {
|
||||
if ((fd = FindFdByHandle(nfds, ntfds->fd_array[i])) != -1) {
|
||||
fds->fds_bits[fd >> 3] |= 1ul << (fd & 7);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct NtTimeval *TimevalToNtTimeval(struct timeval *tv,
|
||||
struct NtTimeval *nttv) {
|
||||
if (tv) {
|
||||
nttv->tv_sec = tv->tv_sec;
|
||||
nttv->tv_usec = tv->tv_usec;
|
||||
return nttv;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int sys_select_nt(int nfds, fd_set *readfds, fd_set *writefds,
|
||||
fd_set *exceptfds, struct timeval *timeout) {
|
||||
int n, rc;
|
||||
struct timespec req, rem;
|
||||
struct NtTimeval nttimeout, *nttimeoutp;
|
||||
struct NtFdSet *ntreadfds, *ntwritefds, *ntexceptfds;
|
||||
if (readfds || writefds || exceptfds) {
|
||||
nfds = MIN(ARRAYLEN(readfds->fds_bits), ROUNDUP(nfds, 8)) >> 3;
|
||||
ntreadfds = FdSetToNtFdSet(nfds, readfds);
|
||||
ntwritefds = FdSetToNtFdSet(nfds, writefds);
|
||||
ntexceptfds = FdSetToNtFdSet(nfds, exceptfds);
|
||||
nttimeoutp = TimevalToNtTimeval(timeout, &nttimeout);
|
||||
if ((rc = __sys_select_nt(0, ntreadfds, ntwritefds, ntexceptfds,
|
||||
nttimeoutp)) != -1) {
|
||||
NtFdSetToFdSet(nfds, readfds, ntreadfds);
|
||||
NtFdSetToFdSet(nfds, writefds, ntwritefds);
|
||||
NtFdSetToFdSet(nfds, exceptfds, ntexceptfds);
|
||||
} else {
|
||||
__winsockerr();
|
||||
uint64_t millis;
|
||||
int i, pfds, events, fdcount;
|
||||
struct pollfd fds[64];
|
||||
|
||||
// check for interrupts early before doing work
|
||||
if (_check_interrupts(false, g_fds.p)) return eintr();
|
||||
|
||||
// convert bitsets to pollfd
|
||||
for (pfds = i = 0; i < nfds; ++i) {
|
||||
events = 0;
|
||||
if (readfds && FD_ISSET(i, readfds)) events |= POLLIN;
|
||||
if (writefds && FD_ISSET(i, writefds)) events |= POLLOUT;
|
||||
if (exceptfds && FD_ISSET(i, exceptfds)) events |= POLLERR;
|
||||
if (events) {
|
||||
if (pfds < ARRAYLEN(fds)) {
|
||||
fds[pfds].fd = i;
|
||||
fds[pfds].events = events;
|
||||
fds[pfds].revents = 0;
|
||||
pfds += 1;
|
||||
} else {
|
||||
return enomem();
|
||||
}
|
||||
}
|
||||
free(ntreadfds);
|
||||
free(ntwritefds);
|
||||
free(ntexceptfds);
|
||||
} else if (timeout) {
|
||||
req.tv_sec = timeout->tv_sec;
|
||||
req.tv_nsec = timeout->tv_usec * 1000;
|
||||
rem.tv_sec = 0;
|
||||
rem.tv_nsec = 0;
|
||||
rc = sys_nanosleep_nt(&req, &rem);
|
||||
timeout->tv_sec = rem.tv_sec;
|
||||
timeout->tv_usec = rem.tv_nsec / 1000;
|
||||
} else {
|
||||
rc = pause();
|
||||
}
|
||||
return rc;
|
||||
|
||||
// convert the wait time to a word
|
||||
if (!timeout || __builtin_add_overflow(timeout->tv_sec,
|
||||
timeout->tv_usec / 1000, &millis)) {
|
||||
millis = -1;
|
||||
}
|
||||
|
||||
// call our nt poll implementation
|
||||
fdcount = sys_poll_nt(fds, pfds, &millis);
|
||||
if (fdcount == -1) return -1;
|
||||
|
||||
// convert pollfd back to bitsets
|
||||
if (readfds) FD_ZERO(readfds);
|
||||
if (writefds) FD_ZERO(writefds);
|
||||
if (exceptfds) FD_ZERO(exceptfds);
|
||||
for (i = 0; i < fdcount; ++i) {
|
||||
if (fds[i].revents & POLLIN) FD_SET(fds[i].fd, readfds);
|
||||
if (fds[i].revents & POLLOUT) FD_SET(fds[i].fd, writefds);
|
||||
if (fds[i].revents & (POLLERR | POLLNVAL)) FD_SET(fds[i].fd, exceptfds);
|
||||
}
|
||||
|
||||
// store remaining time back in caller's timeval
|
||||
if (timeout) {
|
||||
timeout->tv_sec = millis / 1000;
|
||||
timeout->tv_usec = millis % 1000 * 1000;
|
||||
}
|
||||
|
||||
return fdcount;
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#include "libc/calls/struct/timeval.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/sock/sock.h"
|
||||
#include "libc/sock/select.h"
|
||||
|
||||
/**
|
||||
* Does what poll() does except with a complicated bitset API.
|
||||
|
|
89
libc/sock/send-nt.c
Normal file
89
libc/sock/send-nt.c
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*-*- 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/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/nt/enum/wait.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/winsock.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/sock/yoink.inc"
|
||||
#include "libc/sysv/consts/fileno.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Performs stream socket send on the New Technology.
|
||||
*
|
||||
* @param fd must be a socket
|
||||
* @return number of bytes handed off, or -1 w/ errno
|
||||
*/
|
||||
textwindows ssize_t sys_send_nt(int fd, const struct iovec *iov, size_t iovlen,
|
||||
uint32_t flags) {
|
||||
ssize_t rc;
|
||||
uint32_t i, sent = 0;
|
||||
struct NtIovec iovnt[16];
|
||||
struct NtOverlapped overlapped = {.hEvent = WSACreateEvent()};
|
||||
|
||||
if (_check_interrupts(true, g_fds.p)) return eintr();
|
||||
|
||||
if (!WSASend(g_fds.p[fd].handle, iovnt, __iovec2nt(iovnt, iov, iovlen), &sent,
|
||||
flags, &overlapped, NULL)) {
|
||||
rc = sent;
|
||||
goto Finished;
|
||||
}
|
||||
|
||||
if (WSAGetLastError() != kNtErrorIoPending) {
|
||||
STRACE("WSASend failed %lm");
|
||||
rc = __winsockerr();
|
||||
goto Finished;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
i = WSAWaitForMultipleEvents(1, &overlapped.hEvent, true,
|
||||
__SIG_POLLING_INTERVAL_MS, true);
|
||||
if (i == kNtWaitFailed) {
|
||||
STRACE("WSAWaitForMultipleEvents failed %lm");
|
||||
rc = __winsockerr();
|
||||
goto Finished;
|
||||
} else if (i == kNtWaitTimeout) {
|
||||
if (_check_interrupts(true, g_fds.p)) {
|
||||
rc = eintr();
|
||||
goto Finished;
|
||||
}
|
||||
} else if (i == kNtWaitIoCompletion) {
|
||||
STRACE("IOCP TRIGGERED EINTR");
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!WSAGetOverlappedResult(g_fds.p[fd].handle, &overlapped, &sent, false,
|
||||
&flags)) {
|
||||
STRACE("WSAGetOverlappedResult failed %lm");
|
||||
rc = __winsockerr();
|
||||
goto Finished;
|
||||
}
|
||||
|
||||
rc = sent;
|
||||
Finished:
|
||||
WSACloseEvent(overlapped.hEvent);
|
||||
return rc;
|
||||
}
|
|
@ -16,7 +16,15 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/struct/iovec.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/sock/sock.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Sends data to network socket.
|
||||
|
@ -32,5 +40,27 @@
|
|||
* @restartable (unless SO_RCVTIMEO)
|
||||
*/
|
||||
ssize_t send(int fd, const void *buf, size_t size, int flags) {
|
||||
return sendto(fd, buf, size, flags, NULL, 0);
|
||||
ssize_t rc;
|
||||
if (IsAsan() && !__asan_is_valid(buf, size)) {
|
||||
rc = efault();
|
||||
} else if (!IsWindows()) {
|
||||
rc = sys_sendto(fd, buf, size, flags, 0, 0);
|
||||
} else if (__isfdopen(fd)) {
|
||||
if (__isfdkind(fd, kFdSocket)) {
|
||||
rc = sys_send_nt(fd, (struct iovec[]){{buf, size}}, 1, flags);
|
||||
} else if (__isfdkind(fd, kFdFile)) {
|
||||
if (flags) {
|
||||
rc = einval();
|
||||
} else {
|
||||
rc = sys_write_nt(fd, (struct iovec[]){{buf, size}}, 1, -1);
|
||||
}
|
||||
} else {
|
||||
rc = enotsock();
|
||||
}
|
||||
} else {
|
||||
rc = ebadf();
|
||||
}
|
||||
STRACE("send(%d, %#.*hhs%s, %'zu, %#x) → %'ld% lm", fd, MAX(0, MIN(40, rc)),
|
||||
buf, rc > 40 ? "..." : "", size, flags, rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -16,16 +16,17 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/nt/enum/wait.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/struct/overlapped.h"
|
||||
#include "libc/nt/winsock.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/sock/yoink.inc"
|
||||
#include "libc/sysv/consts/fileno.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Performs send(), sendto(), or writev() on Windows NT.
|
||||
* Performs datagram socket send on the New Technology.
|
||||
*
|
||||
* @param fd must be a socket
|
||||
* @return number of bytes handed off, or -1 w/ errno
|
||||
|
@ -33,12 +34,53 @@
|
|||
textwindows ssize_t sys_sendto_nt(int fd, const struct iovec *iov,
|
||||
size_t iovlen, uint32_t flags,
|
||||
void *opt_in_addr, uint32_t in_addrsize) {
|
||||
uint32_t sent;
|
||||
ssize_t rc;
|
||||
uint32_t i, sent = 0;
|
||||
struct NtIovec iovnt[16];
|
||||
if (WSASendTo(g_fds.p[fd].handle, iovnt, __iovec2nt(iovnt, iov, iovlen),
|
||||
&sent, flags, opt_in_addr, in_addrsize, NULL, NULL) != -1) {
|
||||
return sent;
|
||||
} else {
|
||||
return __winsockerr();
|
||||
struct NtOverlapped overlapped = {.hEvent = WSACreateEvent()};
|
||||
|
||||
if (_check_interrupts(true, g_fds.p)) return eintr();
|
||||
|
||||
if (!WSASendTo(g_fds.p[fd].handle, iovnt, __iovec2nt(iovnt, iov, iovlen),
|
||||
&sent, flags, opt_in_addr, in_addrsize, &overlapped, NULL)) {
|
||||
rc = sent;
|
||||
goto Finished;
|
||||
}
|
||||
|
||||
if (WSAGetLastError() != kNtErrorIoPending) {
|
||||
STRACE("WSASendTo failed %lm");
|
||||
rc = __winsockerr();
|
||||
goto Finished;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
i = WSAWaitForMultipleEvents(1, &overlapped.hEvent, true,
|
||||
__SIG_POLLING_INTERVAL_MS, true);
|
||||
if (i == kNtWaitFailed) {
|
||||
STRACE("WSAWaitForMultipleEvents failed %lm");
|
||||
rc = __winsockerr();
|
||||
goto Finished;
|
||||
} else if (i == kNtWaitTimeout) {
|
||||
if (_check_interrupts(true, g_fds.p)) {
|
||||
rc = eintr();
|
||||
goto Finished;
|
||||
}
|
||||
} else if (i == kNtWaitIoCompletion) {
|
||||
STRACE("IOCP TRIGGERED EINTR");
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!WSAGetOverlappedResult(g_fds.p[fd].handle, &overlapped, &sent, false,
|
||||
&flags)) {
|
||||
STRACE("WSAGetOverlappedResult failed %lm");
|
||||
rc = __winsockerr();
|
||||
goto Finished;
|
||||
}
|
||||
|
||||
rc = sent;
|
||||
Finished:
|
||||
WSACloseEvent(overlapped.hEvent);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -18,9 +18,11 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/struct/iovec.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/sock/sock.h"
|
||||
#include "libc/str/str.h"
|
||||
|
@ -49,36 +51,44 @@
|
|||
*/
|
||||
ssize_t sendto(int fd, const void *buf, size_t size, uint32_t flags,
|
||||
const void *opt_addr, uint32_t addrsize) {
|
||||
ssize_t rc;
|
||||
char addr2[sizeof(struct sockaddr_un_bsd)];
|
||||
if (IsAsan() && (!__asan_is_valid(buf, size) ||
|
||||
(opt_addr && !__asan_is_valid(opt_addr, addrsize)))) {
|
||||
return efault();
|
||||
}
|
||||
_firewall(opt_addr, addrsize);
|
||||
if (!IsWindows()) {
|
||||
if (!IsBsd() || !opt_addr) {
|
||||
return sys_sendto(fd, buf, size, flags, opt_addr, addrsize);
|
||||
} else {
|
||||
char addr2[sizeof(
|
||||
struct sockaddr_un_bsd)]; /* sockaddr_un_bsd is the largest */
|
||||
if (addrsize > sizeof(addr2)) return einval();
|
||||
memcpy(&addr2, opt_addr, addrsize);
|
||||
sockaddr2bsd(&addr2[0]);
|
||||
return sys_sendto(fd, buf, size, flags, &addr2[0], addrsize);
|
||||
}
|
||||
rc = efault();
|
||||
} else {
|
||||
if (__isfdopen(fd)) {
|
||||
if (__isfdkind(fd, kFdSocket)) {
|
||||
return sys_sendto_nt(fd, (struct iovec[]){{buf, size}}, 1, flags,
|
||||
opt_addr, addrsize);
|
||||
} else if (__isfdkind(fd, kFdFile)) { /* e.g. socketpair() */
|
||||
if (flags) return einval();
|
||||
if (opt_addr) return eisconn();
|
||||
return sys_write_nt(fd, (struct iovec[]){{buf, size}}, 1, -1);
|
||||
_firewall(opt_addr, addrsize);
|
||||
if (!IsWindows()) {
|
||||
if (!IsBsd() || !opt_addr) {
|
||||
rc = sys_sendto(fd, buf, size, flags, opt_addr, addrsize);
|
||||
} else if (addrsize > sizeof(addr2)) {
|
||||
rc = einval();
|
||||
} else {
|
||||
return enotsock();
|
||||
memcpy(&addr2, opt_addr, addrsize);
|
||||
sockaddr2bsd(&addr2[0]);
|
||||
rc = sys_sendto(fd, buf, size, flags, &addr2[0], addrsize);
|
||||
}
|
||||
} else if (__isfdopen(fd)) {
|
||||
if (__isfdkind(fd, kFdSocket)) {
|
||||
rc = sys_sendto_nt(fd, (struct iovec[]){{buf, size}}, 1, flags,
|
||||
opt_addr, addrsize);
|
||||
} else if (__isfdkind(fd, kFdFile)) {
|
||||
if (flags) {
|
||||
rc = einval();
|
||||
} else if (opt_addr) {
|
||||
rc = eisconn();
|
||||
} else {
|
||||
rc = sys_write_nt(fd, (struct iovec[]){{buf, size}}, 1, -1);
|
||||
}
|
||||
} else {
|
||||
rc = enotsock();
|
||||
}
|
||||
} else {
|
||||
return ebadf();
|
||||
rc = ebadf();
|
||||
}
|
||||
}
|
||||
STRACE("sendto(%d, %#.*hhs%s, %'zu, %#x, %p, %u) → %'ld% lm", fd,
|
||||
MAX(0, MIN(40, rc)), buf, rc > 40 ? "..." : "", size, flags, opt_addr,
|
||||
addrsize, rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ int setsockopt(int fd, int level, int optname, const void *optval,
|
|||
} else {
|
||||
rc = ebadf();
|
||||
}
|
||||
STRACE("setsockopt(%d, %#x, %#x, %p, %'u) → %d% m", fd, level, optname,
|
||||
STRACE("setsockopt(%d, %#x, %#x, %p, %'u) → %d% lm", fd, level, optname,
|
||||
optval, optlen, rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -40,6 +40,6 @@ int shutdown(int fd, int how) {
|
|||
} else {
|
||||
rc = ebadf();
|
||||
}
|
||||
STRACE("shutdown(%d, %d) -> %d% m", fd, how, rc);
|
||||
STRACE("shutdown(%d, %d) -> %d% lm", fd, how, rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -53,6 +53,11 @@ $(LIBC_SOCK_A).pkg: \
|
|||
$(LIBC_SOCK_A_OBJS) \
|
||||
$(foreach x,$(LIBC_SOCK_A_DIRECTDEPS),$($(x)_A).pkg)
|
||||
|
||||
o/$(MODE)/libc/sock/ntstdin.greg.o: \
|
||||
OVERRIDE_COPTS += \
|
||||
-ffreestanding \
|
||||
$(NO_MAGIC)
|
||||
|
||||
LIBC_SOCK_LIBS = $(foreach x,$(LIBC_SOCK_ARTIFACTS),$($(x)))
|
||||
LIBC_SOCK_SRCS = $(foreach x,$(LIBC_SOCK_ARTIFACTS),$($(x)_SRCS))
|
||||
LIBC_SOCK_HDRS = $(foreach x,$(LIBC_SOCK_ARTIFACTS),$($(x)_HDRS))
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nt/enum/fileflagandattributes.h"
|
||||
#include "libc/nt/iphlpapi.h"
|
||||
#include "libc/nt/winsock.h"
|
||||
#include "libc/sock/internal.h"
|
||||
|
@ -42,9 +43,10 @@ textwindows int sys_socket_nt(int family, int type, int protocol) {
|
|||
int64_t h;
|
||||
struct SockFd *sockfd;
|
||||
int fd, oflags, truetype;
|
||||
if ((fd = __reservefd()) == -1) return -1;
|
||||
if ((fd = __reservefd(-1)) == -1) return -1;
|
||||
truetype = type & ~(SOCK_CLOEXEC | SOCK_NONBLOCK);
|
||||
if ((h = WSASocket(family, truetype, protocol, NULL, 0, 0)) != -1) {
|
||||
if ((h = WSASocket(family, truetype, protocol, NULL, 0,
|
||||
kNtWsaFlagOverlapped)) != -1) {
|
||||
oflags = 0;
|
||||
if (type & SOCK_CLOEXEC) oflags |= O_CLOEXEC;
|
||||
if (type & SOCK_NONBLOCK) oflags |= O_NONBLOCK;
|
||||
|
@ -59,9 +61,9 @@ textwindows int sys_socket_nt(int family, int type, int protocol) {
|
|||
sockfd->family = family;
|
||||
sockfd->type = truetype;
|
||||
sockfd->protocol = protocol;
|
||||
sockfd->event = WSACreateEvent();
|
||||
g_fds.p[fd].kind = kFdSocket;
|
||||
g_fds.p[fd].flags = oflags;
|
||||
g_fds.p[fd].mode = 0140666;
|
||||
g_fds.p[fd].handle = h;
|
||||
g_fds.p[fd].extra = (uintptr_t)sockfd;
|
||||
return fd;
|
||||
|
|
|
@ -46,7 +46,7 @@ int socket(int family, int type, int protocol) {
|
|||
} else {
|
||||
rc = sys_socket_nt(family, type, protocol);
|
||||
}
|
||||
STRACE("socket(%s, %s, %s) → %d% m", __describe_socket_family(family),
|
||||
STRACE("socket(%s, %s, %s) → %d% lm", __describe_socket_family(family),
|
||||
__describe_socket_type(type), __describe_socket_protocol(protocol),
|
||||
rc);
|
||||
return rc;
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "libc/nt/createfile.h"
|
||||
#include "libc/nt/enum/accessmask.h"
|
||||
#include "libc/nt/enum/creationdisposition.h"
|
||||
#include "libc/nt/enum/fileflagandattributes.h"
|
||||
#include "libc/nt/ipc.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/sock/internal.h"
|
||||
|
@ -42,34 +43,32 @@ textwindows int sys_socketpair_nt(int family, int type, int proto, int sv[2]) {
|
|||
if (type & SOCK_CLOEXEC) oflags |= O_CLOEXEC;
|
||||
type &= ~SOCK_CLOEXEC;
|
||||
|
||||
mode = kNtPipeWait;
|
||||
if (type == SOCK_STREAM) {
|
||||
mode |= kNtPipeReadmodeByte | kNtPipeTypeByte;
|
||||
mode = kNtPipeTypeByte | kNtPipeReadmodeByte;
|
||||
} else if ((type == SOCK_DGRAM) || (type == SOCK_SEQPACKET)) {
|
||||
mode |= kNtPipeReadmodeMessage | kNtPipeTypeMessage;
|
||||
mode = kNtPipeTypeMessage | kNtPipeReadmodeMessage;
|
||||
} else {
|
||||
return eopnotsupp();
|
||||
}
|
||||
|
||||
CreatePipeName(pipename);
|
||||
if ((reader = __reservefd()) == -1) return -1;
|
||||
if ((writer = __reservefd()) == -1) {
|
||||
if ((reader = __reservefd(-1)) == -1) return -1;
|
||||
if ((writer = __reservefd(-1)) == -1) {
|
||||
__releasefd(reader);
|
||||
return -1;
|
||||
}
|
||||
if ((hpipe = CreateNamedPipe(pipename, kNtPipeAccessDuplex, mode, 1, 65536,
|
||||
65536, 0, &kNtIsInheritable)) == -1) {
|
||||
if ((hpipe = CreateNamedPipe(
|
||||
pipename, kNtPipeAccessDuplex | kNtFileFlagOverlapped, mode, 1,
|
||||
65536, 65536, 0, &kNtIsInheritable)) == -1) {
|
||||
__releasefd(writer);
|
||||
__releasefd(reader);
|
||||
return -1;
|
||||
}
|
||||
|
||||
h1 = CreateFile(pipename, kNtGenericWrite | kNtGenericRead,
|
||||
0, // Not shared
|
||||
&kNtIsInheritable, kNtOpenExisting, 0, 0);
|
||||
h1 = CreateFile(pipename, kNtGenericWrite | kNtGenericRead, 0,
|
||||
&kNtIsInheritable, kNtOpenExisting, kNtFileFlagOverlapped, 0);
|
||||
if (h1 == -1) {
|
||||
CloseHandle(hpipe);
|
||||
__winerr();
|
||||
__releasefd(writer);
|
||||
__releasefd(reader);
|
||||
return -1;
|
||||
|
@ -77,10 +76,12 @@ textwindows int sys_socketpair_nt(int family, int type, int proto, int sv[2]) {
|
|||
|
||||
g_fds.p[reader].kind = kFdFile;
|
||||
g_fds.p[reader].flags = oflags;
|
||||
g_fds.p[reader].mode = 0140444;
|
||||
g_fds.p[reader].handle = hpipe;
|
||||
|
||||
g_fds.p[writer].kind = kFdFile;
|
||||
g_fds.p[writer].flags = oflags;
|
||||
g_fds.p[writer].mode = 0140222;
|
||||
g_fds.p[writer].handle = h1;
|
||||
|
||||
sv[0] = reader;
|
||||
|
|
|
@ -30,6 +30,11 @@ textwindows int64_t __winsockblock(int64_t fh, unsigned eventbit, int64_t 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]) {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
STATIC_YOINK("kNtWsaData"); // for winmain
|
||||
STATIC_YOINK("WSAGetLastError"); // for kprintf
|
||||
STATIC_YOINK("sys_closesocket_nt"); // for close
|
||||
STATIC_YOINK("sys_recvfrom_nt"); // for readv
|
||||
STATIC_YOINK("sys_sendto_nt"); // for writev
|
||||
STATIC_YOINK("sys_recv_nt"); // for readv
|
||||
STATIC_YOINK("sys_send_nt"); // for writev
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue