1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#include "mos/device/timer.hpp"
4
5#include "mos/device/clocksource.hpp"
6#include "mos/lib/structures/list.hpp"
7#include "mos/lib/sync/spinlock.hpp"
8#include "mos/platform/platform.hpp"
9#include "mos/tasks/schedule.hpp"
10#include "mos/tasks/signal.hpp"
11
12static list_head timer_queue; ///< list of timers that are waiting to be executed
13static spinlock_t timer_queue_lock;
14
15static bool timer_do_wakeup(ktimer_t *timer, void *arg)
16{
17 MOS_UNUSED(arg);
18
19 if (timer->thread)
20 scheduler_wake_thread(thread: timer->thread);
21
22 return true;
23}
24
25void 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
39long timer_msleep(u64 ms)
40{
41 if (!active_clocksource)
42 {
43 spinlock_acquire(&current_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 .timeout = target_val,
53 .thread = current_thread,
54 .ticked = false,
55 .callback = timer_do_wakeup,
56 .arg = NULL,
57 };
58
59 spinlock_acquire(&timer_queue_lock);
60 list_node_append(head: &timer_queue, list_node(&timer));
61 spinlock_release(&timer_queue_lock);
62
63 while (!timer.ticked)
64 {
65 blocked_reschedule();
66 if (signal_has_pending())
67 {
68 spinlock_acquire(&timer_queue_lock);
69 list_remove(&timer);
70 spinlock_release(&timer_queue_lock);
71 return -EINTR; // interrupted by signal
72 }
73 }
74
75 MOS_ASSERT(list_is_empty(list_node(&timer)));
76 return 0;
77}
78