|
61 | 61 | #define PUBSUB_REPLY_PATTERN 16
|
62 | 62 | #define PUBSUB_REPLY_SHARDED 32
|
63 | 63 |
|
| 64 | +/* Special negative values for a callback's `pending_replies` fields. */ |
| 65 | +#define PENDING_REPLY_UNSUBSCRIBE_ALL -1 |
| 66 | +#define PENDING_REPLY_MONITOR -2 |
| 67 | +#define PENDING_REPLY_RESET -3 |
| 68 | + |
64 | 69 | /* Forward declarations of hiredis.c functions */
|
65 | 70 | int __redisAppendCommand(redisContext *c, const char *cmd, size_t len);
|
66 | 71 | void __redisSetError(redisContext *c, int type, const char *str);
|
@@ -172,6 +177,7 @@ static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
|
172 | 177 | ac->sub.patterns = patterns;
|
173 | 178 | ac->sub.shard_channels = shard_channels;
|
174 | 179 | ac->sub.pending_commands = 0;
|
| 180 | + ac->monitor_cb = NULL; |
175 | 181 |
|
176 | 182 | return ac;
|
177 | 183 | oom:
|
@@ -420,6 +426,10 @@ static void __redisAsyncFree(redisAsyncContext *ac) {
|
420 | 426 | dictRelease(ac->sub.shard_channels);
|
421 | 427 | }
|
422 | 428 |
|
| 429 | + if (ac->monitor_cb != NULL) { |
| 430 | + callbackDecrRefCount(ac, ac->monitor_cb); |
| 431 | + } |
| 432 | + |
423 | 433 | /* Signal event lib to clean up */
|
424 | 434 | _EL_CLEANUP(ac);
|
425 | 435 |
|
@@ -584,7 +594,9 @@ static int handlePubsubReply(redisAsyncContext *ac, redisReply *reply,
|
584 | 594 | /* If we've unsubscribed to the last channel, the command is done. */
|
585 | 595 | /* Check if this was the last channel unsubscribed. */
|
586 | 596 | assert(reply->element[2]->type == REDIS_REPLY_INTEGER);
|
587 |
| - if (cb->pending_replies == -1 && reply->element[2]->integer == 0) { |
| 597 | + if (cb->pending_replies == PENDING_REPLY_UNSUBSCRIBE_ALL && |
| 598 | + reply->element[2]->integer == 0) |
| 599 | + { |
588 | 600 | cb->pending_replies = 0;
|
589 | 601 | }
|
590 | 602 |
|
@@ -621,12 +633,28 @@ static int handlePubsubReply(redisAsyncContext *ac, redisReply *reply,
|
621 | 633 | return REDIS_ERR;
|
622 | 634 | }
|
623 | 635 |
|
| 636 | +/* Handle the effects of the RESET command. */ |
| 637 | +static void handleReset(redisAsyncContext *ac) { |
| 638 | + /* Cancel monitoring mode */ |
| 639 | + ac->c.flags &= ~REDIS_MONITORING; |
| 640 | + if (ac->monitor_cb != NULL) { |
| 641 | + callbackDecrRefCount(ac, ac->monitor_cb); |
| 642 | + ac->monitor_cb = NULL; |
| 643 | + } |
| 644 | + |
| 645 | + /* Cancel subscriptions (finalizers are called if any) */ |
| 646 | + ac->c.flags &= ~REDIS_SUBSCRIBED; |
| 647 | + if (ac->sub.channels) dictEmpty(ac->sub.channels); |
| 648 | + if (ac->sub.patterns) dictEmpty(ac->sub.patterns); |
| 649 | + if (ac->sub.shard_channels) dictEmpty(ac->sub.shard_channels); |
| 650 | +} |
| 651 | + |
624 | 652 | void redisProcessCallbacks(redisAsyncContext *ac) {
|
625 | 653 | redisContext *c = &(ac->c);
|
626 |
| - void *reply = NULL; |
| 654 | + redisReply *reply = NULL; |
627 | 655 | int status;
|
628 | 656 |
|
629 |
| - while((status = redisGetReply(c,&reply)) == REDIS_OK) { |
| 657 | + while((status = redisGetReply(c, (void**)&reply)) == REDIS_OK) { |
630 | 658 | if (reply == NULL) {
|
631 | 659 | /* When the connection is being disconnected and there are
|
632 | 660 | * no more replies, this is the cue to really disconnect. */
|
@@ -659,6 +687,15 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
|
659 | 687 | continue;
|
660 | 688 | }
|
661 | 689 |
|
| 690 | + /* Send monitored command to monitor callback */ |
| 691 | + if ((c->flags & REDIS_MONITORING) && |
| 692 | + reply->type == REDIS_REPLY_STATUS && reply->len > 0 && |
| 693 | + reply->str[0] >= '0' && reply->str[0] <= '9') |
| 694 | + { |
| 695 | + __redisRunCallback(ac, ac->monitor_cb, reply); |
| 696 | + continue; |
| 697 | + } |
| 698 | + |
662 | 699 | /* Get callback from queue which was added when the command was sent. */
|
663 | 700 | redisCallback *cb = NULL;
|
664 | 701 | if (pubsub_reply_flags & PUBSUB_REPLY_MESSAGE) {
|
@@ -694,15 +731,33 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
|
694 | 731 | handlePubsubReply(ac, reply, pubsub_reply_flags, cb);
|
695 | 732 | } else {
|
696 | 733 | /* Regular reply. This includes ERR reply for subscribe commands. */
|
| 734 | + |
| 735 | + /* Handle special effects of the command's reply, if any. */ |
| 736 | + if (cb->pending_replies == PENDING_REPLY_RESET && |
| 737 | + reply->type == REDIS_REPLY_STATUS && |
| 738 | + strncmp(reply->str, "RESET", reply->len) == 0) |
| 739 | + { |
| 740 | + handleReset(ac); |
| 741 | + } else if (cb->pending_replies == PENDING_REPLY_MONITOR && |
| 742 | + reply->type == REDIS_REPLY_STATUS && |
| 743 | + strncmp(reply->str, "OK", reply->len) == 0) |
| 744 | + { |
| 745 | + /* Set monitor flag and callback, freeing any old callback. */ |
| 746 | + c->flags |= REDIS_MONITORING; |
| 747 | + if (ac->monitor_cb != NULL) { |
| 748 | + callbackDecrRefCount(ac, ac->monitor_cb); |
| 749 | + } |
| 750 | + ac->monitor_cb = cb; |
| 751 | + callbackIncrRefCount(ac, cb); |
| 752 | + } |
| 753 | + |
| 754 | + /* Invoke callback */ |
697 | 755 | __redisRunCallback(ac, cb, reply);
|
698 | 756 | cb->pending_replies = 0;
|
699 | 757 | }
|
700 | 758 |
|
701 | 759 | if (cb != NULL) {
|
702 |
| - /* If in monitor mode, repush the callback */ |
703 |
| - if ((c->flags & REDIS_MONITORING) && !(c->flags & REDIS_FREEING)) { |
704 |
| - __redisPushCallback(&ac->replies, cb); |
705 |
| - } else if (cb->pending_replies != 0) { |
| 760 | + if (cb->pending_replies != 0) { |
706 | 761 | /* The command needs more repies. Put it first in queue. */
|
707 | 762 | __redisUnshiftCallback(&ac->replies, cb);
|
708 | 763 | } else {
|
@@ -939,15 +994,17 @@ static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn,
|
939 | 994 | cb->pending_replies++;
|
940 | 995 | }
|
941 | 996 | if (cb->pending_replies == 0) {
|
942 |
| - /* No channels specified. This is unsubscribe 'all' or an error. */ |
943 |
| - cb->pending_replies = -1; |
| 997 | + /* No channels specified means unsubscribe all. (This can happens |
| 998 | + * for SUBSCRIBE, but it is an error and then the value of pending |
| 999 | + * replies doesn't matter.) */ |
| 1000 | + cb->pending_replies = PENDING_REPLY_UNSUBSCRIBE_ALL; |
944 | 1001 | }
|
945 | 1002 | c->flags |= REDIS_SUBSCRIBED;
|
946 | 1003 | ac->sub.pending_commands++;
|
947 |
| - } else if (strncasecmp(cstr,"monitor\r\n",9) == 0) { |
948 |
| - /* Set monitor flag */ |
949 |
| - c->flags |= REDIS_MONITORING; |
950 |
| - cb->pending_replies = -1; |
| 1004 | + } else if (strncasecmp(cstr, "monitor", clen) == 0) { |
| 1005 | + cb->pending_replies = PENDING_REPLY_MONITOR; |
| 1006 | + } else if (strncasecmp(cstr, "reset", clen) == 0) { |
| 1007 | + cb->pending_replies = PENDING_REPLY_RESET; |
951 | 1008 | }
|
952 | 1009 |
|
953 | 1010 | if (__redisPushCallback(&ac->replies, cb) != REDIS_OK)
|
|
0 commit comments