Merge remote-tracking branch 'redis/master' into finalizer

This commit is contained in:
Viktor Söderqvist 2023-08-23 16:27:27 +02:00
commit cb6fbbfa52
7 changed files with 224 additions and 22 deletions

29
.github/spellcheck-settings.yml vendored Normal file
View File

@ -0,0 +1,29 @@
matrix:
- name: Markdown
expect_match: false
apsell:
lang: en
d: en_US
ignore-case: true
dictionary:
wordlists:
- .github/wordlist.txt
output: wordlist.dic
pipeline:
- pyspelling.filters.markdown:
markdown_extensions:
- markdown.extensions.extra:
- pyspelling.filters.html:
comments: false
attributes:
- alt
ignores:
- ':matches(code, pre)'
- code
- pre
- blockquote
- img
sources:
- 'README.md'
- 'FAQ.md'
- 'docs/**'

99
.github/wordlist.txt vendored Normal file
View File

@ -0,0 +1,99 @@
ABI
ACLs
alloc
Allocator
allocators
antirez
api
APIs
ASYNC
asyncRedisContext
asyncronous
AUTOFREE
autoload
autoloader
autoloading
Autoloading
backend
backends
behaviour
boolean
CAS
Changelog
customizable
Customizable
CVE
dataset
de
deallocation
ElastiCache
extensibility
FPM
getaddrinfo
gmail
grunder
Grunder
hiredis
Hiredis
HIREDIS
hostname
IANA
IPv
IPV
keepalive
keyspace
keyspaces
KiB
libc
libev
libevent
localhost
Lua
michael
minimalistic
namespace
NOAUTOFREE
NOAUTOFREEREPLIES
NONBLOCK
Noordhuis
OpenSSL
Packagist
pcnoordhuis
PhpRedis
Pieter
pipelined
pipelining
pluggable
Predis
PRERELEASE
printf
PSR
PSUBSCRIBE
rb
Readme
README
rebalanced
rebalancing
redis
Redis
redisAsyncContext
redisContext
redisOptions
redisReader
reusability
REUSEADDR
runtime
Sanfilippo
SHA
sharding
SONAME
SSL
struct
stunnel
subelements
TCP
TLS
unparsed
UNSPEC
URI
variadic

14
.github/workflows/spellcheck.yml vendored Normal file
View File

@ -0,0 +1,14 @@
name: spellcheck
on:
pull_request:
jobs:
check-spelling:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Check Spelling
uses: rojopolis/spellcheck-github-actions@0.33.1
with:
config_path: .github/spellcheck-settings.yml
task_name: Markdown

1
.gitignore vendored
View File

@ -7,3 +7,4 @@
/*.pc
*.dSYM
tags
compile_commands.json

View File

@ -23,6 +23,17 @@ Redis version >= 1.2.0.
The library comes with multiple APIs. There is the
*synchronous API*, the *asynchronous API* and the *reply parsing API*.
## Upgrading to > 1.2.0 (**PRERELEASE**)
* After v1.2.0 we modified how we invoke `poll(2)` to wait for connections to complete, such that we will now retry
the call if it is interrupted by a signal until:
a) The connection succeeds or fails.
b) The overall connection timeout is reached.
In previous versions, an interrupted `poll(2)` call would cause the connection to fail
with `c->err` set to `REDIS_ERR_IO` and `c->errstr` set to `poll(2): Interrupted system call`.
## Upgrading to `1.1.0`
Almost all users will simply need to recompile their applications against the newer version of hiredis.
@ -291,7 +302,7 @@ void redisFree(redisContext *c);
This function immediately closes the socket and then frees the allocations done in
creating the context.
### Sending commands (cont'd)
### Sending commands (continued)
Together with `redisCommand`, the function `redisCommandArgv` can be used to issue commands.
It has the following prototype:

58
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"
@ -172,6 +173,10 @@ int redisKeepAlive(redisContext *c, int interval) {
int val = 1;
redisFD fd = c->fd;
/* TCP_KEEPALIVE makes no sense with AF_UNIX connections */
if (c->connection_type == REDIS_CONN_UNIX)
return REDIS_ERR;
#ifndef _WIN32
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1){
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
@ -271,37 +276,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) {

32
test.c
View File

@ -104,6 +104,13 @@ static long long usec(void) {
#define assert(e) (void)(e)
#endif
#define redisTestPanic(msg) \
do { \
fprintf(stderr, "PANIC: %s (In function \"%s\", file \"%s\", line %d)\n", \
msg, __func__, __FILE__, __LINE__); \
exit(1); \
} while (1)
/* Helper to extract Redis version information. Aborts on any failure. */
#define REDIS_VERSION_FIELD "redis_version:"
void get_redis_version(redisContext *c, int *majorptr, int *minorptr) {
@ -232,7 +239,7 @@ static redisContext *do_connect(struct config config) {
c = redisConnectFd(fd);
}
} else {
assert(NULL);
redisTestPanic("Unknown connection type!");
}
if (c == NULL) {
@ -424,6 +431,24 @@ static void test_tcp_options(struct config cfg) {
redisFree(c);
}
static void test_unix_keepalive(struct config cfg) {
redisContext *c;
redisReply *r;
c = do_connect(cfg);
test("Setting TCP_KEEPALIVE on a unix socket returns an error: ");
test_cond(redisEnableKeepAlive(c) == REDIS_ERR && c->err == 0);
test("Setting TCP_KEEPALIVE on a unix socket doesn't break the connection: ");
r = redisCommand(c, "PING");
test_cond(r != NULL && r->type == REDIS_REPLY_STATUS && r->len == 4 &&
!memcmp(r->str, "PONG", 4));
freeReplyObject(r);
redisFree(c);
}
static void test_reply_reader(void) {
redisReader *reader;
void *reply, *root;
@ -1352,7 +1377,7 @@ static void test_invalid_timeout_errors(struct config config) {
} else if(config.type == CONN_UNIX) {
c = redisConnectUnixWithTimeout(config.unix_sock.path, config.connect_timeout);
} else {
assert(NULL);
redisTestPanic("Unknown connection type!");
}
test_cond(c != NULL && c->err == REDIS_ERR_IO && strcmp(c->errstr, "Invalid timeout specified") == 0);
@ -1368,7 +1393,7 @@ static void test_invalid_timeout_errors(struct config config) {
} else if(config.type == CONN_UNIX) {
c = redisConnectUnixWithTimeout(config.unix_sock.path, config.connect_timeout);
} else {
assert(NULL);
redisTestPanic("Unknown connection type!");
}
test_cond(c != NULL && c->err == REDIS_ERR_IO && strcmp(c->errstr, "Invalid timeout specified") == 0);
@ -2473,6 +2498,7 @@ int main(int argc, char **argv) {
test_blocking_connection_timeouts(cfg);
test_blocking_io_errors(cfg);
test_invalid_timeout_errors(cfg);
test_unix_keepalive(cfg);
if (throughput) test_throughput(cfg);
} else {
test_skipped();