mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
Make more Windows socket fixes and improvements
This change makes send() / sendto() always block on Windows. It's needed because poll(POLLOUT) doesn't guarantee a socket is immediately writable on Windows, and it caused rsync to fail because it made that assumption. The only exception is when a SO_SNDTIMEO is specified which will EAGAIN. Tests are added confirming MSG_WAITALL and MSG_NOSIGNAL work as expected on all our supported OSes. Most of the platform-specific MSG_FOO magnums have been deleted, with the exception of MSG_FASTOPEN. Your --strace log will now show MSG_FOO flags as symbols rather than numbers. I've also removed cv_wait_example_test because it's 0.3% flaky with Qemu under system load since it depends on a process being readily scheduled.
This commit is contained in:
parent
ce2fbf9325
commit
87a6669900
41 changed files with 584 additions and 184 deletions
|
@ -8,8 +8,8 @@ struct thatispacked DescribeFlags {
|
|||
const char *name;
|
||||
};
|
||||
|
||||
const char *_DescribeFlags(char *, size_t, const struct DescribeFlags *,
|
||||
size_t, const char *, unsigned) libcesque;
|
||||
const char *_DescribeFlags(char *, size_t, const struct DescribeFlags *, size_t,
|
||||
const char *, unsigned) libcesque;
|
||||
|
||||
const char *_DescribeArchPrctlCode(char[12], int) libcesque;
|
||||
const char *_DescribeCancelState(char[12], int, int *) libcesque;
|
||||
|
@ -27,6 +27,7 @@ const char *_DescribeItimer(char[12], int) libcesque;
|
|||
const char *_DescribeMapFlags(char[64], int) libcesque;
|
||||
const char *_DescribeMapping(char[8], int, int) libcesque;
|
||||
const char *_DescribeMremapFlags(char[30], int) libcesque;
|
||||
const char *_DescribeMsg(char[16], int) libcesque;
|
||||
const char *_DescribeMsyncFlags(char[48], int) libcesque;
|
||||
const char *_DescribeNtConsoleInFlags(char[256], uint32_t) libcesque;
|
||||
const char *_DescribeNtConsoleOutFlags(char[128], uint32_t) libcesque;
|
||||
|
@ -84,6 +85,7 @@ const char *_DescribeWhichPrio(char[12], int) libcesque;
|
|||
#define DescribeMapFlags(x) _DescribeMapFlags(alloca(64), x)
|
||||
#define DescribeMapping(x, y) _DescribeMapping(alloca(8), x, y)
|
||||
#define DescribeMremapFlags(x) _DescribeMremapFlags(alloca(30), x)
|
||||
#define DescribeMsg(x) _DescribeMsg(alloca(16), x)
|
||||
#define DescribeMsyncFlags(x) _DescribeMsyncFlags(alloca(48), x)
|
||||
#define DescribeNtConsoleInFlags(x) _DescribeNtConsoleInFlags(alloca(256), x)
|
||||
#define DescribeNtConsoleOutFlags(x) _DescribeNtConsoleOutFlags(alloca(128), x)
|
||||
|
|
36
libc/intrin/describemsg.c
Normal file
36
libc/intrin/describemsg.c
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2024 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/describeflags.h"
|
||||
#include "libc/macros.h"
|
||||
#include "libc/sysv/consts/msg.h"
|
||||
|
||||
const char *_DescribeMsg(char buf[16], int x) {
|
||||
const struct DescribeFlags kMsgFlags[] = {
|
||||
{MSG_FASTOPEN, "FASTOPEN"}, // order matters
|
||||
{MSG_OOB, "OOB"}, //
|
||||
{MSG_PEEK, "PEEK"}, //
|
||||
{MSG_DONTROUTE, "DONTROUTE"}, //
|
||||
{MSG_DONTWAIT, "DONTWAIT"}, //
|
||||
{MSG_WAITALL, "WAITALL"}, //
|
||||
{MSG_NOSIGNAL, "NOSIGNAL"}, //
|
||||
{MSG_TRUNC, "TRUNC"}, //
|
||||
{MSG_CTRUNC, "CTRUNC"}, //
|
||||
};
|
||||
return _DescribeFlags(buf, 16, kMsgFlags, ARRAYLEN(kMsgFlags), "MSG_", x);
|
||||
}
|
|
@ -65,7 +65,7 @@ int sys_select_nt(int, fd_set *, fd_set *, fd_set *, struct timeval *,
|
|||
|
||||
size_t __iovec2nt(struct NtIovec[hasatleast 16], const struct iovec *, size_t);
|
||||
|
||||
ssize_t __winsock_block(int64_t, uint32_t, int, uint32_t, uint64_t,
|
||||
ssize_t __winsock_block(int64_t, uint32_t, bool, uint32_t, uint64_t,
|
||||
int (*)(int64_t, struct NtOverlapped *, uint32_t *,
|
||||
void *),
|
||||
void *);
|
||||
|
|
|
@ -17,15 +17,17 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/iovec.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/intrin/fds.h"
|
||||
#include "libc/nt/struct/iovec.h"
|
||||
#include "libc/nt/struct/overlapped.h"
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
#include "libc/nt/winsock.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/sock/syscall_fd.internal.h"
|
||||
#include "libc/sysv/consts/fio.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/vga/vga.internal.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
#define _MSG_OOB 1
|
||||
|
@ -33,6 +35,8 @@
|
|||
#define _MSG_WAITALL 8
|
||||
#define _MSG_DONTWAIT 64
|
||||
|
||||
__msabi extern typeof(__sys_ioctlsocket_nt) *const __imp_ioctlsocket;
|
||||
|
||||
struct RecvArgs {
|
||||
const struct iovec *iov;
|
||||
size_t iovlen;
|
||||
|
@ -54,13 +58,24 @@ textwindows ssize_t sys_recv_nt(int fd, const struct iovec *iov, size_t iovlen,
|
|||
return einval();
|
||||
ssize_t rc;
|
||||
struct Fd *f = g_fds.p + fd;
|
||||
sigset_t m = __sig_block();
|
||||
bool nonblock = !(flags & _MSG_WAITALL) &&
|
||||
((f->flags & O_NONBLOCK) || (flags & _MSG_DONTWAIT));
|
||||
flags &= ~_MSG_DONTWAIT;
|
||||
rc = __winsock_block(f->handle, flags, nonblock, f->rcvtimeo, m,
|
||||
sys_recv_nt_start, &(struct RecvArgs){iov, iovlen});
|
||||
__sig_unblock(m);
|
||||
sigset_t waitmask = __sig_block();
|
||||
|
||||
// "Be aware that if the underlying transport provider does not
|
||||
// support MSG_WAITALL, or if the socket is in a non-blocking mode,
|
||||
// then this call will fail with WSAEOPNOTSUPP. Also, if MSG_WAITALL
|
||||
// is specified along with MSG_OOB, MSG_PEEK, or MSG_PARTIAL, then
|
||||
// this call will fail with WSAEOPNOTSUPP."
|
||||
// —Quoth MSDN § WSARecv
|
||||
if (flags & _MSG_WAITALL)
|
||||
__imp_ioctlsocket(f->handle, FIONBIO, (uint32_t[]){0});
|
||||
|
||||
rc = __winsock_block(f->handle, flags & ~_MSG_DONTWAIT,
|
||||
(f->flags & O_NONBLOCK) || (flags & _MSG_DONTWAIT),
|
||||
f->rcvtimeo, waitmask, sys_recv_nt_start,
|
||||
&(struct RecvArgs){iov, iovlen});
|
||||
|
||||
__sig_unblock(waitmask);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,20 +20,40 @@
|
|||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/iovec.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/describeflags.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/sock/sock.h"
|
||||
#include "libc/sock/syscall_fd.internal.h"
|
||||
#include "libc/sysv/consts/msg.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Receives data from network socket.
|
||||
*
|
||||
* Calling `recv(fd, p, n, 0)` is equivalent to `read(fd, p, n)`.
|
||||
*
|
||||
* Unlike files where the OS tries very hard to fulfill the entire
|
||||
* requested `size` before returning, read operations on sockets aim to
|
||||
* return as quickly as possible. For example, if 10 bytes are requested
|
||||
* and a packet comes in with only 5 bytes, then recv() will most likely
|
||||
* return those 5 bytes before waiting longer. The `MSG_WAITALL` flag
|
||||
* may be passed when waiting longer is desired. In that case, short
|
||||
* reads should only be possible when the connection status changes or
|
||||
* the receive operation is interrupted by a signal.
|
||||
*
|
||||
* @param fd is the file descriptor returned by socket()
|
||||
* @param buf is where received network data gets copied
|
||||
* @param size is the byte capacity of buf
|
||||
* @param flags can have `MSG_OOB`, `MSG_PEEK`, `MSG_DONTWAIT`, `MSG_WAITALL`
|
||||
* @return number of bytes received, 0 on remote close, or -1 w/ errno
|
||||
* @raise EINTR if signal handler was called instead
|
||||
* @raise EINVAL if unknown bits were passed in `flags`
|
||||
* @raise EINVAL if flag isn't supported by host operating system
|
||||
* @raise EINVAL if `MSG_WAITALL` and `MSG_PEEK` were both passed
|
||||
* @raise EBADF if `fd` is an invalid file descriptor
|
||||
* @raise EAGAIN if `MSG_DONTWAIT` was passed and no data was available
|
||||
* @raise EAGAIN if `O_NONBLOCK` is in play and no data was available
|
||||
* @error EINTR, EHOSTUNREACH, ECONNRESET (UDP ICMP Port Unreachable),
|
||||
* EPIPE (if MSG_NOSIGNAL), EMSGSIZE, ENOTSOCK, EFAULT, etc.
|
||||
* @cancelationpoint
|
||||
|
@ -44,7 +64,11 @@ ssize_t recv(int fd, void *buf, size_t size, int flags) {
|
|||
ssize_t rc;
|
||||
BEGIN_CANCELATION_POINT;
|
||||
|
||||
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
|
||||
if ((flags & (MSG_WAITALL | MSG_PEEK)) == (MSG_WAITALL | MSG_PEEK)) {
|
||||
// this is possible on some OSes like Linux but it breaks FreeBSD
|
||||
// and Windows will raise EOPNOTSUPP when it gets passed together
|
||||
return einval();
|
||||
} else if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
|
||||
rc = enotsock();
|
||||
} else if (!IsWindows()) {
|
||||
rc = sys_recvfrom(fd, buf, size, flags, 0, 0);
|
||||
|
@ -65,7 +89,8 @@ ssize_t recv(int fd, void *buf, size_t size, int flags) {
|
|||
}
|
||||
|
||||
END_CANCELATION_POINT;
|
||||
DATATRACE("recv(%d, [%#.*hhs%s], %'zu, %#x) → %'ld% lm", fd,
|
||||
MAX(0, MIN(40, rc)), buf, rc > 40 ? "..." : "", size, flags, rc);
|
||||
DATATRACE("recv(%d, [%#.*hhs%s], %'zu, %s) → %'ld% lm", fd,
|
||||
MAX(0, MIN(40, rc)), buf, rc > 40 ? "..." : "", size,
|
||||
DescribeMsg(flags), rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -59,14 +59,13 @@ textwindows ssize_t sys_recvfrom_nt(int fd, const struct iovec *iov,
|
|||
return einval();
|
||||
ssize_t rc;
|
||||
struct Fd *f = g_fds.p + fd;
|
||||
sigset_t m = __sig_block();
|
||||
bool nonblock = (f->flags & O_NONBLOCK) || (flags & _MSG_DONTWAIT);
|
||||
flags &= ~_MSG_DONTWAIT;
|
||||
rc = __winsock_block(f->handle, flags, nonblock, f->rcvtimeo, m,
|
||||
sys_recvfrom_nt_start,
|
||||
sigset_t waitmask = __sig_block();
|
||||
rc = __winsock_block(f->handle, flags & ~_MSG_DONTWAIT,
|
||||
(f->flags & O_NONBLOCK) || (flags & _MSG_DONTWAIT),
|
||||
f->rcvtimeo, waitmask, sys_recvfrom_nt_start,
|
||||
&(struct RecvFromArgs){iov, iovlen, opt_out_srcaddr,
|
||||
opt_inout_srcaddrsize});
|
||||
__sig_unblock(m);
|
||||
__sig_unblock(waitmask);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "libc/calls/struct/iovec.h"
|
||||
#include "libc/calls/struct/iovec.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/describeflags.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
#include "libc/nt/winsock.h"
|
||||
#include "libc/sock/internal.h"
|
||||
|
@ -95,7 +96,11 @@ ssize_t recvfrom(int fd, void *buf, size_t size, int flags,
|
|||
}
|
||||
|
||||
END_CANCELATION_POINT;
|
||||
DATATRACE("recvfrom(%d, [%#.*hhs%s], %'zu, %#x) → %'ld% lm", fd,
|
||||
MAX(0, MIN(40, rc)), buf, rc > 40 ? "..." : "", size, flags, rc);
|
||||
DATATRACE(
|
||||
"recvfrom(%d, [%#.*hhs%s], %'zu, %s, %s) → %'ld% lm", fd,
|
||||
MAX(0, MIN(40, rc)), buf, rc > 40 ? "..." : "", size, DescribeMsg(flags),
|
||||
DescribeSockaddr(opt_out_srcaddr,
|
||||
opt_inout_srcaddrsize ? *opt_inout_srcaddrsize : 0),
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -17,20 +17,25 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/iovec.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/struct/iovec.internal.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/intrin/fds.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/struct/iovec.h"
|
||||
#include "libc/nt/struct/overlapped.h"
|
||||
#include "libc/nt/winsock.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/sock/syscall_fd.internal.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/vga/vga.internal.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
#define _MSG_OOB 1
|
||||
#define _MSG_DONTROUTE 4
|
||||
#define _MSG_DONTWAIT 64
|
||||
#define _MSG_NOSIGNAL 0x10000000
|
||||
|
||||
struct SendArgs {
|
||||
const struct iovec *iov;
|
||||
|
@ -49,23 +54,24 @@ textwindows static int sys_send_nt_start(int64_t handle,
|
|||
|
||||
textwindows ssize_t sys_send_nt(int fd, const struct iovec *iov, size_t iovlen,
|
||||
uint32_t flags) {
|
||||
if (flags & ~(_MSG_DONTWAIT | _MSG_OOB | _MSG_DONTROUTE))
|
||||
if (flags & ~(_MSG_DONTWAIT | _MSG_OOB | _MSG_DONTROUTE | _MSG_NOSIGNAL))
|
||||
return einval();
|
||||
ssize_t rc;
|
||||
struct Fd *f = g_fds.p + fd;
|
||||
sigset_t m = __sig_block();
|
||||
sigset_t waitmask = __sig_block();
|
||||
|
||||
// we don't check O_NONBLOCK because we want to avoid needing to call
|
||||
// WSAPoll() every time we write() to a non-blocking socket. WIN32 is
|
||||
// unsafe at canceling socket sends. lots of code doesn't check write
|
||||
// return status. good programs that sincerely want to avoid blocking
|
||||
// on send() operations should have already called poll() beforehand.
|
||||
bool nonblock = flags & _MSG_DONTWAIT;
|
||||
rc = __winsock_block(f->handle, flags & ~(_MSG_DONTWAIT | _MSG_NOSIGNAL),
|
||||
false, f->sndtimeo, waitmask, sys_send_nt_start,
|
||||
&(struct SendArgs){iov, iovlen});
|
||||
|
||||
__sig_unblock(waitmask);
|
||||
|
||||
if (rc == -1 && errno == WSAESHUTDOWN) { // ESHUTDOWN
|
||||
errno = kNtErrorBrokenPipe; // EPIPE
|
||||
if (!(flags & _MSG_NOSIGNAL))
|
||||
__sig_raise(SIGPIPE, SI_KERNEL);
|
||||
}
|
||||
|
||||
flags &= ~_MSG_DONTWAIT;
|
||||
rc = __winsock_block(f->handle, flags, -nonblock, f->sndtimeo, m,
|
||||
sys_send_nt_start, &(struct SendArgs){iov, iovlen});
|
||||
__sig_unblock(m);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "libc/calls/struct/iovec.h"
|
||||
#include "libc/calls/struct/iovec.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/describeflags.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
#include "libc/macros.h"
|
||||
#include "libc/sock/internal.h"
|
||||
|
@ -78,7 +79,7 @@ ssize_t send(int fd, const void *buf, size_t size, int flags) {
|
|||
}
|
||||
|
||||
END_CANCELATION_POINT;
|
||||
DATATRACE("send(%d, %#.*hhs%s, %'zu, %#x) → %'ld% lm", fd,
|
||||
MAX(0, MIN(40, rc)), buf, rc > 40 ? "..." : "", size, flags, rc);
|
||||
DATATRACE("send(%d, %#.*hhs%s, %'zu, %s) → %'ld% lm", fd, MAX(0, MIN(40, rc)),
|
||||
buf, rc > 40 ? "..." : "", size, DescribeMsg(flags), rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -17,20 +17,26 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/struct/iovec.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/fds.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/struct/iovec.h"
|
||||
#include "libc/nt/winsock.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/sock/syscall_fd.internal.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
#define _MSG_OOB 1
|
||||
#define _MSG_DONTROUTE 4
|
||||
#define _MSG_DONTWAIT 64
|
||||
#define _MSG_NOSIGNAL 0x10000000
|
||||
|
||||
struct SendToArgs {
|
||||
const struct iovec *iov;
|
||||
|
@ -52,17 +58,25 @@ static textwindows int sys_sendto_nt_start(int64_t handle,
|
|||
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) {
|
||||
if (flags & ~(_MSG_DONTWAIT | _MSG_OOB | _MSG_DONTROUTE))
|
||||
if (flags & ~(_MSG_DONTWAIT | _MSG_OOB | _MSG_DONTROUTE | _MSG_NOSIGNAL))
|
||||
return einval();
|
||||
ssize_t rc;
|
||||
struct Fd *f = g_fds.p + fd;
|
||||
sigset_t m = __sig_block();
|
||||
bool nonblock = (f->flags & O_NONBLOCK) || (flags & _MSG_DONTWAIT);
|
||||
flags &= ~_MSG_DONTWAIT;
|
||||
rc = __winsock_block(
|
||||
f->handle, flags, -nonblock, f->sndtimeo, m, sys_sendto_nt_start,
|
||||
&(struct SendToArgs){iov, iovlen, opt_in_addr, in_addrsize});
|
||||
__sig_unblock(m);
|
||||
sigset_t waitmask = __sig_block();
|
||||
|
||||
rc = __winsock_block(f->handle, flags & ~(_MSG_DONTWAIT | _MSG_NOSIGNAL),
|
||||
false, f->sndtimeo, waitmask, sys_sendto_nt_start,
|
||||
&(struct SendToArgs){iov, iovlen, //
|
||||
opt_in_addr, in_addrsize});
|
||||
|
||||
__sig_unblock(waitmask);
|
||||
|
||||
if (rc == -1 && errno == WSAESHUTDOWN) { // ESHUTDOWN
|
||||
errno = kNtErrorBrokenPipe; // EPIPE
|
||||
if (!(flags & _MSG_NOSIGNAL))
|
||||
__sig_raise(SIGPIPE, SI_KERNEL);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "libc/calls/struct/iovec.h"
|
||||
#include "libc/calls/struct/iovec.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/describeflags.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
#include "libc/macros.h"
|
||||
#include "libc/sock/internal.h"
|
||||
|
@ -88,8 +89,8 @@ ssize_t sendto(int fd, const void *buf, size_t size, int flags,
|
|||
}
|
||||
|
||||
END_CANCELATION_POINT;
|
||||
DATATRACE("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);
|
||||
DATATRACE("sendto(%d, %#.*hhs%s, %'zu, %s, %s) → %'ld% lm", fd,
|
||||
MAX(0, MIN(40, rc)), buf, rc > 40 ? "..." : "", size,
|
||||
DescribeMsg(flags), DescribeSockaddr(opt_addr, addrsize), rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
#ifdef __x86_64__
|
||||
|
||||
textwindows ssize_t
|
||||
__winsock_block(int64_t handle, uint32_t flags, int nonblock,
|
||||
__winsock_block(int64_t handle, uint32_t flags, bool nonblock,
|
||||
uint32_t srwtimeout, sigset_t waitmask,
|
||||
int StartSocketOp(int64_t handle, struct NtOverlapped *overlap,
|
||||
uint32_t *flags, void *arg),
|
||||
|
@ -63,21 +63,6 @@ __winsock_block(int64_t handle, uint32_t flags, int nonblock,
|
|||
bool got_eagain = false;
|
||||
uint32_t other_error = 0;
|
||||
|
||||
// send() and sendto() provide O_NONBLOCK as a negative number
|
||||
// because winsock has a bug that causes CancelIoEx() to cause
|
||||
// WSAGetOverlappedResult() to report errors when it succeeded
|
||||
if (nonblock < 0) {
|
||||
struct sys_pollfd_nt fds[1] = {{handle, POLLOUT}};
|
||||
switch (WSAPoll(fds, 1, 0)) {
|
||||
case -1:
|
||||
return __winsockerr();
|
||||
case 0:
|
||||
return eagain();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// create event handle for overlapped i/o
|
||||
intptr_t event;
|
||||
if (!(event = WSACreateEvent()))
|
||||
|
@ -86,7 +71,10 @@ __winsock_block(int64_t handle, uint32_t flags, int nonblock,
|
|||
struct NtOverlapped overlap = {.hEvent = event};
|
||||
bool32 ok = !StartSocketOp(handle, &overlap, &flags, arg);
|
||||
if (!ok && WSAGetLastError() == kNtErrorIoPending) {
|
||||
if (nonblock > 0) {
|
||||
if (nonblock) {
|
||||
// send() and sendto() shall not pass O_NONBLOCK along to here
|
||||
// because winsock has a bug that causes CancelIoEx() to cause
|
||||
// WSAGetOverlappedResult() to report errors when it succeeded
|
||||
CancelIoEx(handle, &overlap);
|
||||
got_eagain = true;
|
||||
} else {
|
||||
|
|
|
@ -1138,32 +1138,12 @@ syscon reboot RB_NOSYNC 0x20000000 0x20000000 4 4 4 4 4 0x2000000
|
|||
syscon msg MSG_OOB 1 1 1 1 1 1 1 1 # consensus
|
||||
syscon msg MSG_PEEK 2 2 2 2 2 2 2 2 # consensus
|
||||
syscon msg MSG_DONTROUTE 4 4 4 4 4 4 4 4 # consensus
|
||||
syscon msg MSG_FASTOPEN 0x20000000 0x20000000 0 0 0 0 0 0 # TODO
|
||||
syscon msg MSG_WAITALL 0x0100 0x0100 0x40 0x40 0x40 0x40 0x40 8 # bsd consensus
|
||||
syscon msg MSG_MORE 0x8000 0x8000 0 0 0 0 0 0 # send/sendto: manual TCP_CORK hbasically
|
||||
syscon msg MSG_NOSIGNAL 0x4000 0x4000 0x80000 0x80000 0x020000 0x0400 0x0400 0 # send/sendto: don't SIGPIPE on EOF
|
||||
syscon msg MSG_DONTWAIT 0x40 0x40 0x80 0x80 0x80 0x80 0x80 0x40 # send/sendto: manual non-blocking
|
||||
syscon msg MSG_WAITALL 0x0100 0x0100 0x40 0x40 0x40 0x40 0x40 8 # bsd consensus
|
||||
syscon msg MSG_NOSIGNAL 0x4000 0x4000 0x80000 0x80000 0x020000 0x0400 0x0400 0x10000000 # send/sendto: don't raise sigpipe on local shutdown
|
||||
syscon msg MSG_TRUNC 0x20 0x20 0x10 0x10 0x10 0x10 0x10 0x0100 # bsd consensus
|
||||
syscon msg MSG_CTRUNC 8 8 0x20 0x20 0x20 0x20 0x20 0x0200 # bsd consensus
|
||||
syscon msg MSG_ERRQUEUE 0x2000 0x2000 0 0 0 0 0 0x1000 # bsd consensus
|
||||
syscon msg MSG_NOERROR 0x1000 0x1000 0x1000 0x1000 0x1000 0x1000 0x1000 0 # unix consensus
|
||||
syscon msg MSG_EOR 0x80 0x80 8 8 8 8 8 0 # bsd consensus
|
||||
syscon msg MSG_CMSG_CLOEXEC 0x40000000 0x40000000 0 0 0x040000 0x0800 0x0800 0
|
||||
syscon msg MSG_WAITFORONE 0x010000 0x010000 0 0 0x080000 0 0x2000 0
|
||||
syscon msg MSG_BATCH 0x040000 0x040000 0 0 0 0 0 0
|
||||
syscon msg MSG_CONFIRM 0x0800 0x0800 0 0 0 0 0 0
|
||||
syscon msg MSG_EXCEPT 0x2000 0x2000 0 0 0 0 0 0
|
||||
syscon msg MSG_FIN 0x0200 0x0200 0x0100 0x0100 0x0100 0 0 0
|
||||
syscon msg MSG_EOF 0x0200 0x0200 0x0100 0x0100 0x0100 0 0 0
|
||||
syscon msg MSG_INFO 12 12 0 0 0 0 0 0
|
||||
syscon msg MSG_PARITY_ERROR 9 9 0 0 0 0 0 0
|
||||
syscon msg MSG_PROXY 0x10 0x10 0 0 0 0 0 0
|
||||
syscon msg MSG_RST 0x1000 0x1000 0 0 0 0 0 0
|
||||
syscon msg MSG_STAT 11 11 0 0 0 0 0 0
|
||||
syscon msg MSG_SYN 0x0400 0x0400 0 0 0 0 0 0
|
||||
syscon msg MSG_BCAST 0 0 0 0 0 0x100 0x100 0
|
||||
syscon msg MSG_MCAST 0 0 0 0 0 0x200 0x200 0
|
||||
syscon msg MSG_NOTIFICATION 0x8000 0x8000 0 0 0x2000 0 0x4000 0
|
||||
syscon msg MSG_FASTOPEN 0x20000000 0x20000000 -1 -1 -1 -1 -1 -1 #
|
||||
|
||||
# getpriority() / setpriority() magnums (a.k.a. nice)
|
||||
#
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon msg,MSG_BATCH,0x040000,0x040000,0,0,0,0,0,0
|
|
@ -1,2 +0,0 @@
|
|||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon msg,MSG_BCAST,0,0,0,0,0,0x100,0x100,0
|
|
@ -1,2 +0,0 @@
|
|||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon msg,MSG_CMSG_CLOEXEC,0x40000000,0x40000000,0,0,0x040000,0x0800,0x0800,0
|
|
@ -1,2 +0,0 @@
|
|||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon msg,MSG_CONFIRM,0x0800,0x0800,0,0,0,0,0,0
|
|
@ -1,2 +0,0 @@
|
|||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon msg,MSG_EOF,0x0200,0x0200,0x0100,0x0100,0x0100,0,0,0
|
|
@ -1,2 +0,0 @@
|
|||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon msg,MSG_EOR,0x80,0x80,8,8,8,8,8,0
|
|
@ -1,2 +0,0 @@
|
|||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon msg,MSG_ERRQUEUE,0x2000,0x2000,0,0,0,0,0,0x1000
|
|
@ -1,2 +0,0 @@
|
|||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon msg,MSG_EXCEPT,0x2000,0x2000,0,0,0,0,0,0
|
|
@ -1,2 +1,2 @@
|
|||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon msg,MSG_FASTOPEN,0x20000000,0x20000000,0,0,0,0,0,0
|
||||
.syscon msg,MSG_FASTOPEN,0x20000000,0x20000000,-1,-1,-1,-1,-1,-1
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon msg,MSG_FIN,0x0200,0x0200,0x0100,0x0100,0x0100,0,0,0
|
|
@ -1,2 +0,0 @@
|
|||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon msg,MSG_INFO,12,12,0,0,0,0,0,0
|
|
@ -1,2 +0,0 @@
|
|||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon msg,MSG_MCAST,0,0,0,0,0,0x200,0x200,0
|
|
@ -1,2 +0,0 @@
|
|||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon msg,MSG_MORE,0x8000,0x8000,0,0,0,0,0,0
|
|
@ -1,2 +0,0 @@
|
|||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon msg,MSG_NOERROR,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0
|
|
@ -1,2 +1,2 @@
|
|||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon msg,MSG_NOSIGNAL,0x4000,0x4000,0x80000,0x80000,0x020000,0x0400,0x0400,0
|
||||
.syscon msg,MSG_NOSIGNAL,0x4000,0x4000,0x80000,0x80000,0x020000,0x0400,0x0400,0x10000000
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon msg,MSG_NOTIFICATION,0x8000,0x8000,0,0,0x2000,0,0x4000,0
|
|
@ -1,2 +0,0 @@
|
|||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon msg,MSG_PARITY_ERROR,9,9,0,0,0,0,0,0
|
|
@ -1,2 +0,0 @@
|
|||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon msg,MSG_PROXY,0x10,0x10,0,0,0,0,0,0
|
|
@ -1,2 +0,0 @@
|
|||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon msg,MSG_RST,0x1000,0x1000,0,0,0,0,0,0
|
|
@ -1,2 +0,0 @@
|
|||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon msg,MSG_STAT,11,11,0,0,0,0,0,0
|
|
@ -1,2 +0,0 @@
|
|||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon msg,MSG_SYN,0x0400,0x0400,0,0,0,0,0,0
|
|
@ -1,2 +0,0 @@
|
|||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon msg,MSG_WAITFORONE,0x010000,0x010000,0,0,0x080000,0,0x2000,0
|
|
@ -2,41 +2,18 @@
|
|||
#define COSMOPOLITAN_LIBC_SYSV_CONSTS_MSG_H_
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
extern const int MSG_BATCH;
|
||||
extern const int MSG_BCAST;
|
||||
extern const int MSG_CMSG_CLOEXEC;
|
||||
extern const int MSG_CONFIRM;
|
||||
extern const int MSG_CTRUNC;
|
||||
extern const int MSG_DONTROUTE;
|
||||
extern const int MSG_DONTWAIT;
|
||||
extern const int MSG_EOF;
|
||||
extern const int MSG_EOR;
|
||||
extern const int MSG_ERRQUEUE;
|
||||
extern const int MSG_EXCEPT;
|
||||
extern const int MSG_FASTOPEN;
|
||||
extern const int MSG_FIN;
|
||||
extern const int MSG_INFO;
|
||||
extern const int MSG_MCAST;
|
||||
extern const int MSG_MORE;
|
||||
extern const int MSG_NOERROR;
|
||||
extern const int MSG_NOSIGNAL;
|
||||
extern const int MSG_NOTIFICATION;
|
||||
extern const int MSG_OOB;
|
||||
extern const int MSG_PARITY_ERROR;
|
||||
extern const int MSG_PEEK;
|
||||
extern const int MSG_PROXY;
|
||||
extern const int MSG_RST;
|
||||
extern const int MSG_STAT;
|
||||
extern const int MSG_SYN;
|
||||
extern const int MSG_TRUNC;
|
||||
extern const int MSG_WAITALL;
|
||||
extern const int MSG_WAITFORONE;
|
||||
extern const int MSG_NOSIGNAL;
|
||||
extern const int MSG_TRUNC;
|
||||
extern const int MSG_CTRUNC;
|
||||
extern const int MSG_FASTOPEN; /* linux only */
|
||||
|
||||
#define MSG_OOB 1
|
||||
#define MSG_PEEK 2
|
||||
#define MSG_DONTROUTE 4
|
||||
#define MSG_DONTWAIT MSG_DONTWAIT
|
||||
#define MSG_FASTOPEN MSG_FASTOPEN
|
||||
#define MSG_NOSIGNAL MSG_NOSIGNAL
|
||||
#define MSG_WAITALL MSG_WAITALL
|
||||
#define MSG_TRUNC MSG_TRUNC
|
||||
#define MSG_CTRUNC MSG_CTRUNC
|
||||
|
|
196
test/posix/msg_nosignal_test.c
Normal file
196
test/posix/msg_nosignal_test.c
Normal file
|
@ -0,0 +1,196 @@
|
|||
// Copyright 2024 Justine Alexandra Roberts Tunney
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for
|
||||
// any purpose with or without fee is hereby granted, provided that the
|
||||
// above copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||
// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
|
||||
// PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <cosmo.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <netinet/in.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/**
|
||||
* @fileoverview send(MSG_NOSIGNAL) test
|
||||
*
|
||||
* It's possible when writing to a socket for SIGPIPE to be raised. It
|
||||
* can happen for a variety of reasons. The one reason that has broad
|
||||
* consensus across OSes and is officially documented, is if shutdown()
|
||||
* is used on the local end.
|
||||
*/
|
||||
|
||||
struct sockaddr_in serv_addr;
|
||||
atomic_bool g_ready_for_conn;
|
||||
atomic_bool g_ready_for_data;
|
||||
atomic_bool g_ready_for_more;
|
||||
atomic_bool g_ready_for_exit;
|
||||
volatile sig_atomic_t g_got_signal;
|
||||
|
||||
void onsig(int sig) {
|
||||
g_got_signal = sig;
|
||||
}
|
||||
|
||||
void *server_thread(void *arg) {
|
||||
socklen_t len;
|
||||
int server, client;
|
||||
struct sockaddr_in cli_addr;
|
||||
|
||||
// create listening socket
|
||||
server = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (server == -1) {
|
||||
perror("socket");
|
||||
exit(10);
|
||||
}
|
||||
|
||||
// initialize server address
|
||||
memset(&serv_addr, 0, sizeof(serv_addr));
|
||||
serv_addr.sin_family = AF_INET;
|
||||
serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
serv_addr.sin_port = htons(0);
|
||||
|
||||
// bind socket
|
||||
if (bind(server, (struct sockaddr *)&serv_addr, sizeof(serv_addr))) {
|
||||
perror("bind");
|
||||
exit(11);
|
||||
}
|
||||
|
||||
// get assigned port
|
||||
len = sizeof(serv_addr);
|
||||
if (getsockname(server, (struct sockaddr *)&serv_addr, &len)) {
|
||||
perror("getsockname");
|
||||
exit(12);
|
||||
}
|
||||
|
||||
// listen on the socket
|
||||
if (listen(server, SOMAXCONN)) {
|
||||
perror("listen");
|
||||
exit(13);
|
||||
}
|
||||
|
||||
// wake main thread
|
||||
g_ready_for_conn = true;
|
||||
|
||||
// accept connection
|
||||
len = sizeof(cli_addr);
|
||||
client = accept(server, (struct sockaddr *)&cli_addr, &len);
|
||||
if (client == -1) {
|
||||
perror("accept");
|
||||
exit(14);
|
||||
}
|
||||
|
||||
// wake main thread
|
||||
g_ready_for_data = true;
|
||||
|
||||
// wait for thread
|
||||
for (;;)
|
||||
if (g_ready_for_exit)
|
||||
break;
|
||||
|
||||
// close sockets
|
||||
if (close(client))
|
||||
exit(29);
|
||||
if (close(server))
|
||||
exit(28);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main() {
|
||||
|
||||
// create server thread
|
||||
pthread_t th;
|
||||
if (pthread_create(&th, 0, server_thread, 0))
|
||||
return 1;
|
||||
|
||||
// wait for thread
|
||||
for (;;)
|
||||
if (g_ready_for_conn)
|
||||
break;
|
||||
|
||||
// create socket
|
||||
int client = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (client == -1) {
|
||||
perror("socket");
|
||||
return 2;
|
||||
}
|
||||
|
||||
// connect to server
|
||||
if (connect(client, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) {
|
||||
perror("connect");
|
||||
return 3;
|
||||
}
|
||||
|
||||
// wait for thread
|
||||
for (;;)
|
||||
if (g_ready_for_data)
|
||||
break;
|
||||
|
||||
// handle signals
|
||||
struct sigaction sa;
|
||||
sa.sa_flags = 0;
|
||||
sa.sa_handler = onsig;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sigaction(SIGPIPE, &sa, 0);
|
||||
|
||||
// half close socket
|
||||
if (shutdown(client, SHUT_WR))
|
||||
return 15;
|
||||
|
||||
// send first transmission
|
||||
int rc;
|
||||
for (;;) {
|
||||
rc = write(client, "x", 1);
|
||||
if (rc == 1)
|
||||
continue;
|
||||
if (rc != -1)
|
||||
return 4;
|
||||
if (errno != EPIPE) {
|
||||
perror("write");
|
||||
return 5;
|
||||
}
|
||||
// NetBSD is oddly lazy about sending SIGPIPE
|
||||
if (IsNetbsd())
|
||||
for (;;)
|
||||
if (g_got_signal)
|
||||
break;
|
||||
if (g_got_signal != SIGPIPE) {
|
||||
fprintf(stderr, "expected SIGPIPE but got %s\n", strsignal(g_got_signal));
|
||||
return 6;
|
||||
}
|
||||
g_got_signal = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// send first transmission
|
||||
rc = send(client, "x", 1, MSG_NOSIGNAL);
|
||||
if (rc != -1)
|
||||
return 7;
|
||||
if (errno != EPIPE)
|
||||
return 8;
|
||||
if (g_got_signal)
|
||||
return 9;
|
||||
|
||||
g_ready_for_exit = true;
|
||||
|
||||
if (pthread_join(th, 0))
|
||||
return 6;
|
||||
}
|
207
test/posix/msg_waitall_test.c
Normal file
207
test/posix/msg_waitall_test.c
Normal file
|
@ -0,0 +1,207 @@
|
|||
// Copyright 2024 Justine Alexandra Roberts Tunney
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for
|
||||
// any purpose with or without fee is hereby granted, provided that the
|
||||
// above copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||
// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
|
||||
// PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <cosmo.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <netinet/in.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/**
|
||||
* @fileoverview recv(MSG_WAITALL) test
|
||||
*/
|
||||
|
||||
struct sockaddr_in serv_addr;
|
||||
atomic_bool g_ready_for_conn;
|
||||
atomic_bool g_ready_for_data;
|
||||
atomic_bool g_ready_for_more;
|
||||
atomic_bool g_ready_for_exit;
|
||||
|
||||
void *server_thread(void *arg) {
|
||||
socklen_t len;
|
||||
int server, client;
|
||||
struct sockaddr_in cli_addr;
|
||||
|
||||
// create listening socket
|
||||
server = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (server == -1) {
|
||||
perror("socket");
|
||||
exit(10);
|
||||
}
|
||||
|
||||
// initialize server address
|
||||
memset(&serv_addr, 0, sizeof(serv_addr));
|
||||
serv_addr.sin_family = AF_INET;
|
||||
serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
serv_addr.sin_port = htons(0);
|
||||
|
||||
// bind socket
|
||||
if (bind(server, (struct sockaddr *)&serv_addr, sizeof(serv_addr))) {
|
||||
perror("bind");
|
||||
exit(11);
|
||||
}
|
||||
|
||||
// get assigned port
|
||||
len = sizeof(serv_addr);
|
||||
if (getsockname(server, (struct sockaddr *)&serv_addr, &len)) {
|
||||
perror("getsockname");
|
||||
exit(12);
|
||||
}
|
||||
|
||||
// listen on the socket
|
||||
if (listen(server, SOMAXCONN)) {
|
||||
perror("listen");
|
||||
exit(13);
|
||||
}
|
||||
|
||||
// wake main thread
|
||||
g_ready_for_conn = true;
|
||||
|
||||
// accept connection
|
||||
len = sizeof(cli_addr);
|
||||
client = accept(server, (struct sockaddr *)&cli_addr, &len);
|
||||
if (client == -1) {
|
||||
perror("accept");
|
||||
exit(14);
|
||||
}
|
||||
|
||||
// check waitall + dontwait
|
||||
char buf[2];
|
||||
int rc = recv(client, buf, 2, MSG_WAITALL | MSG_DONTWAIT);
|
||||
if (rc != -1)
|
||||
exit(15);
|
||||
if (errno != EAGAIN)
|
||||
exit(16);
|
||||
|
||||
// wake main thread
|
||||
g_ready_for_data = true;
|
||||
|
||||
// check peek
|
||||
rc = recv(client, buf, 2, MSG_PEEK);
|
||||
if (rc == -1) {
|
||||
perror("recv1");
|
||||
exit(17);
|
||||
}
|
||||
if (rc != 1)
|
||||
exit(18);
|
||||
if (buf[0] != 'x')
|
||||
exit(19);
|
||||
|
||||
// check read() has @restartable behavior
|
||||
rc = recv(client, buf, 2, MSG_WAITALL);
|
||||
if (rc == -1) {
|
||||
perror("recv2");
|
||||
exit(21);
|
||||
}
|
||||
if (rc != 2)
|
||||
exit(22);
|
||||
if (buf[0] != 'x')
|
||||
exit(23);
|
||||
if (buf[1] != 'y')
|
||||
exit(24);
|
||||
|
||||
// wake main thread
|
||||
g_ready_for_more = true;
|
||||
|
||||
// check normal recv won't wait
|
||||
rc = read(client, buf, 2);
|
||||
if (rc == -1) {
|
||||
perror("recv3");
|
||||
exit(25);
|
||||
}
|
||||
if (rc != 1)
|
||||
exit(26);
|
||||
if (buf[0] != 'x')
|
||||
exit(27);
|
||||
|
||||
// wait for main thread
|
||||
for (;;)
|
||||
if (g_ready_for_exit)
|
||||
break;
|
||||
|
||||
// close listening socket
|
||||
if (close(server))
|
||||
exit(28);
|
||||
if (close(client))
|
||||
exit(29);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main() {
|
||||
|
||||
// create server thread
|
||||
pthread_t th;
|
||||
if (pthread_create(&th, 0, server_thread, 0))
|
||||
return 1;
|
||||
|
||||
// wait for thread
|
||||
for (;;)
|
||||
if (g_ready_for_conn)
|
||||
break;
|
||||
|
||||
// create socket
|
||||
int client = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (client == -1) {
|
||||
perror("socket");
|
||||
return 2;
|
||||
}
|
||||
|
||||
// connect to server
|
||||
if (connect(client, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) {
|
||||
perror("connect");
|
||||
return 3;
|
||||
}
|
||||
|
||||
// wait for thread
|
||||
for (;;)
|
||||
if (g_ready_for_data)
|
||||
break;
|
||||
|
||||
// send first transmission
|
||||
usleep(100e3);
|
||||
if (write(client, "x", 1) != 1)
|
||||
return 4;
|
||||
usleep(100e3);
|
||||
if (write(client, "y", 1) != 1)
|
||||
return 5;
|
||||
|
||||
// wait for thread
|
||||
for (;;)
|
||||
if (g_ready_for_more)
|
||||
break;
|
||||
|
||||
// send second transmission
|
||||
usleep(100e3);
|
||||
if (write(client, "x", 1) != 1)
|
||||
return 4;
|
||||
usleep(100e3);
|
||||
if (write(client, "y", 1) != 1)
|
||||
return 5;
|
||||
|
||||
g_ready_for_exit = true;
|
||||
|
||||
if (pthread_join(th, 0))
|
||||
return 6;
|
||||
}
|
10
third_party/lua/lunix.c
vendored
10
third_party/lua/lunix.c
vendored
|
@ -3439,12 +3439,14 @@ int LuaUnix(lua_State *L) {
|
|||
LuaSetIntField(L, "SHUT_RDWR", SHUT_RDWR);
|
||||
|
||||
// recvfrom() / sendto() flags
|
||||
LuaSetIntField(L, "MSG_WAITALL", MSG_WAITALL);
|
||||
LuaSetIntField(L, "MSG_DONTROUTE", MSG_DONTROUTE);
|
||||
LuaSetIntField(L, "MSG_PEEK", MSG_PEEK);
|
||||
LuaSetIntField(L, "MSG_OOB", MSG_OOB);
|
||||
LuaSetIntField(L, "MSG_PEEK", MSG_PEEK);
|
||||
LuaSetIntField(L, "MSG_DONTROUTE", MSG_DONTROUTE);
|
||||
LuaSetIntField(L, "MSG_DONTWAIT", MSG_DONTWAIT);
|
||||
LuaSetIntField(L, "MSG_NOSIGNAL", MSG_NOSIGNAL);
|
||||
LuaSetIntField(L, "MSG_MORE", MSG_MORE);
|
||||
LuaSetIntField(L, "MSG_WAITALL", MSG_WAITALL);
|
||||
LuaSetIntField(L, "MSG_TRUNC", MSG_TRUNC);
|
||||
LuaSetIntField(L, "MSG_CTRUNC", MSG_CTRUNC);
|
||||
|
||||
// readdir() type
|
||||
LuaSetIntField(L, "DT_UNKNOWN", DT_UNKNOWN);
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "third_party/nsync/testing/closure.h"
|
||||
#include "third_party/nsync/testing/smprintf.h"
|
||||
#include "third_party/nsync/testing/testing.h"
|
||||
#include "libc/dce.h"
|
||||
#include "third_party/nsync/testing/time_extra.h"
|
||||
|
||||
/* Example use of CV.wait(): A priority queue of strings whose
|
21
third_party/python/Modules/socketmodule.c
vendored
21
third_party/python/Modules/socketmodule.c
vendored
|
@ -6790,24 +6790,9 @@ PyInit__socket(void)
|
|||
PyModule_AddIntMacro(m, MSG_TRUNC);
|
||||
PyModule_AddIntMacro(m, MSG_CTRUNC);
|
||||
PyModule_AddIntMacro(m, MSG_WAITALL);
|
||||
if (MSG_DONTWAIT) PyModule_AddIntMacro(m, MSG_DONTWAIT);
|
||||
if (MSG_EOR) PyModule_AddIntMacro(m, MSG_EOR);
|
||||
if (MSG_NOSIGNAL) PyModule_AddIntMacro(m, MSG_NOSIGNAL);
|
||||
if (MSG_BCAST) PyModule_AddIntMacro(m, MSG_BCAST);
|
||||
if (MSG_MCAST) PyModule_AddIntMacro(m, MSG_MCAST);
|
||||
if (MSG_CMSG_CLOEXEC) PyModule_AddIntMacro(m, MSG_CMSG_CLOEXEC);
|
||||
if (MSG_ERRQUEUE) PyModule_AddIntMacro(m, MSG_ERRQUEUE);
|
||||
if (MSG_CONFIRM) PyModule_AddIntMacro(m, MSG_CONFIRM);
|
||||
if (MSG_MORE) PyModule_AddIntMacro(m, MSG_MORE);
|
||||
if (MSG_NOTIFICATION) PyModule_AddIntMacro(m, MSG_NOTIFICATION);
|
||||
if (MSG_EOF) PyModule_AddIntMacro(m, MSG_EOF);
|
||||
if (MSG_FASTOPEN) PyModule_AddIntMacro(m, MSG_FASTOPEN);
|
||||
#ifdef MSG_BTAG
|
||||
if (MSG_BTAG) PyModule_AddIntMacro(m, MSG_BTAG);
|
||||
#endif
|
||||
#ifdef MSG_ETAG
|
||||
if (MSG_ETAG) PyModule_AddIntMacro(m, MSG_ETAG);
|
||||
#endif
|
||||
PyModule_AddIntMacro(m, MSG_DONTWAIT);
|
||||
PyModule_AddIntMacro(m, MSG_NOSIGNAL);
|
||||
if (MSG_FASTOPEN != -1) PyModule_AddIntMacro(m, MSG_FASTOPEN);
|
||||
|
||||
/* Protocol level and numbers, usable for [gs]etsockopt */
|
||||
PyModule_AddIntMacro(m, SOL_SOCKET);
|
||||
|
|
Loading…
Reference in a new issue