Timeout.c 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. /* vim: set expandtab ts=4 sw=4: */
  2. /*
  3. * You may redistribute this program and/or modify it under the terms of
  4. * the GNU General Public License as published by the Free Software Foundation,
  5. * either version 3 of the License, or (at your option) any later version.
  6. *
  7. * This program is distributed in the hope that it will be useful,
  8. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. * GNU General Public License for more details.
  11. *
  12. * You should have received a copy of the GNU General Public License
  13. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  14. */
  15. #include "util/events/libuv/UvWrapper.h"
  16. #include "memory/Allocator.h"
  17. #include "util/events/libuv/EventBase_pvt.h"
  18. #include "util/events/Timeout.h"
  19. #include "util/Identity.h"
  20. struct Timeout
  21. {
  22. uv_timer_t timer;
  23. void (* callback)(void* callbackContext);
  24. void* callbackContext;
  25. uint64_t milliseconds;
  26. int isInterval;
  27. struct Allocator* alloc;
  28. struct Timeout* next;
  29. struct Timeout** selfPtr;
  30. struct EventBase_pvt* base;
  31. Identity
  32. };
  33. static void linkTo(struct Timeout* timeout)
  34. {
  35. timeout->next = (struct Timeout*) timeout->base->timeouts;
  36. if (timeout->next) {
  37. timeout->next->selfPtr = &timeout->next;
  38. }
  39. timeout->base->timeouts = timeout;
  40. timeout->selfPtr = (struct Timeout**) &timeout->base->timeouts;
  41. }
  42. static void unlinkTo(struct Timeout* timeout)
  43. {
  44. if (timeout->selfPtr) {
  45. *timeout->selfPtr = timeout->next;
  46. if (timeout->next) {
  47. Assert_true(&timeout->next == timeout->next->selfPtr);
  48. timeout->next->selfPtr = timeout->selfPtr;
  49. timeout->next = NULL;
  50. }
  51. timeout->selfPtr = NULL;
  52. }
  53. }
  54. /**
  55. * The callback to be called by libuv.
  56. */
  57. static void handleEvent(uv_timer_t* handle, int status)
  58. {
  59. struct Timeout* timeout = Identity_check((struct Timeout*) handle);
  60. if (!timeout->isInterval) {
  61. Timeout_clearTimeout(timeout);
  62. }
  63. timeout->callback(timeout->callbackContext);
  64. }
  65. static void onFree2(uv_handle_t* timer)
  66. {
  67. Allocator_onFreeComplete(timer->data);
  68. }
  69. static int onFree(struct Allocator_OnFreeJob* job)
  70. {
  71. struct Timeout* t = Identity_check((struct Timeout*) job->userData);
  72. unlinkTo(t);
  73. t->timer.data = job;
  74. uv_close((uv_handle_t*) &t->timer, onFree2);
  75. return Allocator_ONFREE_ASYNC;
  76. }
  77. /**
  78. * Create a timeout event.
  79. * The timeout event will be triggered after the given number of milliseconds.
  80. *
  81. * @param callback the functiont to call.
  82. * @param callbackContext a pointer to pass to the called function.
  83. * @param milliseconds the number of milliseconds to wait before the event happens.
  84. * @param interval if non-zero, this event will repeat instead of triggering once.
  85. * @param eventBase the libevent event base.
  86. * @param allocator the memory allocator to use for allocating the event.
  87. * if this is freed, the event will be safely deleted.
  88. * @return a timeout struct which can be used to clear the timeout.
  89. */
  90. static struct Timeout* setTimeout(void (* const callback)(void* callbackContext),
  91. void* const callbackContext,
  92. const uint64_t milliseconds,
  93. const uint32_t interval,
  94. struct EventBase* eventBase,
  95. struct Allocator* allocator,
  96. char* file,
  97. int line)
  98. {
  99. struct EventBase_pvt* base = EventBase_privatize(eventBase);
  100. struct Allocator* alloc = Allocator__child(allocator, file, line);
  101. struct Timeout* timeout = Allocator_calloc(alloc, sizeof(struct Timeout), 1);
  102. timeout->callback = callback;
  103. timeout->callbackContext = callbackContext;
  104. timeout->milliseconds = milliseconds;
  105. timeout->alloc = alloc;
  106. timeout->isInterval = interval;
  107. timeout->base = base;
  108. Identity_set(timeout);
  109. uv_timer_init(base->loop, &timeout->timer);
  110. uv_timer_start(&timeout->timer, handleEvent, milliseconds, (interval) ? milliseconds : 0);
  111. timeout->timer.data = timeout;
  112. Allocator_onFree(alloc, onFree, timeout);
  113. linkTo(timeout);
  114. return timeout;
  115. }
  116. /** See: Timeout.h */
  117. struct Timeout* Timeout__setTimeout(void (* const callback)(void* callbackContext),
  118. void* const callbackContext,
  119. const uint64_t milliseconds,
  120. struct EventBase* eventBase,
  121. struct Allocator* allocator,
  122. char* file,
  123. int line)
  124. {
  125. return setTimeout(callback, callbackContext, milliseconds, 0, eventBase, allocator, file, line);
  126. }
  127. /** See: Timeout.h */
  128. struct Timeout* Timeout__setInterval(void (* const callback)(void* callbackContext),
  129. void* const callbackContext,
  130. const uint64_t milliseconds,
  131. struct EventBase* eventBase,
  132. struct Allocator* allocator,
  133. char* file,
  134. int line)
  135. {
  136. return setTimeout(callback, callbackContext, milliseconds, 1, eventBase, allocator, file, line);
  137. }
  138. /** See: Timeout.h */
  139. void Timeout_resetTimeout(struct Timeout* timeout,
  140. const uint64_t milliseconds)
  141. {
  142. Timeout_clearTimeout(timeout);
  143. linkTo(timeout);
  144. uv_timer_start(&timeout->timer, handleEvent, milliseconds, 0);
  145. }
  146. /** See: Timeout.h */
  147. void Timeout_clearTimeout(struct Timeout* timeout)
  148. {
  149. unlinkTo(timeout);
  150. if (!uv_is_closing((uv_handle_t*) &timeout->timer)) {
  151. uv_timer_stop(&timeout->timer);
  152. }
  153. }
  154. void Timeout_clearAll(struct EventBase* eventBase)
  155. {
  156. struct EventBase_pvt* base = EventBase_privatize(eventBase);
  157. struct Timeout* to = base->timeouts;
  158. if (!to) { return; }
  159. while (to) {
  160. struct Timeout* next = to->next;
  161. Timeout_clearTimeout(to);
  162. to = next;
  163. }
  164. Assert_true(!base->timeouts);
  165. }
  166. int Timeout_isActive(struct Timeout* timeout)
  167. {
  168. return (timeout && timeout->selfPtr);
  169. }