diff options
author | michael-grunder <michael.grunder@gmail.com> | 2023-07-12 14:23:07 -0700 |
---|---|---|
committer | Michael Grunder <michael.grunder@gmail.com> | 2023-07-25 10:24:06 -0700 |
commit | af95517612c209976352201f5f8f302bf9aef876 (patch) | |
tree | 09f30a641a152987e98f15615ffd90a8a47c5beb | |
parent | dedc6208b15738d7765eff35a61dd09d5830e732 (diff) |
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
-rw-r--r-- | net.c | 52 |
1 files changed, 35 insertions, 17 deletions
@@ -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; + if (redisCheckConnectDone(c, &res) != REDIS_OK || res == 0) { + redisCheckSocketError(c); + return REDIS_ERR; } - __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); - redisNetClose(c); - return REDIS_ERR; + return REDIS_OK; } int redisCheckConnectDone(redisContext *c, int *completed) { |