| 1 | |
| 2 |
|
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 |
|
| 8 | use super::{lock::Backend, lock::Guard, LockClassKey};
|
| 9 | use crate::{
|
| 10 | ffi::{c_int, c_long},
|
| 11 | str::{CStr, CStrExt as _},
|
| 12 | task::{
|
| 13 | MAX_SCHEDULE_TIMEOUT, TASK_FREEZABLE, TASK_INTERRUPTIBLE, TASK_NORMAL, TASK_UNINTERRUPTIBLE,
|
| 14 | },
|
| 15 | time::Jiffies,
|
| 16 | types::Opaque,
|
| 17 | };
|
| 18 | use core::{marker::PhantomPinned, pin::Pin, ptr};
|
| 19 | use pin_init::{pin_data, pin_init, PinInit};
|
| 20 |
|
| 21 | |
| 22 | #[macro_export]
|
| 23 | macro_rules! new_condvar {
|
| 24 | ($($name:literal)?) => {
|
| 25 | $crate::sync::CondVar::new($crate::optional_name!($($name)?), $crate::static_lock_class!())
|
| 26 | };
|
| 27 | }
|
| 28 | pub use new_condvar;
|
| 29 |
|
| 30 | /// A conditional variable.
|
| 31 | ///
|
| 32 | /// Exposes the kernel's [`struct wait_queue_head`] as a condition variable. It allows the caller to
|
| 33 | /// atomically release the given lock and go to sleep. It reacquires the lock when it wakes up. And
|
| 34 | /// it wakes up when notified by another thread (via [`CondVar::notify_one`] or
|
| 35 | /// [`CondVar::notify_all`]) or because the thread received a signal. It may also wake up
|
| 36 | /// spuriously.
|
| 37 | ///
|
| 38 | /// Instances of [`CondVar`] need a lock class and to be pinned. The recommended way to create such
|
| 39 | /// instances is with the [`pin_init`](pin_init::pin_init!) and [`new_condvar`] macros.
|
| 40 | ///
|
| 41 | /// # Examples
|
| 42 | ///
|
| 43 | /// The following is an example of using a condvar with a mutex:
|
| 44 | ///
|
| 45 | /// ```
|
| 46 | /// use kernel::sync::{new_condvar, new_mutex, CondVar, Mutex};
|
| 47 | ///
|
| 48 | /// #[pin_data]
|
| 49 | /// pub struct Example {
|
| 50 | /// #[pin]
|
| 51 | /// value: Mutex<u32>,
|
| 52 | ///
|
| 53 | /// #[pin]
|
| 54 | /// value_changed: CondVar,
|
| 55 | /// }
|
| 56 | ///
|
| 57 | /// /// Waits for `e.value` to become `v`.
|
| 58 | /// fn wait_for_value(e: &Example, v: u32) {
|
| 59 | /// let mut guard = e.value.lock();
|
| 60 | /// while *guard != v {
|
| 61 | /// e.value_changed.wait(&mut guard);
|
| 62 | /// }
|
| 63 | /// }
|
| 64 | ///
|
| 65 | /// /// Increments `e.value` and notifies all potential waiters.
|
| 66 | /// fn increment(e: &Example) {
|
| 67 | /// *e.value.lock() += 1;
|
| 68 | /// e.value_changed.notify_all();
|
| 69 | /// }
|
| 70 | ///
|
| 71 | /// /// Allocates a new boxed `Example`.
|
| 72 | /// fn new_example() -> Result<Pin<KBox<Example>>> {
|
| 73 | |
| 74 | |
| 75 | |
| 76 | |
| 77 | |
| 78 | |
| 79 | |
| 80 | |
| 81 | #[pin_data]
|
| 82 | pub struct CondVar {
|
| 83 | #[pin]
|
| 84 | pub(crate) wait_queue_head: Opaque<bindings::wait_queue_head>,
|
| 85 |
|
| 86 | /// A condvar needs to be pinned because it contains a [`struct list_head`] that is
|
| 87 | |
| 88 | |
| 89 | |
| 90 | #[pin]
|
| 91 | _pin: PhantomPinned,
|
| 92 | }
|
| 93 |
|
| 94 | |
| 95 | unsafe impl Send for CondVar {}
|
| 96 |
|
| 97 | |
| 98 | |
| 99 | unsafe impl Sync for CondVar {}
|
| 100 |
|
| 101 | impl CondVar {
|
| 102 | |
| 103 | pub fn new(name: &'static CStr, key: Pin<&'static LockClassKey>) -> impl PinInit<Self> {
|
| 104 | pin_init!(Self {
|
| 105 | _pin: PhantomPinned,
|
| 106 | |
| 107 | |
| 108 | wait_queue_head <- Opaque::ffi_init(|slot| unsafe {
|
| 109 | bindings::__init_waitqueue_head(slot, name.as_char_ptr(), key.as_ptr())
|
| 110 | }),
|
| 111 | })
|
| 112 | }
|
| 113 |
|
| 114 | fn wait_internal<T: ?Sized, B: Backend>(
|
| 115 | &self,
|
| 116 | wait_state: c_int,
|
| 117 | guard: &mut Guard<'_, T, B>,
|
| 118 | timeout_in_jiffies: c_long,
|
| 119 | ) -> c_long {
|
| 120 | let wait = Opaque::<bindings::wait_queue_entry>::uninit();
|
| 121 |
|
| 122 | |
| 123 | unsafe { bindings::init_wait(wait.get()) };
|
| 124 |
|
| 125 | |
| 126 | unsafe {
|
| 127 | bindings::prepare_to_wait_exclusive(self.wait_queue_head.get(), wait.get(), wait_state)
|
| 128 | };
|
| 129 |
|
| 130 | |
| 131 | let ret = guard.do_unlocked(|| unsafe { bindings::schedule_timeout(timeout_in_jiffies) });
|
| 132 |
|
| 133 | |
| 134 | unsafe { bindings::finish_wait(self.wait_queue_head.get(), wait.get()) };
|
| 135 |
|
| 136 | ret
|
| 137 | }
|
| 138 |
|
| 139 | |
| 140 | |
| 141 | |
| 142 | |
| 143 | |
| 144 | |
| 145 | pub fn wait<T: ?Sized, B: Backend>(&self, guard: &mut Guard<'_, T, B>) {
|
| 146 | self.wait_internal(TASK_UNINTERRUPTIBLE, guard, MAX_SCHEDULE_TIMEOUT);
|
| 147 | }
|
| 148 |
|
| 149 | |
| 150 | |
| 151 | |
| 152 | |
| 153 | |
| 154 | |
| 155 | #[must_use = "wait_interruptible returns if a signal is pending, so the caller must check the return value"]
|
| 156 | pub fn wait_interruptible<T: ?Sized, B: Backend>(&self, guard: &mut Guard<'_, T, B>) -> bool {
|
| 157 | self.wait_internal(TASK_INTERRUPTIBLE, guard, MAX_SCHEDULE_TIMEOUT);
|
| 158 | crate::current!().signal_pending()
|
| 159 | }
|
| 160 |
|
| 161 | |
| 162 | |
| 163 | |
| 164 | |
| 165 | |
| 166 | |
| 167 | #[must_use = "wait_interruptible_freezable returns if a signal is pending, so the caller must check the return value"]
|
| 168 | pub fn wait_interruptible_freezable<T: ?Sized, B: Backend>(
|
| 169 | &self,
|
| 170 | guard: &mut Guard<'_, T, B>,
|
| 171 | ) -> bool {
|
| 172 | self.wait_internal(
|
| 173 | TASK_INTERRUPTIBLE | TASK_FREEZABLE,
|
| 174 | guard,
|
| 175 | MAX_SCHEDULE_TIMEOUT,
|
| 176 | );
|
| 177 | crate::current!().signal_pending()
|
| 178 | }
|
| 179 |
|
| 180 | |
| 181 | |
| 182 | |
| 183 | |
| 184 | |
| 185 | #[must_use = "wait_interruptible_timeout returns if a signal is pending, so the caller must check the return value"]
|
| 186 | pub fn wait_interruptible_timeout<T: ?Sized, B: Backend>(
|
| 187 | &self,
|
| 188 | guard: &mut Guard<'_, T, B>,
|
| 189 | jiffies: Jiffies,
|
| 190 | ) -> CondVarTimeoutResult {
|
| 191 | let jiffies = jiffies.try_into().unwrap_or(MAX_SCHEDULE_TIMEOUT);
|
| 192 | let res = self.wait_internal(TASK_INTERRUPTIBLE, guard, jiffies);
|
| 193 |
|
| 194 | match (res as Jiffies, crate::current!().signal_pending()) {
|
| 195 | (jiffies, true) => CondVarTimeoutResult::Signal { jiffies },
|
| 196 | (0, false) => CondVarTimeoutResult::Timeout,
|
| 197 | (jiffies, false) => CondVarTimeoutResult::Woken { jiffies },
|
| 198 | }
|
| 199 | }
|
| 200 |
|
| 201 | |
| 202 | fn notify(&self, count: c_int) {
|
| 203 | |
| 204 | unsafe {
|
| 205 | bindings::__wake_up(
|
| 206 | self.wait_queue_head.get(),
|
| 207 | TASK_NORMAL,
|
| 208 | count,
|
| 209 | ptr::null_mut(),
|
| 210 | )
|
| 211 | };
|
| 212 | }
|
| 213 |
|
| 214 | |
| 215 | |
| 216 | |
| 217 | |
| 218 | |
| 219 | #[inline]
|
| 220 | pub fn notify_sync(&self) {
|
| 221 | |
| 222 | unsafe { bindings::__wake_up_sync(self.wait_queue_head.get(), TASK_NORMAL) };
|
| 223 | }
|
| 224 |
|
| 225 | |
| 226 | |
| 227 | |
| 228 | |
| 229 | #[inline]
|
| 230 | pub fn notify_one(&self) {
|
| 231 | self.notify(1);
|
| 232 | }
|
| 233 |
|
| 234 | |
| 235 | |
| 236 | |
| 237 | |
| 238 | #[inline]
|
| 239 | pub fn notify_all(&self) {
|
| 240 | self.notify(0);
|
| 241 | }
|
| 242 | }
|
| 243 |
|
| 244 | |
| 245 | pub enum CondVarTimeoutResult {
|
| 246 | |
| 247 | Timeout,
|
| 248 | |
| 249 | Woken {
|
| 250 | |
| 251 | jiffies: Jiffies,
|
| 252 | },
|
| 253 | |
| 254 | Signal {
|
| 255 | |
| 256 | jiffies: Jiffies,
|
| 257 | },
|
| 258 | }
|