Skip to content

Commit

Permalink
Rollup merge of #77610 - hermitcore:dtors, r=m-ou-se
Browse files Browse the repository at this point in the history
revise Hermit's mutex interface to support the behaviour of StaticMutex

#77147 simplifies things by splitting this Mutex type into two types matching the two use cases: StaticMutex and MovableMutex. To support the new behavior of StaticMutex, we move part of the mutex implementation into libstd.

The interface to the OS changed. Consequently, I removed a few functions, which aren't longer needed.
  • Loading branch information
jonas-schievink authored Oct 24, 2020
2 parents a547055 + bf268fe commit e34263d
Show file tree
Hide file tree
Showing 8 changed files with 186 additions and 167 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1366,9 +1366,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

0 comments on commit e34263d

Please sign in to comment.