summaryrefslogtreecommitdiff
path: root/rust/kernel/sync/guard.rs
blob: 757d85eac7afe55d16fca8cbbb7bbdb16c8eb2d5 (plain) (blame)
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
// SPDX-License-Identifier: GPL-2.0

//! A generic lock guard and trait.
//!
//! This module contains a lock guard that can be used with any locking primitive that implements
//! the ([`Lock`]) trait. It also contains the definition of the trait, which can be leveraged by
//! other constructs to work on generic locking primitives.

use super::{LockClassKey, NeedsLockClass};
use crate::{str::CStr, Bool, False, True};
use core::pin::Pin;

/// Allows mutual exclusion primitives that implement the [`Lock`] trait to automatically unlock
/// when a guard goes out of scope. It also provides a safe and convenient way to access the data
/// protected by the lock.
#[must_use = "the lock unlocks immediately when the guard is unused"]
pub struct Guard<'a, L: Lock<I> + ?Sized, I: LockInfo = WriteLock> {
    pub(crate) lock: &'a L,
    pub(crate) context: L::GuardContext,
}

// SAFETY: `Guard` is sync when the data protected by the lock is also sync. This is more
// conservative than the default compiler implementation; more details can be found on
// <https://github.com/rust-lang/rust/issues/41622> -- it refers to `MutexGuard` from the standard
// library.
unsafe impl<L, I> Sync for Guard<'_, L, I>
where
    L: Lock<I> + ?Sized,
    L::Inner: Sync,
    I: LockInfo,
{
}

impl<L: Lock<I> + ?Sized, I: LockInfo> core::ops::Deref for Guard<'_, L, I> {
    type Target = L::Inner;

    fn deref(&self) -> &Self::Target {
        // SAFETY: The caller owns the lock, so it is safe to deref the protected data.
        unsafe { &*self.lock.locked_data().get() }
    }
}

impl<L: Lock<I> + ?Sized, I: LockInfo<Writable = True>> core::ops::DerefMut for Guard<'_, L, I> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        // SAFETY: The caller owns the lock, so it is safe to deref the protected data.
        unsafe { &mut *self.lock.locked_data().get() }
    }
}

impl<L: Lock<I> + ?Sized, I: LockInfo> Drop for Guard<'_, L, I> {
    fn drop(&mut self) {
        // SAFETY: The caller owns the lock, so it is safe to unlock it.
        unsafe { self.lock.unlock(&mut self.context) };
    }
}

impl<'a, L: Lock<I> + ?Sized, I: LockInfo> Guard<'a, L, I> {
    /// Constructs a new immutable lock guard.
    ///
    /// # Safety
    ///
    /// The caller must ensure that it owns the lock.
    pub(crate) unsafe fn new(lock: &'a L, context: L::GuardContext) -> Self {
        Self { lock, context }
    }
}

/// Specifies properties of a lock.
pub trait LockInfo {
    /// Determines if the data protected by a lock is writable.
    type Writable: Bool;
}

/// A marker for locks that only allow reading.
pub struct ReadLock;
impl LockInfo for ReadLock {
    type Writable = False;
}

/// A marker for locks that allow reading and writing.
pub struct WriteLock;
impl LockInfo for WriteLock {
    type Writable = True;
}

/// A generic mutual exclusion primitive.
///
/// [`Guard`] is written such that any mutual exclusion primitive that can implement this trait can
/// also benefit from having an automatic way to unlock itself.
///
/// # Safety
///
/// - Implementers of this trait with the [`WriteLock`] marker must ensure that only one thread/CPU
///   may access the protected data once the lock is held, that is, between calls to `lock_noguard`
///   and `unlock`.
/// - Implementers of all other markers must ensure that a mutable reference to the protected data
///   is not active in any thread/CPU because at least one shared reference is active between calls
///   to `lock_noguard` and `unlock`.
pub unsafe trait Lock<I: LockInfo = WriteLock> {
    /// The type of the data protected by the lock.
    type Inner: ?Sized;

    /// The type of context, if any, that needs to be stored in the guard.
    type GuardContext;

    /// Acquires the lock, making the caller its owner.
    #[must_use]
    fn lock_noguard(&self) -> Self::GuardContext;

    /// Reacquires the lock, making the caller its owner.
    ///
    /// The guard context before the last unlock is passed in.
    ///
    /// Locks that don't require this state on relock can simply use the default implementation
    /// that calls [`Lock::lock_noguard`].
    fn relock(&self, ctx: &mut Self::GuardContext) {
        *ctx = self.lock_noguard();
    }

    /// Releases the lock, giving up ownership of the lock.
    ///
    /// # Safety
    ///
    /// It must only be called by the current owner of the lock.
    unsafe fn unlock(&self, context: &mut Self::GuardContext);

    /// Returns the data protected by the lock.
    fn locked_data(&self) -> &core::cell::UnsafeCell<Self::Inner>;
}

/// A creator of instances of a mutual exclusion (lock) primitive.
pub trait LockFactory {
    /// The parametrised type of the mutual exclusion primitive that can be created by this factory.
    type LockedType<T>;

    /// Constructs a new instance of the mutual exclusion primitive.
    ///
    /// # Safety
    ///
    /// The caller must call [`LockIniter::init_lock`] before using the lock.
    unsafe fn new_lock<T>(data: T) -> Self::LockedType<T>;
}

/// A lock that can be initialised with a single lock class key.
pub trait LockIniter {
    /// Initialises the lock instance so that it can be safely used.
    fn init_lock(self: Pin<&mut Self>, name: &'static CStr, key: &'static LockClassKey);
}

impl<L: LockIniter> NeedsLockClass for L {
    fn init(
        self: Pin<&mut Self>,
        name: &'static CStr,
        key: &'static LockClassKey,
        _: &'static LockClassKey,
    ) {
        self.init_lock(name, key);
    }
}