1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
|
//! A test that verifies the correctness of the [`TimerQueue`].
//!
//! To run this test, you need to activate the `critical-section/std` feature.
use cassette::Cassette;
use parking_lot::Mutex;
use rtic_time::timer_queue::{TimerQueue, TimerQueueBackend};
mod peripheral {
use parking_lot::Mutex;
use std::{
sync::atomic::{AtomicU64, Ordering},
task::{Poll, Waker},
};
use super::TestMonoBackend;
static NOW: AtomicU64 = AtomicU64::new(0);
static WAKERS: Mutex<Vec<Waker>> = Mutex::new(Vec::new());
pub fn tick() -> bool {
NOW.fetch_add(1, Ordering::Release);
let had_wakers = !WAKERS.lock().is_empty();
// Wake up all things waiting for a specific time to happen.
for waker in WAKERS.lock().drain(..) {
waker.wake_by_ref();
}
let had_interrupt = TestMonoBackend::tick(false);
had_interrupt || had_wakers
}
pub fn now() -> u64 {
NOW.load(Ordering::Acquire)
}
pub async fn wait_until(time: u64) {
core::future::poll_fn(|ctx| {
if now() >= time {
Poll::Ready(())
} else {
WAKERS.lock().push(ctx.waker().clone());
Poll::Pending
}
})
.await;
}
}
static COMPARE: Mutex<Option<u64>> = Mutex::new(None);
static TIMER_QUEUE: TimerQueue<TestMonoBackend> = TimerQueue::new();
pub struct TestMonoBackend;
impl TestMonoBackend {
pub fn tick(force_interrupt: bool) -> bool {
let now = peripheral::now();
let compare_reached = Some(now) == Self::compare();
let interrupt = compare_reached || force_interrupt;
if interrupt {
unsafe {
TestMonoBackend::timer_queue().on_monotonic_interrupt();
}
true
} else {
false
}
}
pub fn compare() -> Option<u64> {
COMPARE.lock().clone()
}
}
impl TestMonoBackend {
fn init() {
Self::timer_queue().initialize(Self);
}
}
impl TimerQueueBackend for TestMonoBackend {
type Ticks = u64;
fn now() -> Self::Ticks {
peripheral::now()
}
fn set_compare(instant: Self::Ticks) {
*COMPARE.lock() = Some(instant);
}
fn clear_compare_flag() {}
fn pend_interrupt() {
Self::tick(true);
}
fn timer_queue() -> &'static TimerQueue<Self> {
&TIMER_QUEUE
}
}
#[test]
fn timer_queue() {
TestMonoBackend::init();
let start = 0;
let build_delay_test = |pre_delay: Option<u64>, delay: u64| {
let total = if let Some(pre_delay) = pre_delay {
pre_delay + delay
} else {
delay
};
async move {
// A `pre_delay` simulates a delay in scheduling,
// without the `pre_delay` being present in the timer
// queue
if let Some(pre_delay) = pre_delay {
peripheral::wait_until(start + pre_delay).await;
}
TestMonoBackend::timer_queue().delay(delay).await;
let elapsed = peripheral::now() - start;
println!("{total} ticks delay reached after {elapsed} ticks");
// Expect a delay of one longer, to compensate for timer uncertainty
if elapsed != total + 1 {
panic!("{total} ticks delay was not on time ({elapsed} ticks passed instead)");
}
}
};
macro_rules! cassette {
($($x:ident),* $(,)?) => { $(
// Move the value to ensure that it is owned
let mut $x = $x;
// Shadow the original binding so that it can't be directly accessed
// ever again.
#[allow(unused_mut)]
let mut $x = unsafe {
core::pin::Pin::new_unchecked(&mut $x)
};
let mut $x = Cassette::new($x);
)* }
}
let d1 = build_delay_test(Some(100), 100);
cassette!(d1);
let d2 = build_delay_test(None, 300);
cassette!(d2);
let d3 = build_delay_test(None, 400);
cassette!(d3);
macro_rules! poll {
($($fut:ident),*) => {
$(if !$fut.is_done() {
$fut.poll_on();
})*
};
}
// Do an initial poll to set up all of the waiting futures
poll!(d1, d2, d3);
for _ in 0..500 {
// We only poll the waiting futures if an
// interrupt occured or if an artificial delay
// has passed.
if peripheral::tick() {
poll!(d1, d2, d3);
}
if peripheral::now() == 0 {
// First, we want to be waiting for our 300 tick delay
assert_eq!(TestMonoBackend::compare(), Some(301));
}
if peripheral::now() == 100 {
// After 100 ticks, we enqueue a new delay that is supposed to last
// until the 200-tick-mark
assert_eq!(TestMonoBackend::compare(), Some(201));
}
if peripheral::now() == 201 {
// After 200 ticks, we dequeue the 200-tick-mark delay and
// requeue the 300 tick delay
assert_eq!(TestMonoBackend::compare(), Some(301));
}
if peripheral::now() == 301 {
// After 300 ticks, we dequeue the 300-tick-mark delay and
// go to the 400 tick delay that is already enqueued
assert_eq!(TestMonoBackend::compare(), Some(401));
}
}
assert!(d1.is_done() && d2.is_done() && d3.is_done());
}
|