1 /**
2  * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <hardware/sync.h>
11 #include "pico/stdlib.h"
12 #include "pico/test.h"
13 // Include sys/types.h before inttypes.h to work around issue with
14 // certain versions of GCC and newlib which causes omission of PRIi64
15 #include <sys/types.h>
16 #include <inttypes.h>
17 PICOTEST_MODULE_NAME("pico_time_test", "pico_time test harness");
18 
19 #define NUM_TIMEOUTS 500
20 #define MAX_TIMERS_PER_POOL 250
21 static_assert(PICO_TIME_DEFAULT_ALARM_POOL_MAX_TIMERS >= MAX_TIMERS_PER_POOL, "");
22 #define TEST_LENGTH_US 2000000
23 
24 #ifndef NDEBUG
25 #define NUM_REPEATING_TIMERS 30
26 #else
27 #define NUM_REPEATING_TIMERS 50
28 #endif
29 static struct repeating_timer repeating_timers[NUM_REPEATING_TIMERS];
30 static uint repeating_timer_callback_count[NUM_REPEATING_TIMERS];
31 
32 static struct timeout {
33     alarm_id_t alarm_id;
34     absolute_time_t target;
35     absolute_time_t fired_at;
36     uint pool;
37     uint fired_count;
38     bool cancelled;
39     bool not_cancelled; // tried to cancel but it was done
40 } timeouts[NUM_TIMEOUTS];
41 
timer_callback1(alarm_id_t id,void * user_data)42 int64_t timer_callback1(alarm_id_t id, void *user_data) {
43     struct timeout *timeout = (struct timeout *)user_data;
44     assert(timeout >= timeouts && timeout < (timeouts + NUM_TIMEOUTS));
45     timeout->fired_at = get_absolute_time();
46     timeout->fired_count++;
47 //    printf("%d %d %ld\n", timeout->pool, id, to_us_since_boot(timeout->target));
48     return 0;
49 }
50 
sort_by_target(const void * a,const void * b)51 int sort_by_target(const void *a, const void *b) {
52     const struct timeout *ta = (const struct timeout *)a;
53     const struct timeout *tb = (const struct timeout *)b;
54     int64_t delta = absolute_time_diff_us(tb->target, ta->target);
55     if (delta < 0) return -1;
56     else if (delta > 0) return 1;
57     return 0;
58 }
59 
repeating_timer_callback(struct repeating_timer * t)60 static bool repeating_timer_callback(struct repeating_timer *t) {
61     // check we get the right timer structure
62     uint i = (uintptr_t)t->user_data;
63     hard_assert(i == (t - repeating_timers));
64     repeating_timer_callback_count[i]++;
65     return true;
66 }
67 
68 #ifndef PICO_HARDWARE_TIMER_RESOLUTION_US
69 #define RESOLUTION_ALLOWANCE 0
70 #else
71 #define RESOLUTION_ALLOWANCE PICO_HARDWARE_TIMER_RESOLUTION_US
72 #endif
73 
74 int issue_195_test(void);
75 int issue_1812_test(void);
76 int issue_1953_test(void);
77 
main()78 int main() {
79     setup_default_uart();
80     alarm_pool_init_default();
81 
82     PICOTEST_START();
83     struct alarm_pool *pools[NUM_ALARMS];
84     for(uint i=0; i<NUM_ALARMS; i++) {
85         if (i == alarm_pool_timer_alarm_num(alarm_pool_get_default())) {
86             pools[i] = alarm_pool_get_default();
87         } else {
88             pools[i] = alarm_pool_create(i, MAX_TIMERS_PER_POOL);
89         }
90         PICOTEST_CHECK_AND_ABORT(pools[i], "failed to create timer pool");
91     }
92 
93     // Check default config has valid data in it
94     PICOTEST_START_SECTION("Alarm ordering test");
95 
96     absolute_time_t time_base = get_absolute_time();
97     uint32_t init_ms = 1000;
98     for(uint i = 0; i < NUM_TIMEOUTS; i++) {
99 //        printf("%d %p\n", i);
100         absolute_time_t target;
101         uint pool;
102         if (1 == (i&127u)) {
103             // want occasional duplicate time
104             target = timeouts[i-1].target;
105             pool = timeouts[i-1].pool;
106         } else {
107             target = delayed_by_us(time_base, init_ms + (rand() % TEST_LENGTH_US));
108             pool = rand() % 4;
109         }
110         timeouts[i].target = target;
111         timeouts[i].pool = pool;
112         alarm_id_t id = alarm_pool_add_alarm_at(pools[pool], target, timer_callback1, timeouts + i, true);
113         PICOTEST_CHECK_AND_ABORT(id >=0, "Failed to add timer");
114     }
115     PICOTEST_CHECK(absolute_time_diff_us(time_base, get_absolute_time()) < init_ms * 1000, "This is a flaky test :-(");
116 
117     uint64_t last_fired_at[NUM_ALARMS];
118     uint64_t last_target[NUM_ALARMS];
119     memset(&last_fired_at, 0, sizeof(last_fired_at));
120     printf("Sleeping...\n");
121     sleep_us(TEST_LENGTH_US + 250000);
122     printf("   ...done\n");
123 
124     qsort(timeouts, NUM_TIMEOUTS, sizeof(struct timeout), sort_by_target);
125 
126     uint64_t max_jitter = 0;
127     for(uint i = 0; i < NUM_TIMEOUTS; i++) {
128         printf("%d %d %"PRIi64" : %"PRIi64"\n", timeouts[i].pool, timeouts[i].fired_count, to_us_since_boot(timeouts[i].fired_at), to_us_since_boot(timeouts[i].target));
129         PICOTEST_CHECK(timeouts[i].fired_count, "Timer should have fired");
130         PICOTEST_CHECK(timeouts[i].fired_count < 2, "Timer should only have fired once");
131         uint64_t fired_at = to_us_since_boot(timeouts[i].fired_at);
132         PICOTEST_CHECK(timeouts[i].fired_count != 1 || fired_at >= MAX(RESOLUTION_ALLOWANCE,
133                                                                        to_us_since_boot(timeouts[i].target)) - RESOLUTION_ALLOWANCE, "Timer fired early");
134         // we need to be in order unless the targets are the same in which case order is arbitrary
135         PICOTEST_CHECK(timeouts[i].fired_count != 1 || fired_at > MAX(RESOLUTION_ALLOWANCE, last_fired_at[timeouts[i].pool]) - RESOLUTION_ALLOWANCE ||
136                                to_us_since_boot(timeouts[i].target) == last_target[timeouts[i].pool], "Timer fired out of order");
137         last_fired_at[timeouts[i].pool] = fired_at;
138         last_target[timeouts[i].pool] = to_us_since_boot(timeouts[i].target);
139         if (timeouts[i].fired_count == 1) {
140             uint64_t jitter = absolute_time_diff_us(timeouts[i].target, timeouts[i].fired_at);
141             if (jitter > max_jitter) {
142                 max_jitter = jitter;
143             }
144         }
145     }
146     printf("MAX JITTER: %dus\n", (uint)max_jitter);
147 
148     PICOTEST_END_SECTION();
149 
150     PICOTEST_START_SECTION("Alarm completion or canceled");
151     memset(timeouts, 0, sizeof(timeouts));
152 
153     absolute_time_t time_base = get_absolute_time();
154     // this runs concurrently with the firing, so some are in the past
155     uint approx_past_timeouts = 0;
156 //    uint32_t save = save_and_disable_interrupts();
157     for(uint i = 0; i < NUM_TIMEOUTS; i++) {
158 //        printf("%d %p\n", i);
159         absolute_time_t target = delayed_by_us(time_base, (rand() % TEST_LENGTH_US));
160         if (absolute_time_diff_us(target, get_absolute_time()) >= 0) {
161             approx_past_timeouts++;
162         }
163         uint pool = rand() % 4;
164         timeouts[i].target = target;
165         timeouts[i].pool = pool;
166         alarm_id_t id = alarm_pool_add_alarm_at(pools[pool], target, timer_callback1, timeouts + i, true);
167         timeouts[i].alarm_id = id;
168         PICOTEST_CHECK_AND_ABORT(id >=0, "Failed to add timer");
169         if (id && !(rand() & 6)) {
170             uint j = rand() % (i + 1);
171             if (timeouts[j].alarm_id && !timeouts[j].cancelled && !timeouts[j].not_cancelled) {
172 //                alarm_pool_dump(pools[pool]);
173 //                printf("removing %d\n", timeouts[j].alarm_id);
174                 if (alarm_pool_cancel_alarm(pools[timeouts[j].pool], timeouts[j].alarm_id)) {
175                     timeouts[j].cancelled = true;
176                 } else {
177                     timeouts[j].not_cancelled = true;
178                 }
179 //                printf("removed %d\n", timeouts[j].alarm_id);
180 //                alarm_pool_dump(pools[pool]);
181             }
182         }
183         busy_wait_us_32(2000); // we want to overlap with the firing
184     }
185     printf("approx past timeouts %d/%d\n", approx_past_timeouts, NUM_TIMEOUTS);
186     sleep_us(TEST_LENGTH_US  - 2000 * NUM_TIMEOUTS / 4 + 250000);
187     for(uint i = 0; i < NUM_TIMEOUTS/4; i++) {
188         printf("%d %d %d/%d/%d %"PRIi64" : %"PRIi64"\n", timeouts[i].pool, (int)timeouts[i].alarm_id, timeouts[i].fired_count, timeouts[i].cancelled,
189                timeouts[i].not_cancelled, to_us_since_boot(timeouts[i].fired_at), to_us_since_boot(timeouts[i].target));
190         uint total = timeouts[i].fired_count + timeouts[i].cancelled;
191         PICOTEST_CHECK( timeouts[i].not_cancelled ? timeouts[i].fired_count : true, "Timer that failed to cancel should have fired");
192         PICOTEST_CHECK(total == 1, "Timer should have fired or been cancelled");
193     }
194 
195     PICOTEST_END_SECTION();
196 
197 
198     PICOTEST_START_SECTION("Repeating timertest");
199     for(uint i=0;i<NUM_REPEATING_TIMERS;i++) {
200 
201         add_repeating_timer_us(500+ (rand() & 1023), repeating_timer_callback, (void *)(uintptr_t)i, repeating_timers + i);
202     }
203 
204     // issue #1953 will lockup here if sleep_us >= 6us (PICO_TIME_SLEEP_OVERHEAD_ADJUST_US)
205     absolute_time_t timeout = make_timeout_time_ms(3000);
206     while(absolute_time_diff_us(get_absolute_time(), timeout) > 0) {
207         sleep_us(5);
208     }
209 
210     uint callbacks = 0;
211     for(uint i=0;i<NUM_REPEATING_TIMERS;i++) {
212         PICOTEST_CHECK(cancel_repeating_timer(repeating_timers + i), "Cancelling repeating timer should succeed");
213         PICOTEST_CHECK(repeating_timer_callback_count[i] > 1, "Each repeating timer should have been called back multiple times");
214         callbacks += repeating_timer_callback_count[i];
215     }
216     uint callbacks2 = 0;
217     for(uint i=0;i<NUM_REPEATING_TIMERS;i++) {
218         PICOTEST_CHECK(!cancel_repeating_timer(repeating_timers + i), "Re-cancelling repeating timer should fail");
219         callbacks2 += repeating_timer_callback_count[i];
220     }
221     PICOTEST_CHECK(callbacks == callbacks2, "No repeating timers should have been called back after being cancelled")
222 
223     PICOTEST_END_SECTION();
224 
225     PICOTEST_START_SECTION("end of time");
226     PICOTEST_CHECK(absolute_time_diff_us(at_the_end_of_time, get_absolute_time()) < 0, "now should be before the end of time")
227     PICOTEST_CHECK(absolute_time_diff_us(get_absolute_time(), at_the_end_of_time) > 0, "the end of time should be after now")
228     PICOTEST_CHECK(absolute_time_diff_us(at_the_end_of_time, at_the_end_of_time) == 0, "the end of time should equal itself")
229     absolute_time_t near_the_end_of_time;
230     update_us_since_boot(&near_the_end_of_time, 0x7ffffeffffffffff);
231     PICOTEST_CHECK(absolute_time_diff_us(near_the_end_of_time, at_the_end_of_time) > 0, "near the end of time should be before the end of time")
232     PICOTEST_END_SECTION();
233 
234     if (issue_195_test()) {
235         return -1;
236     }
237     issue_1812_test();
238 
239     // Destroy alarm pools (except for default)
240     for(uint i=0; i<NUM_ALARMS; i++) {
241         if (i != alarm_pool_timer_alarm_num(alarm_pool_get_default())) {
242             alarm_pool_destroy(pools[i]);
243             pools[i] = 0;
244         }
245     }
246 
247     issue_1953_test();
248 
249     PICOTEST_END_TEST();
250 }
251 
252 #define ISSUE_195_TIMER_DELAY 50
253 volatile int issue_195_counter;
issue_195_callback(alarm_id_t id,void * user_data)254 int64_t issue_195_callback(alarm_id_t id, void *user_data) {
255     issue_195_counter++;
256     return -ISSUE_195_TIMER_DELAY;
257 }
258 
issue_195_test(void)259 int issue_195_test(void) {
260     PICOTEST_START_SECTION("Issue #195 race condition - without fix may hang on gcc 10.2.1 release builds");
261     absolute_time_t t1 = get_absolute_time();
262     int id = add_alarm_in_us(ISSUE_195_TIMER_DELAY, issue_195_callback, NULL, true);
263     for(uint i=0;i<5000;i++) {
264         sleep_us(100);
265         sleep_us(100);
266         uint delay = 9; // 9 seems to be the magic number (at least for reproducing on 10.2.1)
267         sleep_us(delay);
268     }
269     absolute_time_t t2 = get_absolute_time();
270     cancel_alarm(id);
271     int expected_count = absolute_time_diff_us(t1, t2) / ISSUE_195_TIMER_DELAY;
272     printf("Timer fires approx_expected=%d actual=%d\n", expected_count, issue_195_counter);
273     PICOTEST_END_SECTION();
274     return 0;
275 }
276 
277 // Setting an alarm should not swallow a sev
issue_1812_test(void)278 int issue_1812_test(void) {
279     PICOTEST_START_SECTION("Issue #1812 defect - Setting an alarm should not ignore a sev");
280 
281     __sev(); // Make sure the call below does not ignore this
282     absolute_time_t before = get_absolute_time();
283     bool result = best_effort_wfe_or_timeout(make_timeout_time_ms(1000));
284     int64_t diff = absolute_time_diff_us(before, get_absolute_time());
285     PICOTEST_CHECK(diff < 250 && !result, "sev ignored by best_effort_wfe_or_timeout")
286 
287     PICOTEST_END_SECTION();
288     return 0;
289 }
290 
timer_callback_issue_1953(repeating_timer_t * rt)291 static bool timer_callback_issue_1953(repeating_timer_t *rt) {
292     static int counter;
293     counter++;
294     return true;
295 }
296 
297 // Callback should only occur if the alarm is set in the past
alarm_pool_stuck_issue_1953(uint alarm,void * data)298 static void alarm_pool_stuck_issue_1953(uint alarm, void *data) {
299     hard_assert(false);
300 }
301 
issue_1953_test(void)302 int issue_1953_test(void) {
303     PICOTEST_START_SECTION("Issue #1953 defect - Alarm can be set in the past");
304     int alarm = hardware_alarm_claim_unused(true);
305     hardware_alarm_set_callback(alarm, alarm_pool_stuck_issue_1953);
306 
307     repeating_timer_t timer1;
308     repeating_timer_t timer2;
309 
310     assert(add_repeating_timer_us(10, timer_callback_issue_1953, NULL, &timer1));
311     assert(add_repeating_timer_us(100, timer_callback_issue_1953, NULL, &timer2));
312 
313     int iterations = 0;
314     while(iterations < 100) {
315         iterations++;
316         hardware_alarm_set_target(alarm, make_timeout_time_ms(1000));
317         sleep_us(500); // lockup in here without the fix for #1953
318         hardware_alarm_cancel(alarm);
319     }
320 
321     cancel_repeating_timer(&timer1);
322     cancel_repeating_timer(&timer2);
323 
324     hardware_alarm_unclaim(alarm);
325     PICOTEST_END_SECTION();
326     return 0;
327 }
328