From 87a66699006dedd9810d8df5b386c70320c2b351 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Wed, 18 Sep 2024 19:54:56 -0700 Subject: [PATCH] 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. --- libc/intrin/describeflags.h | 6 +- libc/intrin/describemsg.c | 36 +++ libc/sock/internal.h | 2 +- libc/sock/recv-nt.c | 33 ++- libc/sock/recv.c | 31 ++- libc/sock/recvfrom-nt.c | 11 +- libc/sock/recvfrom.c | 9 +- libc/sock/send-nt.c | 38 ++-- libc/sock/send.c | 5 +- libc/sock/sendto-nt.c | 30 ++- libc/sock/sendto.c | 7 +- libc/sock/winsockblock.c | 22 +- libc/sysv/consts.sh | 26 +-- libc/sysv/consts/MSG_BATCH.S | 2 - libc/sysv/consts/MSG_BCAST.S | 2 - libc/sysv/consts/MSG_CMSG_CLOEXEC.S | 2 - libc/sysv/consts/MSG_CONFIRM.S | 2 - libc/sysv/consts/MSG_EOF.S | 2 - libc/sysv/consts/MSG_EOR.S | 2 - libc/sysv/consts/MSG_ERRQUEUE.S | 2 - libc/sysv/consts/MSG_EXCEPT.S | 2 - libc/sysv/consts/MSG_FASTOPEN.S | 2 +- libc/sysv/consts/MSG_FIN.S | 2 - libc/sysv/consts/MSG_INFO.S | 2 - libc/sysv/consts/MSG_MCAST.S | 2 - libc/sysv/consts/MSG_MORE.S | 2 - libc/sysv/consts/MSG_NOERROR.S | 2 - libc/sysv/consts/MSG_NOSIGNAL.S | 2 +- libc/sysv/consts/MSG_NOTIFICATION.S | 2 - libc/sysv/consts/MSG_PARITY_ERROR.S | 2 - libc/sysv/consts/MSG_PROXY.S | 2 - libc/sysv/consts/MSG_RST.S | 2 - libc/sysv/consts/MSG_STAT.S | 2 - libc/sysv/consts/MSG_SYN.S | 2 - libc/sysv/consts/MSG_WAITFORONE.S | 2 - libc/sysv/consts/msg.h | 31 +-- test/posix/msg_nosignal_test.c | 196 +++++++++++++++++ test/posix/msg_waitall_test.c | 207 ++++++++++++++++++ third_party/lua/lunix.c | 10 +- ..._wait_example_test.c => cv_wait_example.c} | 1 + third_party/python/Modules/socketmodule.c | 21 +- 41 files changed, 583 insertions(+), 183 deletions(-) create mode 100644 libc/intrin/describemsg.c delete mode 100644 libc/sysv/consts/MSG_BATCH.S delete mode 100644 libc/sysv/consts/MSG_BCAST.S delete mode 100644 libc/sysv/consts/MSG_CMSG_CLOEXEC.S delete mode 100644 libc/sysv/consts/MSG_CONFIRM.S delete mode 100644 libc/sysv/consts/MSG_EOF.S delete mode 100644 libc/sysv/consts/MSG_EOR.S delete mode 100644 libc/sysv/consts/MSG_ERRQUEUE.S delete mode 100644 libc/sysv/consts/MSG_EXCEPT.S delete mode 100644 libc/sysv/consts/MSG_FIN.S delete mode 100644 libc/sysv/consts/MSG_INFO.S delete mode 100644 libc/sysv/consts/MSG_MCAST.S delete mode 100644 libc/sysv/consts/MSG_MORE.S delete mode 100644 libc/sysv/consts/MSG_NOERROR.S delete mode 100644 libc/sysv/consts/MSG_NOTIFICATION.S delete mode 100644 libc/sysv/consts/MSG_PARITY_ERROR.S delete mode 100644 libc/sysv/consts/MSG_PROXY.S delete mode 100644 libc/sysv/consts/MSG_RST.S delete mode 100644 libc/sysv/consts/MSG_STAT.S delete mode 100644 libc/sysv/consts/MSG_SYN.S delete mode 100644 libc/sysv/consts/MSG_WAITFORONE.S create mode 100644 test/posix/msg_nosignal_test.c create mode 100644 test/posix/msg_waitall_test.c rename third_party/nsync/testing/{cv_wait_example_test.c => cv_wait_example.c} (99%) diff --git a/libc/intrin/describeflags.h b/libc/intrin/describeflags.h index 917ef0f46ea..85814c78d54 100644 --- a/libc/intrin/describeflags.h +++ b/libc/intrin/describeflags.h @@ -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) diff --git a/libc/intrin/describemsg.c b/libc/intrin/describemsg.c new file mode 100644 index 00000000000..9cfc5372e58 --- /dev/null +++ b/libc/intrin/describemsg.c @@ -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); +} diff --git a/libc/sock/internal.h b/libc/sock/internal.h index b8704360b68..9dbd690dc34 100644 --- a/libc/sock/internal.h +++ b/libc/sock/internal.h @@ -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 *); diff --git a/libc/sock/recv-nt.c b/libc/sock/recv-nt.c index 14b0e0edd1c..013fc930aae 100644 --- a/libc/sock/recv-nt.c +++ b/libc/sock/recv-nt.c @@ -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; } diff --git a/libc/sock/recv.c b/libc/sock/recv.c index 8d97329b380..31003e7fb1e 100644 --- a/libc/sock/recv.c +++ b/libc/sock/recv.c @@ -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; } diff --git a/libc/sock/recvfrom-nt.c b/libc/sock/recvfrom-nt.c index c94c942606d..e40e6ed6a80 100644 --- a/libc/sock/recvfrom-nt.c +++ b/libc/sock/recvfrom-nt.c @@ -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; } diff --git a/libc/sock/recvfrom.c b/libc/sock/recvfrom.c index d323b775c84..dbcda5b2c51 100644 --- a/libc/sock/recvfrom.c +++ b/libc/sock/recvfrom.c @@ -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; } diff --git a/libc/sock/send-nt.c b/libc/sock/send-nt.c index b12c0f2b45e..c9169003c73 100644 --- a/libc/sock/send-nt.c +++ b/libc/sock/send-nt.c @@ -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; } diff --git a/libc/sock/send.c b/libc/sock/send.c index 25c836ddcb1..81831b27cad 100644 --- a/libc/sock/send.c +++ b/libc/sock/send.c @@ -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; } diff --git a/libc/sock/sendto-nt.c b/libc/sock/sendto-nt.c index 41e3520ba56..831cf65529f 100644 --- a/libc/sock/sendto-nt.c +++ b/libc/sock/sendto-nt.c @@ -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; } diff --git a/libc/sock/sendto.c b/libc/sock/sendto.c index a949c711d00..f5a458aeb72 100644 --- a/libc/sock/sendto.c +++ b/libc/sock/sendto.c @@ -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; } diff --git a/libc/sock/winsockblock.c b/libc/sock/winsockblock.c index 6eb1e702e11..32a7e0c8283 100644 --- a/libc/sock/winsockblock.c +++ b/libc/sock/winsockblock.c @@ -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 { diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index a372572b5cb..63e532bb72c 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -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) # diff --git a/libc/sysv/consts/MSG_BATCH.S b/libc/sysv/consts/MSG_BATCH.S deleted file mode 100644 index e171f4f2fe9..00000000000 --- a/libc/sysv/consts/MSG_BATCH.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon msg,MSG_BATCH,0x040000,0x040000,0,0,0,0,0,0 diff --git a/libc/sysv/consts/MSG_BCAST.S b/libc/sysv/consts/MSG_BCAST.S deleted file mode 100644 index 082634257f8..00000000000 --- a/libc/sysv/consts/MSG_BCAST.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon msg,MSG_BCAST,0,0,0,0,0,0x100,0x100,0 diff --git a/libc/sysv/consts/MSG_CMSG_CLOEXEC.S b/libc/sysv/consts/MSG_CMSG_CLOEXEC.S deleted file mode 100644 index ea66034e30f..00000000000 --- a/libc/sysv/consts/MSG_CMSG_CLOEXEC.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon msg,MSG_CMSG_CLOEXEC,0x40000000,0x40000000,0,0,0x040000,0x0800,0x0800,0 diff --git a/libc/sysv/consts/MSG_CONFIRM.S b/libc/sysv/consts/MSG_CONFIRM.S deleted file mode 100644 index 1948be63a74..00000000000 --- a/libc/sysv/consts/MSG_CONFIRM.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon msg,MSG_CONFIRM,0x0800,0x0800,0,0,0,0,0,0 diff --git a/libc/sysv/consts/MSG_EOF.S b/libc/sysv/consts/MSG_EOF.S deleted file mode 100644 index 3e8168a2a98..00000000000 --- a/libc/sysv/consts/MSG_EOF.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon msg,MSG_EOF,0x0200,0x0200,0x0100,0x0100,0x0100,0,0,0 diff --git a/libc/sysv/consts/MSG_EOR.S b/libc/sysv/consts/MSG_EOR.S deleted file mode 100644 index fe773b16a92..00000000000 --- a/libc/sysv/consts/MSG_EOR.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon msg,MSG_EOR,0x80,0x80,8,8,8,8,8,0 diff --git a/libc/sysv/consts/MSG_ERRQUEUE.S b/libc/sysv/consts/MSG_ERRQUEUE.S deleted file mode 100644 index 02f1934ff9c..00000000000 --- a/libc/sysv/consts/MSG_ERRQUEUE.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon msg,MSG_ERRQUEUE,0x2000,0x2000,0,0,0,0,0,0x1000 diff --git a/libc/sysv/consts/MSG_EXCEPT.S b/libc/sysv/consts/MSG_EXCEPT.S deleted file mode 100644 index da072f0a99f..00000000000 --- a/libc/sysv/consts/MSG_EXCEPT.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon msg,MSG_EXCEPT,0x2000,0x2000,0,0,0,0,0,0 diff --git a/libc/sysv/consts/MSG_FASTOPEN.S b/libc/sysv/consts/MSG_FASTOPEN.S index ff034a75b9a..17aa7bab691 100644 --- a/libc/sysv/consts/MSG_FASTOPEN.S +++ b/libc/sysv/consts/MSG_FASTOPEN.S @@ -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 diff --git a/libc/sysv/consts/MSG_FIN.S b/libc/sysv/consts/MSG_FIN.S deleted file mode 100644 index f0631e333e6..00000000000 --- a/libc/sysv/consts/MSG_FIN.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon msg,MSG_FIN,0x0200,0x0200,0x0100,0x0100,0x0100,0,0,0 diff --git a/libc/sysv/consts/MSG_INFO.S b/libc/sysv/consts/MSG_INFO.S deleted file mode 100644 index d882e3a75c8..00000000000 --- a/libc/sysv/consts/MSG_INFO.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon msg,MSG_INFO,12,12,0,0,0,0,0,0 diff --git a/libc/sysv/consts/MSG_MCAST.S b/libc/sysv/consts/MSG_MCAST.S deleted file mode 100644 index 0f68f532139..00000000000 --- a/libc/sysv/consts/MSG_MCAST.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon msg,MSG_MCAST,0,0,0,0,0,0x200,0x200,0 diff --git a/libc/sysv/consts/MSG_MORE.S b/libc/sysv/consts/MSG_MORE.S deleted file mode 100644 index fa8717d4ff5..00000000000 --- a/libc/sysv/consts/MSG_MORE.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon msg,MSG_MORE,0x8000,0x8000,0,0,0,0,0,0 diff --git a/libc/sysv/consts/MSG_NOERROR.S b/libc/sysv/consts/MSG_NOERROR.S deleted file mode 100644 index 22764bb8d58..00000000000 --- a/libc/sysv/consts/MSG_NOERROR.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon msg,MSG_NOERROR,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0 diff --git a/libc/sysv/consts/MSG_NOSIGNAL.S b/libc/sysv/consts/MSG_NOSIGNAL.S index 48be7e922e0..1a0539eef6c 100644 --- a/libc/sysv/consts/MSG_NOSIGNAL.S +++ b/libc/sysv/consts/MSG_NOSIGNAL.S @@ -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 diff --git a/libc/sysv/consts/MSG_NOTIFICATION.S b/libc/sysv/consts/MSG_NOTIFICATION.S deleted file mode 100644 index 7503b5a505a..00000000000 --- a/libc/sysv/consts/MSG_NOTIFICATION.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon msg,MSG_NOTIFICATION,0x8000,0x8000,0,0,0x2000,0,0x4000,0 diff --git a/libc/sysv/consts/MSG_PARITY_ERROR.S b/libc/sysv/consts/MSG_PARITY_ERROR.S deleted file mode 100644 index 7abd7fafd1c..00000000000 --- a/libc/sysv/consts/MSG_PARITY_ERROR.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon msg,MSG_PARITY_ERROR,9,9,0,0,0,0,0,0 diff --git a/libc/sysv/consts/MSG_PROXY.S b/libc/sysv/consts/MSG_PROXY.S deleted file mode 100644 index e6216ce6417..00000000000 --- a/libc/sysv/consts/MSG_PROXY.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon msg,MSG_PROXY,0x10,0x10,0,0,0,0,0,0 diff --git a/libc/sysv/consts/MSG_RST.S b/libc/sysv/consts/MSG_RST.S deleted file mode 100644 index 3bed0246bbe..00000000000 --- a/libc/sysv/consts/MSG_RST.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon msg,MSG_RST,0x1000,0x1000,0,0,0,0,0,0 diff --git a/libc/sysv/consts/MSG_STAT.S b/libc/sysv/consts/MSG_STAT.S deleted file mode 100644 index cdee6a1109d..00000000000 --- a/libc/sysv/consts/MSG_STAT.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon msg,MSG_STAT,11,11,0,0,0,0,0,0 diff --git a/libc/sysv/consts/MSG_SYN.S b/libc/sysv/consts/MSG_SYN.S deleted file mode 100644 index b57fbeb86e0..00000000000 --- a/libc/sysv/consts/MSG_SYN.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon msg,MSG_SYN,0x0400,0x0400,0,0,0,0,0,0 diff --git a/libc/sysv/consts/MSG_WAITFORONE.S b/libc/sysv/consts/MSG_WAITFORONE.S deleted file mode 100644 index e89b311e270..00000000000 --- a/libc/sysv/consts/MSG_WAITFORONE.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon msg,MSG_WAITFORONE,0x010000,0x010000,0,0,0x080000,0,0x2000,0 diff --git a/libc/sysv/consts/msg.h b/libc/sysv/consts/msg.h index 74deaca2690..3694fc6f65a 100644 --- a/libc/sysv/consts/msg.h +++ b/libc/sysv/consts/msg.h @@ -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_WAITALL; 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_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 diff --git a/test/posix/msg_nosignal_test.c b/test/posix/msg_nosignal_test.c new file mode 100644 index 00000000000..c164a4b9ba7 --- /dev/null +++ b/test/posix/msg_nosignal_test.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @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; +} diff --git a/test/posix/msg_waitall_test.c b/test/posix/msg_waitall_test.c new file mode 100644 index 00000000000..99942383d16 --- /dev/null +++ b/test/posix/msg_waitall_test.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @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; +} diff --git a/third_party/lua/lunix.c b/third_party/lua/lunix.c index 06cd1541b1e..9a8b4487713 100644 --- a/third_party/lua/lunix.c +++ b/third_party/lua/lunix.c @@ -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); diff --git a/third_party/nsync/testing/cv_wait_example_test.c b/third_party/nsync/testing/cv_wait_example.c similarity index 99% rename from third_party/nsync/testing/cv_wait_example_test.c rename to third_party/nsync/testing/cv_wait_example.c index d365d4f9d8c..f94092987da 100644 --- a/third_party/nsync/testing/cv_wait_example_test.c +++ b/third_party/nsync/testing/cv_wait_example.c @@ -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 diff --git a/third_party/python/Modules/socketmodule.c b/third_party/python/Modules/socketmodule.c index 03680235bdc..a8e36826882 100644 --- a/third_party/python/Modules/socketmodule.c +++ b/third_party/python/Modules/socketmodule.c @@ -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);