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
4e3bd789
Commit
4e3bd789
authored
Nov 01, 2010
by
Pieter Noordhuis
Browse files
Add support to lazily disconnect an asynchronous connection
parent
ae5a13f5
Changes
3
Hide whitespace changes
Inline
Side-by-side
async.c
View file @
4e3bd789
...
@@ -41,8 +41,9 @@ static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
...
@@ -41,8 +41,9 @@ static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
}
}
redisAsyncContext
*
redisAsyncConnect
(
const
char
*
ip
,
int
port
)
{
redisAsyncContext
*
redisAsyncConnect
(
const
char
*
ip
,
int
port
)
{
redisContext
*
_c
=
redisConnectNonBlock
(
ip
,
port
);
redisContext
*
c
=
redisConnectNonBlock
(
ip
,
port
);
return
redisAsyncInitialize
(
_c
);
redisAsyncContext
*
ac
=
redisAsyncInitialize
(
c
);
return
ac
;
}
}
int
redisAsyncSetReplyObjectFunctions
(
redisAsyncContext
*
ac
,
redisReplyObjectFunctions
*
fn
)
{
int
redisAsyncSetReplyObjectFunctions
(
redisAsyncContext
*
ac
,
redisReplyObjectFunctions
*
fn
)
{
...
@@ -50,6 +51,40 @@ int redisAsyncSetReplyObjectFunctions(redisAsyncContext *ac, redisReplyObjectFun
...
@@ -50,6 +51,40 @@ int redisAsyncSetReplyObjectFunctions(redisAsyncContext *ac, redisReplyObjectFun
return
redisSetReplyObjectFunctions
(
c
,
fn
);
return
redisSetReplyObjectFunctions
(
c
,
fn
);
}
}
int
redisAsyncSetDisconnectCallback
(
redisAsyncContext
*
ac
,
redisDisconnectCallback
*
fn
)
{
if
(
ac
->
onDisconnect
==
NULL
)
{
ac
->
onDisconnect
=
fn
;
return
REDIS_OK
;
}
return
REDIS_ERR
;
}
/* Tries to do a clean disconnect from Redis, meaning it stops new commands
* from being issued, but tries to flush the output buffer and execute
* callbacks for all remaining replies. */
void
redisAsyncDisconnect
(
redisAsyncContext
*
ac
)
{
redisContext
*
c
=
&
(
ac
->
c
);
c
->
flags
|=
REDIS_DISCONNECTING
;
}
/* Helper function to make the disconnect happen and clean up. */
static
void
__redisAsyncDisconnect
(
redisAsyncContext
*
ac
)
{
redisContext
*
c
=
&
(
ac
->
c
);
int
status
;
/* Signal event lib to stop reading/writing */
if
(
ac
->
evDelRead
)
ac
->
evDelRead
(
ac
->
data
);
if
(
ac
->
evDelWrite
)
ac
->
evDelWrite
(
ac
->
data
);
if
(
ac
->
evCleanup
)
ac
->
evCleanup
(
ac
->
data
);
/* Execute callback with proper status */
status
=
(
c
->
error
==
NULL
)
?
REDIS_OK
:
REDIS_ERR
;
if
(
ac
->
onDisconnect
)
ac
->
onDisconnect
(
ac
,
status
);
/* Cleanup self */
redisFree
(
c
);
}
/* Helper functions to push/shift callbacks */
/* Helper functions to push/shift callbacks */
static
void
__redisPushCallback
(
redisCallbackList
*
list
,
redisCallback
*
cb
)
{
static
void
__redisPushCallback
(
redisCallbackList
*
list
,
redisCallback
*
cb
)
{
if
(
list
->
head
==
NULL
)
if
(
list
->
head
==
NULL
)
...
@@ -69,38 +104,52 @@ static redisCallback *__redisShiftCallback(redisCallbackList *list) {
...
@@ -69,38 +104,52 @@ static redisCallback *__redisShiftCallback(redisCallbackList *list) {
return
cb
;
return
cb
;
}
}
void
redisProcessCallbacks
(
redisAsyncContext
*
ac
)
{
redisContext
*
c
=
&
(
ac
->
c
);
redisCallback
*
cb
;
void
*
reply
=
NULL
;
int
status
;
while
((
status
=
redisGetReply
(
c
,
&
reply
))
==
REDIS_OK
)
{
if
(
reply
==
NULL
)
{
/* When the connection is being disconnected and there are
* no more replies, this is the cue to really disconnect. */
if
(
c
->
flags
&
REDIS_DISCONNECTING
&&
sdslen
(
c
->
obuf
)
==
0
)
{
__redisAsyncDisconnect
(
ac
);
return
;
}
/* When the connection is not being disconnected, simply stop
* trying to get replies and wait for the next loop tick. */
break
;
}
/* Shift callback and execute it */
cb
=
__redisShiftCallback
(
&
ac
->
replies
);
assert
(
cb
!=
NULL
);
if
(
cb
->
fn
!=
NULL
)
{
cb
->
fn
(
ac
,
reply
,
cb
->
privdata
);
}
else
{
c
->
fn
->
freeObject
(
reply
);
}
}
/* Disconnect when there was an error reading the reply */
if
(
status
!=
REDIS_OK
)
__redisAsyncDisconnect
(
ac
);
}
/* This function should be called when the socket is readable.
/* This function should be called when the socket is readable.
* It processes all replies that can be read and executes their callbacks.
* It processes all replies that can be read and executes their callbacks.
*/
*/
void
redisAsyncHandleRead
(
redisAsyncContext
*
ac
)
{
void
redisAsyncHandleRead
(
redisAsyncContext
*
ac
)
{
redisContext
*
c
=
&
(
ac
->
c
);
redisContext
*
c
=
&
(
ac
->
c
);
redisCallback
*
cb
;
void
*
reply
=
NULL
;
int
status
;
if
(
redisBufferRead
(
c
)
==
REDIS_ERR
)
{
if
(
redisBufferRead
(
c
)
==
REDIS_ERR
)
{
// needs error handling
__redisAsyncDisconnect
(
ac
);
assert
(
NULL
);
}
else
{
}
else
{
/* Always re-schedule reads */
/* Always re-schedule reads */
if
(
ac
->
evAddRead
)
ac
->
evAddRead
(
ac
->
data
);
if
(
ac
->
evAddRead
)
ac
->
evAddRead
(
ac
->
data
);
while
((
status
=
redisGetReply
(
c
,
&
reply
))
==
REDIS_OK
)
{
/* Abort when there are no more replies */
if
(
reply
==
NULL
)
break
;
/* Shift callback and execute it */
cb
=
__redisShiftCallback
(
&
ac
->
replies
);
assert
(
cb
!=
NULL
);
if
(
cb
->
fn
!=
NULL
)
{
cb
->
fn
(
ac
,
reply
,
cb
->
privdata
);
}
else
{
c
->
fn
->
freeObject
(
reply
);
}
}
// needs error handling
assert
(
status
==
REDIS_OK
);
}
}
}
}
...
@@ -109,8 +158,7 @@ void redisAsyncHandleWrite(redisAsyncContext *ac) {
...
@@ -109,8 +158,7 @@ void redisAsyncHandleWrite(redisAsyncContext *ac) {
int
done
=
0
;
int
done
=
0
;
if
(
redisBufferWrite
(
c
,
&
done
)
==
REDIS_ERR
)
{
if
(
redisBufferWrite
(
c
,
&
done
)
==
REDIS_ERR
)
{
// needs error handling
__redisAsyncDisconnect
(
ac
);
assert
(
NULL
);
}
else
{
}
else
{
/* Continue writing when not done, stop writing otherwise */
/* Continue writing when not done, stop writing otherwise */
if
(
!
done
)
{
if
(
!
done
)
{
...
@@ -132,6 +180,9 @@ void redisAsyncHandleWrite(redisAsyncContext *ac) {
...
@@ -132,6 +180,9 @@ void redisAsyncHandleWrite(redisAsyncContext *ac) {
static
int
__redisAsyncCommand
(
redisAsyncContext
*
ac
,
redisCallbackFn
*
fn
,
void
*
privdata
,
char
*
cmd
,
size_t
len
)
{
static
int
__redisAsyncCommand
(
redisAsyncContext
*
ac
,
redisCallbackFn
*
fn
,
void
*
privdata
,
char
*
cmd
,
size_t
len
)
{
redisContext
*
c
=
&
(
ac
->
c
);
redisContext
*
c
=
&
(
ac
->
c
);
redisCallback
*
cb
;
redisCallback
*
cb
;
/* Don't accept new commands when the connection is lazily closed. */
if
(
c
->
flags
&
REDIS_DISCONNECTING
)
return
REDIS_ERR
;
c
->
obuf
=
sdscatlen
(
c
->
obuf
,
cmd
,
len
);
c
->
obuf
=
sdscatlen
(
c
->
obuf
,
cmd
,
len
);
/* Store callback */
/* Store callback */
...
...
async.h
View file @
4e3bd789
...
@@ -46,6 +46,9 @@ typedef struct redisCallbackList {
...
@@ -46,6 +46,9 @@ typedef struct redisCallbackList {
redisCallback
*
head
,
*
tail
;
redisCallback
*
head
,
*
tail
;
}
redisCallbackList
;
}
redisCallbackList
;
/* Disconnect callback prototype */
typedef
void
(
redisDisconnectCallback
)(
const
struct
redisAsyncContext
*
,
int
status
);
/* Context for an async connection to Redis */
/* Context for an async connection to Redis */
typedef
struct
redisAsyncContext
{
typedef
struct
redisAsyncContext
{
/* Hold the regular context, so it can be realloc'ed. */
/* Hold the regular context, so it can be realloc'ed. */
...
@@ -57,15 +60,22 @@ typedef struct redisAsyncContext {
...
@@ -57,15 +60,22 @@ typedef struct redisAsyncContext {
void
(
*
evDelRead
)(
void
*
privdata
);
void
(
*
evDelRead
)(
void
*
privdata
);
void
(
*
evAddWrite
)(
void
*
privdata
);
void
(
*
evAddWrite
)(
void
*
privdata
);
void
(
*
evDelWrite
)(
void
*
privdata
);
void
(
*
evDelWrite
)(
void
*
privdata
);
void
(
*
evCleanup
)(
void
*
privdata
);
void
*
data
;
void
*
data
;
/* Called when either the connection is terminated due to an error or per
* user request. The status is set accordingly (REDIS_OK, REDIS_ERR). */
redisDisconnectCallback
*
onDisconnect
;
/* Reply callbacks */
/* Reply callbacks */
redisCallbackList
replies
;
redisCallbackList
replies
;
}
redisAsyncContext
;
}
redisAsyncContext
;
/* Functions that proxy to hiredis */
/* Functions that proxy to hiredis */
redisAsyncContext
*
redisAsyncConnect
(
const
char
*
ip
,
int
port
);
redisAsyncContext
*
redisAsyncConnect
(
const
char
*
ip
,
int
port
);
int
redisAsyncSetReplyObjectFunctions
(
redisAsyncContext
*
c
,
redisReplyObjectFunctions
*
fn
);
int
redisAsyncSetReplyObjectFunctions
(
redisAsyncContext
*
ac
,
redisReplyObjectFunctions
*
fn
);
int
redisAsyncSetDisconnectCallback
(
redisAsyncContext
*
ac
,
redisDisconnectCallback
*
fn
);
void
redisAsyncDisconnect
(
redisAsyncContext
*
ac
);
/* Handle read/write events */
/* Handle read/write events */
void
redisAsyncHandleRead
(
redisAsyncContext
*
ac
);
void
redisAsyncHandleRead
(
redisAsyncContext
*
ac
);
...
@@ -73,8 +83,8 @@ void redisAsyncHandleWrite(redisAsyncContext *ac);
...
@@ -73,8 +83,8 @@ void redisAsyncHandleWrite(redisAsyncContext *ac);
/* Command functions for an async context. Write the command to the
/* Command functions for an async context. Write the command to the
* output buffer and register the provided callback. */
* output buffer and register the provided callback. */
int
redisvAsyncCommand
(
redisAsyncContext
*
c
,
redisCallbackFn
*
fn
,
void
*
privdata
,
const
char
*
format
,
va_list
ap
);
int
redisvAsyncCommand
(
redisAsyncContext
*
a
c
,
redisCallbackFn
*
fn
,
void
*
privdata
,
const
char
*
format
,
va_list
ap
);
int
redisAsyncCommand
(
redisAsyncContext
*
c
,
redisCallbackFn
*
fn
,
void
*
privdata
,
const
char
*
format
,
...);
int
redisAsyncCommand
(
redisAsyncContext
*
a
c
,
redisCallbackFn
*
fn
,
void
*
privdata
,
const
char
*
format
,
...);
int
redisAsyncCommandArgv
(
redisAsyncContext
*
c
,
redisCallbackFn
*
fn
,
void
*
privdata
,
int
argc
,
const
char
**
argv
,
const
size_t
*
argvlen
);
int
redisAsyncCommandArgv
(
redisAsyncContext
*
a
c
,
redisCallbackFn
*
fn
,
void
*
privdata
,
int
argc
,
const
char
**
argv
,
const
size_t
*
argvlen
);
#endif
#endif
hiredis.h
View file @
4e3bd789
...
@@ -43,6 +43,12 @@
...
@@ -43,6 +43,12 @@
* in the flags field is set when the context is connected. */
* in the flags field is set when the context is connected. */
#define REDIS_CONNECTED 0x2
#define REDIS_CONNECTED 0x2
/* The async API might try to disconnect cleanly and flush the output
* buffer and read all subsequent replies before disconnecting.
* This flag means no new commands can come in and the connection
* should be terminated once all replies have been read. */
#define REDIS_DISCONNECTING 0x4
#define REDIS_ERROR -1
#define REDIS_ERROR -1
#define REDIS_REPLY_ERROR 0
#define REDIS_REPLY_ERROR 0
#define REDIS_REPLY_STRING 1
#define REDIS_REPLY_STRING 1
...
...
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