sched.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  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_forced);
  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 Adds time event (faux_ev_t) to scheduling list.
  57. *
  58. * @param [in] sched Allocated and initialized sched object.
  59. * @param [in] ev Allocated and initialized event object.
  60. * @return BOOL_TRUE - success, BOOL_FALSE on error.
  61. */
  62. bool_t faux_sched_add(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 BOOL_FALSE;
  69. if (faux_ev_is_busy(ev))
  70. return BOOL_FALSE; // Don't add busy (already scheduled) event
  71. node = faux_list_add(sched->list, ev);
  72. if (!node) // Something went wrong
  73. return BOOL_FALSE;
  74. faux_ev_set_busy(ev, BOOL_TRUE);
  75. return BOOL_TRUE;
  76. }
  77. /** @brief Internal function to add constructed event to scheduling list.
  78. *
  79. * @param [in] sched Allocated and initialized sched object.
  80. * @param [in] time Absolute time of future event.
  81. * @param [in] ev_id Event ID.
  82. * @param [in] data Pointer to arbitrary data linked to event.
  83. * @param [in] periodic Periodic flag.
  84. * @param [in] period Periodic interval.
  85. * @param [in] cycle_num Number of cycles (FAUX_SCHED_INFINITE for infinite).
  86. * @return Pointer to newly created faux_ev_t object or NULL on error.
  87. */
  88. static faux_ev_t *_sched(faux_sched_t *sched, const struct timespec *time,
  89. int ev_id, void *data, faux_sched_periodic_e periodic,
  90. const struct timespec *period, unsigned int cycle_num)
  91. {
  92. faux_ev_t *ev = NULL;
  93. ev = faux_ev_new(ev_id, data);
  94. assert(ev);
  95. if (!ev)
  96. return NULL;
  97. faux_ev_set_time(ev, time);
  98. if (FAUX_SCHED_PERIODIC == periodic)
  99. faux_ev_set_periodic(ev, period, cycle_num);
  100. if (!faux_sched_add(sched, ev)) { // Something went wrong
  101. faux_ev_free(ev);
  102. return NULL;
  103. }
  104. return ev;
  105. }
  106. /** @brief Adds non-periodic event to scheduling list using absolute time.
  107. *
  108. * @param [in] sched Allocated and initialized sched object.
  109. * @param [in] time Absolute time of future event (FAUX_SCHED_NOW for now).
  110. * @param [in] ev_id Event ID.
  111. * @param [in] data Pointer to arbitrary data linked to event.
  112. * @return Pointer to newly created faux_ev_t object or NULL on error.
  113. */
  114. faux_ev_t *faux_sched_once(
  115. faux_sched_t *sched, const struct timespec *time, int ev_id, void *data)
  116. {
  117. return _sched(sched, time, ev_id, data,
  118. FAUX_SCHED_ONCE, NULL, 0);
  119. }
  120. /** @brief Adds event to scheduling list using interval.
  121. *
  122. * Add interval to the list. The absolute time is calculated by
  123. * adding specified interval to the current absolute time.
  124. *
  125. * @param [in] sched Allocated and initialized sched object.
  126. * @param [in] interval Interval (NULL means "now").
  127. * @param [in] ev_id Event ID.
  128. * @param [in] data Pointer to arbitrary data linked to event.
  129. * @return Pointer to newly created faux_ev_t object or NULL on error.
  130. */
  131. faux_ev_t *faux_sched_once_delayed(faux_sched_t *sched,
  132. const struct timespec *interval, int ev_id, void *data)
  133. {
  134. struct timespec now = {};
  135. struct timespec plan = {};
  136. assert(sched);
  137. if (!sched)
  138. return NULL;
  139. if (!interval)
  140. return faux_sched_once(sched, FAUX_SCHED_NOW, ev_id, data);
  141. faux_timespec_now(&now);
  142. faux_timespec_sum(&plan, &now, interval);
  143. return faux_sched_once(sched, &plan, ev_id, data);
  144. }
  145. /** @brief Adds periodic event to sched list using absolute time for first one.
  146. *
  147. * @param [in] sched Allocated and initialized sched object.
  148. * @param [in] time Absolute time of first event.
  149. * @param [in] ev_id Event ID.
  150. * @param [in] data Pointer to arbitrary data linked to event.
  151. * @param [in] period Period of periodic event.
  152. * @param [in] cycle_num Number of cycles.
  153. * @return Pointer to newly created faux_ev_t object or NULL on error.
  154. */
  155. faux_ev_t *faux_sched_periodic(
  156. faux_sched_t *sched, const struct timespec *time, int ev_id, void *data,
  157. const struct timespec *period, unsigned int cycle_num)
  158. {
  159. return _sched(sched, time, ev_id, data,
  160. FAUX_SCHED_PERIODIC, period, cycle_num);
  161. }
  162. /** @brief Adds periodic event to sched list using period for first one.
  163. *
  164. * @param [in] sched Allocated and initialized sched object.
  165. * @param [in] ev_id Event ID.
  166. * @param [in] data Pointer to arbitrary data linked to event.
  167. * @param [in] period Period of periodic event.
  168. * @param [in] cycle_num Number of cycles.
  169. * @return Pointer to newly created faux_ev_t object or NULL on error.
  170. */
  171. faux_ev_t *faux_sched_periodic_delayed(
  172. faux_sched_t *sched, int ev_id, void *data,
  173. const struct timespec *period, unsigned int cycle_num)
  174. {
  175. struct timespec now = {};
  176. struct timespec plan = {};
  177. assert(sched);
  178. assert(period);
  179. if (!sched || !period)
  180. return NULL;
  181. faux_timespec_now(&now);
  182. faux_timespec_sum(&plan, &now, period);
  183. return faux_sched_periodic(sched, &plan, ev_id, data,
  184. period, cycle_num);
  185. }
  186. /** @brief Returns the interval from current time and next scheduled event.
  187. *
  188. * If event is in the past then return null interval.
  189. * If no events was scheduled then return BOOL_FALSE.
  190. *
  191. * @param [in] sched Allocated and initialized sched object.
  192. * @param [out] interval Calculated interval.
  193. * @return BOOL_TRUE - success, BOOL_FALSE on error or there is no scheduled events.
  194. */
  195. bool_t faux_sched_next_interval(const faux_sched_t *sched, struct timespec *interval)
  196. {
  197. faux_ev_t *ev = NULL;
  198. faux_list_node_t *iter = NULL;
  199. assert(sched);
  200. assert(interval);
  201. if (!sched || !interval)
  202. return BOOL_FALSE;
  203. iter = faux_list_head(sched->list);
  204. if (!iter)
  205. return BOOL_FALSE;
  206. ev = (faux_ev_t *)faux_list_data(iter);
  207. if (!faux_ev_time_left(ev, interval))
  208. return BOOL_FALSE;
  209. return BOOL_TRUE;
  210. }
  211. /** @brief Remove all entries from the list.
  212. *
  213. * @param [in] sched Allocated and initialized sched object.
  214. */
  215. void faux_sched_del_all(faux_sched_t *sched)
  216. {
  217. assert(sched);
  218. if (!sched)
  219. return;
  220. faux_list_del_all(sched->list);
  221. }
  222. /** @brief Pop already coming events from list.
  223. *
  224. * Pop (get and remove from list) timestamp if it's in the past.
  225. * If the timestamp is in the future then do nothing.
  226. * The event object can be rescheduled in a case of periodic event or
  227. * removed from the scheduled list. Removed event must be freed by user.
  228. * User can inspect event object's busy flag to decide if freeing is needed.
  229. * If busy flag is BOOL_TRUE then event is rescheduled. If busy flag is
  230. * BOOL_FALSE then object is ready to be freed.
  231. *
  232. * @param [in] sched Allocated and initialized sched object.
  233. * @return Event object or NULL on error or there is no already coming events.
  234. */
  235. faux_ev_t *faux_sched_pop(faux_sched_t *sched)
  236. {
  237. faux_list_node_t *iter = NULL;
  238. faux_ev_t *ev = NULL;
  239. assert(sched);
  240. if (!sched)
  241. return NULL;
  242. iter = faux_list_head(sched->list);
  243. if (!iter)
  244. return NULL;
  245. ev = (faux_ev_t *)faux_list_data(iter);
  246. if (!faux_timespec_before_now(faux_ev_time(ev)))
  247. return NULL; // No events for this time
  248. faux_list_takeaway(sched->list, iter); // Remove entry from list
  249. faux_ev_set_busy(ev, BOOL_FALSE);
  250. if (faux_ev_reschedule_period(ev))
  251. faux_sched_add(sched, ev);
  252. return ev;
  253. }
  254. /** @brief Deletes all events with specified value from list.
  255. *
  256. * Static function.
  257. *
  258. * @param [in] sched Allocated and initialized sched object.
  259. * @param [in] value Pointer to key value.
  260. * @param [in] cmp_f Callback to compare key and entry.
  261. * @return Number of removed entries or < 0 on error.
  262. */
  263. static ssize_t faux_sched_del_by_something(faux_sched_t *sched, void *value,
  264. faux_list_kcmp_fn cmp_f)
  265. {
  266. faux_list_node_t *node = NULL;
  267. faux_list_node_t *saved = NULL;
  268. ssize_t nodes_deleted = 0;
  269. assert(sched);
  270. if (!sched)
  271. return -1;
  272. while ((node = faux_list_match_node(sched->list, cmp_f,
  273. value, &saved))) {
  274. faux_list_del(sched->list, node);
  275. nodes_deleted++;
  276. }
  277. return nodes_deleted;
  278. }
  279. /** @brief Delete event from list.
  280. *
  281. * @param [in] sched Allocated and initialized sched object.
  282. * @param [in] ptr Pointer to event object.
  283. * @return Number of removed entries or < 0 on error.
  284. */
  285. ssize_t faux_sched_del(faux_sched_t *sched, faux_ev_t *ev)
  286. {
  287. return faux_sched_del_by_something(sched, ev, faux_ev_compare_ptr);
  288. }
  289. /** @brief Deletes all events with specified ID from list.
  290. *
  291. * @param [in] sched Allocated and initialized sched object.
  292. * @param [in] id ID to remove.
  293. * @return Number of removed entries or < 0 on error.
  294. */
  295. ssize_t faux_sched_del_by_id(faux_sched_t *sched, int id)
  296. {
  297. return faux_sched_del_by_something(sched, &id, faux_ev_compare_id);
  298. }
  299. /** @brief Deletes all events with specified data pointer from list.
  300. *
  301. * @param [in] sched Allocated and initialized sched object.
  302. * @param [in] data Data to search entries to remove.
  303. * @return Number of removed entries or < 0 on error.
  304. */
  305. ssize_t faux_sched_del_by_data(faux_sched_t *sched, void *data)
  306. {
  307. return faux_sched_del_by_something(sched, data, faux_ev_compare_data);
  308. }
  309. /** @brief Get scheduled event by specified value.
  310. *
  311. * Static function.
  312. *
  313. * @param [in] sched Allocated and initialized sched object.
  314. * @param [in] value Value to search for.
  315. * @param [in] cmp_f Callback to compare key and entry.
  316. * @param [in,out] saved Iterator.
  317. * @return Event (faux_ev_t) pointer or NULL on error or not found.
  318. */
  319. static faux_ev_t *faux_sched_get_by_something(faux_sched_t *sched, void *value,
  320. faux_list_kcmp_fn cmp_f, faux_list_node_t **saved)
  321. {
  322. faux_list_node_t *node = NULL;
  323. faux_ev_t *ev = NULL;
  324. assert(sched);
  325. if (!sched)
  326. return NULL;
  327. node = faux_list_match_node(sched->list, cmp_f, value, saved);
  328. if (!node)
  329. return NULL;
  330. ev = (faux_ev_t *)faux_list_data(node);
  331. return ev;
  332. }
  333. /** @brief Get sched entries with specified event ID.
  334. *
  335. * @param [in] sched Allocated and initialized sched object.
  336. * @param [in] ev_id Event ID to search for.
  337. * @param [in,out] saved Iterator.
  338. * @return Event (faux_ev_t) pointer or NULL on error or not found.
  339. */
  340. faux_ev_t *faux_sched_get_by_id(faux_sched_t *sched, int ev_id,
  341. faux_list_node_t **saved)
  342. {
  343. return faux_sched_get_by_something(sched, &ev_id,
  344. faux_ev_compare_id, saved);
  345. }
  346. /** @brief Get sched entries with specified user data pointer.
  347. *
  348. * @param [in] sched Allocated and initialized sched object.
  349. * @param [in] data Pointer to user data to search for.
  350. * @param [in,out] saved Iterator.
  351. * @return Event (faux_ev_t) pointer or NULL on error or not found.
  352. */
  353. faux_ev_t *faux_sched_get_by_data(faux_sched_t *sched, void *data,
  354. faux_list_node_t **saved)
  355. {
  356. return faux_sched_get_by_something(sched, data,
  357. faux_ev_compare_data, saved);
  358. }