Simulation Core
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. #ifndef HIREDIS_POLL_H
  2. #define HIREDIS_POLL_H
  3. #include "../async.h"
  4. #include "../sockcompat.h"
  5. #include <string.h> // for memset
  6. #include <errno.h>
  7. /* Values to return from redisPollTick */
  8. #define REDIS_POLL_HANDLED_READ 1
  9. #define REDIS_POLL_HANDLED_WRITE 2
  10. #define REDIS_POLL_HANDLED_TIMEOUT 4
  11. /* An adapter to allow manual polling of the async context by checking the state
  12. * of the underlying file descriptor. Useful in cases where there is no formal
  13. * IO event loop but regular ticking can be used, such as in game engines. */
  14. typedef struct redisPollEvents {
  15. redisAsyncContext *context;
  16. redisFD fd;
  17. char reading, writing;
  18. char in_tick;
  19. char deleted;
  20. double deadline;
  21. } redisPollEvents;
  22. static double redisPollTimevalToDouble(struct timeval *tv) {
  23. if (tv == NULL)
  24. return 0.0;
  25. return tv->tv_sec + tv->tv_usec / 1000000.00;
  26. }
  27. static double redisPollGetNow(void) {
  28. #ifndef _MSC_VER
  29. struct timeval tv;
  30. gettimeofday(&tv,NULL);
  31. return redisPollTimevalToDouble(&tv);
  32. #else
  33. FILETIME ft;
  34. ULARGE_INTEGER li;
  35. GetSystemTimeAsFileTime(&ft);
  36. li.HighPart = ft.dwHighDateTime;
  37. li.LowPart = ft.dwLowDateTime;
  38. return (double)li.QuadPart * 1e-7;
  39. #endif
  40. }
  41. /* Poll for io, handling any pending callbacks. The timeout argument can be
  42. * positive to wait for a maximum given time for IO, zero to poll, or negative
  43. * to wait forever */
  44. static int redisPollTick(redisAsyncContext *ac, double timeout) {
  45. int reading, writing;
  46. struct pollfd pfd;
  47. int handled;
  48. int ns;
  49. int itimeout;
  50. redisPollEvents *e = (redisPollEvents*)ac->ev.data;
  51. if (!e)
  52. return 0;
  53. /* local flags, won't get changed during callbacks */
  54. reading = e->reading;
  55. writing = e->writing;
  56. if (!reading && !writing)
  57. return 0;
  58. pfd.fd = e->fd;
  59. pfd.events = 0;
  60. if (reading)
  61. pfd.events = POLLIN;
  62. if (writing)
  63. pfd.events |= POLLOUT;
  64. if (timeout >= 0.0) {
  65. itimeout = (int)(timeout * 1000.0);
  66. } else {
  67. itimeout = -1;
  68. }
  69. ns = poll(&pfd, 1, itimeout);
  70. if (ns < 0) {
  71. /* ignore the EINTR error */
  72. if (errno != EINTR)
  73. return ns;
  74. ns = 0;
  75. }
  76. handled = 0;
  77. e->in_tick = 1;
  78. if (ns) {
  79. if (reading && (pfd.revents & POLLIN)) {
  80. redisAsyncHandleRead(ac);
  81. handled |= REDIS_POLL_HANDLED_READ;
  82. }
  83. /* on Windows, connection failure is indicated with the Exception fdset.
  84. * handle it the same as writable. */
  85. if (writing && (pfd.revents & (POLLOUT | POLLERR))) {
  86. /* context Read callback may have caused context to be deleted, e.g.
  87. by doing an redisAsyncDisconnect() */
  88. if (!e->deleted) {
  89. redisAsyncHandleWrite(ac);
  90. handled |= REDIS_POLL_HANDLED_WRITE;
  91. }
  92. }
  93. }
  94. /* perform timeouts */
  95. if (!e->deleted && e->deadline != 0.0) {
  96. double now = redisPollGetNow();
  97. if (now >= e->deadline) {
  98. /* deadline has passed. disable timeout and perform callback */
  99. e->deadline = 0.0;
  100. redisAsyncHandleTimeout(ac);
  101. handled |= REDIS_POLL_HANDLED_TIMEOUT;
  102. }
  103. }
  104. /* do a delayed cleanup if required */
  105. if (e->deleted)
  106. hi_free(e);
  107. else
  108. e->in_tick = 0;
  109. return handled;
  110. }
  111. static void redisPollAddRead(void *data) {
  112. redisPollEvents *e = (redisPollEvents*)data;
  113. e->reading = 1;
  114. }
  115. static void redisPollDelRead(void *data) {
  116. redisPollEvents *e = (redisPollEvents*)data;
  117. e->reading = 0;
  118. }
  119. static void redisPollAddWrite(void *data) {
  120. redisPollEvents *e = (redisPollEvents*)data;
  121. e->writing = 1;
  122. }
  123. static void redisPollDelWrite(void *data) {
  124. redisPollEvents *e = (redisPollEvents*)data;
  125. e->writing = 0;
  126. }
  127. static void redisPollCleanup(void *data) {
  128. redisPollEvents *e = (redisPollEvents*)data;
  129. /* if we are currently processing a tick, postpone deletion */
  130. if (e->in_tick)
  131. e->deleted = 1;
  132. else
  133. hi_free(e);
  134. }
  135. static void redisPollScheduleTimer(void *data, struct timeval tv)
  136. {
  137. redisPollEvents *e = (redisPollEvents*)data;
  138. double now = redisPollGetNow();
  139. e->deadline = now + redisPollTimevalToDouble(&tv);
  140. }
  141. static int redisPollAttach(redisAsyncContext *ac) {
  142. redisContext *c = &(ac->c);
  143. redisPollEvents *e;
  144. /* Nothing should be attached when something is already attached */
  145. if (ac->ev.data != NULL)
  146. return REDIS_ERR;
  147. /* Create container for context and r/w events */
  148. e = (redisPollEvents*)hi_malloc(sizeof(*e));
  149. if (e == NULL)
  150. return REDIS_ERR;
  151. memset(e, 0, sizeof(*e));
  152. e->context = ac;
  153. e->fd = c->fd;
  154. e->reading = e->writing = 0;
  155. e->in_tick = e->deleted = 0;
  156. e->deadline = 0.0;
  157. /* Register functions to start/stop listening for events */
  158. ac->ev.addRead = redisPollAddRead;
  159. ac->ev.delRead = redisPollDelRead;
  160. ac->ev.addWrite = redisPollAddWrite;
  161. ac->ev.delWrite = redisPollDelWrite;
  162. ac->ev.scheduleTimer = redisPollScheduleTimer;
  163. ac->ev.cleanup = redisPollCleanup;
  164. ac->ev.data = e;
  165. return REDIS_OK;
  166. }
  167. #endif /* HIREDIS_POLL_H */