sched.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. /** @brief Mechanism to shedule events.
  2. *
  3. * It's an ordered list of events. Events are ordered by the time. The earlier
  4. * events are closer to list head. The events can be one-time ("once") and
  5. * periodic. Periodic events have period and number of cycles (can be infinite).
  6. * User can schedule events specifying absolute time of future event or interval
  7. * from now to the moment of event. Periodic events will be rescheduled
  8. * automatically using specified period.
  9. *
  10. * User can get interval from now to next event time. User can get upcoming
  11. * events one-by-one.
  12. *
  13. * Each scheduled event can has arbitrary ID and pointer to arbitrary data
  14. * linked to this event. The ID can be used for type of event for
  15. * example or something else. The linked data can be a service structure.
  16. */
  17. #include <sys/time.h>
  18. #include <time.h>
  19. #include <errno.h>
  20. #include <stdint.h>
  21. #include <assert.h>
  22. #include "private.h"
  23. #include "faux/faux.h"
  24. #include "faux/time.h"
  25. #include "faux/list.h"
  26. #include "faux/sched.h"
  27. /** @brief Allocates new sched object.
  28. *
  29. * Before working with sched object it must be allocated and initialized.
  30. *
  31. * @return Allocated and initialized sched object or NULL on error.
  32. */
  33. faux_sched_t *faux_sched_new(void)
  34. {
  35. faux_sched_t *sched = NULL;
  36. sched = faux_zmalloc(sizeof(*sched));
  37. if (!sched)
  38. return NULL;
  39. // Init
  40. sched->list = faux_list_new(FAUX_LIST_SORTED, FAUX_LIST_NONUNIQUE,
  41. faux_ev_compare, NULL, faux_ev_free);
  42. return sched;
  43. }
  44. /** @brief Frees the sched object.
  45. *
  46. * After using the sched object must be freed. Function frees object itself
  47. * and all events stored within sched object.
  48. */
  49. void faux_sched_free(faux_sched_t *sched)
  50. {
  51. if (!sched)
  52. return;
  53. faux_list_free(sched->list);
  54. faux_free(sched);
  55. }
  56. /** @brief Internal function to add existent event to scheduling list.
  57. *
  58. * @param [in] sched Allocated and initialized sched object.
  59. * @param [in] ev Existent ev object.
  60. * @return 0 - success, < 0 on error.
  61. */
  62. static int _sched_ev(faux_sched_t *sched, faux_ev_t *ev)
  63. {
  64. faux_list_node_t *node = NULL;
  65. assert(sched);
  66. assert(ev);
  67. if (!sched || !ev)
  68. return -1;
  69. node = faux_list_add(sched->list, ev);
  70. if (!node) // Something went wrong
  71. return -1;
  72. return 0;
  73. }
  74. /** @brief Internal function to add constructed event to scheduling list.
  75. *
  76. * @param [in] sched Allocated and initialized sched object.
  77. * @param [in] time Absolute time of future event.
  78. * @param [in] ev_id Event ID.
  79. * @param [in] data Pointer to arbitrary data linked to event.
  80. * @param [in] periodic Periodic flag.
  81. * @param [in] period Periodic interval.
  82. * @param [in] cycle_num Number of cycles (FAUX_SCHED_INFINITE for infinite).
  83. * @return 0 - success, < 0 on error.
  84. */
  85. static int _sched(faux_sched_t *sched, const struct timespec *time,
  86. int ev_id, void *data, faux_sched_periodic_t periodic,
  87. const struct timespec *period, unsigned int cycle_num)
  88. {
  89. faux_ev_t *ev = NULL;
  90. ev = faux_ev_new(time, ev_id, data);
  91. assert(ev);
  92. if (!ev)
  93. return -1;
  94. if (FAUX_SCHED_PERIODIC == periodic)
  95. faux_ev_periodic(ev, period, cycle_num);
  96. if (_sched_ev(sched, ev) < 0) { // Something went wrong
  97. faux_ev_free(ev);
  98. return -1;
  99. }
  100. return 0;
  101. }
  102. /** @brief Adds non-periodic event to scheduling list using absolute time.
  103. *
  104. * @param [in] sched Allocated and initialized sched object.
  105. * @param [in] time Absolute time of future event (FAUX_SCHED_NOW for now).
  106. * @param [in] ev_id Event ID.
  107. * @param [in] data Pointer to arbitrary data linked to event.
  108. * @return 0 - success, < 0 on error.
  109. */
  110. int faux_sched_once(
  111. faux_sched_t *sched, const struct timespec *time, int ev_id, void *data)
  112. {
  113. return _sched(sched, time, ev_id, data,
  114. FAUX_SCHED_ONCE, NULL, 0);
  115. }
  116. /** @brief Adds event to scheduling list using interval.
  117. *
  118. * Add interval to the list. The absolute time is calculated by
  119. * adding specified interval to the current absolute time.
  120. *
  121. * @param [in] sched Allocated and initialized sched object.
  122. * @param [in] interval Interval (NULL means "now").
  123. * @param [in] ev_id Event ID.
  124. * @param [in] data Pointer to arbitrary data linked to event.
  125. * @return 0 - success, < 0 on error.
  126. */
  127. int faux_sched_once_delayed(faux_sched_t *sched,
  128. const struct timespec *interval, int ev_id, void *data)
  129. {
  130. struct timespec now = {};
  131. struct timespec plan = {};
  132. assert(sched);
  133. if (!sched)
  134. return -1;
  135. if (!interval)
  136. return faux_sched_once(sched, FAUX_SCHED_NOW, ev_id, data);
  137. faux_timespec_now(&now);
  138. faux_timespec_sum(&plan, &now, interval);
  139. return faux_sched_once(sched, &plan, ev_id, data);
  140. }
  141. /** @brief Adds periodic event to sched list using absolute time for first one.
  142. *
  143. * @param [in] sched Allocated and initialized sched object.
  144. * @param [in] time Absolute time of first event.
  145. * @param [in] ev_id Event ID.
  146. * @param [in] data Pointer to arbitrary data linked to event.
  147. * @param [in] period Period of periodic event.
  148. * @param [in] cycle_num Number of cycles.
  149. * @return 0 - success, < 0 on error.
  150. */
  151. int faux_sched_periodic(
  152. faux_sched_t *sched, const struct timespec *time, int ev_id, void *data,
  153. const struct timespec *period, unsigned int cycle_num)
  154. {
  155. return _sched(sched, time, ev_id, data,
  156. FAUX_SCHED_PERIODIC, period, cycle_num);
  157. }
  158. /** @brief Adds periodic event to sched list using period for first one.
  159. *
  160. * @param [in] sched Allocated and initialized sched object.
  161. * @param [in] ev_id Event ID.
  162. * @param [in] data Pointer to arbitrary data linked to event.
  163. * @param [in] period Period of periodic event.
  164. * @param [in] cycle_num Number of cycles.
  165. * @return 0 - success, < 0 on error.
  166. */
  167. int faux_sched_periodic_delayed(
  168. faux_sched_t *sched, int ev_id, void *data,
  169. const struct timespec *period, unsigned int cycle_num)
  170. {
  171. struct timespec now = {};
  172. struct timespec plan = {};
  173. assert(sched);
  174. assert(period);
  175. if (!sched || !period)
  176. return -1;
  177. faux_timespec_now(&now);
  178. faux_timespec_sum(&plan, &now, period);
  179. return faux_sched_periodic(sched, &plan, ev_id, data,
  180. period, cycle_num);
  181. }
  182. /** @brief Returns the interval from current time and next scheduled event.
  183. *
  184. * If event is in the past then return null interval.
  185. * If no events was scheduled then return -1.
  186. *
  187. * @param [in] sched Allocated and initialized sched object.
  188. * @param [out] interval Calculated interval.
  189. * @return 0 - success, < 0 on error or when there is no scheduled events.
  190. */
  191. int faux_sched_next_interval(faux_sched_t *sched, struct timespec *interval)
  192. {
  193. faux_ev_t *ev = NULL;
  194. faux_list_node_t *iter = NULL;
  195. assert(sched);
  196. assert(interval);
  197. if (!sched || !interval)
  198. return -1;
  199. iter = faux_list_head(sched->list);
  200. if (!iter)
  201. return -1;
  202. ev = (faux_ev_t *)faux_list_data(iter);
  203. return faux_ev_time_left(ev, interval);
  204. }
  205. /** @brief Remove all entries from the list.
  206. *
  207. * @param [in] sched Allocated and initialized sched object.
  208. */
  209. void faux_sched_empty(faux_sched_t *sched)
  210. {
  211. assert(sched);
  212. if (!sched)
  213. return;
  214. faux_list_empty(sched->list);
  215. }
  216. /** @brief Pop already coming events from list.
  217. *
  218. * Pop (get and remove from list) timestamp if it's in the past.
  219. * If the timestamp is in the future then do nothing.
  220. *
  221. * @param [in] sched Allocated and initialized sched object.
  222. * @param [out] ev_id ID of upcoming event.
  223. * @param [out] data Data of upcoming event.
  224. * @return 0 - success, < 0 on error.
  225. */
  226. int faux_sched_pop(faux_sched_t *sched, int *ev_id, void **data)
  227. {
  228. faux_list_node_t *iter = NULL;
  229. faux_ev_t *ev = NULL;
  230. assert(sched);
  231. if (!sched)
  232. return -1;
  233. iter = faux_list_head(sched->list);
  234. if (!iter)
  235. return -1;
  236. ev = (faux_ev_t *)faux_list_data(iter);
  237. if (!faux_timespec_before_now(faux_ev_time(ev)))
  238. return -1; // No events for this time
  239. faux_list_takeaway(sched->list, iter); // Remove entry from list
  240. if (ev_id)
  241. *ev_id = faux_ev_id(ev);
  242. if (data)
  243. *data = faux_ev_data(ev);
  244. if (faux_ev_reschedule_period(ev) < 0) {
  245. faux_ev_free(ev);
  246. } else {
  247. _sched_ev(sched, ev);
  248. }
  249. return 0;
  250. }
  251. /** @brief Removes all events with specified ID from list.
  252. *
  253. * @param [in] sched Allocated and initialized sched object.
  254. * @param [in] id ID to remove.
  255. * @return Number of removed entries or < 0 on error.
  256. */
  257. int faux_sched_remove_by_id(faux_sched_t *sched, int id)
  258. {
  259. faux_list_node_t *node = NULL;
  260. faux_list_node_t *saved = NULL;
  261. int nodes_deleted = 0;
  262. assert(sched);
  263. if (!sched)
  264. return -1;
  265. while ((node = faux_list_match_node(sched->list,
  266. faux_ev_compare_id, &id, &saved))) {
  267. faux_list_del(sched->list, node);
  268. nodes_deleted++;
  269. }
  270. return nodes_deleted;
  271. }
  272. /** @brief Removes all events with specified data pointer from list.
  273. *
  274. * @param [in] sched Allocated and initialized sched object.
  275. * @param [in] data Data to search entries to remove.
  276. * @return Number of removed entries or < 0 on error.
  277. */
  278. int faux_sched_remove_by_data(faux_sched_t *sched, void *data)
  279. {
  280. faux_list_node_t *node = NULL;
  281. faux_list_node_t *saved = NULL;
  282. int nodes_deleted = 0;
  283. assert(sched);
  284. if (!sched)
  285. return -1;
  286. while ((node = faux_list_match_node(sched->list,
  287. faux_ev_compare_data, data, &saved))) {
  288. faux_list_del(sched->list, node);
  289. nodes_deleted++;
  290. }
  291. return nodes_deleted;
  292. }
  293. /** @brief Get time of events with specified data pointer from list.
  294. *
  295. * @param [in] sched Allocated and initialized sched object.
  296. * @param [in] data Data to search entries to remove.
  297. * @return Number of removed entries or < 0 on error.
  298. */
  299. const struct timespec *faux_sched_time_by_data(faux_sched_t *sched, void *data)
  300. {
  301. faux_list_node_t *node = NULL;
  302. faux_ev_t *ev = NULL;
  303. assert(sched);
  304. if (!sched)
  305. return NULL;
  306. node = faux_list_match_node(sched->list, faux_ev_compare_data, data, NULL);
  307. if (!node)
  308. return NULL;
  309. ev = faux_list_data(node);
  310. if (!ev)
  311. return NULL;
  312. return faux_ev_time(ev);
  313. }