Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

revise Hermit's mutex interface to support the behaviour of StaticMutex #77610

Merged
merged 13 commits into from
Oct 25, 2020
Merged
4 changes: 2 additions & 2 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1328,9 +1328,9 @@ dependencies = [

[[package]]
name = "hermit-abi"
version = "0.1.15"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9"
checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8"
dependencies = [
"compiler_builtins",
"libc",
Expand Down
2 changes: 1 addition & 1 deletion library/alloc/src/alloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ pub fn handle_alloc_error(layout: Layout) -> ! {
unsafe { oom_impl(layout) }
}

#[cfg(not(any(test, bootstrap)))]
#[cfg(not(any(target_os = "hermit", test, bootstrap)))]
#[doc(hidden)]
#[allow(unused_attributes)]
#[unstable(feature = "alloc_internals", issue = "none")]
Expand Down
2 changes: 1 addition & 1 deletion library/std/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ dlmalloc = { version = "0.1", features = ['rustc-dep-of-std'] }
fortanix-sgx-abi = { version = "0.3.2", features = ['rustc-dep-of-std'] }

[target.'cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_os = "hermit"))'.dependencies]
hermit-abi = { version = "0.1.15", features = ['rustc-dep-of-std'] }
hermit-abi = { version = "0.1.17", features = ['rustc-dep-of-std'] }

[target.wasm32-wasi.dependencies]
wasi = { version = "0.9.0", features = ['rustc-dep-of-std'], default-features = false }
Expand Down
4 changes: 0 additions & 4 deletions library/std/src/sys/hermit/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,10 +334,6 @@ impl File {
pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
Err(Error::from_raw_os_error(22))
}

pub fn diverge(&self) -> ! {
loop {}
}
}

impl DirBuilder {
Expand Down
1 change: 1 addition & 0 deletions library/std/src/sys/hermit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ pub mod net;
pub mod os;
pub mod path;
pub mod pipe;
#[path = "../unsupported/process.rs"]
pub mod process;
pub mod rwlock;
pub mod stack_overflow;
Expand Down
190 changes: 180 additions & 10 deletions library/std/src/sys/hermit/mutex.rs
Original file line number Diff line number Diff line change
@@ -1,44 +1,214 @@
use crate::cell::UnsafeCell;
use crate::collections::VecDeque;
use crate::ffi::c_void;
use crate::ops::{Deref, DerefMut, Drop};
use crate::ptr;
use crate::sync::atomic::{spin_loop_hint, AtomicUsize, Ordering};
use crate::sys::hermit::abi;

/// This type provides a lock based on busy waiting to realize mutual exclusion
///
/// # Description
///
/// This structure behaves a lot like a common mutex. There are some differences:
///
/// - By using busy waiting, it can be used outside the runtime.
/// - It is a so called ticket lock and is completly fair.
#[cfg_attr(target_arch = "x86_64", repr(align(128)))]
#[cfg_attr(not(target_arch = "x86_64"), repr(align(64)))]
struct Spinlock<T: ?Sized> {
queue: AtomicUsize,
dequeue: AtomicUsize,
data: UnsafeCell<T>,
}

unsafe impl<T: ?Sized + Send> Sync for Spinlock<T> {}
unsafe impl<T: ?Sized + Send> Send for Spinlock<T> {}

/// A guard to which the protected data can be accessed
///
/// When the guard falls out of scope it will release the lock.
struct SpinlockGuard<'a, T: ?Sized + 'a> {
dequeue: &'a AtomicUsize,
data: &'a mut T,
}

impl<T> Spinlock<T> {
pub const fn new(user_data: T) -> Spinlock<T> {
Spinlock {
queue: AtomicUsize::new(0),
dequeue: AtomicUsize::new(1),
data: UnsafeCell::new(user_data),
}
}

#[inline]
fn obtain_lock(&self) {
let ticket = self.queue.fetch_add(1, Ordering::SeqCst) + 1;
while self.dequeue.load(Ordering::SeqCst) != ticket {
spin_loop_hint();
}
}

#[inline]
pub unsafe fn lock(&self) -> SpinlockGuard<'_, T> {
self.obtain_lock();
SpinlockGuard { dequeue: &self.dequeue, data: &mut *self.data.get() }
}
}

impl<T: ?Sized + Default> Default for Spinlock<T> {
fn default() -> Spinlock<T> {
Spinlock::new(Default::default())
}
}

impl<'a, T: ?Sized> Deref for SpinlockGuard<'a, T> {
type Target = T;
fn deref(&self) -> &T {
&*self.data
}
}

impl<'a, T: ?Sized> DerefMut for SpinlockGuard<'a, T> {
fn deref_mut(&mut self) -> &mut T {
&mut *self.data
}
}

impl<'a, T: ?Sized> Drop for SpinlockGuard<'a, T> {
/// The dropping of the SpinlockGuard will release the lock it was created from.
fn drop(&mut self) {
self.dequeue.fetch_add(1, Ordering::SeqCst);
}
}

/// Realize a priority queue for tasks
struct PriorityQueue {
queues: [Option<VecDeque<abi::Tid>>; abi::NO_PRIORITIES],
prio_bitmap: u64,
}

impl PriorityQueue {
pub const fn new() -> PriorityQueue {
PriorityQueue {
queues: [
None, None, None, None, None, None, None, None, None, None, None, None, None, None,
None, None, None, None, None, None, None, None, None, None, None, None, None, None,
None, None, None,
],
prio_bitmap: 0,
}
}

/// Add a task id by its priority to the queue
pub fn push(&mut self, prio: abi::Priority, id: abi::Tid) {
let i: usize = prio.into().into();
self.prio_bitmap |= (1 << i) as u64;
if let Some(queue) = &mut self.queues[i] {
queue.push_back(id);
} else {
let mut queue = VecDeque::new();
queue.push_back(id);
self.queues[i] = Some(queue);
}
}

fn pop_from_queue(&mut self, queue_index: usize) -> Option<abi::Tid> {
if let Some(queue) = &mut self.queues[queue_index] {
let id = queue.pop_front();

if queue.is_empty() {
self.prio_bitmap &= !(1 << queue_index as u64);
}

id
} else {
None
}
}

/// Pop the task handle with the highest priority from the queue
pub fn pop(&mut self) -> Option<abi::Tid> {
for i in 0..abi::NO_PRIORITIES {
if self.prio_bitmap & (1 << i) != 0 {
return self.pop_from_queue(i);
}
}

None
}
}

struct MutexInner {
locked: bool,
blocked_task: PriorityQueue,
}

impl MutexInner {
pub const fn new() -> MutexInner {
MutexInner { locked: false, blocked_task: PriorityQueue::new() }
}
}

pub struct Mutex {
inner: *const c_void,
inner: Spinlock<MutexInner>,
}

pub type MovableMutex = Box<Mutex>;

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

impl Mutex {
pub const fn new() -> Mutex {
Mutex { inner: ptr::null() }
Mutex { inner: Spinlock::new(MutexInner::new()) }
}

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

#[inline]
pub unsafe fn lock(&self) {
let _ = abi::sem_timedwait(self.inner, 0);
loop {
let mut guard = self.inner.lock();
if guard.locked == false {
guard.locked = true;
return;
} else {
let prio = abi::get_priority();
let id = abi::getpid();

guard.blocked_task.push(prio, id);
abi::block_current_task();
drop(guard);
abi::yield_now();
}
}
}

#[inline]
pub unsafe fn unlock(&self) {
let _ = abi::sem_post(self.inner);
let mut guard = self.inner.lock();
guard.locked = false;
if let Some(tid) = guard.blocked_task.pop() {
abi::wakeup_task(tid);
}
}

#[inline]
pub unsafe fn try_lock(&self) -> bool {
let result = abi::sem_trywait(self.inner);
result == 0
let mut guard = self.inner.lock();
if guard.locked == false {
guard.locked = true;
}
guard.locked
}

#[inline]
pub unsafe fn destroy(&self) {
let _ = abi::sem_destroy(self.inner);
}
pub unsafe fn destroy(&self) {}
}

pub struct ReentrantMutex {
Expand Down
Loading