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:
Justine Tunney 2022-04-14 23:39:48 -07:00
parent 233144b19d
commit 933411ba99
266 changed files with 8761 additions and 4344 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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