1 | // SPDX-License-Identifier: GPL-3.0-or-later |
2 | |
3 | #include "mos/device/timer.h" |
4 | |
5 | #include "mos/device/clocksource.h" |
6 | #include "mos/lib/structures/list.h" |
7 | #include "mos/lib/sync/spinlock.h" |
8 | #include "mos/platform/platform.h" |
9 | #include "mos/tasks/schedule.h" |
10 | #include "mos/tasks/signal.h" |
11 | |
12 | static list_head timer_queue = LIST_HEAD_INIT(timer_queue); ///< list of timers that are waiting to be executed |
13 | static spinlock_t timer_queue_lock = SPINLOCK_INIT; |
14 | |
15 | static bool timer_do_wakeup(ktimer_t *timer, void *arg) |
16 | { |
17 | MOS_UNUSED(arg); |
18 | |
19 | if (timer->thread != NULL) |
20 | scheduler_wake_thread(thread: timer->thread); |
21 | |
22 | return true; |
23 | } |
24 | |
25 | void timer_tick() |
26 | { |
27 | spinlock_acquire(&timer_queue_lock); |
28 | list_foreach(ktimer_t, timer, timer_queue) |
29 | { |
30 | if (active_clocksource_ticks() >= (u64) timer->timeout) |
31 | { |
32 | if (timer->callback(timer, timer->arg)) |
33 | timer->ticked = true, list_remove(timer); |
34 | } |
35 | } |
36 | spinlock_release(&timer_queue_lock); |
37 | } |
38 | |
39 | long timer_msleep(u64 ms) |
40 | { |
41 | if (!active_clocksource) |
42 | { |
43 | spinlock_acquire(¤t_thread->state_lock); |
44 | reschedule(); // simulate a reschedule if no clocksource is available |
45 | return 0; |
46 | } |
47 | |
48 | const u64 offset = ms * active_clocksource->frequency / 1000; |
49 | const u64 target_val = active_clocksource_ticks() + offset; |
50 | |
51 | ktimer_t timer = { |
52 | .list_node = LIST_NODE_INIT(timer), |
53 | .timeout = target_val, |
54 | .thread = current_thread, |
55 | .ticked = false, |
56 | .callback = timer_do_wakeup, |
57 | .arg = NULL, |
58 | }; |
59 | |
60 | spinlock_acquire(&timer_queue_lock); |
61 | list_node_append(head: &timer_queue, list_node(&timer)); |
62 | spinlock_release(&timer_queue_lock); |
63 | |
64 | while (!timer.ticked) |
65 | { |
66 | blocked_reschedule(); |
67 | if (signal_has_pending()) |
68 | { |
69 | spinlock_acquire(&timer_queue_lock); |
70 | list_remove(&timer); |
71 | spinlock_release(&timer_queue_lock); |
72 | return -EINTR; // interrupted by signal |
73 | } |
74 | } |
75 | |
76 | MOS_ASSERT(list_is_empty(list_node(&timer))); |
77 | return 0; |
78 | } |
79 | |