timeout.c 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. /*
  2. *
  3. * Embedded Linux library
  4. *
  5. * Copyright (C) 2011-2014 Intel Corporation. All rights reserved.
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation; either
  10. * version 2.1 of the License, or (at your option) any later version.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this library; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  20. *
  21. */
  22. #ifdef HAVE_CONFIG_H
  23. #include <config.h>
  24. #endif
  25. #define _GNU_SOURCE
  26. #include <errno.h>
  27. #include <unistd.h>
  28. #include <stdbool.h>
  29. #include <string.h>
  30. #include <sys/epoll.h>
  31. #include <sys/timerfd.h>
  32. #include <time.h>
  33. #include <limits.h>
  34. #include "useful.h"
  35. #include "timeout.h"
  36. #include "main-private.h"
  37. #include "private.h"
  38. /**
  39. * SECTION:timeout
  40. * @short_description: Timeout support
  41. *
  42. * Timeout support
  43. */
  44. /**
  45. * l_timeout:
  46. *
  47. * Opague object representing the timeout.
  48. */
  49. struct l_timeout {
  50. int fd;
  51. l_timeout_notify_cb_t callback;
  52. l_timeout_destroy_cb_t destroy;
  53. void *user_data;
  54. };
  55. static void timeout_destroy(void *user_data)
  56. {
  57. struct l_timeout *timeout = user_data;
  58. close(timeout->fd);
  59. timeout->fd = -1;
  60. if (timeout->destroy)
  61. timeout->destroy(timeout->user_data);
  62. }
  63. static void timeout_callback(int fd, uint32_t events, void *user_data)
  64. {
  65. struct l_timeout *timeout = user_data;
  66. uint64_t expired;
  67. ssize_t result;
  68. result = read(timeout->fd, &expired, sizeof(expired));
  69. if (result != sizeof(expired))
  70. return;
  71. if (timeout->callback)
  72. timeout->callback(timeout, timeout->user_data);
  73. }
  74. static inline int timeout_set(int fd, unsigned int seconds, long nanoseconds)
  75. {
  76. struct itimerspec itimer;
  77. memset(&itimer, 0, sizeof(itimer));
  78. itimer.it_interval.tv_sec = 0;
  79. itimer.it_interval.tv_nsec = 0;
  80. itimer.it_value.tv_sec = seconds;
  81. itimer.it_value.tv_nsec = nanoseconds;
  82. return timerfd_settime(fd, 0, &itimer, NULL);
  83. }
  84. static bool convert_ms(uint64_t milliseconds, unsigned int *seconds,
  85. long *nanoseconds)
  86. {
  87. uint64_t big_seconds = milliseconds / 1000;
  88. if (big_seconds > UINT_MAX)
  89. return false;
  90. *seconds = big_seconds;
  91. *nanoseconds = (milliseconds % 1000) * 1000000L;
  92. return true;
  93. }
  94. /**
  95. * timeout_create_with_nanoseconds:
  96. * @seconds: number of seconds
  97. * @nanoseconds: number of nanoseconds
  98. * @callback: timeout callback function
  99. * @user_data: user data provided to timeout callback function
  100. * @destroy: destroy function for user data
  101. *
  102. * Create new timeout callback handling.
  103. *
  104. * The timeout will only fire once. The timeout handling needs to be rearmed
  105. * with one of the l_timeout_modify functions to trigger again.
  106. *
  107. * Returns: a newly allocated #l_timeout object. On failure, the function
  108. * returns NULL.
  109. **/
  110. static struct l_timeout *timeout_create_with_nanoseconds(unsigned int seconds,
  111. long nanoseconds, l_timeout_notify_cb_t callback,
  112. void *user_data, l_timeout_destroy_cb_t destroy)
  113. {
  114. struct l_timeout *timeout;
  115. int err;
  116. if (unlikely(!callback))
  117. return NULL;
  118. timeout = l_new(struct l_timeout, 1);
  119. timeout->callback = callback;
  120. timeout->destroy = destroy;
  121. timeout->user_data = user_data;
  122. timeout->fd = timerfd_create(CLOCK_MONOTONIC,
  123. TFD_NONBLOCK | TFD_CLOEXEC);
  124. if (timeout->fd < 0) {
  125. l_free(timeout);
  126. return NULL;
  127. }
  128. if (seconds > 0 || nanoseconds > 0) {
  129. if (timeout_set(timeout->fd, seconds, nanoseconds) < 0) {
  130. close(timeout->fd);
  131. l_free(timeout);
  132. return NULL;
  133. }
  134. }
  135. err = watch_add(timeout->fd, EPOLLIN | EPOLLONESHOT, timeout_callback,
  136. timeout, timeout_destroy);
  137. if (err < 0) {
  138. l_free(timeout);
  139. return NULL;
  140. }
  141. return timeout;
  142. }
  143. /**
  144. * l_timeout_create:
  145. * @seconds: timeout in seconds
  146. * @callback: timeout callback function
  147. * @user_data: user data provided to timeout callback function
  148. * @destroy: destroy function for user data
  149. *
  150. * Create new timeout callback handling.
  151. *
  152. * The timeout will only fire once. The timeout handling needs to be rearmed
  153. * with one of the l_timeout_modify functions to trigger again.
  154. *
  155. * Returns: a newly allocated #l_timeout object. On failure, the function
  156. * returns NULL.
  157. **/
  158. LIB_EXPORT struct l_timeout *l_timeout_create(unsigned int seconds,
  159. l_timeout_notify_cb_t callback,
  160. void *user_data, l_timeout_destroy_cb_t destroy)
  161. {
  162. return timeout_create_with_nanoseconds(seconds, 0, callback,
  163. user_data, destroy);
  164. }
  165. /**
  166. * l_timeout_create_ms:
  167. * @milliseconds: timeout in milliseconds
  168. * @callback: timeout callback function
  169. * @user_data: user data provided to timeout callback function
  170. * @destroy: destroy function for user data
  171. *
  172. * Create new timeout callback handling.
  173. *
  174. * The timeout will only fire once. The timeout handling needs to be rearmed
  175. * with one of the l_timeout_modify functions to trigger again.
  176. *
  177. * Returns: a newly allocated #l_timeout object. On failure, the function
  178. * returns NULL.
  179. **/
  180. LIB_EXPORT struct l_timeout *l_timeout_create_ms(uint64_t milliseconds,
  181. l_timeout_notify_cb_t callback,
  182. void *user_data, l_timeout_destroy_cb_t destroy)
  183. {
  184. unsigned int seconds;
  185. long nanoseconds;
  186. if (!convert_ms(milliseconds, &seconds, &nanoseconds))
  187. return NULL;
  188. return timeout_create_with_nanoseconds(seconds, nanoseconds, callback,
  189. user_data, destroy);
  190. }
  191. /**
  192. * l_timeout_modify:
  193. * @timeout: timeout object
  194. * @seconds: timeout in seconds
  195. *
  196. * Modify an existing @timeout and rearm it.
  197. **/
  198. LIB_EXPORT void l_timeout_modify(struct l_timeout *timeout,
  199. unsigned int seconds)
  200. {
  201. if (unlikely(!timeout))
  202. return;
  203. if (unlikely(timeout->fd < 0))
  204. return;
  205. if (seconds > 0) {
  206. if (timeout_set(timeout->fd, seconds, 0) < 0)
  207. return;
  208. }
  209. watch_modify(timeout->fd, EPOLLIN | EPOLLONESHOT, true);
  210. }
  211. /**
  212. * l_timeout_modify_ms:
  213. * @timeout: timeout object
  214. * @milliseconds: number of milliseconds
  215. *
  216. * Modify an existing @timeout and rearm it.
  217. **/
  218. LIB_EXPORT void l_timeout_modify_ms(struct l_timeout *timeout,
  219. uint64_t milliseconds)
  220. {
  221. if (unlikely(!timeout))
  222. return;
  223. if (unlikely(timeout->fd < 0))
  224. return;
  225. if (milliseconds > 0) {
  226. unsigned int sec;
  227. long nanosec;
  228. if (!convert_ms(milliseconds, &sec, &nanosec) ||
  229. timeout_set(timeout->fd, sec, nanosec) < 0)
  230. return;
  231. }
  232. watch_modify(timeout->fd, EPOLLIN | EPOLLONESHOT, true);
  233. }
  234. /**
  235. * l_timeout_remove:
  236. * @timeout: timeout object
  237. *
  238. * Remove timeout handling.
  239. **/
  240. LIB_EXPORT void l_timeout_remove(struct l_timeout *timeout)
  241. {
  242. if (unlikely(!timeout))
  243. return;
  244. watch_remove(timeout->fd, false);
  245. l_free(timeout);
  246. }
  247. /**
  248. * l_timeout_set_callback:
  249. * @timeout: timeout object
  250. * @callback: The new callback
  251. * @user_data: The new user_data
  252. * @destroy: The new destroy function
  253. *
  254. * Sets the new notify callback for @timeout. If the old user_data object had
  255. * a destroy function set, then that function will be called.
  256. */
  257. LIB_EXPORT void l_timeout_set_callback(struct l_timeout *timeout,
  258. l_timeout_notify_cb_t callback,
  259. void *user_data,
  260. l_timeout_destroy_cb_t destroy)
  261. {
  262. if (unlikely(!timeout))
  263. return;
  264. if (timeout->destroy)
  265. timeout->destroy(timeout->user_data);
  266. timeout->callback = callback;
  267. timeout->user_data = user_data;
  268. timeout->destroy = destroy;
  269. }