| 1 | |
| 2 |
|
| 3 | |
| 4 | |
| 5 | |
| 6 |
|
| 7 | use crate::{
|
| 8 | bindings,
|
| 9 | error::to_result,
|
| 10 | prelude::*,
|
| 11 | sync::LockClassKey,
|
| 12 | types::{
|
| 13 | NotThreadSafe,
|
| 14 | Opaque, |
| 15 | },
|
| 16 | };
|
| 17 |
|
| 18 | use pin_init::pin_data;
|
| 19 |
|
| 20 | |
| 21 | #[doc(hidden)]
|
| 22 | #[macro_export]
|
| 23 | macro_rules! new_srcu {
|
| 24 | ($($name:literal)?) => {
|
| 25 | $crate::sync::Srcu::new($crate::optional_name!($($name)?), $crate::static_lock_class!())
|
| 26 | };
|
| 27 | }
|
| 28 | pub use new_srcu;
|
| 29 |
|
| 30 | /// Sleepable read-copy update primitive.
|
| 31 | ///
|
| 32 | /// SRCU readers may sleep while holding the read-side guard.
|
| 33 | ///
|
| 34 | /// The destructor waits for active readers and callbacks, so it may sleep.
|
| 35 | /// If a read-side guard has been leaked, dropping an [`Srcu`] may never return.
|
| 36 | ///
|
| 37 | /// # Invariants
|
| 38 | ///
|
| 39 | /// This represents a valid `struct srcu_struct` initialized by the C SRCU API
|
| 40 | /// and it remains pinned and valid until the pinned destructor runs.
|
| 41 | #[repr(transparent)]
|
| 42 | #[pin_data(PinnedDrop)]
|
| 43 | pub struct Srcu {
|
| 44 | #[pin]
|
| 45 | inner: Opaque<bindings::srcu_struct>,
|
| 46 | }
|
| 47 |
|
| 48 | impl Srcu {
|
| 49 | /// Creates a new SRCU instance.
|
| 50 | #[inline]
|
| 51 | pub fn new(name: &'static CStr, key: Pin<&'static LockClassKey>) -> impl PinInit<Self, Error> {
|
| 52 | try_pin_init!(Self {
|
| 53 | // INVARIANT: On success, the C initializer creates a valid `srcu_struct` and
|
| 54 | // it remains pinned until `PinnedDrop` runs.
|
| 55 | inner <- Opaque::try_ffi_init(|ptr: *mut bindings::srcu_struct| {
|
| 56 | // SAFETY: `ptr` points to valid uninitialised memory for a `srcu_struct`.
|
| 57 | to_result(unsafe {
|
| 58 | bindings::init_srcu_struct_with_key(ptr, name.as_char_ptr(), key.as_ptr())
|
| 59 | })
|
| 60 | }),
|
| 61 | })
|
| 62 | }
|
| 63 |
|
| 64 | /// Enters an SRCU read-side critical section.
|
| 65 | ///
|
| 66 | /// Leaking the returned [`Guard`] leaves the SRCU read-side critical
|
| 67 | /// section active and makes `drop` sleep forever.
|
| 68 | #[inline]
|
| 69 | pub fn read_lock(&self) -> Guard<'_> {
|
| 70 | // SAFETY: By the type invariants, `self` contains a valid `struct srcu_struct`.
|
| 71 | let idx = unsafe { bindings::srcu_read_lock(self.inner.get()) };
|
| 72 |
|
| 73 | // INVARIANT: `idx` was returned by `srcu_read_lock()` for this `Srcu`.
|
| 74 | Guard {
|
| 75 | srcu: self,
|
| 76 | idx,
|
| 77 | _not_send: NotThreadSafe,
|
| 78 | }
|
| 79 | }
|
| 80 |
|
| 81 | /// Waits until all pre-existing SRCU readers have completed.
|
| 82 | #[inline]
|
| 83 | pub fn synchronize(&self) {
|
| 84 | // SAFETY: By the type invariants, `self` contains a valid `struct srcu_struct`.
|
| 85 | unsafe { bindings::synchronize_srcu(self.inner.get()) };
|
| 86 | }
|
| 87 |
|
| 88 | /// Waits until all pre-existing SRCU readers have completed, expedited.
|
| 89 | ///
|
| 90 | /// This requests a lower-latency grace period than [`Srcu::synchronize`] typically
|
| 91 | /// at the cost of higher system-wide overhead. Prefer [`Srcu::synchronize`] by default
|
| 92 | /// and use this variant only when reducing reset or teardown latency is more important
|
| 93 | /// than the extra cost.
|
| 94 | #[inline]
|
| 95 | pub fn synchronize_expedited(&self) {
|
| 96 | // SAFETY: By the type invariants, `self` contains a valid `struct srcu_struct`.
|
| 97 | unsafe { bindings::synchronize_srcu_expedited(self.inner.get()) };
|
| 98 | }
|
| 99 | }
|
| 100 |
|
| 101 | #[pinned_drop]
|
| 102 | impl PinnedDrop for Srcu {
|
| 103 | fn drop(self: Pin<&mut Self>) {
|
| 104 | let ptr = self.inner.get();
|
| 105 |
|
| 106 | if crate::warn_on!(
|
| 107 | // SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct`
|
| 108 | // and `srcu_readers_active()` only checks the active reader count.
|
| 109 | unsafe { bindings::srcu_readers_active(ptr) }
|
| 110 | ) {
|
| 111 | // `cleanup_srcu_struct()` may return early if there are still active readers.
|
| 112 | // This should only happen if a guard was leaked with `mem::forget`, which is
|
| 113 | // "WRONG" code and may cause a UAF because Rust will free the `srcu_struct`
|
| 114 | // while it is still referenced from the C side (e.g. by `call_srcu()` callbacks).
|
| 115 | //
|
| 116 | // Another consequence of leaking guards is that `call_srcu()` callbacks will
|
| 117 | // never run because the grace period can never complete due to permanently
|
| 118 | // active readers (i.e. leaked guards).
|
| 119 | //
|
| 120 | // If this ever happens, that means the guard was leaked by mistake and the
|
| 121 | // caller must fix the bug. Sleeping here is intentional and less harmful
|
| 122 | // than risking a UAF.
|
| 123 | //
|
| 124 | // SAFETY: By the type invariants, `self` contains a valid and pinned
|
| 125 | // `struct srcu_struct`.
|
| 126 | unsafe { bindings::synchronize_srcu(ptr) };
|
| 127 | }
|
| 128 |
|
| 129 | // Ensure all SRCU callbacks have been finished before freeing.
|
| 130 | // SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct`.
|
| 131 | unsafe { bindings::srcu_barrier(ptr) };
|
| 132 |
|
| 133 | // SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct`.
|
| 134 | unsafe { bindings::cleanup_srcu_struct(ptr) };
|
| 135 | }
|
| 136 | }
|
| 137 |
|
| 138 | // SAFETY: `srcu_struct` may be shared and used across threads.
|
| 139 | unsafe impl Send for Srcu {}
|
| 140 | // SAFETY: `srcu_struct` may be shared and used concurrently.
|
| 141 | unsafe impl Sync for Srcu {}
|
| 142 |
|
| 143 | /// Guard for an active SRCU read-side critical section on a particular [`Srcu`].
|
| 144 | ///
|
| 145 | /// Leaking this guard with [`core::mem::forget`] leaves the SRCU read-side
|
| 146 | /// critical section active and makes dropping the associated [`Srcu`] sleep forever.
|
| 147 | ///
|
| 148 | /// # Invariants
|
| 149 | ///
|
| 150 | /// `idx` is the index returned by `srcu_read_lock()` for `srcu`.
|
| 151 | #[must_use = "if unused, the lock will be immediately unlocked"]
|
| 152 | pub struct Guard<'a> {
|
| 153 | srcu: &'a Srcu,
|
| 154 | idx: i32,
|
| 155 | _not_send: NotThreadSafe,
|
| 156 | }
|
| 157 |
|
| 158 | impl Guard<'_> {
|
| 159 | /// Explicitly releases the SRCU read-side critical section.
|
| 160 | #[inline]
|
| 161 | pub fn unlock(self) {}
|
| 162 | }
|
| 163 |
|
| 164 | impl Drop for Guard<'_> {
|
| 165 | #[inline]
|
| 166 | fn drop(&mut self) {
|
| 167 | // SAFETY: `Guard` is only constructible through `Srcu::read_lock()`,
|
| 168 | // which returns a valid index for the SRCU instance.
|
| 169 | unsafe { bindings::srcu_read_unlock(self.srcu.inner.get(), self.idx) };
|
| 170 | }
|
| 171 | }
|