async.c 25 KB

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