诸暨麻将添加redis
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 
 
 

768 lignes
25 KiB

  1. /*
  2. * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
  3. * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
  4. *
  5. * All rights reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions are met:
  9. *
  10. * * Redistributions of source code must retain the above copyright notice,
  11. * this list of conditions and the following disclaimer.
  12. * * Redistributions in binary form must reproduce the above copyright
  13. * notice, this list of conditions and the following disclaimer in the
  14. * documentation and/or other materials provided with the distribution.
  15. * * Neither the name of Redis nor the names of its contributors may be used
  16. * to endorse or promote products derived from this software without
  17. * specific prior written permission.
  18. *
  19. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  20. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  21. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  22. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  23. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  24. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  25. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  26. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  27. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  28. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  29. * POSSIBILITY OF SUCH DAMAGE.
  30. */
  31. #include "fmacros.h"
  32. #include "alloc.h"
  33. #include <stdlib.h>
  34. #include <string.h>
  35. #ifndef _MSC_VER
  36. #include <strings.h>
  37. #endif
  38. #include <assert.h>
  39. #include <ctype.h>
  40. #include <errno.h>
  41. #include "async.h"
  42. #include "net.h"
  43. #include "dict.c"
  44. #include "sds.h"
  45. #include "win32.h"
  46. #include "async_private.h"
  47. /* Forward declaration of function in hiredis.c */
  48. int __redisAppendCommand(redisContext *c, const char *cmd, size_t len);
  49. /* Functions managing dictionary of callbacks for pub/sub. */
  50. static unsigned int callbackHash(const void *key) {
  51. return dictGenHashFunction((const unsigned char *)key,
  52. sdslen((const sds)key));
  53. }
  54. static void *callbackValDup(void *privdata, const void *src) {
  55. ((void) privdata);
  56. redisCallback *dup = hi_malloc(sizeof(*dup));
  57. memcpy(dup,src,sizeof(*dup));
  58. return dup;
  59. }
  60. static int callbackKeyCompare(void *privdata, const void *key1, const void *key2) {
  61. int l1, l2;
  62. ((void) privdata);
  63. l1 = sdslen((const sds)key1);
  64. l2 = sdslen((const sds)key2);
  65. if (l1 != l2) return 0;
  66. return memcmp(key1,key2,l1) == 0;
  67. }
  68. static void callbackKeyDestructor(void *privdata, void *key) {
  69. ((void) privdata);
  70. sdsfree((sds)key);
  71. }
  72. static void callbackValDestructor(void *privdata, void *val) {
  73. ((void) privdata);
  74. free(val);
  75. }
  76. static dictType callbackDict = {
  77. callbackHash,
  78. NULL,
  79. callbackValDup,
  80. callbackKeyCompare,
  81. callbackKeyDestructor,
  82. callbackValDestructor
  83. };
  84. static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
  85. redisAsyncContext *ac;
  86. ac = realloc(c,sizeof(redisAsyncContext));
  87. if (ac == NULL)
  88. return NULL;
  89. c = &(ac->c);
  90. /* The regular connect functions will always set the flag REDIS_CONNECTED.
  91. * For the async API, we want to wait until the first write event is
  92. * received up before setting this flag, so reset it here. */
  93. c->flags &= ~REDIS_CONNECTED;
  94. ac->err = 0;
  95. ac->errstr = NULL;
  96. ac->data = NULL;
  97. ac->ev.data = NULL;
  98. ac->ev.addRead = NULL;
  99. ac->ev.delRead = NULL;
  100. ac->ev.addWrite = NULL;
  101. ac->ev.delWrite = NULL;
  102. ac->ev.cleanup = NULL;
  103. ac->ev.scheduleTimer = NULL;
  104. ac->onConnect = NULL;
  105. ac->onDisconnect = NULL;
  106. ac->replies.head = NULL;
  107. ac->replies.tail = NULL;
  108. ac->sub.invalid.head = NULL;
  109. ac->sub.invalid.tail = NULL;
  110. ac->sub.channels = dictCreate(&callbackDict,NULL);
  111. ac->sub.patterns = dictCreate(&callbackDict,NULL);
  112. return ac;
  113. }
  114. /* We want the error field to be accessible directly instead of requiring
  115. * an indirection to the redisContext struct. */
  116. static void __redisAsyncCopyError(redisAsyncContext *ac) {
  117. if (!ac)
  118. return;
  119. redisContext *c = &(ac->c);
  120. ac->err = c->err;
  121. ac->errstr = c->errstr;
  122. }
  123. redisAsyncContext *redisAsyncConnectWithOptions(const redisOptions *options) {
  124. redisOptions myOptions = *options;
  125. redisContext *c;
  126. redisAsyncContext *ac;
  127. myOptions.options |= REDIS_OPT_NONBLOCK;
  128. c = redisConnectWithOptions(&myOptions);
  129. if (c == NULL) {
  130. return NULL;
  131. }
  132. ac = redisAsyncInitialize(c);
  133. if (ac == NULL) {
  134. redisFree(c);
  135. return NULL;
  136. }
  137. __redisAsyncCopyError(ac);
  138. return ac;
  139. }
  140. redisAsyncContext *redisAsyncConnect(const char *ip, int port) {
  141. redisOptions options = {0};
  142. REDIS_OPTIONS_SET_TCP(&options, ip, port);
  143. return redisAsyncConnectWithOptions(&options);
  144. }
  145. redisAsyncContext *redisAsyncConnectBind(const char *ip, int port,
  146. const char *source_addr) {
  147. redisOptions options = {0};
  148. REDIS_OPTIONS_SET_TCP(&options, ip, port);
  149. options.endpoint.tcp.source_addr = source_addr;
  150. return redisAsyncConnectWithOptions(&options);
  151. }
  152. redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port,
  153. const char *source_addr) {
  154. redisOptions options = {0};
  155. REDIS_OPTIONS_SET_TCP(&options, ip, port);
  156. options.options |= REDIS_OPT_REUSEADDR;
  157. options.endpoint.tcp.source_addr = source_addr;
  158. return redisAsyncConnectWithOptions(&options);
  159. }
  160. redisAsyncContext *redisAsyncConnectUnix(const char *path) {
  161. redisOptions options = {0};
  162. REDIS_OPTIONS_SET_UNIX(&options, path);
  163. return redisAsyncConnectWithOptions(&options);
  164. }
  165. int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn) {
  166. if (ac->onConnect == NULL) {
  167. ac->onConnect = fn;
  168. /* The common way to detect an established connection is to wait for
  169. * the first write event to be fired. This assumes the related event
  170. * library functions are already set. */
  171. _EL_ADD_WRITE(ac);
  172. return REDIS_OK;
  173. }
  174. return REDIS_ERR;
  175. }
  176. int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn) {
  177. if (ac->onDisconnect == NULL) {
  178. ac->onDisconnect = fn;
  179. return REDIS_OK;
  180. }
  181. return REDIS_ERR;
  182. }
  183. /* Helper functions to push/shift callbacks */
  184. static int __redisPushCallback(redisCallbackList *list, redisCallback *source) {
  185. redisCallback *cb;
  186. /* Copy callback from stack to heap */
  187. cb = malloc(sizeof(*cb));
  188. if (cb == NULL)
  189. return REDIS_ERR_OOM;
  190. if (source != NULL) {
  191. memcpy(cb,source,sizeof(*cb));
  192. cb->next = NULL;
  193. }
  194. /* Store callback in list */
  195. if (list->head == NULL)
  196. list->head = cb;
  197. if (list->tail != NULL)
  198. list->tail->next = cb;
  199. list->tail = cb;
  200. return REDIS_OK;
  201. }
  202. static int __redisShiftCallback(redisCallbackList *list, redisCallback *target) {
  203. redisCallback *cb = list->head;
  204. if (cb != NULL) {
  205. list->head = cb->next;
  206. if (cb == list->tail)
  207. list->tail = NULL;
  208. /* Copy callback from heap to stack */
  209. if (target != NULL)
  210. memcpy(target,cb,sizeof(*cb));
  211. free(cb);
  212. return REDIS_OK;
  213. }
  214. return REDIS_ERR;
  215. }
  216. static void __redisRunCallback(redisAsyncContext *ac, redisCallback *cb, redisReply *reply) {
  217. redisContext *c = &(ac->c);
  218. if (cb->fn != NULL) {
  219. c->flags |= REDIS_IN_CALLBACK;
  220. cb->fn(ac,reply,cb->privdata);
  221. c->flags &= ~REDIS_IN_CALLBACK;
  222. }
  223. }
  224. /* Helper function to free the context. */
  225. static void __redisAsyncFree(redisAsyncContext *ac) {
  226. redisContext *c = &(ac->c);
  227. redisCallback cb;
  228. dictIterator *it;
  229. dictEntry *de;
  230. /* Execute pending callbacks with NULL reply. */
  231. while (__redisShiftCallback(&ac->replies,&cb) == REDIS_OK)
  232. __redisRunCallback(ac,&cb,NULL);
  233. /* Execute callbacks for invalid commands */
  234. while (__redisShiftCallback(&ac->sub.invalid,&cb) == REDIS_OK)
  235. __redisRunCallback(ac,&cb,NULL);
  236. /* Run subscription callbacks callbacks with NULL reply */
  237. it = dictGetIterator(ac->sub.channels);
  238. while ((de = dictNext(it)) != NULL)
  239. __redisRunCallback(ac,dictGetEntryVal(de),NULL);
  240. dictReleaseIterator(it);
  241. dictRelease(ac->sub.channels);
  242. it = dictGetIterator(ac->sub.patterns);
  243. while ((de = dictNext(it)) != NULL)
  244. __redisRunCallback(ac,dictGetEntryVal(de),NULL);
  245. dictReleaseIterator(it);
  246. dictRelease(ac->sub.patterns);
  247. /* Signal event lib to clean up */
  248. _EL_CLEANUP(ac);
  249. /* Execute disconnect callback. When redisAsyncFree() initiated destroying
  250. * this context, the status will always be REDIS_OK. */
  251. if (ac->onDisconnect && (c->flags & REDIS_CONNECTED)) {
  252. if (c->flags & REDIS_FREEING) {
  253. ac->onDisconnect(ac,REDIS_OK);
  254. } else {
  255. ac->onDisconnect(ac,(ac->err == 0) ? REDIS_OK : REDIS_ERR);
  256. }
  257. }
  258. /* Cleanup self */
  259. redisFree(c);
  260. }
  261. /* Free the async context. When this function is called from a callback,
  262. * control needs to be returned to redisProcessCallbacks() before actual
  263. * free'ing. To do so, a flag is set on the context which is picked up by
  264. * redisProcessCallbacks(). Otherwise, the context is immediately free'd. */
  265. void redisAsyncFree(redisAsyncContext *ac) {
  266. redisContext *c = &(ac->c);
  267. c->flags |= REDIS_FREEING;
  268. if (!(c->flags & REDIS_IN_CALLBACK))
  269. __redisAsyncFree(ac);
  270. }
  271. /* Helper function to make the disconnect happen and clean up. */
  272. void __redisAsyncDisconnect(redisAsyncContext *ac) {
  273. redisContext *c = &(ac->c);
  274. /* Make sure error is accessible if there is any */
  275. __redisAsyncCopyError(ac);
  276. if (ac->err == 0) {
  277. /* For clean disconnects, there should be no pending callbacks. */
  278. int ret = __redisShiftCallback(&ac->replies,NULL);
  279. assert(ret == REDIS_ERR);
  280. } else {
  281. /* Disconnection is caused by an error, make sure that pending
  282. * callbacks cannot call new commands. */
  283. c->flags |= REDIS_DISCONNECTING;
  284. }
  285. /* cleanup event library on disconnect.
  286. * this is safe to call multiple times */
  287. _EL_CLEANUP(ac);
  288. /* For non-clean disconnects, __redisAsyncFree() will execute pending
  289. * callbacks with a NULL-reply. */
  290. if (!(c->flags & REDIS_NO_AUTO_FREE)) {
  291. __redisAsyncFree(ac);
  292. }
  293. }
  294. /* Tries to do a clean disconnect from Redis, meaning it stops new commands
  295. * from being issued, but tries to flush the output buffer and execute
  296. * callbacks for all remaining replies. When this function is called from a
  297. * callback, there might be more replies and we can safely defer disconnecting
  298. * to redisProcessCallbacks(). Otherwise, we can only disconnect immediately
  299. * when there are no pending callbacks. */
  300. void redisAsyncDisconnect(redisAsyncContext *ac) {
  301. redisContext *c = &(ac->c);
  302. c->flags |= REDIS_DISCONNECTING;
  303. /** unset the auto-free flag here, because disconnect undoes this */
  304. c->flags &= ~REDIS_NO_AUTO_FREE;
  305. if (!(c->flags & REDIS_IN_CALLBACK) && ac->replies.head == NULL)
  306. __redisAsyncDisconnect(ac);
  307. }
  308. static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply, redisCallback *dstcb) {
  309. redisContext *c = &(ac->c);
  310. dict *callbacks;
  311. redisCallback *cb;
  312. dictEntry *de;
  313. int pvariant;
  314. char *stype;
  315. sds sname;
  316. /* Custom reply functions are not supported for pub/sub. This will fail
  317. * very hard when they are used... */
  318. if (reply->type == REDIS_REPLY_ARRAY) {
  319. assert(reply->elements >= 2);
  320. assert(reply->element[0]->type == REDIS_REPLY_STRING);
  321. stype = reply->element[0]->str;
  322. pvariant = (tolower(stype[0]) == 'p') ? 1 : 0;
  323. if (pvariant)
  324. callbacks = ac->sub.patterns;
  325. else
  326. callbacks = ac->sub.channels;
  327. /* Locate the right callback */
  328. assert(reply->element[1]->type == REDIS_REPLY_STRING);
  329. sname = sdsnewlen(reply->element[1]->str,reply->element[1]->len);
  330. de = dictFind(callbacks,sname);
  331. if (de != NULL) {
  332. cb = dictGetEntryVal(de);
  333. /* If this is an subscribe reply decrease pending counter. */
  334. if (strcasecmp(stype+pvariant,"subscribe") == 0) {
  335. cb->pending_subs -= 1;
  336. }
  337. memcpy(dstcb,cb,sizeof(*dstcb));
  338. /* If this is an unsubscribe message, remove it. */
  339. if (strcasecmp(stype+pvariant,"unsubscribe") == 0) {
  340. if (cb->pending_subs == 0)
  341. dictDelete(callbacks,sname);
  342. /* If this was the last unsubscribe message, revert to
  343. * non-subscribe mode. */
  344. assert(reply->element[2]->type == REDIS_REPLY_INTEGER);
  345. /* Unset subscribed flag only when no pipelined pending subscribe. */
  346. if (reply->element[2]->integer == 0
  347. && dictSize(ac->sub.channels) == 0
  348. && dictSize(ac->sub.patterns) == 0)
  349. c->flags &= ~REDIS_SUBSCRIBED;
  350. }
  351. }
  352. sdsfree(sname);
  353. } else {
  354. /* Shift callback for invalid commands. */
  355. __redisShiftCallback(&ac->sub.invalid,dstcb);
  356. }
  357. return REDIS_OK;
  358. }
  359. void redisProcessCallbacks(redisAsyncContext *ac) {
  360. redisContext *c = &(ac->c);
  361. redisCallback cb = {NULL, NULL, 0, NULL};
  362. void *reply = NULL;
  363. int status;
  364. while((status = redisGetReply(c,&reply)) == REDIS_OK) {
  365. if (reply == NULL) {
  366. /* When the connection is being disconnected and there are
  367. * no more replies, this is the cue to really disconnect. */
  368. if (c->flags & REDIS_DISCONNECTING && sdslen(c->obuf) == 0
  369. && ac->replies.head == NULL) {
  370. __redisAsyncDisconnect(ac);
  371. return;
  372. }
  373. /* If monitor mode, repush callback */
  374. if(c->flags & REDIS_MONITORING) {
  375. __redisPushCallback(&ac->replies,&cb);
  376. }
  377. /* When the connection is not being disconnected, simply stop
  378. * trying to get replies and wait for the next loop tick. */
  379. break;
  380. }
  381. /* Even if the context is subscribed, pending regular callbacks will
  382. * get a reply before pub/sub messages arrive. */
  383. if (__redisShiftCallback(&ac->replies,&cb) != REDIS_OK) {
  384. /*
  385. * A spontaneous reply in a not-subscribed context can be the error
  386. * reply that is sent when a new connection exceeds the maximum
  387. * number of allowed connections on the server side.
  388. *
  389. * This is seen as an error instead of a regular reply because the
  390. * server closes the connection after sending it.
  391. *
  392. * To prevent the error from being overwritten by an EOF error the
  393. * connection is closed here. See issue #43.
  394. *
  395. * Another possibility is that the server is loading its dataset.
  396. * In this case we also want to close the connection, and have the
  397. * user wait until the server is ready to take our request.
  398. */
  399. if (((redisReply*)reply)->type == REDIS_REPLY_ERROR) {
  400. c->err = REDIS_ERR_OTHER;
  401. snprintf(c->errstr,sizeof(c->errstr),"%s",((redisReply*)reply)->str);
  402. c->reader->fn->freeObject(reply);
  403. __redisAsyncDisconnect(ac);
  404. return;
  405. }
  406. /* No more regular callbacks and no errors, the context *must* be subscribed or monitoring. */
  407. assert((c->flags & REDIS_SUBSCRIBED || c->flags & REDIS_MONITORING));
  408. if(c->flags & REDIS_SUBSCRIBED)
  409. __redisGetSubscribeCallback(ac,reply,&cb);
  410. }
  411. if (cb.fn != NULL) {
  412. __redisRunCallback(ac,&cb,reply);
  413. c->reader->fn->freeObject(reply);
  414. /* Proceed with free'ing when redisAsyncFree() was called. */
  415. if (c->flags & REDIS_FREEING) {
  416. __redisAsyncFree(ac);
  417. return;
  418. }
  419. } else {
  420. /* No callback for this reply. This can either be a NULL callback,
  421. * or there were no callbacks to begin with. Either way, don't
  422. * abort with an error, but simply ignore it because the client
  423. * doesn't know what the server will spit out over the wire. */
  424. c->reader->fn->freeObject(reply);
  425. }
  426. }
  427. /* Disconnect when there was an error reading the reply */
  428. if (status != REDIS_OK)
  429. __redisAsyncDisconnect(ac);
  430. }
  431. /* Internal helper function to detect socket status the first time a read or
  432. * write event fires. When connecting was not successful, the connect callback
  433. * is called with a REDIS_ERR status and the context is free'd. */
  434. static int __redisAsyncHandleConnect(redisAsyncContext *ac) {
  435. int completed = 0;
  436. redisContext *c = &(ac->c);
  437. if (redisCheckConnectDone(c, &completed) == REDIS_ERR) {
  438. /* Error! */
  439. redisCheckSocketError(c);
  440. if (ac->onConnect) ac->onConnect(ac, REDIS_ERR);
  441. __redisAsyncDisconnect(ac);
  442. return REDIS_ERR;
  443. } else if (completed == 1) {
  444. /* connected! */
  445. if (ac->onConnect) ac->onConnect(ac, REDIS_OK);
  446. c->flags |= REDIS_CONNECTED;
  447. return REDIS_OK;
  448. } else {
  449. return REDIS_OK;
  450. }
  451. }
  452. void redisAsyncRead(redisAsyncContext *ac) {
  453. redisContext *c = &(ac->c);
  454. if (redisBufferRead(c) == REDIS_ERR) {
  455. __redisAsyncDisconnect(ac);
  456. } else {
  457. /* Always re-schedule reads */
  458. _EL_ADD_READ(ac);
  459. redisProcessCallbacks(ac);
  460. }
  461. }
  462. /* This function should be called when the socket is readable.
  463. * It processes all replies that can be read and executes their callbacks.
  464. */
  465. void redisAsyncHandleRead(redisAsyncContext *ac) {
  466. redisContext *c = &(ac->c);
  467. if (!(c->flags & REDIS_CONNECTED)) {
  468. /* Abort connect was not successful. */
  469. if (__redisAsyncHandleConnect(ac) != REDIS_OK)
  470. return;
  471. /* Try again later when the context is still not connected. */
  472. if (!(c->flags & REDIS_CONNECTED))
  473. return;
  474. }
  475. c->funcs->async_read(ac);
  476. }
  477. void redisAsyncWrite(redisAsyncContext *ac) {
  478. redisContext *c = &(ac->c);
  479. int done = 0;
  480. if (redisBufferWrite(c,&done) == REDIS_ERR) {
  481. __redisAsyncDisconnect(ac);
  482. } else {
  483. /* Continue writing when not done, stop writing otherwise */
  484. if (!done)
  485. _EL_ADD_WRITE(ac);
  486. else
  487. _EL_DEL_WRITE(ac);
  488. /* Always schedule reads after writes */
  489. _EL_ADD_READ(ac);
  490. }
  491. }
  492. void redisAsyncHandleWrite(redisAsyncContext *ac) {
  493. redisContext *c = &(ac->c);
  494. if (!(c->flags & REDIS_CONNECTED)) {
  495. /* Abort connect was not successful. */
  496. if (__redisAsyncHandleConnect(ac) != REDIS_OK)
  497. return;
  498. /* Try again later when the context is still not connected. */
  499. if (!(c->flags & REDIS_CONNECTED))
  500. return;
  501. }
  502. c->funcs->async_write(ac);
  503. }
  504. void __redisSetError(redisContext *c, int type, const char *str);
  505. void redisAsyncHandleTimeout(redisAsyncContext *ac) {
  506. redisContext *c = &(ac->c);
  507. redisCallback cb;
  508. if ((c->flags & REDIS_CONNECTED) && ac->replies.head == NULL) {
  509. /* Nothing to do - just an idle timeout */
  510. return;
  511. }
  512. if (!c->err) {
  513. __redisSetError(c, REDIS_ERR_TIMEOUT, "Timeout");
  514. }
  515. if (!(c->flags & REDIS_CONNECTED) && ac->onConnect) {
  516. ac->onConnect(ac, REDIS_ERR);
  517. }
  518. while (__redisShiftCallback(&ac->replies, &cb) == REDIS_OK) {
  519. __redisRunCallback(ac, &cb, NULL);
  520. }
  521. /**
  522. * TODO: Don't automatically sever the connection,
  523. * rather, allow to ignore <x> responses before the queue is clear
  524. */
  525. __redisAsyncDisconnect(ac);
  526. }
  527. /* Sets a pointer to the first argument and its length starting at p. Returns
  528. * the number of bytes to skip to get to the following argument. */
  529. static const char *nextArgument(const char *start, const char **str, size_t *len) {
  530. const char *p = start;
  531. if (p[0] != '$') {
  532. p = strchr(p,'$');
  533. if (p == NULL) return NULL;
  534. }
  535. *len = (int)strtol(p+1,NULL,10);
  536. p = strchr(p,'\r');
  537. assert(p);
  538. *str = p+2;
  539. return p+2+(*len)+2;
  540. }
  541. /* Helper function for the redisAsyncCommand* family of functions. Writes a
  542. * formatted command to the output buffer and registers the provided callback
  543. * function with the context. */
  544. static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len) {
  545. redisContext *c = &(ac->c);
  546. redisCallback cb;
  547. struct dict *cbdict;
  548. dictEntry *de;
  549. redisCallback *existcb;
  550. int pvariant, hasnext;
  551. const char *cstr, *astr;
  552. size_t clen, alen;
  553. const char *p;
  554. sds sname;
  555. int ret;
  556. /* Don't accept new commands when the connection is about to be closed. */
  557. if (c->flags & (REDIS_DISCONNECTING | REDIS_FREEING)) return REDIS_ERR;
  558. /* Setup callback */
  559. cb.fn = fn;
  560. cb.privdata = privdata;
  561. cb.pending_subs = 1;
  562. /* Find out which command will be appended. */
  563. p = nextArgument(cmd,&cstr,&clen);
  564. assert(p != NULL);
  565. hasnext = (p[0] == '$');
  566. pvariant = (tolower(cstr[0]) == 'p') ? 1 : 0;
  567. cstr += pvariant;
  568. clen -= pvariant;
  569. if (hasnext && strncasecmp(cstr,"subscribe\r\n",11) == 0) {
  570. c->flags |= REDIS_SUBSCRIBED;
  571. /* Add every channel/pattern to the list of subscription callbacks. */
  572. while ((p = nextArgument(p,&astr,&alen)) != NULL) {
  573. sname = sdsnewlen(astr,alen);
  574. if (pvariant)
  575. cbdict = ac->sub.patterns;
  576. else
  577. cbdict = ac->sub.channels;
  578. de = dictFind(cbdict,sname);
  579. if (de != NULL) {
  580. existcb = dictGetEntryVal(de);
  581. cb.pending_subs = existcb->pending_subs + 1;
  582. }
  583. ret = dictReplace(cbdict,sname,&cb);
  584. if (ret == 0) sdsfree(sname);
  585. }
  586. } else if (strncasecmp(cstr,"unsubscribe\r\n",13) == 0) {
  587. /* It is only useful to call (P)UNSUBSCRIBE when the context is
  588. * subscribed to one or more channels or patterns. */
  589. if (!(c->flags & REDIS_SUBSCRIBED)) return REDIS_ERR;
  590. /* (P)UNSUBSCRIBE does not have its own response: every channel or
  591. * pattern that is unsubscribed will receive a message. This means we
  592. * should not append a callback function for this command. */
  593. } else if(strncasecmp(cstr,"monitor\r\n",9) == 0) {
  594. /* Set monitor flag and push callback */
  595. c->flags |= REDIS_MONITORING;
  596. __redisPushCallback(&ac->replies,&cb);
  597. } else {
  598. if (c->flags & REDIS_SUBSCRIBED)
  599. /* This will likely result in an error reply, but it needs to be
  600. * received and passed to the callback. */
  601. __redisPushCallback(&ac->sub.invalid,&cb);
  602. else
  603. __redisPushCallback(&ac->replies,&cb);
  604. }
  605. __redisAppendCommand(c,cmd,len);
  606. /* Always schedule a write when the write buffer is non-empty */
  607. _EL_ADD_WRITE(ac);
  608. return REDIS_OK;
  609. }
  610. int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap) {
  611. char *cmd;
  612. int len;
  613. int status;
  614. len = redisvFormatCommand(&cmd,format,ap);
  615. /* We don't want to pass -1 or -2 to future functions as a length. */
  616. if (len < 0)
  617. return REDIS_ERR;
  618. status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
  619. free(cmd);
  620. return status;
  621. }
  622. int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...) {
  623. va_list ap;
  624. int status;
  625. va_start(ap,format);
  626. status = redisvAsyncCommand(ac,fn,privdata,format,ap);
  627. va_end(ap);
  628. return status;
  629. }
  630. int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen) {
  631. sds cmd;
  632. int len;
  633. int status;
  634. len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen);
  635. if (len < 0)
  636. return REDIS_ERR;
  637. status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
  638. sdsfree(cmd);
  639. return status;
  640. }
  641. int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len) {
  642. int status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
  643. return status;
  644. }
  645. void redisAsyncSetTimeout(redisAsyncContext *ac, struct timeval tv) {
  646. if (!ac->c.timeout) {
  647. ac->c.timeout = hi_calloc(1, sizeof(tv));
  648. }
  649. if (tv.tv_sec == ac->c.timeout->tv_sec &&
  650. tv.tv_usec == ac->c.timeout->tv_usec) {
  651. return;
  652. }
  653. *ac->c.timeout = tv;
  654. }