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
use core::{
cell::UnsafeCell,
mem::forget,
panic::{RefUnwindSafe, UnwindSafe},
};
use crate::{
bindings::{rt_cond, rt_cond_broadcast, rt_cond_signal, rt_cond_timedwait, rt_cond_wait},
ptr_macros::{ptr_to_field, ptr_to_field_mut},
sync::{MutexGuard, sem::c_sem_init},
tick::Utick,
};
#[repr(transparent)]
pub struct Condvar {
cond: UnsafeCell<rt_cond>,
}
unsafe impl Send for Condvar {}
unsafe impl Sync for Condvar {}
impl UnwindSafe for Condvar {}
impl RefUnwindSafe for Condvar {}
pub(crate) const fn c_cond_init(c: *mut rt_cond) -> rt_cond {
rt_cond {
sem: c_sem_init(ptr_to_field_mut!(c, sem), 0, 1),
}
}
impl Condvar {
/// Initialize a new `Condvar`.
///
/// # Safety
///
/// This must be called with a pointer to a `static` `Condvar` and be used to initialize that
/// same `Condvar`. Users should use the `rt::sync::condvar!` macro to create a `Condvar`
/// rather than call `Condvar::init` directly.
#[must_use]
pub const unsafe fn init(this: *const Self) -> Condvar {
let cond = UnsafeCell::raw_get(ptr_to_field!(this, cond));
Condvar {
cond: UnsafeCell::new(c_cond_init(cond)),
}
}
/// Wake at most one task that is waiting on the `Condvar`.
#[inline]
pub fn signal(&self) {
unsafe { rt_cond_signal(self.cond.get()) }
}
/// Wake all tasks that are waiting on the `Condvar`.
#[inline]
pub fn broadcast(&self) {
unsafe { rt_cond_broadcast(self.cond.get()) }
}
/// Alias for `Condvar::signal` to match `std::sync::Condvar`.
#[inline]
pub fn notify_one(&self) {
self.signal()
}
/// Alias for `Condvar::broadcast` to match `std::sync::Condvar`.
#[inline]
pub fn notify_all(&self) {
self.broadcast()
}
/// Unlock the mutex until the `Condvar` has been signaled, and re-acquire the mutex afterward.
#[inline]
pub fn wait<T>(&self, guard: &MutexGuard<T>) {
unsafe { rt_cond_wait(self.cond.get(), guard.ptr()) }
}
/// Repeatedly `wait` on the `Condvar` while `condition` returns `true`.
pub fn wait_while<T, F>(&self, guard: &MutexGuard<T>, condition: F)
where
F: Fn(&T) -> bool,
{
while condition(guard) {
self.wait(guard);
}
}
/// `wait_while` but allow mutation of the guard and other state from the `condition` function.
pub fn wait_while_mut<T, F>(&self, guard: &mut MutexGuard<T>, mut condition: F)
where
F: FnMut(&mut T) -> bool,
{
while condition(guard) {
self.wait(guard);
}
}
#[inline]
pub fn timed_wait<'a, T>(
&self,
guard: MutexGuard<'a, T>,
ticks: Utick,
) -> Option<MutexGuard<'a, T>> {
if unsafe { rt_cond_timedwait(self.cond.get(), guard.ptr(), ticks) } {
Some(guard)
} else {
// The mutex was already unlocked inside of rt_cond_timedwait and only re-locked if
// successful. On failure, we don't return the guard, but we must also avoid dropping
// it, which would unlock the mutex again.
forget(guard);
None
}
}
}
#[macro_export]
macro_rules! condvar {
($name: ident) => {
static $name: $crate::sync::Condvar = {
let ptr = &raw const $name;
unsafe { $crate::sync::Condvar::init(ptr) }
};
};
}
#[cfg(test)]
mod tests {
#[test]
fn signal() {
condvar!(CONDVAR);
CONDVAR.signal();
}
}