Skip to content

Commit 16d65d0

Browse files
committed
revise Hermit's mutex interface to support the behaviour of StaticMutex
rust-lang#77147 simplifies things by splitting this Mutex type into two types matching the two use cases: StaticMutex and MovableMutex. To support the behavior of StaticMutex, we move part of the mutex implementation into libstd.
1 parent c6bebc1 commit 16d65d0

File tree

1 file changed

+182
-8
lines changed

1 file changed

+182
-8
lines changed

library/std/src/sys/hermit/mutex.rs

+182-8
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,217 @@
1+
use crate::cell::UnsafeCell;
2+
use crate::collections::VecDeque;
13
use crate::ffi::c_void;
4+
use crate::ops::{Deref, DerefMut, Drop};
25
use crate::ptr;
6+
use crate::sync::atomic::{AtomicUsize, Ordering, spin_loop_hint};
37
use crate::sys::hermit::abi;
48

9+
/// This type provides a lock based on busy waiting to realize mutual exclusion
10+
///
11+
/// # Description
12+
///
13+
/// This structure behaves a lot like a common mutex. There are some differences:
14+
///
15+
/// - By using busy waiting, it can be used outside the runtime.
16+
/// - It is a so called ticket lock (https://en.wikipedia.org/wiki/Ticket_lock)
17+
/// and completly fair.
18+
#[cfg_attr(target_arch = "x86_64", repr(align(128)))]
19+
#[cfg_attr(not(target_arch = "x86_64"), repr(align(64)))]
20+
struct Spinlock<T: ?Sized> {
21+
queue: AtomicUsize,
22+
dequeue: AtomicUsize,
23+
data: UnsafeCell<T>,
24+
}
25+
26+
unsafe impl<T: ?Sized + Send> Sync for Spinlock<T> {}
27+
unsafe impl<T: ?Sized + Send> Send for Spinlock<T> {}
28+
29+
/// A guard to which the protected data can be accessed
30+
///
31+
/// When the guard falls out of scope it will release the lock.
32+
struct SpinlockGuard<'a, T: ?Sized + 'a> {
33+
dequeue: &'a AtomicUsize,
34+
data: &'a mut T,
35+
}
36+
37+
impl<T> Spinlock<T> {
38+
pub const fn new(user_data: T) -> Spinlock<T> {
39+
SpinlockGuard { dequeue: &self.dequeue, data: &mut *self.data.get() }
40+
}
41+
42+
#[inline]
43+
fn obtain_lock(&self) {
44+
let ticket = self.queue.fetch_add(1, Ordering::SeqCst) + 1;
45+
while self.dequeue.load(Ordering::SeqCst) != ticket {
46+
spin_loop_hint();
47+
}
48+
}
49+
50+
#[inline]
51+
pub unsafe fn lock(&self) -> SpinlockGuard<'_, T> {
52+
self.obtain_lock();
53+
SpinlockGuard {
54+
dequeue: &self.dequeue,
55+
data: &mut *self.data.get(),
56+
}
57+
}
58+
}
59+
60+
impl<T: ?Sized + Default> Default for Spinlock<T> {
61+
fn default() -> Spinlock<T> {
62+
Spinlock::new(Default::default())
63+
}
64+
}
65+
66+
impl<'a, T: ?Sized> Deref for SpinlockGuard<'a, T> {
67+
type Target = T;
68+
fn deref(&self) -> &T {
69+
&*self.data
70+
}
71+
}
72+
73+
impl<'a, T: ?Sized> DerefMut for SpinlockGuard<'a, T> {
74+
fn deref_mut(&mut self) -> &mut T {
75+
&mut *self.data
76+
}
77+
}
78+
79+
impl<'a, T: ?Sized> Drop for SpinlockGuard<'a, T> {
80+
/// The dropping of the SpinlockGuard will release the lock it was created from.
81+
fn drop(&mut self) {
82+
self.dequeue.fetch_add(1, Ordering::SeqCst);
83+
}
84+
}
85+
86+
/// Realize a priority queue for tasks
87+
struct PriorityQueue {
88+
queues: [Option<VecDeque<abi::Tid>>; abi::NO_PRIORITIES],
89+
prio_bitmap: u64,
90+
}
91+
92+
impl PriorityQueue {
93+
pub const fn new() -> PriorityQueue {
94+
PriorityQueue {
95+
queues: [
96+
None, None, None, None, None, None, None, None, None, None, None, None, None, None,
97+
None, None, None, None, None, None, None, None, None, None, None, None, None, None,
98+
None, None, None,
99+
],
100+
prio_bitmap: 0,
101+
}
102+
}
103+
104+
/// Add a task handle by its priority to the queue
105+
pub fn push(&mut self, prio: abi::Priority, id: abi::Tid) {
106+
let i: usize = prio.into().into();
107+
self.prio_bitmap |= (1 << i) as u64;
108+
if let Some(queue) = &mut self.queues[i] {
109+
queue.push_back(id);
110+
} else {
111+
let mut queue = VecDeque::new();
112+
queue.push_back(id);
113+
self.queues[i] = Some(queue);
114+
}
115+
}
116+
117+
fn pop_from_queue(&mut self, queue_index: usize) -> Option<abi::Tid> {
118+
if let Some(queue) = &mut self.queues[queue_index] {
119+
let id = queue.pop_front();
120+
121+
if queue.is_empty() {
122+
self.prio_bitmap &= !(1 << queue_index as u64);
123+
}
124+
125+
id
126+
} else {
127+
None
128+
}
129+
}
130+
131+
/// Pop the task handle with the highest priority from the queue
132+
pub fn pop(&mut self) -> Option<abi::Tid> {
133+
for i in 0..abi::NO_PRIORITIES {
134+
if self.prio_bitmap & (1 << i) != 0 {
135+
return self.pop_from_queue(i);
136+
}
137+
}
138+
139+
None
140+
}
141+
}
142+
143+
struct MutexInner {
144+
locked: bool,
145+
blocked_task: PriorityQueue,
146+
}
147+
148+
impl MutexInner {
149+
pub const fn new() -> MutexInner {
150+
MutexInner {
151+
locked: false,
152+
blocked_task: PriorityQueue::new(),
153+
}
154+
}
155+
}
156+
5157
pub struct Mutex {
6-
inner: *const c_void,
158+
inner: Spinlock<MutexInner>,
7159
}
8160

9161
unsafe impl Send for Mutex {}
10162
unsafe impl Sync for Mutex {}
11163

12164
impl Mutex {
13165
pub const fn new() -> Mutex {
14-
Mutex { inner: ptr::null() }
166+
Mutex {
167+
inner: Spinlock::new(MutexInner::new()),
168+
}
15169
}
16170

17171
#[inline]
18172
pub unsafe fn init(&mut self) {
19-
let _ = abi::sem_init(&mut self.inner as *mut *const c_void, 1);
173+
self.inner = Spinlock::new(MutexInner::new());
20174
}
21175

22176
#[inline]
23177
pub unsafe fn lock(&self) {
24-
let _ = abi::sem_timedwait(self.inner, 0);
178+
loop {
179+
let mut guard = self.inner.lock();
180+
if guard.locked == false {
181+
guard.locked = true;
182+
return;
183+
} else {
184+
let prio = abi::get_priority();
185+
let id = abi::getpid();
186+
187+
guard.blocked_task.push(prio, id);
188+
abi::block_current_task();
189+
drop(guard);
190+
abi::yield_now();
191+
}
192+
}
25193
}
26194

27195
#[inline]
28196
pub unsafe fn unlock(&self) {
29-
let _ = abi::sem_post(self.inner);
197+
let mut guard = self.inner.lock();
198+
guard.locked = false;
199+
if let Some(tid) = guard.blocked_task.pop() {
200+
abi::wakeup_task(tid);
201+
}
30202
}
31203

32204
#[inline]
33205
pub unsafe fn try_lock(&self) -> bool {
34-
let result = abi::sem_trywait(self.inner);
35-
result == 0
206+
let mut guard = self.inner.lock();
207+
if guard.locked == false {
208+
guard.locked = true;
209+
}
210+
guard.locked
36211
}
37212

38213
#[inline]
39214
pub unsafe fn destroy(&self) {
40-
let _ = abi::sem_destroy(self.inner);
41215
}
42216
}
43217

0 commit comments

Comments
 (0)