Skip to main content

abi_definitions/task/
mutex.rs

1use core::{
2    mem::{align_of, size_of},
3    ptr::drop_in_place,
4};
5use synchronization::blocking_mutex::{Mutex, raw::CriticalSectionRawMutex};
6use task::TaskIdentifier;
7
8use crate::XilaTaskIdentifier;
9
10#[derive(Debug, Clone, Copy, Default)]
11struct MutexState {
12    task: Option<TaskIdentifier>,
13    lock_count: u32, // For recursive mutexes
14}
15
16pub struct RawMutex {
17    mutex: Mutex<CriticalSectionRawMutex, MutexState>,
18    recursive: bool,
19}
20
21impl RawMutex {
22    pub fn new(recursive: bool) -> Self {
23        Self {
24            mutex: Mutex::new(MutexState::default()),
25            recursive,
26        }
27    }
28
29    pub fn is_valid_pointer(pointer: *const RawMutex) -> bool {
30        !pointer.is_null() && (pointer as usize).is_multiple_of(align_of::<Self>())
31    }
32
33    /// Transforms a pointer to a reference.
34    ///
35    /// # Safety
36    ///
37    /// This function is unsafe because it dereferences a raw pointer.
38    /// The caller must ensure the pointer is valid and points to properly initialized memory.
39    pub unsafe fn from_pointer<'a>(pointer: *const RawMutex) -> Option<&'a Self> {
40        unsafe {
41            if !Self::is_valid_pointer(pointer) {
42                return None;
43            }
44            Some(&*pointer)
45        }
46    }
47
48    /// Transforms a mutable pointer to a mutable reference.
49    ///
50    /// # Safety
51    ///
52    /// This function is unsafe because it dereferences a raw pointer.
53    /// The caller must ensure the pointer is valid and points to properly initialized memory.
54    pub unsafe fn from_mutable_pointer<'a>(pointer: *mut RawMutex) -> Option<&'a mut Self> {
55        unsafe {
56            if !Self::is_valid_pointer(pointer) {
57                return None;
58            }
59            Some(&mut *pointer)
60        }
61    }
62
63    pub fn lock(&self, task: TaskIdentifier) -> bool {
64        unsafe {
65            self.mutex.lock_mut(|state| {
66                if let Some(owner) = state.task {
67                    if owner == task && self.recursive {
68                        // Recursive lock
69                        state.lock_count += 1;
70                        return true;
71                    }
72                    // Mutex is already locked by another task
73                    return false;
74                }
75
76                // Lock is available
77                state.task = Some(task);
78                state.lock_count = 1;
79                true
80            })
81        }
82    }
83
84    pub fn unlock(&self, task: TaskIdentifier) -> bool {
85        unsafe {
86            self.mutex.lock_mut(|state| {
87                // Check if current task owns the mutex
88                if let Some(owner) = state.task
89                    && owner == task
90                {
91                    if self.recursive && state.lock_count > 1 {
92                        // Decrement lock count for recursive mutex
93                        state.lock_count -= 1;
94                    } else {
95                        // Unlock the mutex
96                        state.task = None;
97                        state.lock_count = 0;
98                    }
99                    return true; // Successfully unlocked
100                }
101                false // Not owned by current task or not locked
102            })
103        }
104    }
105}
106
107#[unsafe(no_mangle)]
108pub static RAW_MUTEX_SIZE: usize = size_of::<RawMutex>();
109
110/// This function is used to initialize a mutex.
111///
112/// # Safety
113///
114/// This function is unsafe because it dereferences raw pointers.
115///
116/// # Errors
117///
118/// This function may return an error if the mutex is not initialized.
119#[unsafe(no_mangle)]
120pub unsafe extern "C" fn xila_initialize_mutex(mutex: *mut RawMutex) -> bool {
121    unsafe {
122        if mutex.is_null() {
123            return false;
124        }
125
126        if !(mutex as usize).is_multiple_of(align_of::<RawMutex>()) {
127            return false;
128        }
129
130        mutex.write(RawMutex::new(false));
131
132        true
133    }
134}
135
136/// Initialize a recursive mutex.
137///
138/// # Safety
139///
140/// The caller must ensure:
141/// - `mutex` points to valid, uninitialized memory
142/// - The memory is properly aligned for `Raw_mutex_type`
143/// - The memory will remain valid for the lifetime of the mutex
144#[unsafe(no_mangle)]
145pub unsafe extern "C" fn xila_initialize_recursive_mutex(mutex: *mut RawMutex) -> bool {
146    unsafe {
147        if mutex.is_null() {
148            return false;
149        }
150
151        if !(mutex as usize).is_multiple_of(align_of::<RawMutex>()) {
152            return false;
153        }
154
155        mutex.write(RawMutex::new(true));
156
157        true
158    }
159}
160
161/// Lock a mutex (blocking).
162///
163/// # Safety
164///
165/// The caller must ensure:
166/// - `mutex` points to a valid, initialized `Raw_mutex_type`
167/// - The mutex remains valid for the duration of the call
168#[unsafe(no_mangle)]
169pub unsafe extern "C" fn xila_lock_mutex(mutex: *mut RawMutex, task: XilaTaskIdentifier) -> bool {
170    unsafe {
171        let mutex = match RawMutex::from_mutable_pointer(mutex) {
172            Some(mutex) => mutex,
173            None => return false,
174        };
175
176        mutex.lock(task.into())
177    }
178}
179
180/// Unlock a mutex (blocking).
181///
182/// # Safety
183///
184/// The caller must ensure:
185/// - `mutex` points to a valid, initialized `Raw_mutex_type`
186/// - The mutex remains valid for the duration of the call
187/// - The current task owns the mutex
188#[unsafe(no_mangle)]
189pub unsafe extern "C" fn xila_unlock_mutex(mutex: *mut RawMutex, task: XilaTaskIdentifier) -> bool {
190    unsafe {
191        let mutex = match RawMutex::from_mutable_pointer(mutex) {
192            Some(mutex) => mutex,
193            None => return false,
194        };
195
196        mutex.unlock(task.into())
197    }
198}
199
200/// Destroy a mutex.
201///
202/// # Safety
203///
204/// The caller must ensure:
205/// - `mutex` points to a valid, initialized `Raw_mutex_type` allocated with Box
206/// - The mutex is not currently locked
207/// - No other threads are waiting on the mutex
208#[unsafe(no_mangle)]
209pub unsafe extern "C" fn xila_destroy_mutex(mutex: *mut RawMutex) -> bool {
210    unsafe {
211        let mutex = match RawMutex::from_mutable_pointer(mutex) {
212            Some(mutex) => mutex,
213            None => return false,
214        };
215
216        // Drop the mutex, which will release any resources it holds
217        drop_in_place(mutex);
218
219        true // Mutex is dropped here
220    }
221}