timer-list.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. /*
  2. * Copyright (c) 2015-2020 Red Hat, Inc.
  3. *
  4. * All rights reserved.
  5. *
  6. * Author: Jan Friesse (jfriesse@redhat.com)
  7. *
  8. * This software licensed under BSD license, the text of which follows:
  9. *
  10. * Redistribution and use in source and binary forms, with or without
  11. * modification, are permitted provided that the following conditions are met:
  12. *
  13. * - Redistributions of source code must retain the above copyright notice,
  14. * this list of conditions and the following disclaimer.
  15. * - Redistributions in binary form must reproduce the above copyright notice,
  16. * this list of conditions and the following disclaimer in the documentation
  17. * and/or other materials provided with the distribution.
  18. * - Neither the name of the Red Hat, Inc. nor the names of its
  19. * contributors may be used to endorse or promote products derived from this
  20. * software without specific prior written permission.
  21. *
  22. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  23. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  24. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  25. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  26. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  27. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  28. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  29. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  30. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  31. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  32. * THE POSSIBILITY OF SUCH DAMAGE.
  33. */
  34. #include <assert.h>
  35. #include <string.h>
  36. #include "timer-list.h"
  37. void
  38. timer_list_init(struct timer_list *tlist)
  39. {
  40. memset(tlist, 0, sizeof(*tlist));
  41. TAILQ_INIT(&tlist->free_list);
  42. }
  43. static PRIntervalTime
  44. timer_list_entry_time_to_expire(const struct timer_list_entry *entry, PRIntervalTime current_time)
  45. {
  46. PRIntervalTime diff, half_interval;
  47. diff = entry->expire_time - current_time;
  48. half_interval = ~0;
  49. half_interval /= 2;
  50. if (diff > half_interval) {
  51. return (0);
  52. }
  53. return (diff);
  54. }
  55. static int
  56. timer_list_entry_cmp(const struct timer_list_entry *entry1,
  57. const struct timer_list_entry *entry2, PRIntervalTime current_time)
  58. {
  59. PRIntervalTime diff1, diff2;
  60. int res;
  61. diff1 = timer_list_entry_time_to_expire(entry1, current_time);
  62. diff2 = timer_list_entry_time_to_expire(entry2, current_time);
  63. res = 0;
  64. if (diff1 < diff2) res = -1;
  65. if (diff1 > diff2) res = 1;
  66. return (res);
  67. }
  68. static size_t
  69. timer_list_heap_index_left(size_t index)
  70. {
  71. return (2 * index + 1);
  72. }
  73. static size_t
  74. timer_list_heap_index_right(size_t index)
  75. {
  76. return (2 * index + 2);
  77. }
  78. static size_t
  79. timer_list_heap_index_parent(size_t index)
  80. {
  81. return ((index - 1) / 2);
  82. }
  83. static void
  84. timer_list_heap_entry_set(struct timer_list *tlist, size_t item_pos, struct timer_list_entry *entry)
  85. {
  86. assert(item_pos < tlist->size);
  87. tlist->entries[item_pos] = entry;
  88. tlist->entries[item_pos]->heap_pos = item_pos;
  89. }
  90. static struct timer_list_entry *
  91. timer_list_heap_entry_get(struct timer_list *tlist, size_t item_pos)
  92. {
  93. assert(item_pos < tlist->size);
  94. return (tlist->entries[item_pos]);
  95. }
  96. static void
  97. timer_list_heap_sift_up(struct timer_list *tlist, size_t item_pos)
  98. {
  99. size_t parent_pos;
  100. struct timer_list_entry *parent_entry;
  101. struct timer_list_entry *item_entry;
  102. item_entry = timer_list_heap_entry_get(tlist, item_pos);
  103. parent_pos = timer_list_heap_index_parent(item_pos);
  104. while (item_pos > 0 &&
  105. (parent_entry = timer_list_heap_entry_get(tlist, parent_pos),
  106. timer_list_entry_cmp(parent_entry, item_entry, item_entry->epoch) > 0)) {
  107. /*
  108. * Swap item and parent
  109. */
  110. timer_list_heap_entry_set(tlist, parent_pos, item_entry);
  111. timer_list_heap_entry_set(tlist, item_pos, parent_entry);
  112. item_pos = parent_pos;
  113. parent_pos = timer_list_heap_index_parent(item_pos);
  114. }
  115. }
  116. static void
  117. timer_list_heap_sift_down(struct timer_list *tlist, size_t item_pos)
  118. {
  119. int cont;
  120. size_t left_pos, right_pos, smallest_pos;
  121. struct timer_list_entry *left_entry;
  122. struct timer_list_entry *right_entry;
  123. struct timer_list_entry *smallest_entry;
  124. struct timer_list_entry *tmp_entry;
  125. cont = 1;
  126. while (cont) {
  127. smallest_pos = item_pos;
  128. left_pos = timer_list_heap_index_left(item_pos);
  129. right_pos = timer_list_heap_index_right(item_pos);
  130. smallest_entry = timer_list_heap_entry_get(tlist, smallest_pos);
  131. if (left_pos < tlist->size &&
  132. (left_entry = timer_list_heap_entry_get(tlist, left_pos),
  133. timer_list_entry_cmp(left_entry, smallest_entry, smallest_entry->epoch) < 0)) {
  134. smallest_entry = left_entry;
  135. smallest_pos = left_pos;
  136. }
  137. if (right_pos < tlist->size &&
  138. (right_entry = timer_list_heap_entry_get(tlist, right_pos),
  139. timer_list_entry_cmp(right_entry, smallest_entry, smallest_entry->epoch) < 0)) {
  140. smallest_entry = right_entry;
  141. smallest_pos = right_pos;
  142. }
  143. if (smallest_pos == item_pos) {
  144. /*
  145. * Item is smallest (or has no children) -> heap property is restored
  146. */
  147. cont = 0;
  148. } else {
  149. /*
  150. * Swap item with smallest child
  151. */
  152. tmp_entry = timer_list_heap_entry_get(tlist, item_pos);
  153. timer_list_heap_entry_set(tlist, item_pos, smallest_entry);
  154. timer_list_heap_entry_set(tlist, smallest_pos, tmp_entry);
  155. item_pos = smallest_pos;
  156. }
  157. }
  158. }
  159. static void
  160. timer_list_heap_delete(struct timer_list *tlist, struct timer_list_entry *entry)
  161. {
  162. size_t entry_pos;
  163. struct timer_list_entry *replacement_entry;
  164. int cmp_entries;
  165. entry_pos = entry->heap_pos;
  166. entry->heap_pos = (~(size_t)0);
  167. /*
  168. * Swap element with last element
  169. */
  170. replacement_entry = timer_list_heap_entry_get(tlist, tlist->size - 1);
  171. timer_list_heap_entry_set(tlist, entry_pos, replacement_entry);
  172. /*
  173. * And "remove" last element (= entry)
  174. */
  175. tlist->size--;
  176. /*
  177. * Up (or down) heapify based on replacement item size
  178. */
  179. cmp_entries = timer_list_entry_cmp(replacement_entry, entry, entry->epoch);
  180. if (cmp_entries < 0) {
  181. timer_list_heap_sift_up(tlist, entry_pos);
  182. } else if (cmp_entries > 0) {
  183. timer_list_heap_sift_down(tlist, entry_pos);
  184. }
  185. }
  186. /*
  187. * Check if heap is valid.
  188. * - Shape property is always fullfiled because of storage in array
  189. * - Check heap property
  190. */
  191. int
  192. timer_list_debug_is_valid_heap(struct timer_list *tlist)
  193. {
  194. size_t i;
  195. size_t left_pos, right_pos;
  196. struct timer_list_entry *left_entry;
  197. struct timer_list_entry *right_entry;
  198. struct timer_list_entry *cur_entry;
  199. for (i = 0; i < tlist->size; i++) {
  200. cur_entry = timer_list_heap_entry_get(tlist, i);
  201. left_pos = timer_list_heap_index_left(i);
  202. right_pos = timer_list_heap_index_right(i);
  203. if (left_pos < tlist->size &&
  204. (left_entry = timer_list_heap_entry_get(tlist, left_pos),
  205. timer_list_entry_cmp(left_entry, cur_entry, cur_entry->epoch) < 0)) {
  206. return (0);
  207. }
  208. if (right_pos < tlist->size &&
  209. (right_entry = timer_list_heap_entry_get(tlist, right_pos),
  210. timer_list_entry_cmp(right_entry, cur_entry, cur_entry->epoch) < 0)) {
  211. return (0);
  212. }
  213. }
  214. return (1);
  215. }
  216. static int
  217. timer_list_insert_into_list(struct timer_list *tlist, struct timer_list_entry *new_entry)
  218. {
  219. size_t new_size;
  220. struct timer_list_entry **new_entries;
  221. /*
  222. * This can overflow and it's not a problem
  223. */
  224. new_entry->expire_time = new_entry->epoch + PR_MillisecondsToInterval(new_entry->interval);
  225. /*
  226. * Heap insert
  227. */
  228. if (tlist->size + 1 > tlist->allocated) {
  229. new_size = (tlist->allocated + 1) * 2;
  230. new_entries = realloc(tlist->entries, new_size * sizeof(tlist->entries[0]));
  231. if (new_entries == NULL) {
  232. return (-1);
  233. }
  234. tlist->allocated = new_size;
  235. tlist->entries = new_entries;
  236. }
  237. tlist->size++;
  238. timer_list_heap_entry_set(tlist, tlist->size - 1, new_entry);
  239. timer_list_heap_sift_up(tlist, tlist->size - 1);
  240. return (0);
  241. }
  242. struct timer_list_entry *
  243. timer_list_add(struct timer_list *tlist, PRUint32 interval, timer_list_cb_fn func, void *data1,
  244. void *data2)
  245. {
  246. struct timer_list_entry *new_entry;
  247. if (interval < 1 || interval > TIMER_LIST_MAX_INTERVAL || func == NULL) {
  248. return (NULL);
  249. }
  250. if (!TAILQ_EMPTY(&tlist->free_list)) {
  251. /*
  252. * Use free list entry
  253. */
  254. new_entry = TAILQ_FIRST(&tlist->free_list);
  255. TAILQ_REMOVE(&tlist->free_list, new_entry, entries);
  256. } else {
  257. /*
  258. * Alloc new entry
  259. */
  260. new_entry = malloc(sizeof(*new_entry));
  261. if (new_entry == NULL) {
  262. return (NULL);
  263. }
  264. }
  265. memset(new_entry, 0, sizeof(*new_entry));
  266. new_entry->epoch = PR_IntervalNow();
  267. new_entry->interval = interval;
  268. new_entry->func = func;
  269. new_entry->user_data1 = data1;
  270. new_entry->user_data2 = data2;
  271. new_entry->is_active = 1;
  272. new_entry->heap_pos = (~(size_t)0);
  273. if (timer_list_insert_into_list(tlist, new_entry) != 0) {
  274. TAILQ_INSERT_HEAD(&tlist->free_list, new_entry, entries);
  275. return (NULL);
  276. }
  277. return (new_entry);
  278. }
  279. void
  280. timer_list_entry_reschedule(struct timer_list *tlist, struct timer_list_entry *entry)
  281. {
  282. if (entry->is_active) {
  283. timer_list_heap_delete(tlist, entry);
  284. entry->epoch = PR_IntervalNow();
  285. timer_list_insert_into_list(tlist, entry);
  286. }
  287. }
  288. void
  289. timer_list_expire(struct timer_list *tlist)
  290. {
  291. PRIntervalTime now;
  292. struct timer_list_entry *entry;
  293. int res;
  294. now = PR_IntervalNow();
  295. while (tlist->size > 0 &&
  296. (entry = timer_list_heap_entry_get(tlist, 0),
  297. timer_list_entry_time_to_expire(entry, now) == 0)) {
  298. /*
  299. * Expired
  300. */
  301. res = entry->func(entry->user_data1, entry->user_data2);
  302. if (res == 0) {
  303. /*
  304. * Move item to free list
  305. */
  306. timer_list_entry_delete(tlist, entry);
  307. } else if (entry->is_active) {
  308. /*
  309. * Schedule again
  310. */
  311. timer_list_heap_delete(tlist, entry);
  312. entry->epoch = now;
  313. timer_list_insert_into_list(tlist, entry);
  314. }
  315. }
  316. }
  317. PRIntervalTime
  318. timer_list_time_to_expire(struct timer_list *tlist)
  319. {
  320. struct timer_list_entry *entry;
  321. if (tlist->size == 0) {
  322. return (PR_INTERVAL_NO_TIMEOUT);
  323. }
  324. entry = timer_list_heap_entry_get(tlist, 0);
  325. return (timer_list_entry_time_to_expire(entry, PR_IntervalNow()));
  326. }
  327. uint32_t
  328. timer_list_time_to_expire_ms(struct timer_list *tlist)
  329. {
  330. struct timer_list_entry *entry;
  331. uint32_t u32;
  332. if (tlist->size == 0) {
  333. u32 = ~((uint32_t)0);
  334. return (u32);
  335. }
  336. entry = timer_list_heap_entry_get(tlist, 0);
  337. return (PR_IntervalToMilliseconds(timer_list_entry_time_to_expire(entry, PR_IntervalNow())));
  338. }
  339. void
  340. timer_list_entry_delete(struct timer_list *tlist, struct timer_list_entry *entry)
  341. {
  342. if (entry->is_active) {
  343. /*
  344. * Remove item from heap and move it to free list
  345. */
  346. timer_list_heap_delete(tlist, entry);
  347. TAILQ_INSERT_HEAD(&tlist->free_list, entry, entries);
  348. entry->is_active = 0;
  349. }
  350. }
  351. void
  352. timer_list_free(struct timer_list *tlist)
  353. {
  354. struct timer_list_entry *entry;
  355. struct timer_list_entry *entry_next;
  356. size_t i;
  357. for (i = 0; i < tlist->size; i++) {
  358. free(timer_list_heap_entry_get(tlist, i));
  359. }
  360. free(tlist->entries);
  361. entry = TAILQ_FIRST(&tlist->free_list);
  362. while (entry != NULL) {
  363. entry_next = TAILQ_NEXT(entry, entries);
  364. free(entry);
  365. entry = entry_next;
  366. }
  367. timer_list_init(tlist);
  368. }
  369. PRUint32
  370. timer_list_entry_get_interval(const struct timer_list_entry *entry)
  371. {
  372. return (entry->interval);
  373. }
  374. int
  375. timer_list_entry_set_interval(struct timer_list *tlist, struct timer_list_entry *entry,
  376. PRUint32 interval)
  377. {
  378. if (interval < 1 || interval > TIMER_LIST_MAX_INTERVAL) {
  379. return (-1);
  380. }
  381. if (!entry->is_active) {
  382. return (-1);
  383. }
  384. timer_list_heap_delete(tlist, entry);
  385. entry->interval = interval;
  386. entry->epoch = PR_IntervalNow();
  387. timer_list_insert_into_list(tlist, entry);
  388. return (0);
  389. }