mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-07 06:53:33 +00:00
Fix bug with send() on Windows in O_NONBLOCK mode
There is a bug in WIN32 where using CancelIoEx() on an overlapped i/o op initiated by WSASend() will cause WSAGetOverlappedResult() to report the operation failed when it actually succeeded. We now work around that, by having send and sendto initially consult WSAPoll() on O_NONBLOCK sockets
This commit is contained in:
parent
5aa970bc4e
commit
3c58ecd00c
4 changed files with 22 additions and 5 deletions
|
@ -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, bool, uint32_t, uint64_t,
|
||||
ssize_t __winsock_block(int64_t, uint32_t, int, uint32_t, uint64_t,
|
||||
int (*)(int64_t, struct NtOverlapped *, uint32_t *,
|
||||
void *),
|
||||
void *);
|
||||
|
|
|
@ -56,7 +56,7 @@ textwindows ssize_t sys_send_nt(int fd, const struct iovec *iov, size_t iovlen,
|
|||
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,
|
||||
rc = __winsock_block(f->handle, flags, -nonblock, f->sndtimeo, m,
|
||||
sys_send_nt_start, &(struct SendArgs){iov, iovlen});
|
||||
__sig_unblock(m);
|
||||
return rc;
|
||||
|
|
|
@ -60,7 +60,7 @@ textwindows ssize_t sys_sendto_nt(int fd, const struct iovec *iov,
|
|||
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,
|
||||
f->handle, flags, -nonblock, f->sndtimeo, m, sys_sendto_nt_start,
|
||||
&(struct SendToArgs){iov, iovlen, opt_in_addr, in_addrsize});
|
||||
__sig_unblock(m);
|
||||
return rc;
|
||||
|
|
|
@ -30,17 +30,19 @@
|
|||
#include "libc/nt/events.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/overlapped.h"
|
||||
#include "libc/nt/struct/pollfd.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/nt/winsock.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/sysv/consts/poll.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
textwindows ssize_t
|
||||
__winsock_block(int64_t handle, uint32_t flags, bool nonblock,
|
||||
__winsock_block(int64_t handle, uint32_t flags, int nonblock,
|
||||
uint32_t srwtimeout, sigset_t waitmask,
|
||||
int StartSocketOp(int64_t handle, struct NtOverlapped *overlap,
|
||||
uint32_t *flags, void *arg),
|
||||
|
@ -61,6 +63,21 @@ __winsock_block(int64_t handle, uint32_t flags, bool 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()))
|
||||
|
@ -69,7 +86,7 @@ __winsock_block(int64_t handle, uint32_t flags, bool nonblock,
|
|||
struct NtOverlapped overlap = {.hEvent = event};
|
||||
bool32 ok = !StartSocketOp(handle, &overlap, &flags, arg);
|
||||
if (!ok && WSAGetLastError() == kNtErrorIoPending) {
|
||||
if (nonblock) {
|
||||
if (nonblock > 0) {
|
||||
CancelIoEx(handle, &overlap);
|
||||
got_eagain = true;
|
||||
} else {
|
||||
|
|
Loading…
Reference in a new issue