Skip to content

Commit 87a6669

Browse files
committed
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.
1 parent ce2fbf9 commit 87a6669

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+583
-183
lines changed

libc/intrin/describeflags.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ struct thatispacked DescribeFlags {
88
const char *name;
99
};
1010

11-
const char *_DescribeFlags(char *, size_t, const struct DescribeFlags *,
12-
size_t, const char *, unsigned) libcesque;
11+
const char *_DescribeFlags(char *, size_t, const struct DescribeFlags *, size_t,
12+
const char *, unsigned) libcesque;
1313

1414
const char *_DescribeArchPrctlCode(char[12], int) libcesque;
1515
const char *_DescribeCancelState(char[12], int, int *) libcesque;
@@ -27,6 +27,7 @@ const char *_DescribeItimer(char[12], int) libcesque;
2727
const char *_DescribeMapFlags(char[64], int) libcesque;
2828
const char *_DescribeMapping(char[8], int, int) libcesque;
2929
const char *_DescribeMremapFlags(char[30], int) libcesque;
30+
const char *_DescribeMsg(char[16], int) libcesque;
3031
const char *_DescribeMsyncFlags(char[48], int) libcesque;
3132
const char *_DescribeNtConsoleInFlags(char[256], uint32_t) libcesque;
3233
const char *_DescribeNtConsoleOutFlags(char[128], uint32_t) libcesque;
@@ -84,6 +85,7 @@ const char *_DescribeWhichPrio(char[12], int) libcesque;
8485
#define DescribeMapFlags(x) _DescribeMapFlags(alloca(64), x)
8586
#define DescribeMapping(x, y) _DescribeMapping(alloca(8), x, y)
8687
#define DescribeMremapFlags(x) _DescribeMremapFlags(alloca(30), x)
88+
#define DescribeMsg(x) _DescribeMsg(alloca(16), x)
8789
#define DescribeMsyncFlags(x) _DescribeMsyncFlags(alloca(48), x)
8890
#define DescribeNtConsoleInFlags(x) _DescribeNtConsoleInFlags(alloca(256), x)
8991
#define DescribeNtConsoleOutFlags(x) _DescribeNtConsoleOutFlags(alloca(128), x)

libc/intrin/describemsg.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
2+
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
3+
╞══════════════════════════════════════════════════════════════════════════════╡
4+
│ Copyright 2024 Justine Alexandra Roberts Tunney │
5+
│ │
6+
│ Permission to use, copy, modify, and/or distribute this software for │
7+
│ any purpose with or without fee is hereby granted, provided that the │
8+
│ above copyright notice and this permission notice appear in all copies. │
9+
│ │
10+
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
11+
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
12+
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
13+
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
14+
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
15+
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
16+
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
17+
│ PERFORMANCE OF THIS SOFTWARE. │
18+
╚─────────────────────────────────────────────────────────────────────────────*/
19+
#include "libc/intrin/describeflags.h"
20+
#include "libc/macros.h"
21+
#include "libc/sysv/consts/msg.h"
22+
23+
const char *_DescribeMsg(char buf[16], int x) {
24+
const struct DescribeFlags kMsgFlags[] = {
25+
{MSG_FASTOPEN, "FASTOPEN"}, // order matters
26+
{MSG_OOB, "OOB"}, //
27+
{MSG_PEEK, "PEEK"}, //
28+
{MSG_DONTROUTE, "DONTROUTE"}, //
29+
{MSG_DONTWAIT, "DONTWAIT"}, //
30+
{MSG_WAITALL, "WAITALL"}, //
31+
{MSG_NOSIGNAL, "NOSIGNAL"}, //
32+
{MSG_TRUNC, "TRUNC"}, //
33+
{MSG_CTRUNC, "CTRUNC"}, //
34+
};
35+
return _DescribeFlags(buf, 16, kMsgFlags, ARRAYLEN(kMsgFlags), "MSG_", x);
36+
}

libc/sock/internal.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ int sys_select_nt(int, fd_set *, fd_set *, fd_set *, struct timeval *,
6565

6666
size_t __iovec2nt(struct NtIovec[hasatleast 16], const struct iovec *, size_t);
6767

68-
ssize_t __winsock_block(int64_t, uint32_t, int, uint32_t, uint64_t,
68+
ssize_t __winsock_block(int64_t, uint32_t, bool, uint32_t, uint64_t,
6969
int (*)(int64_t, struct NtOverlapped *, uint32_t *,
7070
void *),
7171
void *);

libc/sock/recv-nt.c

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,26 @@
1717
│ PERFORMANCE OF THIS SOFTWARE. │
1818
╚─────────────────────────────────────────────────────────────────────────────*/
1919
#include "libc/calls/internal.h"
20-
#include "libc/calls/struct/iovec.h"
2120
#include "libc/calls/struct/sigset.internal.h"
22-
#include "libc/intrin/fds.h"
2321
#include "libc/nt/struct/iovec.h"
22+
#include "libc/nt/struct/overlapped.h"
23+
#include "libc/nt/thunk/msabi.h"
2424
#include "libc/nt/winsock.h"
2525
#include "libc/sock/internal.h"
2626
#include "libc/sock/syscall_fd.internal.h"
27+
#include "libc/sysv/consts/fio.h"
2728
#include "libc/sysv/consts/o.h"
2829
#include "libc/sysv/errfuns.h"
30+
#include "libc/vga/vga.internal.h"
2931
#ifdef __x86_64__
3032

3133
#define _MSG_OOB 1
3234
#define _MSG_PEEK 2
3335
#define _MSG_WAITALL 8
3436
#define _MSG_DONTWAIT 64
3537

38+
__msabi extern typeof(__sys_ioctlsocket_nt) *const __imp_ioctlsocket;
39+
3640
struct RecvArgs {
3741
const struct iovec *iov;
3842
size_t iovlen;
@@ -54,13 +58,24 @@ textwindows ssize_t sys_recv_nt(int fd, const struct iovec *iov, size_t iovlen,
5458
return einval();
5559
ssize_t rc;
5660
struct Fd *f = g_fds.p + fd;
57-
sigset_t m = __sig_block();
58-
bool nonblock = !(flags & _MSG_WAITALL) &&
59-
((f->flags & O_NONBLOCK) || (flags & _MSG_DONTWAIT));
60-
flags &= ~_MSG_DONTWAIT;
61-
rc = __winsock_block(f->handle, flags, nonblock, f->rcvtimeo, m,
62-
sys_recv_nt_start, &(struct RecvArgs){iov, iovlen});
63-
__sig_unblock(m);
61+
sigset_t waitmask = __sig_block();
62+
63+
// "Be aware that if the underlying transport provider does not
64+
// support MSG_WAITALL, or if the socket is in a non-blocking mode,
65+
// then this call will fail with WSAEOPNOTSUPP. Also, if MSG_WAITALL
66+
// is specified along with MSG_OOB, MSG_PEEK, or MSG_PARTIAL, then
67+
// this call will fail with WSAEOPNOTSUPP."
68+
// —Quoth MSDN § WSARecv
69+
if (flags & _MSG_WAITALL)
70+
__imp_ioctlsocket(f->handle, FIONBIO, (uint32_t[]){0});
71+
72+
rc = __winsock_block(f->handle, flags & ~_MSG_DONTWAIT,
73+
(f->flags & O_NONBLOCK) || (flags & _MSG_DONTWAIT),
74+
f->rcvtimeo, waitmask, sys_recv_nt_start,
75+
&(struct RecvArgs){iov, iovlen});
76+
77+
__sig_unblock(waitmask);
78+
6479
return rc;
6580
}
6681

libc/sock/recv.c

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,40 @@
2020
#include "libc/calls/internal.h"
2121
#include "libc/calls/struct/iovec.internal.h"
2222
#include "libc/dce.h"
23+
#include "libc/intrin/describeflags.h"
2324
#include "libc/intrin/strace.h"
2425
#include "libc/sock/internal.h"
2526
#include "libc/sock/sock.h"
2627
#include "libc/sock/syscall_fd.internal.h"
28+
#include "libc/sysv/consts/msg.h"
2729
#include "libc/sysv/errfuns.h"
2830

2931
/**
3032
* Receives data from network socket.
3133
*
34+
* Calling `recv(fd, p, n, 0)` is equivalent to `read(fd, p, n)`.
35+
*
36+
* Unlike files where the OS tries very hard to fulfill the entire
37+
* requested `size` before returning, read operations on sockets aim to
38+
* return as quickly as possible. For example, if 10 bytes are requested
39+
* and a packet comes in with only 5 bytes, then recv() will most likely
40+
* return those 5 bytes before waiting longer. The `MSG_WAITALL` flag
41+
* may be passed when waiting longer is desired. In that case, short
42+
* reads should only be possible when the connection status changes or
43+
* the receive operation is interrupted by a signal.
44+
*
3245
* @param fd is the file descriptor returned by socket()
3346
* @param buf is where received network data gets copied
3447
* @param size is the byte capacity of buf
3548
* @param flags can have `MSG_OOB`, `MSG_PEEK`, `MSG_DONTWAIT`, `MSG_WAITALL`
3649
* @return number of bytes received, 0 on remote close, or -1 w/ errno
50+
* @raise EINTR if signal handler was called instead
51+
* @raise EINVAL if unknown bits were passed in `flags`
52+
* @raise EINVAL if flag isn't supported by host operating system
53+
* @raise EINVAL if `MSG_WAITALL` and `MSG_PEEK` were both passed
54+
* @raise EBADF if `fd` is an invalid file descriptor
55+
* @raise EAGAIN if `MSG_DONTWAIT` was passed and no data was available
56+
* @raise EAGAIN if `O_NONBLOCK` is in play and no data was available
3757
* @error EINTR, EHOSTUNREACH, ECONNRESET (UDP ICMP Port Unreachable),
3858
* EPIPE (if MSG_NOSIGNAL), EMSGSIZE, ENOTSOCK, EFAULT, etc.
3959
* @cancelationpoint
@@ -44,7 +64,11 @@ ssize_t recv(int fd, void *buf, size_t size, int flags) {
4464
ssize_t rc;
4565
BEGIN_CANCELATION_POINT;
4666

47-
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
67+
if ((flags & (MSG_WAITALL | MSG_PEEK)) == (MSG_WAITALL | MSG_PEEK)) {
68+
// this is possible on some OSes like Linux but it breaks FreeBSD
69+
// and Windows will raise EOPNOTSUPP when it gets passed together
70+
return einval();
71+
} else if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
4872
rc = enotsock();
4973
} else if (!IsWindows()) {
5074
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) {
6589
}
6690

6791
END_CANCELATION_POINT;
68-
DATATRACE("recv(%d, [%#.*hhs%s], %'zu, %#x) → %'ld% lm", fd,
69-
MAX(0, MIN(40, rc)), buf, rc > 40 ? "..." : "", size, flags, rc);
92+
DATATRACE("recv(%d, [%#.*hhs%s], %'zu, %s) → %'ld% lm", fd,
93+
MAX(0, MIN(40, rc)), buf, rc > 40 ? "..." : "", size,
94+
DescribeMsg(flags), rc);
7095
return rc;
7196
}

libc/sock/recvfrom-nt.c

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,13 @@ textwindows ssize_t sys_recvfrom_nt(int fd, const struct iovec *iov,
5959
return einval();
6060
ssize_t rc;
6161
struct Fd *f = g_fds.p + fd;
62-
sigset_t m = __sig_block();
63-
bool nonblock = (f->flags & O_NONBLOCK) || (flags & _MSG_DONTWAIT);
64-
flags &= ~_MSG_DONTWAIT;
65-
rc = __winsock_block(f->handle, flags, nonblock, f->rcvtimeo, m,
66-
sys_recvfrom_nt_start,
62+
sigset_t waitmask = __sig_block();
63+
rc = __winsock_block(f->handle, flags & ~_MSG_DONTWAIT,
64+
(f->flags & O_NONBLOCK) || (flags & _MSG_DONTWAIT),
65+
f->rcvtimeo, waitmask, sys_recvfrom_nt_start,
6766
&(struct RecvFromArgs){iov, iovlen, opt_out_srcaddr,
6867
opt_inout_srcaddrsize});
69-
__sig_unblock(m);
68+
__sig_unblock(waitmask);
7069
return rc;
7170
}
7271

libc/sock/recvfrom.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "libc/calls/struct/iovec.h"
2222
#include "libc/calls/struct/iovec.internal.h"
2323
#include "libc/dce.h"
24+
#include "libc/intrin/describeflags.h"
2425
#include "libc/intrin/strace.h"
2526
#include "libc/nt/winsock.h"
2627
#include "libc/sock/internal.h"
@@ -95,7 +96,11 @@ ssize_t recvfrom(int fd, void *buf, size_t size, int flags,
9596
}
9697

9798
END_CANCELATION_POINT;
98-
DATATRACE("recvfrom(%d, [%#.*hhs%s], %'zu, %#x) → %'ld% lm", fd,
99-
MAX(0, MIN(40, rc)), buf, rc > 40 ? "..." : "", size, flags, rc);
99+
DATATRACE(
100+
"recvfrom(%d, [%#.*hhs%s], %'zu, %s, %s) → %'ld% lm", fd,
101+
MAX(0, MIN(40, rc)), buf, rc > 40 ? "..." : "", size, DescribeMsg(flags),
102+
DescribeSockaddr(opt_out_srcaddr,
103+
opt_inout_srcaddrsize ? *opt_inout_srcaddrsize : 0),
104+
rc);
100105
return rc;
101106
}

libc/sock/send-nt.c

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,25 @@
1717
│ PERFORMANCE OF THIS SOFTWARE. │
1818
╚─────────────────────────────────────────────────────────────────────────────*/
1919
#include "libc/calls/internal.h"
20-
#include "libc/calls/struct/iovec.h"
20+
#include "libc/calls/sig.internal.h"
21+
#include "libc/calls/struct/iovec.internal.h"
2122
#include "libc/calls/struct/sigset.internal.h"
22-
#include "libc/intrin/fds.h"
23+
#include "libc/errno.h"
24+
#include "libc/nt/errors.h"
2325
#include "libc/nt/struct/iovec.h"
26+
#include "libc/nt/struct/overlapped.h"
2427
#include "libc/nt/winsock.h"
2528
#include "libc/sock/internal.h"
26-
#include "libc/sock/syscall_fd.internal.h"
27-
#include "libc/sysv/consts/o.h"
29+
#include "libc/sysv/consts/sicode.h"
30+
#include "libc/sysv/consts/sig.h"
2831
#include "libc/sysv/errfuns.h"
32+
#include "libc/vga/vga.internal.h"
2933
#ifdef __x86_64__
3034

3135
#define _MSG_OOB 1
3236
#define _MSG_DONTROUTE 4
3337
#define _MSG_DONTWAIT 64
38+
#define _MSG_NOSIGNAL 0x10000000
3439

3540
struct SendArgs {
3641
const struct iovec *iov;
@@ -49,23 +54,24 @@ textwindows static int sys_send_nt_start(int64_t handle,
4954

5055
textwindows ssize_t sys_send_nt(int fd, const struct iovec *iov, size_t iovlen,
5156
uint32_t flags) {
52-
if (flags & ~(_MSG_DONTWAIT | _MSG_OOB | _MSG_DONTROUTE))
57+
if (flags & ~(_MSG_DONTWAIT | _MSG_OOB | _MSG_DONTROUTE | _MSG_NOSIGNAL))
5358
return einval();
5459
ssize_t rc;
5560
struct Fd *f = g_fds.p + fd;
56-
sigset_t m = __sig_block();
61+
sigset_t waitmask = __sig_block();
5762

58-
// we don't check O_NONBLOCK because we want to avoid needing to call
59-
// WSAPoll() every time we write() to a non-blocking socket. WIN32 is
60-
// unsafe at canceling socket sends. lots of code doesn't check write
61-
// return status. good programs that sincerely want to avoid blocking
62-
// on send() operations should have already called poll() beforehand.
63-
bool nonblock = flags & _MSG_DONTWAIT;
63+
rc = __winsock_block(f->handle, flags & ~(_MSG_DONTWAIT | _MSG_NOSIGNAL),
64+
false, f->sndtimeo, waitmask, sys_send_nt_start,
65+
&(struct SendArgs){iov, iovlen});
66+
67+
__sig_unblock(waitmask);
68+
69+
if (rc == -1 && errno == WSAESHUTDOWN) { // ESHUTDOWN
70+
errno = kNtErrorBrokenPipe; // EPIPE
71+
if (!(flags & _MSG_NOSIGNAL))
72+
__sig_raise(SIGPIPE, SI_KERNEL);
73+
}
6474

65-
flags &= ~_MSG_DONTWAIT;
66-
rc = __winsock_block(f->handle, flags, -nonblock, f->sndtimeo, m,
67-
sys_send_nt_start, &(struct SendArgs){iov, iovlen});
68-
__sig_unblock(m);
6975
return rc;
7076
}
7177

libc/sock/send.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "libc/calls/struct/iovec.h"
2222
#include "libc/calls/struct/iovec.internal.h"
2323
#include "libc/dce.h"
24+
#include "libc/intrin/describeflags.h"
2425
#include "libc/intrin/strace.h"
2526
#include "libc/macros.h"
2627
#include "libc/sock/internal.h"
@@ -78,7 +79,7 @@ ssize_t send(int fd, const void *buf, size_t size, int flags) {
7879
}
7980

8081
END_CANCELATION_POINT;
81-
DATATRACE("send(%d, %#.*hhs%s, %'zu, %#x) → %'ld% lm", fd,
82-
MAX(0, MIN(40, rc)), buf, rc > 40 ? "..." : "", size, flags, rc);
82+
DATATRACE("send(%d, %#.*hhs%s, %'zu, %s) → %'ld% lm", fd, MAX(0, MIN(40, rc)),
83+
buf, rc > 40 ? "..." : "", size, DescribeMsg(flags), rc);
8384
return rc;
8485
}

libc/sock/sendto-nt.c

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,26 @@
1717
│ PERFORMANCE OF THIS SOFTWARE. │
1818
╚─────────────────────────────────────────────────────────────────────────────*/
1919
#include "libc/calls/internal.h"
20+
#include "libc/calls/sig.internal.h"
2021
#include "libc/calls/struct/iovec.h"
2122
#include "libc/calls/struct/sigset.internal.h"
23+
#include "libc/errno.h"
2224
#include "libc/intrin/fds.h"
25+
#include "libc/nt/errors.h"
2326
#include "libc/nt/struct/iovec.h"
2427
#include "libc/nt/winsock.h"
2528
#include "libc/sock/internal.h"
2629
#include "libc/sock/syscall_fd.internal.h"
2730
#include "libc/sysv/consts/o.h"
31+
#include "libc/sysv/consts/sicode.h"
32+
#include "libc/sysv/consts/sig.h"
2833
#include "libc/sysv/errfuns.h"
2934
#ifdef __x86_64__
3035

3136
#define _MSG_OOB 1
3237
#define _MSG_DONTROUTE 4
3338
#define _MSG_DONTWAIT 64
39+
#define _MSG_NOSIGNAL 0x10000000
3440

3541
struct SendToArgs {
3642
const struct iovec *iov;
@@ -52,17 +58,25 @@ static textwindows int sys_sendto_nt_start(int64_t handle,
5258
textwindows ssize_t sys_sendto_nt(int fd, const struct iovec *iov,
5359
size_t iovlen, uint32_t flags,
5460
void *opt_in_addr, uint32_t in_addrsize) {
55-
if (flags & ~(_MSG_DONTWAIT | _MSG_OOB | _MSG_DONTROUTE))
61+
if (flags & ~(_MSG_DONTWAIT | _MSG_OOB | _MSG_DONTROUTE | _MSG_NOSIGNAL))
5662
return einval();
5763
ssize_t rc;
5864
struct Fd *f = g_fds.p + fd;
59-
sigset_t m = __sig_block();
60-
bool nonblock = (f->flags & O_NONBLOCK) || (flags & _MSG_DONTWAIT);
61-
flags &= ~_MSG_DONTWAIT;
62-
rc = __winsock_block(
63-
f->handle, flags, -nonblock, f->sndtimeo, m, sys_sendto_nt_start,
64-
&(struct SendToArgs){iov, iovlen, opt_in_addr, in_addrsize});
65-
__sig_unblock(m);
65+
sigset_t waitmask = __sig_block();
66+
67+
rc = __winsock_block(f->handle, flags & ~(_MSG_DONTWAIT | _MSG_NOSIGNAL),
68+
false, f->sndtimeo, waitmask, sys_sendto_nt_start,
69+
&(struct SendToArgs){iov, iovlen, //
70+
opt_in_addr, in_addrsize});
71+
72+
__sig_unblock(waitmask);
73+
74+
if (rc == -1 && errno == WSAESHUTDOWN) { // ESHUTDOWN
75+
errno = kNtErrorBrokenPipe; // EPIPE
76+
if (!(flags & _MSG_NOSIGNAL))
77+
__sig_raise(SIGPIPE, SI_KERNEL);
78+
}
79+
6680
return rc;
6781
}
6882

0 commit comments

Comments
 (0)