Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
hiredis
Commits
298e9325
Commit
298e9325
authored
Oct 31, 2010
by
Pieter Noordhuis
Browse files
First step in decoupling reply callbacks from internals
parent
a3a405bc
Changes
3
Hide whitespace changes
Inline
Side-by-side
hiredis.c
View file @
298e9325
...
@@ -639,28 +639,10 @@ static redisContext *redisContextInit(redisReplyObjectFunctions *fn) {
...
@@ -639,28 +639,10 @@ static redisContext *redisContextInit(redisReplyObjectFunctions *fn) {
c
->
obuf
=
sdsempty
();
c
->
obuf
=
sdsempty
();
c
->
fn
=
fn
==
NULL
?
&
defaultFunctions
:
fn
;
c
->
fn
=
fn
==
NULL
?
&
defaultFunctions
:
fn
;
c
->
reader
=
redisReplyReaderCreate
(
c
->
fn
);
c
->
reader
=
redisReplyReaderCreate
(
c
->
fn
);
c
->
callbacks
=
calloc
(
sizeof
(
redisCallback
),
1
);
c
->
clen
=
1
;
c
->
cpos
=
0
;
return
c
;
return
c
;
}
}
void
redisDisconnect
(
redisContext
*
c
)
{
void
redisDisconnect
(
redisContext
*
c
)
{
int
i
;
redisCallback
cb
;
/* Non-blocking context: call pending callbacks with the NULL reply */
if
(
!
(
c
->
flags
&
REDIS_BLOCK
))
{
for
(
i
=
0
;
i
<
c
->
cpos
;
i
++
)
{
cb
=
c
->
callbacks
[
i
];
if
(
cb
.
fn
!=
NULL
)
{
cb
.
fn
(
c
,
NULL
,
cb
.
privdata
);
}
}
/* Reset callback index */
c
->
cpos
=
0
;
}
if
(
c
->
cbDisconnect
.
fn
!=
NULL
)
if
(
c
->
cbDisconnect
.
fn
!=
NULL
)
c
->
cbDisconnect
.
fn
(
c
,
c
->
cbDisconnect
.
privdata
);
c
->
cbDisconnect
.
fn
(
c
,
c
->
cbDisconnect
.
privdata
);
close
(
c
->
fd
);
close
(
c
->
fd
);
...
@@ -679,8 +661,6 @@ void redisFree(redisContext *c) {
...
@@ -679,8 +661,6 @@ void redisFree(redisContext *c) {
sdsfree
(
c
->
error
);
sdsfree
(
c
->
error
);
if
(
c
->
obuf
!=
NULL
)
if
(
c
->
obuf
!=
NULL
)
sdsfree
(
c
->
obuf
);
sdsfree
(
c
->
obuf
);
if
(
c
->
clen
>
0
)
free
(
c
->
callbacks
);
redisReplyReaderFree
(
c
->
reader
);
redisReplyReaderFree
(
c
->
reader
);
free
(
c
);
free
(
c
);
}
}
...
@@ -759,38 +739,6 @@ int redisGetReply(redisContext *c, void **reply) {
...
@@ -759,38 +739,6 @@ int redisGetReply(redisContext *c, void **reply) {
return
REDIS_OK
;
return
REDIS_OK
;
}
}
static
void
redisPopCallback
(
redisContext
*
c
)
{
assert
(
c
->
cpos
>
0
);
if
(
c
->
cpos
>
1
)
memmove
(
&
c
->
callbacks
[
0
],
&
c
->
callbacks
[
1
],(
c
->
cpos
-
1
)
*
sizeof
(
redisCallback
));
c
->
cpos
--
;
}
int
redisProcessCallbacks
(
redisContext
*
c
)
{
void
*
reply
=
NULL
;
redisCallback
cb
;
/* Continue while there are callbacks */
while
(
c
->
cpos
>
0
)
{
cb
=
c
->
callbacks
[
0
];
if
(
redisGetReply
(
c
,
&
reply
)
==
REDIS_ERR
)
return
REDIS_ERR
;
if
(
reply
!=
NULL
)
{
redisPopCallback
(
c
);
if
(
cb
.
fn
!=
NULL
)
{
cb
.
fn
(
c
,
reply
,
cb
.
privdata
);
}
else
{
c
->
fn
->
freeObject
(
reply
);
}
}
else
{
/* Stop trying */
break
;
}
}
return
REDIS_OK
;
}
/* Write the output buffer to the socket.
/* Write the output buffer to the socket.
*
*
* Returns REDIS_OK when the buffer is empty, or (a part of) the buffer was
* Returns REDIS_OK when the buffer is empty, or (a part of) the buffer was
...
@@ -853,24 +801,10 @@ static int redisCommandWriteBlock(redisContext *c, void **reply, char *str, size
...
@@ -853,24 +801,10 @@ static int redisCommandWriteBlock(redisContext *c, void **reply, char *str, size
return
REDIS_OK
;
return
REDIS_OK
;
}
}
static
int
redisCommandWriteNonBlock
(
redisContext
*
c
,
redisCallback
*
cb
,
char
*
str
,
size_t
len
)
{
static
int
redisCommandWriteNonBlock
(
redisContext
*
c
,
char
*
str
,
size_t
len
)
{
assert
(
!
(
c
->
flags
&
REDIS_BLOCK
));
assert
(
!
(
c
->
flags
&
REDIS_BLOCK
));
c
->
obuf
=
sdscatlen
(
c
->
obuf
,
str
,
len
);
c
->
obuf
=
sdscatlen
(
c
->
obuf
,
str
,
len
);
/* Make sure there is space for the callback. */
assert
(
c
->
cpos
<=
c
->
clen
);
if
(
c
->
cpos
==
c
->
clen
)
{
c
->
clen
++
;
c
->
callbacks
=
realloc
(
c
->
callbacks
,
c
->
clen
*
sizeof
(
redisCallback
));
}
if
(
cb
!=
NULL
)
{
c
->
callbacks
[
c
->
cpos
]
=
*
cb
;
}
else
{
memset
(
&
c
->
callbacks
[
c
->
cpos
],
0
,
sizeof
(
redisCallback
));
}
c
->
cpos
++
;
/* Fire write callback */
/* Fire write callback */
if
(
c
->
cbCommand
.
fn
!=
NULL
)
if
(
c
->
cbCommand
.
fn
!=
NULL
)
c
->
cbCommand
.
fn
(
c
,
c
->
cbCommand
.
privdata
);
c
->
cbCommand
.
fn
(
c
,
c
->
cbCommand
.
privdata
);
...
@@ -880,13 +814,12 @@ static int redisCommandWriteNonBlock(redisContext *c, redisCallback *cb, char *s
...
@@ -880,13 +814,12 @@ static int redisCommandWriteNonBlock(redisContext *c, redisCallback *cb, char *s
/* Write a formatted command to the output buffer. If the given context is
/* Write a formatted command to the output buffer. If the given context is
* blocking, immediately read the reply into the "reply" pointer. When the
* blocking, immediately read the reply into the "reply" pointer. When the
* context is non-blocking, the "reply" pointer will not be used and
a
* context is non-blocking, the "reply" pointer will not be used and
the
*
NULL callback will be appended to the list of callbacks
.
*
command is simply appended to the write buffer
.
*
*
* Returns the reply when a reply was succesfully retrieved. Returns NULL
* Returns the reply when a reply was succesfully retrieved. Returns NULL
* otherwise. When NULL is returned in a blocking context, provided that
* otherwise. When NULL is returned in a blocking context, the error field
* the reply build functions did not return NULL when building the reply,
* in the context will be set. */
* the error field in the context will be set. */
void
*
redisCommand
(
redisContext
*
c
,
const
char
*
format
,
...)
{
void
*
redisCommand
(
redisContext
*
c
,
const
char
*
format
,
...)
{
va_list
ap
;
va_list
ap
;
char
*
cmd
;
char
*
cmd
;
...
@@ -902,33 +835,8 @@ void *redisCommand(redisContext *c, const char *format, ...) {
...
@@ -902,33 +835,8 @@ void *redisCommand(redisContext *c, const char *format, ...) {
return
reply
;
return
reply
;
}
}
}
else
{
}
else
{
redisCommandWriteNonBlock
(
c
,
NULL
,
cmd
,
len
);
redisCommandWriteNonBlock
(
c
,
cmd
,
len
);
}
}
free
(
cmd
);
free
(
cmd
);
return
NULL
;
return
NULL
;
}
}
/* Write a formatted command to the output buffer. Registers the provided
* callback function and argument in the callback list.
*
* Always returns NULL. In a non-blocking context this will never fail because
* this function does not do any I/O. In a blocking context this function will
* have no effect (a callback in a blocking context makes no sense). */
void
*
redisCommandWithCallback
(
redisContext
*
c
,
redisCallbackFn
*
fn
,
void
*
privdata
,
const
char
*
format
,
...)
{
va_list
ap
;
char
*
cmd
;
int
len
;
int
status
;
redisCallback
cb
=
{
fn
,
privdata
};
/* This function may only be used in a non-blocking context. */
if
(
c
->
flags
&
REDIS_BLOCK
)
return
NULL
;
va_start
(
ap
,
format
);
len
=
redisvFormatCommand
(
&
cmd
,
format
,
ap
);
va_end
(
ap
);
status
=
redisCommandWriteNonBlock
(
c
,
&
cb
,
cmd
,
len
);
free
(
cmd
);
return
NULL
;
}
hiredis.h
View file @
298e9325
...
@@ -78,20 +78,12 @@ struct redisContext; /* need forward declaration of redisContext */
...
@@ -78,20 +78,12 @@ struct redisContext; /* need forward declaration of redisContext */
/* Callbacks triggered on non-reply events. */
/* Callbacks triggered on non-reply events. */
typedef
void
(
redisContextCallbackFn
)(
struct
redisContext
*
,
void
*
);
typedef
void
(
redisContextCallbackFn
)(
struct
redisContext
*
,
void
*
);
/* Reply callback prototype and container */
typedef
void
(
redisCallbackFn
)(
struct
redisContext
*
,
redisReply
*
,
void
*
);
/* Callback containers */
/* Callback containers */
typedef
struct
redisContextCallback
{
typedef
struct
redisContextCallback
{
redisContextCallbackFn
*
fn
;
redisContextCallbackFn
*
fn
;
void
*
privdata
;
void
*
privdata
;
}
redisContextCallback
;
}
redisContextCallback
;
typedef
struct
redisCallback
{
redisCallbackFn
*
fn
;
void
*
privdata
;
}
redisCallback
;
/* Context for a connection to Redis */
/* Context for a connection to Redis */
typedef
struct
redisContext
{
typedef
struct
redisContext
{
int
fd
;
int
fd
;
...
@@ -107,11 +99,6 @@ typedef struct redisContext {
...
@@ -107,11 +99,6 @@ typedef struct redisContext {
redisContextCallback
cbDisconnect
;
redisContextCallback
cbDisconnect
;
redisContextCallback
cbCommand
;
redisContextCallback
cbCommand
;
redisContextCallback
cbFree
;
redisContextCallback
cbFree
;
/* Reply callbacks */
redisCallback
*
callbacks
;
int
cpos
;
int
clen
;
}
redisContext
;
}
redisContext
;
void
freeReplyObject
(
void
*
reply
);
void
freeReplyObject
(
void
*
reply
);
...
@@ -134,7 +121,6 @@ void redisFree(redisContext *c);
...
@@ -134,7 +121,6 @@ void redisFree(redisContext *c);
int
redisBufferRead
(
redisContext
*
c
);
int
redisBufferRead
(
redisContext
*
c
);
int
redisBufferWrite
(
redisContext
*
c
,
int
*
done
);
int
redisBufferWrite
(
redisContext
*
c
,
int
*
done
);
int
redisGetReply
(
redisContext
*
c
,
void
**
reply
);
int
redisGetReply
(
redisContext
*
c
,
void
**
reply
);
int
redisProcessCallbacks
(
redisContext
*
c
);
/* The disconnect callback is called *immediately* when redisDisconnect()
/* The disconnect callback is called *immediately* when redisDisconnect()
* is called. It is called only once for every redisContext (since hiredis
* is called. It is called only once for every redisContext (since hiredis
...
@@ -154,19 +140,7 @@ void redisSetFreeCallback(redisContext *c, redisContextCallbackFn *fn, void *pri
...
@@ -154,19 +140,7 @@ void redisSetFreeCallback(redisContext *c, redisContextCallbackFn *fn, void *pri
* an error occurs, it returns NULL and you should read redisContext->error
* an error occurs, it returns NULL and you should read redisContext->error
* to find out what's wrong. In a non-blocking context, it has the same effect
* to find out what's wrong. In a non-blocking context, it has the same effect
* as calling redisCommandWithCallback() with a NULL callback, and will always
* as calling redisCommandWithCallback() with a NULL callback, and will always
* return NULL.
* return NULL. */
*
* Note: using a NULL reply for an error might conflict with custom reply
* reader functions that have NULL as a valid return value (e.g. for the nil
* return value). Therefore, it is recommended never to return NULL from your
* custom reply object functions. */
void
*
redisCommand
(
redisContext
*
c
,
const
char
*
format
,
...);
void
*
redisCommand
(
redisContext
*
c
,
const
char
*
format
,
...);
/* Issue a command to Redis from a non-blocking context. The formatted command
* is appended to the write buffer and the provided callback is registered.
*
* Note: when called with a blocking context, this function will not do
* anything and immediately returns NULL. */
void
*
redisCommandWithCallback
(
redisContext
*
c
,
redisCallbackFn
*
fn
,
void
*
privdata
,
const
char
*
format
,
...);
#endif
#endif
test.c
View file @
298e9325
...
@@ -236,110 +236,110 @@ static void cleanup() {
...
@@ -236,110 +236,110 @@ static void cleanup() {
redisFree
(
c
);
redisFree
(
c
);
}
}
static
long
__test_callback_flags
=
0
;
//
static long __test_callback_flags = 0;
static
void
__test_callback
(
redisContext
*
c
,
void
*
privdata
)
{
//
static void __test_callback(redisContext *c, void *privdata) {
((
void
)
c
);
//
((void)c);
/* Shift to detect execution order */
//
/* Shift to detect execution order */
__test_callback_flags
<<=
8
;
//
__test_callback_flags <<= 8;
__test_callback_flags
|=
(
long
)
privdata
;
//
__test_callback_flags |= (long)privdata;
}
//
}
//
static
void
__test_reply_callback
(
redisContext
*
c
,
redisReply
*
reply
,
void
*
privdata
)
{
//
static void __test_reply_callback(redisContext *c, redisReply *reply, void *privdata) {
((
void
)
c
);
//
((void)c);
/* Shift to detect execution order */
//
/* Shift to detect execution order */
__test_callback_flags
<<=
8
;
//
__test_callback_flags <<= 8;
__test_callback_flags
|=
(
long
)
privdata
;
//
__test_callback_flags |= (long)privdata;
if
(
reply
)
freeReplyObject
(
reply
);
//
if (reply) freeReplyObject(reply);
}
//
}
//
static
redisContext
*
__connect_nonblock
()
{
//
static redisContext *__connect_nonblock() {
/* Reset callback flags */
//
/* Reset callback flags */
__test_callback_flags
=
0
;
//
__test_callback_flags = 0;
return
redisConnectNonBlock
(
"127.0.0.1"
,
6379
,
NULL
);
//
return redisConnectNonBlock("127.0.0.1", 6379, NULL);
}
//
}
//
static
void
test_nonblocking_connection
()
{
//
static void test_nonblocking_connection() {
redisContext
*
c
;
//
redisContext *c;
int
wdone
=
0
;
//
int wdone = 0;
//
test
(
"Calls command callback when command is issued: "
);
//
test("Calls command callback when command is issued: ");
c
=
__connect_nonblock
();
//
c = __connect_nonblock();
redisSetCommandCallback
(
c
,
__test_callback
,(
void
*
)
1
);
//
redisSetCommandCallback(c,__test_callback,(void*)1);
redisCommand
(
c
,
"PING"
);
//
redisCommand(c,"PING");
test_cond
(
__test_callback_flags
==
1
);
//
test_cond(__test_callback_flags == 1);
redisFree
(
c
);
//
redisFree(c);
//
test
(
"Calls disconnect callback on redisDisconnect: "
);
//
test("Calls disconnect callback on redisDisconnect: ");
c
=
__connect_nonblock
();
//
c = __connect_nonblock();
redisSetDisconnectCallback
(
c
,
__test_callback
,(
void
*
)
2
);
//
redisSetDisconnectCallback(c,__test_callback,(void*)2);
redisDisconnect
(
c
);
//
redisDisconnect(c);
test_cond
(
__test_callback_flags
==
2
);
//
test_cond(__test_callback_flags == 2);
redisFree
(
c
);
//
redisFree(c);
//
test
(
"Calls disconnect callback and free callback on redisFree: "
);
//
test("Calls disconnect callback and free callback on redisFree: ");
c
=
__connect_nonblock
();
//
c = __connect_nonblock();
redisSetDisconnectCallback
(
c
,
__test_callback
,(
void
*
)
2
);
//
redisSetDisconnectCallback(c,__test_callback,(void*)2);
redisSetFreeCallback
(
c
,
__test_callback
,(
void
*
)
4
);
//
redisSetFreeCallback(c,__test_callback,(void*)4);
redisFree
(
c
);
//
redisFree(c);
test_cond
(
__test_callback_flags
==
((
2
<<
8
)
|
4
));
//
test_cond(__test_callback_flags == ((2 << 8) | 4));
//
test
(
"redisBufferWrite against empty write buffer: "
);
//
test("redisBufferWrite against empty write buffer: ");
c
=
__connect_nonblock
();
//
c = __connect_nonblock();
test_cond
(
redisBufferWrite
(
c
,
&
wdone
)
==
REDIS_OK
&&
wdone
==
1
);
//
test_cond(redisBufferWrite(c,&wdone) == REDIS_OK && wdone == 1);
redisFree
(
c
);
//
redisFree(c);
//
test
(
"redisBufferWrite against not yet connected fd: "
);
//
test("redisBufferWrite against not yet connected fd: ");
c
=
__connect_nonblock
();
//
c = __connect_nonblock();
redisCommand
(
c
,
"PING"
);
//
redisCommand(c,"PING");
test_cond
(
redisBufferWrite
(
c
,
NULL
)
==
REDIS_ERR
&&
//
test_cond(redisBufferWrite(c,NULL) == REDIS_ERR &&
strncmp
(
c
->
error
,
"write:"
,
6
)
==
0
);
//
strncmp(c->error,"write:",6) == 0);
redisFree
(
c
);
//
redisFree(c);
//
test
(
"redisBufferWrite against closed fd: "
);
//
test("redisBufferWrite against closed fd: ");
c
=
__connect_nonblock
();
//
c = __connect_nonblock();
redisCommand
(
c
,
"PING"
);
//
redisCommand(c,"PING");
redisDisconnect
(
c
);
//
redisDisconnect(c);
test_cond
(
redisBufferWrite
(
c
,
NULL
)
==
REDIS_ERR
&&
//
test_cond(redisBufferWrite(c,NULL) == REDIS_ERR &&
strncmp
(
c
->
error
,
"write:"
,
6
)
==
0
);
//
strncmp(c->error,"write:",6) == 0);
redisFree
(
c
);
//
redisFree(c);
//
test
(
"Process callbacks in the right sequence: "
);
//
test("Process callbacks in the right sequence: ");
c
=
__connect_nonblock
();
//
c = __connect_nonblock();
redisCommandWithCallback
(
c
,
__test_reply_callback
,(
void
*
)
1
,
"PING"
);
//
redisCommandWithCallback(c,__test_reply_callback,(void*)1,"PING");
redisCommandWithCallback
(
c
,
__test_reply_callback
,(
void
*
)
2
,
"PING"
);
//
redisCommandWithCallback(c,__test_reply_callback,(void*)2,"PING");
redisCommandWithCallback
(
c
,
__test_reply_callback
,(
void
*
)
3
,
"PING"
);
//
redisCommandWithCallback(c,__test_reply_callback,(void*)3,"PING");
//
/* Write output buffer */
//
/* Write output buffer */
wdone
=
0
;
//
wdone = 0;
while
(
!
wdone
)
{
//
while(!wdone) {
usleep
(
500
);
//
usleep(500);
redisBufferWrite
(
c
,
&
wdone
);
//
redisBufferWrite(c,&wdone);
}
//
}
//
/* Read until at least one callback is executed (the 3 replies will
//
/* Read until at least one callback is executed (the 3 replies will
* arrive in a single packet, causing all callbacks to be executed in
//
* arrive in a single packet, causing all callbacks to be executed in
* a single pass). */
//
* a single pass). */
while
(
__test_callback_flags
==
0
)
{
//
while(__test_callback_flags == 0) {
assert
(
redisBufferRead
(
c
)
==
REDIS_OK
);
//
assert(redisBufferRead(c) == REDIS_OK);
redisProcessCallbacks
(
c
);
//
redisProcessCallbacks(c);
}
//
}
test_cond
(
__test_callback_flags
==
0x010203
);
//
test_cond(__test_callback_flags == 0x010203);
redisFree
(
c
);
//
redisFree(c);
//
test
(
"redisDisconnect executes pending callbacks with NULL reply: "
);
//
test("redisDisconnect executes pending callbacks with NULL reply: ");
c
=
__connect_nonblock
();
//
c = __connect_nonblock();
redisSetDisconnectCallback
(
c
,
__test_callback
,(
void
*
)
1
);
//
redisSetDisconnectCallback(c,__test_callback,(void*)1);
redisCommandWithCallback
(
c
,
__test_reply_callback
,(
void
*
)
2
,
"PING"
);
//
redisCommandWithCallback(c,__test_reply_callback,(void*)2,"PING");
redisDisconnect
(
c
);
//
redisDisconnect(c);
test_cond
(
__test_callback_flags
==
0x0201
);
//
test_cond(__test_callback_flags == 0x0201);
redisFree
(
c
);
//
redisFree(c);
}
//
}
int
main
(
void
)
{
int
main
(
void
)
{
test_format_commands
();
test_format_commands
();
test_blocking_connection
();
test_blocking_connection
();
test_reply_reader
();
test_reply_reader
();
test_nonblocking_connection
();
//
test_nonblocking_connection();
test_throughput
();
test_throughput
();
cleanup
();
cleanup
();
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment