|
5 | 5 |
|
6 | 6 | #define MAX_TICKS UINT32_MAX
|
7 | 7 |
|
8 |
| -// Returns 0 if a <= b < c, 1 otherwise |
9 |
| -static int within_range(uint32_t a, uint32_t b, uint32_t c) { |
10 |
| - return (b - a) < (b - c); |
11 |
| -} |
12 |
| - |
13 | 8 | /** \brief Convert milliseconds to clock ticks
|
14 | 9 | *
|
15 | 10 | * WARNING: This function will assert if the output
|
@@ -40,7 +35,7 @@ static uint32_t ms_to_ticks(uint32_t ms) {
|
40 | 35 | return ticks;
|
41 | 36 | }
|
42 | 37 |
|
43 |
| -static uint32_t ticks_to_ms(uint32_t ticks) { |
| 38 | +uint32_t libtock_alarm_ticks_to_ms(uint32_t ticks) { |
44 | 39 | // `ticks_to_ms`'s conversion will be accurate to within the range
|
45 | 40 | // 0 to 1 milliseconds less than the exact conversion
|
46 | 41 | // (true millisecond conversion - [0,1) milliseconds).
|
@@ -75,19 +70,45 @@ static uint32_t ticks_to_ms(uint32_t ticks) {
|
75 | 70 | static libtock_alarm_ticks_t* root = NULL;
|
76 | 71 |
|
77 | 72 | static void root_insert(libtock_alarm_ticks_t* alarm) {
|
| 73 | + // We want to insert the new alarm just before an existing alarm |
| 74 | + // that should expire next after it, as `alarm_upcall` breaks as |
| 75 | + // soon as it finds an alarm that should not fire yet. To do so, we |
| 76 | + // need to account for an empty list, a non-empty list where the new |
| 77 | + // alarm should be last, as well as computing relative expirations |
| 78 | + // when alarm expirations may overflow the clock 0 or 1 times. |
| 79 | + |
78 | 80 | if (root == NULL) {
|
79 | 81 | root = alarm;
|
80 | 82 | root->next = NULL;
|
81 | 83 | root->prev = NULL;
|
82 | 84 | return;
|
83 | 85 | }
|
84 | 86 |
|
| 87 | + // Compute the tick value at which the new alarm will expire. |
| 88 | + uint32_t new_expiration = alarm->reference + alarm->dt; |
| 89 | + // Determine whether the clock overflows before the new alarm |
| 90 | + // expires. Because ticks are 32-bit, a clock can overflow at most |
| 91 | + // once before a 32-bit alarm fires. |
| 92 | + bool new_overflows = alarm->reference > (UINT32_MAX - alarm->dt); |
| 93 | + |
85 | 94 | libtock_alarm_ticks_t** cur = &root;
|
86 | 95 | libtock_alarm_ticks_t* prev = NULL;
|
87 | 96 | while (*cur != NULL) {
|
| 97 | + // Compute the tick value at which this alarm will expire. |
88 | 98 | uint32_t cur_expiration = (*cur)->reference + (*cur)->dt;
|
89 |
| - uint32_t new_expiration = alarm->reference + alarm->dt; |
90 |
| - if (!within_range(alarm->reference, cur_expiration, new_expiration)) { |
| 99 | + // Determine whether the clock overflows before this alarm |
| 100 | + // expires. |
| 101 | + bool cur_overflows = (*cur)->reference > (UINT32_MAX - (*cur)->dt); |
| 102 | + |
| 103 | + // This alarm (`cur`) happens after the new alarm (`alarm`) if: |
| 104 | + // - both overflow or neither overflow, and cur expiration is |
| 105 | + // larger than the new expiration |
| 106 | + // - or, only cur overflows |
| 107 | + // |
| 108 | + // If the new alarm overflows and this alarm doesn't, this alarm |
| 109 | + // happens _before_ the new alarm. |
| 110 | + if (((cur_overflows == new_overflows) && (cur_expiration > new_expiration)) || |
| 111 | + cur_overflows) { |
91 | 112 | // insert before
|
92 | 113 | libtock_alarm_ticks_t* tmp = *cur;
|
93 | 114 | *cur = alarm;
|
@@ -231,7 +252,7 @@ int libtock_alarm_in_ms(uint32_t ms, libtock_alarm_callback cb, void* opaque, li
|
231 | 252 | // schedule multiple alarms to reach the full length. We calculate the number of full overflows
|
232 | 253 | // and the remainder ticks to reach the target length of time. The overflows use the
|
233 | 254 | // `overflow_callback` for each intermediate overflow.
|
234 |
| - const uint32_t max_ticks_in_ms = ticks_to_ms(MAX_TICKS); |
| 255 | + const uint32_t max_ticks_in_ms = libtock_alarm_ticks_to_ms(MAX_TICKS); |
235 | 256 | if (ms > max_ticks_in_ms) {
|
236 | 257 | // overflows_left is the number of intermediate alarms that need to be scheduled to reach the target
|
237 | 258 | // dt_ms. After the alarm in this block is scheduled, we have this many overflows left (hence the reason
|
|
0 commit comments