Skip to main content

ksync/
brwlock.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use core::marker::PhantomData;
6use core::pin::Pin;
7use core::ptr::addr_of_mut;
8use pin_init::{PinInit, pin_data, pin_init, pinned_drop};
9
10use crate::{
11    ExclusiveLockPolicy, LockPolicy, LockdepTracked, RawBrwLockPi, RawBrwLockPiReadPolicy,
12    RawBrwLockPiStorage, RawLock,
13};
14use lockdep::LockClass;
15
16type LockdepTrackedBrwLockReadPolicy = LockdepTracked<RawBrwLockPiStorage, RawBrwLockPiReadPolicy>;
17type LockdepTrackedBrwLockWritePolicy = <RawBrwLockPi as RawLock>::DefaultPolicy;
18
19/// A priority-inheriting reader-writer lock.
20#[repr(transparent)]
21#[pin_data]
22pub struct BrwLockPi<Class: LockClass> {
23    #[pin]
24    lock: RawBrwLockPi,
25    _marker: PhantomData<Class>,
26}
27
28// SAFETY: BrwLockPi is safe to share across threads because the underlying RawBrwLockPi is Sync.
29unsafe impl<Class: LockClass> Sync for BrwLockPi<Class> {}
30// SAFETY: BrwLockPi is safe to send across threads because the underlying RawBrwLockPi is Send.
31unsafe impl<Class: LockClass> Send for BrwLockPi<Class> {}
32
33impl<Class: LockClass> BrwLockPi<Class> {
34    /// Safe dynamic initialization inside pin context.
35    pub fn init() -> impl PinInit<Self, core::convert::Infallible> {
36        pin_init!(Self {
37            // SAFETY: `Class::ID` is either null or a valid, static `LockClassId`.
38            lock <- unsafe { <RawBrwLockPi as RawLock>::init(Class::ID) },
39            _marker: PhantomData,
40        })
41    }
42
43    /// Acquire the read lock and return a stack-pinned validation guard.
44    #[inline]
45    pub fn read_lock(
46        &self,
47    ) -> impl PinInit<BrwLockPiReadGuard<'_, Class>, core::convert::Infallible> {
48        BrwLockPiReadGuard::new(self)
49    }
50
51    /// Acquire the write lock and return a stack-pinned validation guard.
52    #[inline]
53    pub fn write_lock(
54        &self,
55    ) -> impl PinInit<BrwLockPiWriteGuard<'_, Class>, core::convert::Infallible> {
56        BrwLockPiWriteGuard::new(self)
57    }
58
59    /// Returns a reference to the underlying raw lock.
60    #[inline]
61    pub fn raw_lock(&self) -> &RawBrwLockPi {
62        &self.lock
63    }
64}
65
66/// A validation guard representing reader lock ownership and active list participation.
67#[repr(C)]
68#[pin_data(PinnedDrop)]
69pub struct BrwLockPiReadGuard<'a, Class: LockClass> {
70    lock: &'a BrwLockPi<Class>,
71    #[pin]
72    state: <LockdepTrackedBrwLockReadPolicy as LockPolicy<RawBrwLockPi>>::GuardState,
73    token: crate::LockToken<'a, Class>,
74}
75
76impl<'a, Class: LockClass> BrwLockPiReadGuard<'a, Class> {
77    /// Creates a new stack-pinned validation guard initialization block.
78    pub fn new(lock: &'a BrwLockPi<Class>) -> impl PinInit<Self, core::convert::Infallible> {
79        // SAFETY: The closure correctly initializes all fields of the allocated
80        // `BrwLockPiReadGuard` and satisfies all safety requirements of `pin_init_from_closure`.
81        unsafe {
82            pin_init::pin_init_from_closure(
83                move |this: *mut Self| -> Result<(), core::convert::Infallible> {
84                    let lock_addr = addr_of_mut!((*this).lock);
85                    core::ptr::write(lock_addr, lock);
86
87                    let state_addr = addr_of_mut!((*this).state);
88                    core::ptr::write(state_addr, Default::default());
89
90                    <LockdepTrackedBrwLockReadPolicy as LockPolicy<RawBrwLockPi>>::acquire(
91                        &lock.lock,
92                        &mut *state_addr,
93                        (),
94                    );
95
96                    let token_addr = addr_of_mut!((*this).token);
97                    core::ptr::write(token_addr, crate::LockToken::new());
98
99                    Ok(())
100                },
101            )
102        }
103    }
104
105    /// Returns a shared reference to the lock proof `LockToken`.
106    #[inline]
107    pub fn token(&self) -> &crate::LockToken<'a, Class> {
108        &self.token
109    }
110
111    /// Returns true if this guard holds the given `target` lock instance.
112    #[inline]
113    pub fn is_locked_by(&self, target: &BrwLockPi<Class>) -> bool {
114        core::ptr::eq(self.lock, target)
115    }
116
117    /// Temporarily releases the read lock before executing the given callable `f` and then
118    /// re-acquires the read lock.
119    #[inline]
120    pub fn call_unlocked<R, F: FnOnce() -> R>(self: Pin<&mut Self>, f: F) -> R {
121        // SAFETY: `state` is pinned on the stack and valid.
122        unsafe {
123            let me = self.get_unchecked_mut();
124            <LockdepTrackedBrwLockReadPolicy as LockPolicy<RawBrwLockPi>>::release(
125                &me.lock.lock,
126                &mut me.state,
127            );
128            let result = f();
129            <LockdepTrackedBrwLockReadPolicy as LockPolicy<RawBrwLockPi>>::reacquire(
130                &me.lock.lock,
131                &mut me.state,
132            );
133            result
134        }
135    }
136}
137
138#[pinned_drop]
139impl<'a, Class: LockClass> PinnedDrop for BrwLockPiReadGuard<'a, Class> {
140    fn drop(self: Pin<&mut Self>) {
141        // SAFETY: `get_unchecked_mut` is safe because we do not move the fields out of Pin.
142        // `release` is safe because the read lock was acquired when creating this guard,
143        // and we are releasing it with the same pinned state.
144        unsafe {
145            let me = self.get_unchecked_mut();
146            <LockdepTrackedBrwLockReadPolicy as LockPolicy<RawBrwLockPi>>::release(
147                &me.lock.lock,
148                &mut me.state,
149            );
150        }
151    }
152}
153
154/// A validation guard representing writer lock ownership and active list participation.
155#[repr(C)]
156pub struct BrwLockPiWriteGuard<
157    'a,
158    Class: LockClass,
159    P: ExclusiveLockPolicy<RawBrwLockPi> = LockdepTrackedBrwLockWritePolicy,
160> {
161    lock: &'a BrwLockPi<Class>,
162    state: P::GuardState,
163    token: crate::LockToken<'a, Class>,
164}
165
166impl<'a, Class: LockClass, P: ExclusiveLockPolicy<RawBrwLockPi>> BrwLockPiWriteGuard<'a, Class, P> {
167    /// Creates a new stack-pinned validation guard initialization block.
168    pub fn new(lock: &'a BrwLockPi<Class>) -> impl PinInit<Self, core::convert::Infallible>
169    where
170        P: ExclusiveLockPolicy<RawBrwLockPi, AcquireArgs = ()>,
171    {
172        // SAFETY: The closure correctly initializes all fields of the allocated
173        // `BrwLockPiWriteGuard` and satisfies all safety requirements of `pin_init_from_closure`.
174        unsafe {
175            pin_init::pin_init_from_closure(
176                move |this: *mut Self| -> Result<(), core::convert::Infallible> {
177                    let lock_addr = addr_of_mut!((*this).lock);
178                    core::ptr::write(lock_addr, lock);
179
180                    let state_addr = addr_of_mut!((*this).state);
181                    core::ptr::write(state_addr, Default::default());
182
183                    P::acquire(&lock.lock, &mut *state_addr, ());
184
185                    let token_addr = addr_of_mut!((*this).token);
186                    core::ptr::write(token_addr, crate::LockToken::new());
187
188                    Ok(())
189                },
190            )
191        }
192    }
193
194    /// Returns a shared reference to the lock proof `LockToken`.
195    #[inline]
196    pub fn token(&self) -> &crate::LockToken<'a, Class> {
197        &self.token
198    }
199
200    /// Returns a mutable reference to the lock proof `LockToken` inside this pinned projection.
201    #[inline]
202    pub fn token_mut(self: Pin<&mut Self>) -> &mut crate::LockToken<'a, Class> {
203        // SAFETY: We are accessing `token` mutably but `LockToken` is a ZST and does not require
204        // pinning invariants to be maintained.
205        let me = unsafe { self.get_unchecked_mut() };
206        &mut me.token
207    }
208
209    /// Returns true if this guard holds the given `target` lock instance.
210    #[inline]
211    pub fn is_locked_by(&self, target: &BrwLockPi<Class>) -> bool {
212        core::ptr::eq(self.lock, target)
213    }
214
215    /// Temporarily releases the write lock before executing the given callable `f` and then
216    /// re-acquires the write lock.
217    #[inline]
218    pub fn call_unlocked<R, F: FnOnce() -> R>(self: Pin<&mut Self>, f: F) -> R {
219        // SAFETY: `state` is pinned on the stack and valid.
220        unsafe {
221            let me = self.get_unchecked_mut();
222            P::release(&me.lock.lock, &mut me.state);
223            let result = f();
224            P::reacquire(&me.lock.lock, &mut me.state);
225            result
226        }
227    }
228}
229
230impl<'a, Class: LockClass, P: ExclusiveLockPolicy<RawBrwLockPi>> Drop
231    for BrwLockPiWriteGuard<'a, Class, P>
232{
233    #[inline]
234    fn drop(&mut self) {
235        // SAFETY: A guard is only created pinned, by `new`, and `self` is not moved after it is
236        // dropped. `release` is safe because the write lock was acquired when creating this guard,
237        // and we are releasing it with the same pinned state.
238        unsafe {
239            P::release(&self.lock.lock, &mut self.state);
240        }
241    }
242}
243
244#[cfg(test)]
245mod tests {
246    extern crate std;
247
248    use super::{LockdepTrackedBrwLockReadPolicy, LockdepTrackedBrwLockWritePolicy};
249    use crate::{BrwLockPi, LockPolicy, RawBrwLockPi, RawLock, guarded};
250    use pin_init::{pin_init, stack_pin_init};
251    use std::string::String;
252    use std::vec::Vec;
253
254    #[guarded]
255    struct Database {
256        #[brwlock]
257        lock: BrwLockPi,
258
259        #[guarded_by(lock)]
260        data: Vec<String>,
261
262        #[guarded_by(lock)]
263        query_count: u64,
264    }
265
266    fn read_data(db: &Database) -> usize {
267        lock!(let guard = db.read_lock());
268        let len = guard.data().len();
269
270        let fields = guard.fields();
271        let _ = fields.data.len();
272        let _ = *fields.query_count;
273
274        len
275    }
276
277    fn append_data(db: &Database, value: String) {
278        lock!(let mut guard = db.write_lock());
279        let fields = guard.as_mut().fields_mut();
280        fields.data.push(value);
281        *fields.query_count += 1;
282    }
283
284    #[test]
285    fn test_brwlock_projections() {
286        stack_pin_init!(let db = pin_init!(Database {
287            lock <- BrwLockPi::init(),
288            data: Vec::new().into(),
289            query_count: 0.into(),
290        }));
291
292        assert_eq!(read_data(&db), 0);
293        append_data(&db, String::from("hello"));
294        assert_eq!(read_data(&db), 1);
295
296        lock!(let guard = db.read_lock());
297        assert_eq!(guard.data()[0], "hello");
298        assert_eq!(*guard.query_count(), 1);
299    }
300
301    #[pin_init::pin_data]
302    struct BrwLockTest {
303        #[pin]
304        lock: RawBrwLockPi,
305        state: std::sync::atomic::AtomicU32,
306        kill: std::sync::atomic::AtomicBool,
307    }
308
309    fn run_test(readers: usize, writers: usize) {
310        use std::sync::atomic::Ordering;
311
312        stack_pin_init!(let test = pin_init!(BrwLockTest {
313            // SAFETY: Null class ID is valid for an untracked test lock.
314            lock <- unsafe { <RawBrwLockPi as RawLock>::init(core::ptr::null()) },
315            state: std::sync::atomic::AtomicU32::new(0),
316            kill: std::sync::atomic::AtomicBool::new(false),
317        }));
318
319        std::thread::scope(|s| {
320            let mut threads = std::vec::Vec::new();
321
322            for _ in 0..readers {
323                threads.push(s.spawn(|| {
324                    let mut state =
325                        <LockdepTrackedBrwLockReadPolicy as LockPolicy<RawBrwLockPi>>::GuardState::default();
326                    while !test.kill.load(Ordering::Relaxed) {
327                        // SAFETY: lock is initialized and pinned.
328                        unsafe {
329                            <LockdepTrackedBrwLockReadPolicy as LockPolicy<RawBrwLockPi>>::acquire(
330                                &test.lock,
331                                &mut state,
332                                (),
333                            );
334                        }
335                        let _ = test.state.fetch_add(1, Ordering::Relaxed);
336                        std::thread::yield_now();
337                        let _ = test.state.fetch_sub(1, Ordering::Relaxed);
338                        // SAFETY: lock is held in read mode.
339                        unsafe {
340                            <LockdepTrackedBrwLockReadPolicy as LockPolicy<RawBrwLockPi>>::release(
341                                &test.lock,
342                                &mut state,
343                            );
344                        }
345                        std::thread::yield_now();
346                    }
347                }));
348            }
349
350            for _ in 0..writers {
351                threads.push(s.spawn(|| {
352                    let mut state = <LockdepTrackedBrwLockWritePolicy as LockPolicy<
353                        RawBrwLockPi,
354                    >>::GuardState::default();
355                    while !test.kill.load(Ordering::Relaxed) {
356                        // SAFETY: lock is initialized and pinned.
357                        unsafe {
358                            <LockdepTrackedBrwLockWritePolicy as LockPolicy<RawBrwLockPi>>::acquire(
359                                &test.lock,
360                                &mut state,
361                                (),
362                            );
363                        }
364                        let _ = test.state.fetch_add(0x10000, Ordering::Relaxed);
365                        std::thread::yield_now();
366                        let _ = test.state.fetch_sub(0x10000, Ordering::Relaxed);
367                        // SAFETY: lock is held in write mode.
368                        unsafe {
369                            <LockdepTrackedBrwLockWritePolicy as LockPolicy<RawBrwLockPi>>::release(
370                                &test.lock, &mut state,
371                            );
372                        }
373                        std::thread::yield_now();
374                    }
375                }));
376            }
377
378            let start = std::time::Instant::now();
379            while start.elapsed() < std::time::Duration::from_millis(300) {
380                let local_state = test.state.load(Ordering::Relaxed);
381                let num_readers = (local_state & 0xffff) as usize;
382                let num_writers = (local_state >> 16) as usize;
383
384                assert!(num_readers <= readers, "Too many readers: {}", num_readers);
385                assert!(num_writers <= 1, "Too many writers: {}", num_writers);
386                assert!(
387                    num_readers == 0 || num_writers == 0,
388                    "Both readers ({}) and writers ({}) active!",
389                    num_readers,
390                    num_writers
391                );
392
393                std::thread::yield_now();
394            }
395
396            test.kill.store(true, Ordering::SeqCst);
397        });
398    }
399
400    #[test]
401    fn test_parallel_readers() {
402        run_test(8, 0);
403    }
404
405    #[test]
406    fn test_single_writer() {
407        run_test(0, 4);
408    }
409
410    #[test]
411    fn test_readers_and_writers() {
412        run_test(4, 2);
413    }
414
415    #[allow(dead_code)]
416    struct CustomBrwLockClass;
417    impl lockdep::LockClass for CustomBrwLockClass {
418        const ID: *mut core::ffi::c_void = core::ptr::null_mut();
419    }
420
421    #[guarded]
422    struct CustomBrwLockStruct {
423        #[brwlock(CustomBrwLockClass)]
424        lock: BrwLockPi<CustomBrwLockClass>,
425        #[guarded_by(lock)]
426        value: u32,
427    }
428
429    #[test]
430    fn test_custom_brwlock_class() {
431        stack_pin_init!(let s = pin_init!(CustomBrwLockStruct {
432            lock <- BrwLockPi::init(),
433            value: 42.into(),
434        }));
435
436        lock!(let guard = s.read_lock());
437        assert_eq!(*guard.value(), 42);
438    }
439
440    #[guarded]
441    struct MultiLockBrwStruct {
442        #[brwlock]
443        rw: BrwLockPi,
444        #[mutex]
445        mu: crate::KMutex,
446        #[guarded_by(rw)]
447        only_rw: u32,
448        #[guarded_by(rw, mu)]
449        shared: u32,
450        #[pin]
451        #[guarded_by(rw, mu)]
452        pinned_shared: u32,
453        #[guarded_by(rw, mu)]
454        once_shared: crate::KOnceCell<u32>,
455    }
456
457    #[test]
458    fn test_multi_lock_brwlock_and_mutex() {
459        stack_pin_init!(let s = pin_init!(MultiLockBrwStruct {
460            rw <- BrwLockPi::init(),
461            mu <- crate::KMutex::init(),
462            only_rw: 1.into(),
463            shared: 10.into(),
464            pinned_shared: 100.into(),
465            once_shared: crate::KOnceCell::new(),
466        }));
467
468        {
469            lock!(let rg = s.read_rw());
470            assert_eq!(*rg.only_rw(), 1);
471            assert_eq!(*rg.shared(), 10);
472            assert_eq!(*rg.pinned_shared(), 100);
473            assert_eq!(rg.once_shared(), None);
474            assert_eq!(*rg.fields().shared, 10);
475            assert_eq!(*rg.as_token_guard().shared(), 10);
476        }
477        {
478            lock!(let mg = s.lock_mu());
479            assert_eq!(*mg.shared(), 10);
480            assert_eq!(*mg.pinned_shared(), 100);
481            assert_eq!(mg.once_shared(), None);
482        }
483        {
484            lock!(let mut wg = s.write_rw());
485            lock!(let mut mg = s.lock_mu());
486            *wg.as_mut().shared_mut(&mut mg) = 25;
487            assert_eq!(*wg.shared(), 25);
488            *mg.as_mut().shared_mut(&mut wg) = 50;
489            assert_eq!(*mg.shared(), 50);
490
491            *wg.as_mut().pinned_shared_mut(&mut mg) = 200;
492            assert_eq!(*mg.pinned_shared(), 200);
493
494            let _ = wg.as_mut().once_shared_cell(&mut mg).get_or_init(|| 300);
495            assert_eq!(wg.once_shared(), Some(&300));
496
497            let wf_mut = wg.as_mut().fields_mut();
498            *wf_mut.only_rw = 2;
499            assert_eq!(*wf_mut.shared, 50);
500            assert_eq!(*wf_mut.pinned_shared, 200);
501            assert_eq!(wf_mut.once_shared, Some(&300));
502
503            // Test `BrwLockPi` `WriteTokenGuard` as both receiver and argument.
504            let mut wtg = wg.as_mut().as_token_guard_mut();
505            let mut mtg = mg.as_mut().as_token_guard_mut();
506            *wtg.reborrow().shared_mut(&mut mtg) = 75;
507            assert_eq!(*wtg.as_token_guard().shared(), 75);
508            *mtg.reborrow().shared_mut(&mut wtg) = 90;
509            assert_eq!(*wtg.shared(), 90);
510        }
511    }
512}