Emulate Linux socket timeout signaling on Windows

This commit is contained in:
Justine Tunney 2024-09-17 00:24:08 -07:00
parent 65e425fbca
commit b14dddcc18
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
8 changed files with 246 additions and 29 deletions

View file

@ -48,40 +48,34 @@ textwindows int sys_getsockopt_nt(struct Fd *fd, int level, int optname,
}
if (level == SOL_SOCKET && optname == SO_ERROR) {
if (in_optlen >= sizeof(int)) {
int err;
uint32_t len = sizeof(err);
if (__imp_getsockopt(fd->handle, SOL_SOCKET, SO_ERROR, &err, &len) == -1)
return __winsockerr();
*(int *)out_opt_optval = __dos2errno(err);
*inout_optlen = sizeof(int);
} else {
if (in_optlen < sizeof(int))
return einval();
}
int err;
uint32_t len = sizeof(err);
if (__imp_getsockopt(fd->handle, SOL_SOCKET, SO_ERROR, &err, &len) == -1)
return __winsockerr();
*(int *)out_opt_optval = __dos2errno(err);
*inout_optlen = sizeof(int);
}
if (level == SOL_SOCKET &&
(optname == SO_RCVTIMEO || optname == SO_SNDTIMEO)) {
if (in_optlen >= sizeof(struct timeval)) {
if (optname == SO_RCVTIMEO) {
ms = fd->rcvtimeo;
} else {
ms = fd->sndtimeo;
}
((struct timeval *)out_opt_optval)->tv_sec = ms / 1000;
((struct timeval *)out_opt_optval)->tv_usec = ms % 1000 * 1000;
*inout_optlen = sizeof(struct timeval);
return 0;
} else {
if (in_optlen < sizeof(struct timeval))
return einval();
if (optname == SO_RCVTIMEO) {
ms = fd->rcvtimeo;
} else {
ms = fd->sndtimeo;
}
*(struct timeval *)out_opt_optval = timeval_frommillis(ms);
*inout_optlen = sizeof(struct timeval);
return 0;
}
// TODO(jart): Use WSAIoctl?
if (__imp_getsockopt(fd->handle, level, optname, out_opt_optval,
inout_optlen) == -1) {
inout_optlen) == -1)
return __winsockerr();
}
if (level == SOL_SOCKET) {
if (optname == SO_LINGER && in_optlen == sizeof(struct linger)) {

View file

@ -38,7 +38,7 @@
* EPIPE (if MSG_NOSIGNAL), EMSGSIZE, ENOTSOCK, EFAULT, etc.
* @cancelationpoint
* @asyncsignalsafe
* @restartable (unless SO_RCVTIMEO)
* @restartable (unless SO_RCVTIMEO on Linux or Windows)
*/
ssize_t recv(int fd, void *buf, size_t size, int flags) {
ssize_t rc;

View file

@ -49,7 +49,7 @@
* EPIPE (if MSG_NOSIGNAL), EMSGSIZE, ENOTSOCK, EFAULT, etc.
* @cancelationpoint
* @asyncsignalsafe
* @restartable (unless SO_RCVTIMEO)
* @restartable (unless SO_RCVTIMEO on Linux or Windows)
*/
ssize_t recvfrom(int fd, void *buf, size_t size, int flags,
struct sockaddr *opt_out_srcaddr,

View file

@ -51,7 +51,7 @@
* EPIPE (if MSG_NOSIGNAL), EMSGSIZE, ENOTSOCK, EFAULT, etc.
* @cancelationpoint
* @asyncsignalsafe
* @restartable (unless SO_RCVTIMEO)
* @restartable (unless SO_SNDTIMEO on Linux or Windows)
*/
ssize_t send(int fd, const void *buf, size_t size, int flags) {
ssize_t rc;

View file

@ -51,7 +51,7 @@
* EPIPE (if MSG_NOSIGNAL), EMSGSIZE, ENOTSOCK, EFAULT, etc.
* @cancelationpoint
* @asyncsignalsafe
* @restartable (unless SO_RCVTIMEO)
* @restartable (unless SO_SNDTIMEO on Linux or Windows)
*/
ssize_t sendto(int fd, const void *buf, size_t size, int flags,
const struct sockaddr *opt_addr, uint32_t addrsize) {

View file

@ -36,14 +36,17 @@ textwindows int sys_setsockopt_nt(struct Fd *fd, int level, int optname,
const void *optval, uint32_t optlen) {
// socket read/write timeouts
// timeout of zero means wait forever (default)
if (level == SOL_SOCKET &&
(optname == SO_RCVTIMEO || optname == SO_SNDTIMEO)) {
if (!(optval && optlen == sizeof(struct timeval)))
if (!optval)
return einval();
if (optlen < sizeof(struct timeval))
return einval();
const struct timeval *tv = optval;
int64_t ms = timeval_tomillis(*tv);
if (ms > -1u)
ms = 0; // wait forever (default) yes zero actually means this
ms = -1u;
if (optname == SO_RCVTIMEO)
fd->rcvtimeo = ms;
if (optname == SO_SNDTIMEO)
@ -51,7 +54,7 @@ textwindows int sys_setsockopt_nt(struct Fd *fd, int level, int optname,
return 0; // we want to handle this on our own
}
// how to make close() a blocking i/o call
// how to make close() a blocking i/o call lool
union {
uint32_t millis;
struct linger_nt linger;

View file

@ -191,6 +191,11 @@ __winsock_block(int64_t handle, uint32_t flags, int nonblock,
// check if signal handler without SA_RESTART was called
if (handler_was_called & SIG_HANDLED_NO_RESTART)
return eintr();
// emulates linux behavior of having timeouts @norestart
if (handler_was_called & SIG_HANDLED_SA_RESTART)
if (srwtimeout)
return eintr();
}
// otherwise try the i/o operation again