relibc/sync/
pthread_mutex.rs1use core::{
2 cell::Cell,
3 sync::atomic::{AtomicU32 as AtomicUint, Ordering},
4};
5
6use crate::{
7 error::Errno,
8 header::{errno::*, pthread::*, time::timespec},
9};
10
11use crate::platform::{Pal, Sys, types::c_int};
12
13use super::FutexWaitResult;
14
15pub struct RlctMutex {
16 inner: AtomicUint,
18 recursive_count: AtomicUint,
19
20 ty: Ty,
21 robust: bool,
22}
23
24const STATE_UNLOCKED: u32 = 0;
25const WAITING_BIT: u32 = 1 << 31;
26const INDEX_MASK: u32 = !WAITING_BIT;
27
28const RECURSIVE_COUNT_MAX_INCLUSIVE: u32 = u32::MAX;
30const SPIN_COUNT: usize = 0;
33
34impl RlctMutex {
35 pub(crate) fn new(attr: &RlctMutexAttr) -> Result<Self, Errno> {
36 let RlctMutexAttr {
37 prioceiling,
38 protocol,
39 pshared: _,
40 robust,
41 ty,
42 } = *attr;
43
44 Ok(Self {
45 inner: AtomicUint::new(STATE_UNLOCKED),
46 recursive_count: AtomicUint::new(0),
47 robust: match robust {
48 PTHREAD_MUTEX_STALLED => false,
49 PTHREAD_MUTEX_ROBUST => true,
50
51 _ => return Err(Errno(EINVAL)),
52 },
53 ty: match ty {
54 PTHREAD_MUTEX_DEFAULT => Ty::Def,
55 PTHREAD_MUTEX_ERRORCHECK => Ty::Errck,
56 PTHREAD_MUTEX_RECURSIVE => Ty::Recursive,
57 PTHREAD_MUTEX_NORMAL => Ty::Normal,
58
59 _ => return Err(Errno(EINVAL)),
60 },
61 })
62 }
63 pub fn prioceiling(&self) -> Result<c_int, Errno> {
64 todo_skip!(0, "pthread_getprioceiling: not implemented");
65 Ok(0)
66 }
67 pub fn replace_prioceiling(&self, _: c_int) -> Result<c_int, Errno> {
68 todo_skip!(0, "pthread_setprioceiling: not implemented");
69 Ok(0)
70 }
71 pub fn make_consistent(&self) -> Result<(), Errno> {
72 todo_skip!(0, "pthread robust mutexes: not implemented");
73 Ok(())
74 }
75 fn lock_inner(&self, deadline: Option<×pec>) -> Result<(), Errno> {
76 let this_thread = os_tid_invalid_after_fork();
77
78 loop {
81 let result = self.inner.compare_exchange_weak(
82 STATE_UNLOCKED,
83 this_thread,
84 Ordering::Acquire,
85 Ordering::Relaxed,
86 );
87
88 match result {
89 Ok(_) => {
91 if self.ty == Ty::Recursive {
92 self.increment_recursive_count()?;
93 }
94 return Ok(());
95 }
96 Err(thread) if thread & INDEX_MASK == this_thread && self.ty == Ty::Recursive => {
98 self.increment_recursive_count()?;
99 return Ok(());
100 }
101 Err(thread) if thread & INDEX_MASK == this_thread && self.ty == Ty::Errck => {
103 return Err(Errno(EAGAIN));
104 }
105 Err(thread) if thread & INDEX_MASK == 0 => {
107 continue;
108 }
109 Err(thread) => {
111 if crate::sync::futex_wait(&self.inner, thread, deadline)
129 == FutexWaitResult::TimedOut
130 {
131 return Err(Errno(ETIMEDOUT));
132 }
133 }
134 }
135 }
136 }
137 pub fn lock(&self) -> Result<(), Errno> {
138 self.lock_inner(None)
139 }
140 pub fn lock_with_timeout(&self, deadline: ×pec) -> Result<(), Errno> {
141 self.lock_inner(Some(deadline))
142 }
143 fn increment_recursive_count(&self) -> Result<(), Errno> {
144 let prev_recursive_count = self.recursive_count.load(Ordering::Relaxed);
151
152 if prev_recursive_count == RECURSIVE_COUNT_MAX_INCLUSIVE {
153 return Err(Errno(EAGAIN));
154 }
155
156 self.recursive_count
157 .store(prev_recursive_count + 1, Ordering::Relaxed);
158
159 Ok(())
160 }
161 pub fn try_lock(&self) -> Result<(), Errno> {
162 let this_thread = os_tid_invalid_after_fork();
163
164 let result = self.inner.compare_exchange(
166 STATE_UNLOCKED,
167 this_thread,
168 Ordering::Acquire,
169 Ordering::Relaxed,
170 );
171
172 if self.ty == Ty::Recursive {
173 match result {
174 Err(index) if index & INDEX_MASK != this_thread => return Err(Errno(EBUSY)),
175 _ => (),
176 }
177
178 self.increment_recursive_count()?;
179
180 return Ok(());
181 }
182
183 match result {
184 Ok(_) => Ok(()),
185 Err(index) if index & INDEX_MASK == this_thread && self.ty == Ty::Errck => {
186 Err(Errno(EDEADLK))
187 }
188 Err(_) => Err(Errno(EBUSY)),
189 }
190 }
191 pub fn unlock(&self) -> Result<(), Errno> {
193 if self.robust || matches!(self.ty, Ty::Recursive | Ty::Errck) {
194 if self.inner.load(Ordering::Relaxed) & INDEX_MASK != os_tid_invalid_after_fork() {
195 return Err(Errno(EPERM));
196 }
197
198 core::sync::atomic::fence(Ordering::Acquire);
200 }
201
202 if self.ty == Ty::Recursive {
203 let next = self.recursive_count.load(Ordering::Relaxed) - 1;
204 self.recursive_count.store(next, Ordering::Relaxed);
205
206 if next > 0 {
207 return Ok(());
208 }
209 }
210
211 self.inner.store(STATE_UNLOCKED, Ordering::Release);
212 crate::sync::futex_wake(&self.inner, i32::MAX);
213 Ok(())
220 }
221}
222
223#[repr(u8)]
224#[derive(PartialEq)]
225enum Ty {
226 Normal,
230 Def,
231
232 Errck,
233 Recursive,
234}
235
236#[thread_local]
238static CACHED_OS_TID_INVALID_AFTER_FORK: Cell<u32> = Cell::new(0);
239
240fn os_tid_invalid_after_fork() -> u32 {
242 let value = CACHED_OS_TID_INVALID_AFTER_FORK.get();
247
248 if value == 0 {
249 let tid = Sys::gettid();
250
251 assert_ne!(tid, -1, "failed to obtain current thread ID");
252
253 CACHED_OS_TID_INVALID_AFTER_FORK.set(tid as u32);
254
255 tid as u32
256 } else {
257 value
258 }
259}