sched.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  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. saved = faux_list_head(sched->list);
  273. while ((node = faux_list_match_node(sched->list, cmp_f,
  274. value, &saved))) {
  275. faux_list_del(sched->list, node);
  276. nodes_deleted++;
  277. }
  278. return nodes_deleted;
  279. }
  280. /** @brief Delete event from list.
  281. *
  282. * @param [in] sched Allocated and initialized sched object.
  283. * @param [in] ptr Pointer to event object.
  284. * @return Number of removed entries or < 0 on error.
  285. */
  286. ssize_t faux_sched_del(faux_sched_t *sched, faux_ev_t *ev)
  287. {
  288. return faux_sched_del_by_something(sched, ev, faux_ev_compare_ptr);
  289. }
  290. /** @brief Deletes all events with specified ID from list.
  291. *
  292. * @param [in] sched Allocated and initialized sched object.
  293. * @param [in] id ID to remove.
  294. * @return Number of removed entries or < 0 on error.
  295. */
  296. ssize_t faux_sched_del_by_id(faux_sched_t *sched, int id)
  297. {
  298. return faux_sched_del_by_something(sched, &id, faux_ev_compare_id);
  299. }
  300. /** @brief Deletes all events with specified data pointer from list.
  301. *
  302. * @param [in] sched Allocated and initialized sched object.
  303. * @param [in] data Data to search entries to remove.
  304. * @return Number of removed entries or < 0 on error.
  305. */
  306. ssize_t faux_sched_del_by_data(faux_sched_t *sched, void *data)
  307. {
  308. return faux_sched_del_by_something(sched, data, faux_ev_compare_data);
  309. }
  310. /** @brief Init scheduled event iterator.
  311. *
  312. * @param [in] sched Allocated and initialized sched object.
  313. * @return Event iterator or NULL on error or empty scheduled event list.
  314. */
  315. faux_list_node_t *faux_sched_init_ev_iter(faux_sched_t *sched)
  316. {
  317. assert(sched);
  318. if (!sched)
  319. return NULL;
  320. return faux_list_head(sched->list);
  321. }
  322. /** @brief Get scheduled event by specified value.
  323. *
  324. * Static function.
  325. * Saved iterator 'saved' must be initialized to list head before usage.
  326. *
  327. * @param [in] sched Allocated and initialized sched object.
  328. * @param [in] value Value to search for.
  329. * @param [in] cmp_f Callback to compare key and entry.
  330. * @param [in,out] saved Iterator.
  331. * @return Event (faux_ev_t) pointer or NULL on error or not found.
  332. */
  333. static faux_ev_t *faux_sched_get_by_something(faux_sched_t *sched, void *value,
  334. faux_list_kcmp_fn cmp_f, faux_list_node_t **saved)
  335. {
  336. faux_list_node_t *node = NULL;
  337. faux_ev_t *ev = NULL;
  338. assert(sched);
  339. if (!sched)
  340. return NULL;
  341. node = faux_list_match_node(sched->list, cmp_f, value, saved);
  342. if (!node)
  343. return NULL;
  344. ev = (faux_ev_t *)faux_list_data(node);
  345. return ev;
  346. }
  347. /** @brief Get sched entries with specified event ID.
  348. *
  349. * Saved iterator 'saved' must be initialized to list head before usage.
  350. *
  351. * @param [in] sched Allocated and initialized sched object.
  352. * @param [in] ev_id Event ID to search for.
  353. * @param [in,out] saved Iterator.
  354. * @return Event (faux_ev_t) pointer or NULL on error or not found.
  355. */
  356. faux_ev_t *faux_sched_get_by_id(faux_sched_t *sched, int ev_id,
  357. faux_list_node_t **saved)
  358. {
  359. return faux_sched_get_by_something(sched, &ev_id,
  360. faux_ev_compare_id, saved);
  361. }
  362. /** @brief Get sched entries with specified user data pointer.
  363. *
  364. * Saved iterator 'saved' must be initialized to list head before usage.
  365. *
  366. * @param [in] sched Allocated and initialized sched object.
  367. * @param [in] data Pointer to user data to search for.
  368. * @param [in,out] saved Iterator.
  369. * @return Event (faux_ev_t) pointer or NULL on error or not found.
  370. */
  371. faux_ev_t *faux_sched_get_by_data(faux_sched_t *sched, void *data,
  372. faux_list_node_t **saved)
  373. {
  374. return faux_sched_get_by_something(sched, data,
  375. faux_ev_compare_data, saved);
  376. }