Retry poll(2) if we are intterupted.

This commit adds logic to retry our poll call when waiting for the
connection to complete, in the event that we are interrupted by a
signal.

Additionally we do some simple bookkeeping to keep track of the overall
timeout specified by the user.

Fixes #1206
This commit is contained in:
michael-grunder 2023-07-12 14:23:07 -07:00
parent dedc6208b1
commit 97679e57ba

54
net.c
View File

@ -41,6 +41,7 @@
#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
#include <time.h>
#include "net.h"
#include "sds.h"
@ -271,37 +272,54 @@ static int redisContextTimeoutMsec(redisContext *c, long *result)
return REDIS_OK;
}
static long redisPollMillis(void) {
#ifndef _MSC_VER
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
return (now.tv_sec * 1000) + now.tv_nsec / 1000000;
#else
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
return (((long long)ft.dwHighDateTime << 32) | ft.dwLowDateTime) / 10;
#endif
}
static int redisContextWaitReady(redisContext *c, long msec) {
struct pollfd wfd[1];
struct pollfd wfd;
long end;
int res;
wfd[0].fd = c->fd;
wfd[0].events = POLLOUT;
if (errno != EINPROGRESS) {
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
redisNetClose(c);
return REDIS_ERR;
}
if (errno == EINPROGRESS) {
int res;
wfd.fd = c->fd;
wfd.events = POLLOUT;
end = msec >= 0 ? redisPollMillis() + msec : 0;
if ((res = poll(wfd, 1, msec)) == -1) {
while ((res = poll(&wfd, 1, msec)) <= 0) {
if (res < 0 && errno != EINTR) {
__redisSetErrorFromErrno(c, REDIS_ERR_IO, "poll(2)");
redisNetClose(c);
return REDIS_ERR;
} else if (res == 0) {
} else if (res == 0 || (msec >= 0 && redisPollMillis() >= end)) {
errno = ETIMEDOUT;
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
__redisSetErrorFromErrno(c, REDIS_ERR_IO, NULL);
redisNetClose(c);
return REDIS_ERR;
} else {
/* res < 0 && errno == EINTR, try again */
}
if (redisCheckConnectDone(c, &res) != REDIS_OK || res == 0) {
redisCheckSocketError(c);
return REDIS_ERR;
}
return REDIS_OK;
}
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
redisNetClose(c);
return REDIS_ERR;
if (redisCheckConnectDone(c, &res) != REDIS_OK || res == 0) {
redisCheckSocketError(c);
return REDIS_ERR;
}
return REDIS_OK;
}
int redisCheckConnectDone(redisContext *c, int *completed) {