zephyr/sys/sync/
mutex.rs

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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
// Copyright (c) 2024 Linaro LTD
// SPDX-License-Identifier: Apache-2.0

//! Zephyr `k_mutex` wrapper.
//!
//! This module implements a thing wrapper around the `k_mutex` type in Zephyr.  It works with the
//! kernel [`object`] system, to allow the mutexes to be defined statically.
//!
//! [`object`]: crate::object

use core::fmt;
#[cfg(CONFIG_RUST_ALLOC)]
use core::mem;
use crate::{
    error::{Result, to_result_void},
    raw::{
        k_condvar,
        k_condvar_init,
        k_condvar_broadcast,
        k_condvar_signal,
        k_condvar_wait,
        k_mutex,
        k_mutex_init,
        k_mutex_lock,
        k_mutex_unlock,
    },
    time::Timeout,
};
use crate::object::{
    Fixed,
    StaticKernelObject,
    Wrapped,
};
use crate::sys::K_FOREVER;

/// A Zephyr `k_mutux` usable from safe Rust code.
///
/// This merely wraps a pointer to the kernel object.  It implements clone, send and sync as it is
/// safe to have multiple instances of these, as well as use them across multiple threads.
///
/// Note that these are Safe in the sense that memory safety is guaranteed.  Attempts to
/// recursively lock, or incorrect nesting can easily result in deadlock.
///
/// Safety: Typically, the Mutex type in Rust does not implement Clone, and must be shared between
/// threads using Arc.  However, these sys Mutexes are wrappers around static kernel objects, and
/// Drop doesn't make sense for them.  In addition, Arc requires alloc, and one possible place to
/// make use of the sys Mutex is to be able to do so in an environment without alloc.
///
/// This mutex type of only of limited use to application programs.  It can be used as a simple
/// binary semaphore, although it has strict semantics, requiring the release to be called by the
/// same thread that called lock.  It can be used to protect data that Rust itself is either not
/// managing, or is managing in an unsafe way.
///
/// For a Mutex type that is useful in a Rust type of manner, please see the regular [`sync::Mutex`]
/// type.
///
/// [`sync::Mutex`]: http://example.com/TODO
pub struct Mutex {
    /// The raw Zephyr mutex.
    item: Fixed<k_mutex>,
}

impl Mutex {
    /// Create a new Mutex in an unlocked state.
    ///
    /// Create a new dynamically allocated Mutex.  The Mutex can only be used from system threads.
    #[cfg(CONFIG_RUST_ALLOC)]
    pub fn new() -> Result<Mutex> {
        let item: Fixed<k_mutex> = Fixed::new(unsafe { mem::zeroed() });
        unsafe {
            to_result_void(k_mutex_init(item.get()))?;
        }
        Ok(Mutex { item })
    }

    /// Lock a Zephyr Mutex.
    ///
    /// Will wait for the lock, returning status, with `Ok(())` indicating the lock has been
    /// acquired, and an error indicating a timeout (Zephyr returns different errors depending on
    /// the reason).
    pub fn lock<T>(&self, timeout: T) -> Result<()>
        where T: Into<Timeout>,
    {
        let timeout: Timeout = timeout.into();
        to_result_void(unsafe { k_mutex_lock(self.item.get(), timeout.0) })
    }

    /// Unlock a Zephyr Mutex.
    ///
    /// The mutex must already be locked by the calling thread.  Mutexes may not be unlocked in
    /// ISRs.
    pub fn unlock(&self) -> Result<()> {
        to_result_void(unsafe { k_mutex_unlock(self.item.get()) })
    }
}


/// A static Zephyr `k_mutex`
///
/// This is intended to be used from within the `kobj_define!` macro.  It declares a static
/// `k_mutex` that will be properly registered with the Zephyr object system.  Call [`init_once`] to
/// get the [`Mutex`] that it represents.
///
/// [`init_once`]: StaticMutex::init_once
pub type StaticMutex = StaticKernelObject<k_mutex>;

unsafe impl Sync for Mutex {}
unsafe impl Send for Mutex {}

// Sync and Send are meaningful, as the underlying Zephyr API can use these values from any thread.
// Care must be taken to use these in a safe manner.
unsafe impl Sync for StaticMutex {}
unsafe impl Send for StaticMutex {}

impl Wrapped for StaticKernelObject<k_mutex> {
    type T = Mutex;

    /// Mutex initializers take no argument.
    type I = ();

    fn get_wrapped(&self, _arg: Self::I) -> Mutex {
        let ptr = self.value.get();
        unsafe {
            k_mutex_init(ptr);
        }
        Mutex { 
            item: Fixed::Static(ptr),
        }
    }
}

impl fmt::Debug for Mutex {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "sys::Mutex {:?}", self.item.get())
    }
}

/// A Condition Variable
///
/// Lightweight wrappers for Zephyr's `k_condvar`.
pub struct Condvar {
    /// The underlying `k_condvar`.
    item: Fixed<k_condvar>,
}

#[doc(hidden)]
pub type StaticCondvar = StaticKernelObject<k_condvar>;

unsafe impl Sync for StaticKernelObject<k_condvar> { }

unsafe impl Sync for Condvar {}
unsafe impl Send for Condvar {}

impl Condvar {
    /// Create a new Condvar.
    ///
    /// Create a new dynamically allocated Condvar.  The Condvar can only be used from system threads.
    #[cfg(CONFIG_RUST_ALLOC)]
    pub fn new() -> Result<Condvar> {
        let item: Fixed<k_condvar> = Fixed::new(unsafe { mem::zeroed() });
        unsafe {
            to_result_void(k_condvar_init(item.get()))?;
        }
        Ok(Condvar { item })
    }

    /// Wait for someone else using this mutex/condvar pair to notify.
    ///
    /// Note that this requires the lock to be held by use, but as this is a low-level binding to
    /// Zephyr's interfaces, this is not enforced.  See [`sync::Condvar`] for a safer and easier to
    /// use interface.
    ///
    /// [`sync::Condvar`]: http://www.example.com/TODO
    // /// [`sync::Condvar`]: crate::sync::Condvar
    pub fn wait(&self, lock: &Mutex) {
        unsafe { k_condvar_wait(self.item.get(), lock.item.get(), K_FOREVER); }
    }

    // TODO: timeout.

    /// Wake a single thread waiting on this condition variable.
    pub fn notify_one(&self) {
        unsafe { k_condvar_signal(self.item.get()); }
    }

    /// Wake all threads waiting on this condition variable.
    pub fn notify_all(&self) {
        unsafe { k_condvar_broadcast(self.item.get()); }
    }
}

impl fmt::Debug for Condvar {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "sys::Condvar {:?}", self.item.get())
    }
}

impl Wrapped for StaticCondvar {
    type T = Condvar;

    /// Condvar initializers take no argument.
    type I = ();

    fn get_wrapped(&self, _arg: Self::I) -> Condvar {
        let ptr = self.value.get();
        unsafe {
            k_condvar_init(ptr);
        }
        Condvar {
            item: Fixed::Static(ptr),
        }
    }
}