From 558d43082c6d3943c6b023c6b7956ea6cc9979eb Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 5 Mar 2023 15:52:48 +0100 Subject: [PATCH] WIP: Expand native pthreads implementation. --- src/header/mod.rs | 1 + src/header/pthread/attr.rs | 147 +++++++++++++++++++ src/header/pthread/barrier.rs | 85 +++++++++++ src/header/pthread/cbindgen.toml | 9 ++ src/header/pthread/cond.rs | 84 +++++++++++ src/header/pthread/mod.rs | 185 ++++++++++++++++++++++++ src/header/pthread/mutex.rs | 88 ++++++++++++ src/header/pthread/once.rs | 18 +++ src/header/pthread/rwlock.rs | 144 +++++++++++++++++++ src/header/pthread/spin.rs | 50 +++++++ src/header/sched/mod.rs | 2 +- src/header/signal/linux.rs | 2 + src/header/signal/redox.rs | 2 + src/lib.rs | 5 +- src/platform/pal/mod.rs | 4 +- src/platform/pte.rs | 124 ---------------- src/platform/redox/mod.rs | 4 + src/platform/types.rs | 14 ++ src/pthread/mod.rs | 235 +++++++++++++++++++++++++++++++ src/sync/mod.rs | 17 +-- src/sync/waitval.rs | 39 +++++ 21 files changed, 1123 insertions(+), 136 deletions(-) create mode 100644 src/header/pthread/attr.rs create mode 100644 src/header/pthread/barrier.rs create mode 100644 src/header/pthread/cbindgen.toml create mode 100644 src/header/pthread/cond.rs create mode 100644 src/header/pthread/mod.rs create mode 100644 src/header/pthread/mutex.rs create mode 100644 src/header/pthread/once.rs create mode 100644 src/header/pthread/rwlock.rs create mode 100644 src/header/pthread/spin.rs create mode 100644 src/pthread/mod.rs create mode 100644 src/sync/waitval.rs diff --git a/src/header/mod.rs b/src/header/mod.rs index 648f47a933..d1696f33c1 100644 --- a/src/header/mod.rs +++ b/src/header/mod.rs @@ -23,6 +23,7 @@ pub mod netinet_in; pub mod netinet_ip; pub mod netinet_tcp; pub mod poll; +pub mod pthread; pub mod pwd; pub mod regex; pub mod sched; diff --git a/src/header/pthread/attr.rs b/src/header/pthread/attr.rs new file mode 100644 index 0000000000..fbfcf45e6f --- /dev/null +++ b/src/header/pthread/attr.rs @@ -0,0 +1,147 @@ +use super::*; + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct Attr { + pub detachstate: u8, + pub inheritsched: u8, + pub schedpolicy: u8, + pub scope: u8, + pub guardsize: usize, + pub stacksize: usize, + pub stack: usize, + pub param: sched_param, +} +impl Default for Attr { + fn default() -> Self { + Self { + // Default according to POSIX. + detachstate: PTHREAD_CREATE_JOINABLE as _, + // Default according to POSIX. + inheritsched: PTHREAD_INHERIT_SCHED as _, + // TODO: Linux + // Redox uses a round-robin scheduler + schedpolicy: SCHED_RR as _, + // TODO: Linux uses this one. + scope: PTHREAD_SCOPE_SYSTEM as _, + guardsize: Sys::getpagesize(), + // TODO + stack: 0, + // TODO + stacksize: 1024 * 1024, + param: sched_param { + // TODO + sched_priority: 0, + } + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_attr_destroy(_attr: *mut pthread_attr_t) -> c_int { + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_attr_getdetachstate(attr: *const pthread_attr_t, detachstate: *mut c_int) -> c_int { + core::ptr::write(detachstate, (*attr).detachstate as _); + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_attr_getguardsize(attr: *const pthread_attr_t, size: *mut size_t) -> c_int { + core::ptr::write(size, (*attr).guardsize); + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_attr_getinheritsched(attr: *const pthread_attr_t, inheritsched: *mut c_int) -> c_int { + core::ptr::write(inheritsched, (*attr).inheritsched as _); + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_attr_getschedparam(attr: *const pthread_attr_t, param: *mut sched_param) -> c_int { + core::ptr::write(param, (*attr).param); + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_attr_getschedpolicy(attr: *const pthread_attr_t, policy: *mut c_int) -> c_int { + core::ptr::write(policy, (*attr).schedpolicy as _); + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_attr_getscope(attr: *const pthread_attr_t, scope: *mut c_int) -> c_int { + core::ptr::write(scope, (*attr).scope as _); + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_attr_getstack(attr: *const pthread_attr_t, stackaddr: *mut *mut c_void, stacksize: *mut size_t) -> c_int { + core::ptr::write(stackaddr, (*attr).stack as _); + core::ptr::write(stacksize, (*attr).stacksize as _); + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_attr_getstacksize(attr: *const pthread_attr_t, stacksize: *mut c_int) -> c_int { + core::ptr::write(stacksize, (*attr).stacksize as _); + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_attr_init(attr: *mut pthread_attr_t) -> c_int { + core::ptr::write(attr, Attr::default()); + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_attr_setdetachstate(attr: *mut pthread_attr_t, detachstate: c_int) -> c_int { + (*attr).detachstate = detachstate as _; + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_attr_setguardsize(attr: *mut pthread_attr_t, guardsize: c_int) -> c_int { + (*attr).guardsize = guardsize as _; + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_attr_setinheritsched(attr: *mut pthread_attr_t, inheritsched: c_int) -> c_int { + (*attr).inheritsched = inheritsched as _; + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_attr_setschedparam(attr: *mut pthread_attr_t, param: *const sched_param) -> c_int { + (*attr).param = core::ptr::read(param); + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_attr_setschedpolicy(attr: *mut pthread_attr_t, policy: c_int) -> c_int { + (*attr).schedpolicy = policy as u8; + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_attr_setscope(attr: *mut pthread_attr_t, scope: c_int) -> c_int { + (*attr).scope = scope as u8; + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_attr_setstack(attr: *mut pthread_attr_t, stackaddr: *mut c_void, stacksize: size_t) -> c_int { + (*attr).stack = stackaddr as usize; + (*attr).stacksize = stacksize; + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_attr_setstacksize(attr: *mut pthread_attr_t, stacksize: size_t) -> c_int { + (*attr).stacksize = stacksize; + 0 +} diff --git a/src/header/pthread/barrier.rs b/src/header/pthread/barrier.rs new file mode 100644 index 0000000000..a53be0467a --- /dev/null +++ b/src/header/pthread/barrier.rs @@ -0,0 +1,85 @@ +use crate::header::errno::*; + +use crate::sync::AtomicLock; +use core::sync::atomic::{AtomicU32, Ordering}; + +use super::*; + +#[repr(C)] +pub struct Barrier { + count: AtomicU32, + original_count: u32, + epoch: AtomicLock, +} + +#[repr(C)] +pub struct BarrierAttr { + pshared: c_int, +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_barrier_destroy(barrier: *mut pthread_barrier_t) -> c_int { + // Behavior is undefined if any thread is currently waiting. + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_barrier_init(barrier: *mut pthread_barrier_t, attr: *const pthread_barrierattr_t, count: c_uint) -> c_int { + if count == 0 { + return EINVAL; + } + + core::ptr::write(barrier, Barrier { + count: AtomicU32::new(0), + original_count: count, + epoch: AtomicLock::new(0), + }); + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_barrier_wait(barrier: *mut pthread_barrier_t) -> c_int { + let barrier: &pthread_barrier_t = &*barrier; + + // TODO: Orderings + let mut cached = barrier.count.load(Ordering::SeqCst); + + loop { + let new = if cached == barrier.original_count - 1 { 0 } else { cached + 1 }; + + match barrier.count.compare_exchange_weak(cached, new, Ordering::SeqCst, Ordering::SeqCst) { + Ok(_) => if new == 0 { + // We reached COUNT waits, and will thus be the thread notifying every other + // waiter. + + todo!(); + + return PTHREAD_BARRIER_SERIAL_THREAD; + } else { + // We increased the wait count, but this was not sufficient. We will thus have to + // wait for the epoch to tick up. + todo!(); + + return 0; + } + Err(value) => { + cached = value; + core::hint::spin_loop(); + } + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_barrierattr_init(attr: *mut pthread_barrierattr_t) -> c_int { + // PTHREAD_PROCESS_PRIVATE is default according to POSIX. + core::ptr::write(attr, BarrierAttr { pshared: PTHREAD_PROCESS_PRIVATE }); + + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_barrierattr_setpshared(attr: *mut pthread_barrierattr_t, pshared: c_int) -> c_int { + (*attr).pshared = pshared; + 0 +} diff --git a/src/header/pthread/cbindgen.toml b/src/header/pthread/cbindgen.toml new file mode 100644 index 0000000000..afb38e336e --- /dev/null +++ b/src/header/pthread/cbindgen.toml @@ -0,0 +1,9 @@ +sys_includes = ["sched.h", "time.h"] +include_guard = "_RELIBC_PTHREAD_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true diff --git a/src/header/pthread/cond.rs b/src/header/pthread/cond.rs new file mode 100644 index 0000000000..4ec2018bc7 --- /dev/null +++ b/src/header/pthread/cond.rs @@ -0,0 +1,84 @@ +use super::*; + +// PTHREAD_COND_INITIALIZER + +#[repr(C)] +pub struct CondAttr { + clock: clockid_t, + pshared: c_int, +} + +#[repr(C)] +pub struct Cond { +} + +// #[no_mangle] +pub extern "C" fn pthread_cond_broadcast(cond: *mut pthread_cond_t) -> c_int { + todo!() +} + +// #[no_mangle] +pub extern "C" fn pthread_cond_destroy(cond: *mut pthread_cond_t) -> c_int { + todo!() +} + +// #[no_mangle] +pub extern "C" fn pthread_cond_init(cond: *mut pthread_cond_t, attr: *const pthread_condattr_t) -> c_int { + todo!() +} + +// #[no_mangle] +pub extern "C" fn pthread_cond_signal(cond: *mut pthread_cond_t) -> c_int { + todo!() +} + +// #[no_mangle] +pub extern "C" fn pthread_cond_timedwait(cond: *mut pthread_cond_t, mutex: *const pthread_mutex_t, timeout: *const timespec) -> c_int { + todo!() +} + +// #[no_mangle] +pub extern "C" fn pthread_cond_wait(cond: *mut pthread_cond_t, mutex: *const pthread_mutex_t) -> c_int { + todo!() +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_condattr_destroy(condattr: *mut pthread_condattr_t) -> c_int { + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_condattr_getclock(condattr: *const pthread_condattr_t, clock: *mut clockid_t) -> c_int { + core::ptr::write(clock, (*condattr).clock); + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_condattr_getpshared(condattr: *const pthread_condattr_t, pshared: *mut c_int) -> c_int { + core::ptr::write(pshared, (*condattr).pshared); + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_condattr_init(condattr: *mut pthread_condattr_t) -> c_int { + core::ptr::write(condattr, CondAttr { + // FIXME: system clock + clock: 0, + // Default + pshared: PTHREAD_PROCESS_PRIVATE, + }); + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_condattr_setclock(condattr: *mut pthread_condattr_t, clock: clockid_t) -> c_int { + (*condattr).clock = clock; + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_condattr_setpshared(condattr: *mut pthread_condattr_t, pshared: c_int) -> c_int { + (*condattr).pshared = pshared; + 0 +} + diff --git a/src/header/pthread/mod.rs b/src/header/pthread/mod.rs new file mode 100644 index 0000000000..fe1bac2110 --- /dev/null +++ b/src/header/pthread/mod.rs @@ -0,0 +1,185 @@ +//! pthread.h implementation for Redox, following https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/pthread.h.html + +use core::ptr::NonNull; + +use crate::platform::{self, Pal, Sys, types::*}; +use crate::header::{sched::*, time::timespec}; +use crate::pthread; + +pub const PTHREAD_BARRIER_SERIAL_THREAD: c_int = 1; + +pub const PTHREAD_CANCEL_ASYNCHRONOUS: c_int = 0; +pub const PTHREAD_CANCEL_ENABLE: c_int = 0; +pub const PTHREAD_CANCEL_DEFERRED: c_int = 0; +pub const PTHREAD_CANCEL_DISABLE: c_int = 0; +pub const PTHREAD_CANCELED: *mut c_void = core::ptr::null_mut(); + +pub const PTHREAD_CREATE_DETACHED: c_int = 0; +pub const PTHREAD_CREATE_JOINABLE: c_int = 1; + +pub const PTHREAD_EXPLICIT_SCHED: c_int = 0; +pub const PTHREAD_INHERIT_SCHED: c_int = 1; + +pub const PTHREAD_MUTEX_DEFAULT: c_int = 0; +pub const PTHREAD_MUTEX_ERRORCHECK: c_int = 0; +pub const PTHREAD_MUTEX_NORMAL: c_int = 0; +pub const PTHREAD_MUTEX_RECURSIVE: c_int = 0; +pub const PTHREAD_MUTEX_ROBUST: c_int = 0; +pub const PTHREAD_MUTEX_STALLED: c_int = 0; + +pub const PTHREAD_PRIO_INHERIT: c_int = 0; + +pub const PTHREAD_PRIO_NONE: c_int = 0; + +pub const PTHREAD_PRIO_PROTECT: c_int = 0; + +pub const PTHREAD_PROCESS_SHARED: c_int = 0; +pub const PTHREAD_PROCESS_PRIVATE: c_int = 1; + +pub const PTHREAD_SCOPE_PROCESS: c_int = 0; +pub const PTHREAD_SCOPE_SYSTEM: c_int = 1; + +#[no_mangle] +pub unsafe extern "C" fn pthread_atfork(prepare: extern "C" fn(), parent: extern "C" fn(), child: extern "C" fn()) -> c_int { + let mut guard = pthread::FORK_HANDLERS.lock(); + + // TODO: try_reserve + + guard.prepare.push(prepare); + guard.child.push(child); + guard.parent.push(parent); + + 0 +} + +pub mod attr; +pub use self::attr::*; + +pub mod barrier; +pub use self::barrier::*; + +#[no_mangle] +pub unsafe extern "C" fn pthread_cancel(thread: pthread_t) -> c_int { + match pthread::cancel(&*thread.cast()) { + Ok(()) => 0, + Err(pthread::Errno(error)) => error, + } +} + +pub mod cond; +pub use self::cond::*; + +#[no_mangle] +pub unsafe extern "C" fn pthread_create(pthread: *mut pthread_t, attr: *const pthread_attr_t, start_routine: extern "C" fn(arg: *mut c_void) -> *mut c_void, arg: *mut c_void) -> c_int { + let attr = NonNull::new(attr as *mut _).map(|n| n.as_ref()); + + match pthread::create(attr, start_routine, arg) { + Ok(ptr) => { + core::ptr::write(pthread, ptr); + 0 + } + Err(pthread::Errno(code)) => code, + } +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_detach(pthread: pthread_t) -> c_int { + match pthread::detach(&*pthread.cast()) { + Ok(()) => 0, + Err(pthread::Errno(errno)) => errno, + } +} + +#[no_mangle] +pub extern "C" fn pthread_equal(pthread1: pthread_t, pthread2: pthread_t) -> c_int { + core::ptr::eq(pthread1, pthread2).into() +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_exit(retval: *mut c_void) -> ! { + pthread::exit_current_thread(pthread::Retval(retval)) +} + +// #[no_mangle] +pub extern "C" fn pthread_getconcurrency() -> c_int { + todo!() +} + +// #[no_mangle] +pub extern "C" fn pthread_getcpuclockid(thread: pthread_t, clock: *mut clockid_t) -> c_int { + todo!() +} + +// #[no_mangle] +pub extern "C" fn pthread_getschedparam(thread: pthread_t, policy: *mut clockid_t, param: *mut sched_param) -> c_int { + todo!() +} + +// #[no_mangle] +pub extern "C" fn pthread_getspecific(key: pthread_key_t) -> *mut c_void { + todo!() +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_join(thread: pthread_t, retval: *mut *mut c_void) -> c_int { + match pthread::join(&*thread.cast()) { + Ok(pthread::Retval(ret)) => { + core::ptr::write(retval, ret); + 0 + } + Err(pthread::Errno(error)) => error, + } +} + +// #[no_mangle] +pub extern "C" fn pthread_key_create(key: *mut pthread_key_t, destructor: extern "C" fn(value: *mut c_void)) -> c_int { + todo!() +} + +// #[no_mangle] +pub extern "C" fn pthread_key_delete(key: pthread_key_t) -> c_int { + todo!() +} + +pub mod mutex; +pub use self::mutex::*; + +pub mod once; +pub use self::once::*; + +pub mod rwlock; +pub use self::rwlock::*; + +pub unsafe extern "C" fn pthread_self() -> pthread_t { + pthread::current_thread().unwrap_unchecked() as *const _ as *mut _ +} +pub extern "C" fn pthread_setcancelstate(state: c_int, oldstate: *mut c_int) -> c_int { + todo!(); +} +pub extern "C" fn pthread_setcanceltype(ty: c_int, oldty: *mut c_int) -> c_int { + todo!(); +} + +pub extern "C" fn pthread_setconcurrency(concurrency: c_int) -> c_int { + todo!(); +} + +pub extern "C" fn pthread_setschedparam(thread: pthread_t, policy: c_int, param: *const sched_param) -> c_int { + todo!(); +} +pub extern "C" fn pthread_setschedprio(thread: pthread_t, prio: c_int) -> c_int { + todo!(); +} +pub extern "C" fn pthread_setspecific(key: pthread_key_t, value: *const c_void) -> c_int { + todo!(); +} + +pub mod spin; +pub use self::spin::*; + +pub unsafe extern "C" fn pthread_testcancel() { + pthread::testcancel(); +} + +// pthread_cleanup_pop() +// pthread_cleanup_push() diff --git a/src/header/pthread/mutex.rs b/src/header/pthread/mutex.rs new file mode 100644 index 0000000000..eec8d5bdf8 --- /dev/null +++ b/src/header/pthread/mutex.rs @@ -0,0 +1,88 @@ +use super::*; + +// PTHREAD_MUTEX_INITIALIZER + +#[repr(C)] +pub struct Mutex { +} + +#[repr(C)] +pub struct MutexAttr { +} + +pub extern "C" fn pthread_mutex_consistent(mutex: *mut pthread_mutex_t) -> c_int { + todo!(); +} +pub extern "C" fn pthread_mutex_destroy(mutex: *mut pthread_mutex_t) -> c_int { + todo!(); +} + +pub extern "C" fn pthread_mutex_getprioceiling(mutex: *const pthread_mutex_t, prioceiling: *mut c_int) -> c_int { + todo!(); +} + +pub extern "C" fn pthread_mutex_init(mutex: *mut pthread_mutex_t, attr: *const pthread_mutexattr_t) -> c_int { + todo!(); +} +pub extern "C" fn pthread_mutex_lock(mutex: *mut pthread_mutex_t) -> c_int { + todo!(); +} + +pub extern "C" fn pthread_mutex_setprioceiling(mutex: *mut pthread_mutex_t, prioceiling: c_int, old_prioceiling: *mut c_int) -> c_int { + todo!(); +} + +pub extern "C" fn pthread_mutex_timedlock(mutex: *mut pthread_mutex_t, timespec: *const timespec) -> c_int { + todo!(); +} +pub extern "C" fn pthread_mutex_trylock(mutex: *mut pthread_mutex_t) -> c_int { + todo!(); +} +pub extern "C" fn pthread_mutex_unlock(mutex: *mut pthread_mutex_t) -> c_int { + todo!(); +} +pub extern "C" fn pthread_mutexattr_destroy(attr: *mut pthread_mutexattr_t) -> c_int { + todo!(); +} + +pub extern "C" fn pthread_mutexattr_getprioceiling(attr: *const pthread_mutexattr_t, prioceiling: *mut c_int) -> c_int { + todo!(); +} + + +pub extern "C" fn pthread_mutexattr_getprotocol(attr: *const pthread_mutexattr_t, protocol: *mut c_int) -> c_int { + todo!(); +} + +pub extern "C" fn pthread_mutexattr_getpshared(attr: *const pthread_mutexattr_t, pshared: *mut c_int) -> c_int { + todo!(); +} + +pub extern "C" fn pthread_mutexattr_getrobust(attr: *const pthread_mutexattr_t, robust: *mut c_int) -> c_int { + todo!(); +} +pub extern "C" fn pthread_mutexattr_gettype(attr: *const pthread_mutexattr_t, ty: *mut c_int) -> c_int { + todo!(); +} +pub extern "C" fn pthread_mutexattr_init(attr: *mut pthread_mutexattr_t) -> c_int { + todo!(); +} + +pub extern "C" fn pthread_mutexattr_setprioceiling(attr: *mut pthread_mutexattr_t, prioceiling: c_int) -> c_int { + todo!(); +} + +pub extern "C" fn pthread_mutexattr_setprotocol(attr: *mut pthread_mutexattr_t, protocol: c_int) -> c_int { + todo!(); +} + +pub extern "C" fn pthread_mutexattr_setpshared(attr: *mut pthread_mutexattr_t, pshared: c_int) -> c_int { + todo!(); +} + +pub extern "C" fn pthread_mutexattr_setrobust(attr: *mut pthread_mutexattr_t, robust: c_int) -> c_int { + todo!(); +} +pub extern "C" fn pthread_mutexattr_settype(attr: *mut pthread_mutexattr_t, ty: c_int) -> c_int { + todo!(); +} diff --git a/src/header/pthread/once.rs b/src/header/pthread/once.rs new file mode 100644 index 0000000000..f5cc09169c --- /dev/null +++ b/src/header/pthread/once.rs @@ -0,0 +1,18 @@ +use super::*; + +#[repr(C)] +pub struct Once { + inner: crate::sync::Once<()>, +} + +// PTHREAD_ONCE_INIT + +#[no_mangle] +pub unsafe extern "C" fn pthread_once(once: *mut pthread_once_t, constructor: extern "C" fn()) -> c_int { + let once: &pthread_once_t = &*once; + + // TODO: Cancellation points + once.inner.call_once(|| constructor()); + + 0 +} diff --git a/src/header/pthread/rwlock.rs b/src/header/pthread/rwlock.rs new file mode 100644 index 0000000000..fbc20c0232 --- /dev/null +++ b/src/header/pthread/rwlock.rs @@ -0,0 +1,144 @@ +use super::*; + +use crate::sync::AtomicLock; +use core::sync::atomic::Ordering; + +use crate::header::errno::EBUSY; + +// PTHREAD_RWLOCK_INITIALIZER + +// TODO: Optimize for short waits and long waits, using AtomicLock::wait_until, but still +// supporting timeouts. +// TODO: Add futex ops that use bitmasks. + +const EXCLUSIVE: u32 = (1 << (u32::BITS - 1)) - 1; +// Separate "waiting for wrlocks" and "waiting for rdlocks"? +//const WAITING: u32 = 1 << (u32::BITS - 1); + +#[repr(C)] +pub struct Rwlock { + state: AtomicLock, +} + +#[repr(C)] +pub struct RwlockAttr { + pshared: c_int, +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_rwlock_destroy(rwlock: *mut pthread_rwlock_t) -> c_int { + // (Informing the compiler that this pointer is valid, might improve optimizations.) + let _rwlock: &pthread_rwlock_t = &*rwlock; + 0 +} +#[no_mangle] +pub unsafe extern "C" fn pthread_rwlock_init(rwlock: *mut pthread_rwlock_t, _attr: *const pthread_rwlockattr_t) -> c_int { + core::ptr::write(rwlock, Rwlock { + state: AtomicLock::new(0), + }); + + 0 +} +#[no_mangle] +pub unsafe extern "C" fn pthread_rwlock_rdlock(rwlock: *mut pthread_rwlock_t) -> c_int { + pthread_rwlock_timedrdlock(rwlock, core::ptr::null()) +} +#[no_mangle] +pub unsafe extern "C" fn pthread_rwlock_timedrdlock(rwlock: *mut pthread_rwlock_t, timeout: *const timespec) -> c_int { + let rwlock: &pthread_rwlock_t = &*rwlock; + let timeout = NonNull::new(timeout as *mut _).map(|n| n.as_ref()); + + loop { + if pthread_rwlock_tryrdlock(rwlock as *const _ as *mut _) == EBUSY { + rwlock.state.wait_if(EXCLUSIVE as i32, timeout); + } + return 0; + } +} +#[no_mangle] +pub unsafe extern "C" fn pthread_rwlock_timedwrlock(rwlock: *mut pthread_rwlock_t, timeout: *const timespec) -> c_int { + let rwlock: &pthread_rwlock_t = &*rwlock; + let timeout = NonNull::new(timeout as *mut _).map(|n| n.as_ref()); + + loop { + if pthread_rwlock_trywrlock(rwlock as *const _ as *mut _) == EBUSY { + rwlock.state.wait_if(EXCLUSIVE as i32, timeout); + } + return 0; + } +} +#[no_mangle] +pub unsafe extern "C" fn pthread_rwlock_tryrdlock(rwlock: *mut pthread_rwlock_t) -> c_int { + let rwlock: &pthread_rwlock_t = &*rwlock; + + let mut cached = rwlock.state.load(Ordering::Acquire) as u32; + + loop { + let old = if cached == EXCLUSIVE { 0 } else { cached }; + let new = old + 1; + + assert_ne!(new, EXCLUSIVE, "overflow"); + + match rwlock.state.compare_exchange_weak(cached as i32, new as i32, Ordering::Acquire, Ordering::Relaxed) { + Ok(_) => return 0, + Err(value) if value as u32 == EXCLUSIVE => return EBUSY, + Err(value) => { + cached = value as u32; + core::hint::spin_loop(); + } + } + } +} +#[no_mangle] +pub unsafe extern "C" fn pthread_rwlock_trywrlock(rwlock: *mut pthread_rwlock_t) -> c_int { + let rwlock: &pthread_rwlock_t = &*rwlock; + + match rwlock.state.compare_exchange(0, EXCLUSIVE as i32, Ordering::Acquire, Ordering::Relaxed) { + Ok(_) => 0, + Err(_) => EBUSY, + } +} +#[no_mangle] +pub unsafe extern "C" fn pthread_rwlock_unlock(rwlock: *const pthread_rwlock_t) -> c_int { + let rwlock: &pthread_rwlock_t = &*rwlock; + + let old = rwlock.state.swap(0, Ordering::Release) as u32; + + if old == EXCLUSIVE { + rwlock.state.notify_all(); + } + + 0 +} +#[no_mangle] +pub unsafe extern "C" fn pthread_rwlock_wrlock(rwlock: *mut pthread_rwlock_t) -> c_int { + pthread_rwlock_timedwrlock(rwlock, core::ptr::null()) +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_rwlockattr_destroy(_attr: *mut pthread_rwlockattr_t) -> c_int { + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_rwlockattr_getpshared(attr: *const pthread_rwlockattr_t, pshared: *mut c_int) -> c_int { + core::ptr::write(pshared, (*attr).pshared); + + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_rwlockattr_init(attr: *mut pthread_rwlockattr_t) -> c_int { + core::ptr::write(attr, RwlockAttr { + // Default according to POSIX. + pshared: PTHREAD_PROCESS_PRIVATE, + }); + + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn pthread_rwlockattr_setpshared(attr: *mut pthread_rwlockattr_t, pshared: c_int) -> c_int { + (*attr).pshared = pshared; + 0 +} diff --git a/src/header/pthread/spin.rs b/src/header/pthread/spin.rs new file mode 100644 index 0000000000..5233e04d20 --- /dev/null +++ b/src/header/pthread/spin.rs @@ -0,0 +1,50 @@ +use core::sync::atomic::{AtomicI32 as AtomicInt, Ordering}; + +use crate::header::errno::EBUSY; + +use super::*; + +#[repr(C)] +pub struct Spinlock { + inner: AtomicInt, +} + +pub const UNLOCKED: c_int = 0; +pub const LOCKED: c_int = 1; + +pub unsafe extern "C" fn pthread_spin_destroy(spinlock: *mut pthread_spinlock_t) -> c_int { + 0 +} +pub unsafe extern "C" fn pthread_spin_init(spinlock: *mut pthread_spinlock_t, _pshared: c_int) -> c_int { + // TODO: pshared doesn't matter in most situations, as memory is just memory, but this may be + // different on some architectures... + + core::ptr::write(spinlock, Spinlock { inner: AtomicInt::new(UNLOCKED) }); + + 0 +} +pub unsafe extern "C" fn pthread_spin_lock(spinlock: *mut pthread_spinlock_t) -> c_int { + let spinlock: &Spinlock = &*spinlock; + + loop { + match spinlock.inner.compare_exchange_weak(UNLOCKED, LOCKED, Ordering::Acquire, Ordering::Relaxed) { + Ok(_) => return 0, + Err(_) => core::hint::spin_loop(), + } + } +} +pub unsafe extern "C" fn pthread_spin_trylock(spinlock: *mut pthread_spinlock_t) -> c_int { + let spinlock: &Spinlock = &*spinlock; + + match spinlock.inner.compare_exchange(UNLOCKED, LOCKED, Ordering::Acquire, Ordering::Relaxed) { + Ok(_) => 0, + Err(_) => EBUSY, + } +} +pub unsafe extern "C" fn pthread_spin_unlock(spinlock: *mut pthread_spinlock_t) -> c_int { + let spinlock: &Spinlock = &*spinlock; + + spinlock.inner.store(UNLOCKED, Ordering::Release); + + 0 +} diff --git a/src/header/sched/mod.rs b/src/header/sched/mod.rs index 5af034374f..a4fe3e32d1 100644 --- a/src/header/sched/mod.rs +++ b/src/header/sched/mod.rs @@ -4,7 +4,7 @@ use crate::platform::{Pal, Sys, types::*}; use crate::header::time::timespec; #[repr(C)] -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug)] pub struct sched_param { sched_priority: c_int, } diff --git a/src/header/signal/linux.rs b/src/header/signal/linux.rs index 0313827aa3..de013cdc13 100644 --- a/src/header/signal/linux.rs +++ b/src/header/signal/linux.rs @@ -58,6 +58,8 @@ pub const SIGSYS: usize = 31; pub const SIGUNUSED: usize = SIGSYS; pub const NSIG: usize = 32; +pub const SIGRTMIN: usize = 34; + pub const SA_NOCLDSTOP: usize = 1; pub const SA_NOCLDWAIT: usize = 2; pub const SA_SIGINFO: usize = 4; diff --git a/src/header/signal/redox.rs b/src/header/signal/redox.rs index 1e8b227a8c..65e85afe5b 100644 --- a/src/header/signal/redox.rs +++ b/src/header/signal/redox.rs @@ -66,6 +66,8 @@ pub const SIGPWR: usize = 30; pub const SIGSYS: usize = 31; pub const NSIG: usize = 32; +pub const SIGRTMIN: usize = 34; + pub const SA_NOCLDSTOP: usize = 0x00000001; pub const SA_NOCLDWAIT: usize = 0x00000002; pub const SA_SIGINFO: usize = 0x00000004; diff --git a/src/lib.rs b/src/lib.rs index e545fd6973..5807999152 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,10 +6,10 @@ #![feature(allocator_api)] #![feature(array_chunks)] #![feature(asm_const)] -#![feature(box_into_pin)] +#![feature(atomic_mut_ptr)] #![feature(c_variadic)] -#![feature(const_btree_new)] #![feature(core_intrinsics)] +#![feature(int_roundings)] #![feature(lang_items)] #![feature(linkage)] #![feature(stmt_expr_attributes)] @@ -57,6 +57,7 @@ pub mod header; pub mod io; pub mod ld_so; pub mod platform; +pub mod pthread; pub mod start; pub mod sync; diff --git a/src/platform/pal/mod.rs b/src/platform/pal/mod.rs index c6bdc770a3..21a0f41383 100644 --- a/src/platform/pal/mod.rs +++ b/src/platform/pal/mod.rs @@ -47,6 +47,8 @@ pub trait Pal { fn exit(status: c_int) -> !; + fn exit_thread() -> !; + fn fchdir(fildes: c_int) -> c_int; fn fchmod(fildes: c_int, mode: mode_t) -> c_int; @@ -155,7 +157,7 @@ pub trait Pal { fn pipe2(fildes: &mut [c_int], flags: c_int) -> c_int; - unsafe fn pte_clone(stack: *mut usize) -> pid_t; + unsafe fn pte_clone(stack: *mut usize) -> crate::pthread::OsTid; fn read(fildes: c_int, buf: &mut [u8]) -> ssize_t; diff --git a/src/platform/pte.rs b/src/platform/pte.rs index a75d5cc014..e8796dba17 100644 --- a/src/platform/pte.rs +++ b/src/platform/pte.rs @@ -12,10 +12,6 @@ use crate::{ sys_mman, time::{clock_gettime, timespec, CLOCK_MONOTONIC}, }, - ld_so::{ - linker::Linker, - tcb::{Master, Tcb}, - }, platform::{ types::{c_int, c_long, c_uint, c_void, pid_t, size_t, time_t}, Pal, Sys, @@ -59,126 +55,6 @@ unsafe fn locals<'a>() -> &'a mut BTreeMap { &mut *LOCALS.get() } -// pte_osResult pte_osInit(void) -#[no_mangle] -pub unsafe extern "C" fn pte_osInit() -> pte_osResult { - PTE_OS_OK -} - -/// A shim to wrap thread entry points in logic to set up TLS, for example -unsafe extern "C" fn pte_osThreadShim( - entryPoint: pte_osThreadEntryPoint, - argv: *mut c_void, - mutex: pte_osMutexHandle, - tls_size: usize, - tls_masters_ptr: *mut Master, - tls_masters_len: usize, - tls_linker_ptr: *const Mutex, - tls_mspace: usize, -) { - // The kernel allocated TLS does not have masters set, so do not attempt to copy it. - // It will be copied by the kernel. - if !tls_masters_ptr.is_null() { - let tcb = Tcb::new(tls_size).unwrap(); - tcb.masters_ptr = tls_masters_ptr; - tcb.masters_len = tls_masters_len; - tcb.linker_ptr = tls_linker_ptr; - tcb.mspace = tls_mspace; - tcb.copy_masters().unwrap(); - tcb.activate(); - } - - // Wait until pte_osThreadStart - pte_osMutexLock(mutex); - entryPoint(argv); - pte_osThreadExit(); -} - -#[no_mangle] -pub unsafe extern "C" fn pte_osThreadCreate( - entryPoint: pte_osThreadEntryPoint, - stackSize: c_int, - _initialPriority: c_int, - argv: *mut c_void, - ppte_osThreadHandle: *mut pte_osThreadHandle, -) -> pte_osResult { - // Create a locked mutex, unlocked by pte_osThreadStart - let mutex: pte_osMutexHandle = Box::into_raw(Box::new(Mutex::locked(()))); - - let stack_size = if stackSize == 0 { - 1024 * 1024 - } else { - stackSize as usize - }; - let stack_base = sys_mman::mmap( - ptr::null_mut(), - stack_size, - sys_mman::PROT_READ | sys_mman::PROT_WRITE, - sys_mman::MAP_SHARED | sys_mman::MAP_ANONYMOUS, - -1, - 0, - ); - if stack_base as isize == -1 { - return PTE_OS_GENERAL_FAILURE; - } - let stack_end = stack_base.add(stack_size); - let mut stack = stack_end as *mut usize; - { - let mut push = |value: usize| { - stack = stack.offset(-1); - *stack = value; - }; - - //WARNING: Stack must be 128-bit aligned for SSE - if let Some(tcb) = Tcb::current() { - push(tcb.mspace as usize); - push(tcb.linker_ptr as usize); - push(tcb.masters_len); - push(tcb.masters_ptr as usize); - push(tcb.tls_len); - } else { - push(ALLOCATOR.get_book_keeper()); - push(0); - push(0); - push(0); - push(0); - } - - push(mutex as usize); - - push(argv as usize); - push(entryPoint as usize); - - push(pte_osThreadShim as usize); - } - - let id = Sys::pte_clone(stack); - if id < 0 { - return PTE_OS_GENERAL_FAILURE; - } - - pte_osMutexLock(&mut pid_mutexes_lock); - if pid_mutexes.is_none() { - pid_mutexes = Some(BTreeMap::new()); - } - pid_mutexes.as_mut().unwrap().insert(id, mutex); - pte_osMutexUnlock(&mut pid_mutexes_lock); - - pte_osMutexLock(&mut pid_stacks_lock); - if pid_stacks.is_none() { - pid_stacks = Some(BTreeMap::new()); - } - pid_stacks - .as_mut() - .unwrap() - .insert(id, (stack_base, stack_size)); - pte_osMutexUnlock(&mut pid_stacks_lock); - - *ppte_osThreadHandle = id; - - PTE_OS_OK -} - #[no_mangle] pub unsafe extern "C" fn pte_osThreadStart(handle: pte_osThreadHandle) -> pte_osResult { let mut ret = PTE_OS_GENERAL_FAILURE; diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs index decf4fd350..f182211709 100644 --- a/src/platform/redox/mod.rs +++ b/src/platform/redox/mod.rs @@ -1005,4 +1005,8 @@ impl Pal for Sys { // GETPID on Redox is 20, which is WRITEV on Linux e(unsafe { syscall::syscall5(syscall::number::SYS_GETPID, !0, !0, !0, !0, !0) }) != !0 } + + fn exit_thread() -> ! { + Self::exit(0) + } } diff --git a/src/platform/types.rs b/src/platform/types.rs index 76f231acb1..50440b17c0 100644 --- a/src/platform/types.rs +++ b/src/platform/types.rs @@ -76,3 +76,17 @@ pub type suseconds_t = c_int; pub type clock_t = c_long; pub type clockid_t = c_int; pub type timer_t = *mut c_void; + +pub type pthread_attr_t = crate::header::pthread::attr::Attr; +pub type pthread_barrier_t = crate::header::pthread::barrier::Barrier; +pub type pthread_barrierattr_t = crate::header::pthread::barrier::BarrierAttr; +pub type pthread_cond_t = crate::header::pthread::cond::Cond; +pub type pthread_condattr_t = crate::header::pthread::cond::CondAttr; +pub type pthread_key_t = *mut c_void; +pub type pthread_mutex_t = crate::header::pthread::mutex::Mutex; +pub type pthread_mutexattr_t = crate::header::pthread::mutex::MutexAttr; +pub type pthread_once_t = crate::header::pthread::once::Once; +pub type pthread_rwlock_t = crate::header::pthread::rwlock::Rwlock; +pub type pthread_rwlockattr_t = crate::header::pthread::rwlock::RwlockAttr; +pub type pthread_spinlock_t = crate::header::pthread::spin::Spinlock; +pub type pthread_t = *mut c_void; diff --git a/src/pthread/mod.rs b/src/pthread/mod.rs new file mode 100644 index 0000000000..39649b1efd --- /dev/null +++ b/src/pthread/mod.rs @@ -0,0 +1,235 @@ +use core::cell::{Cell, UnsafeCell}; +use core::ptr::NonNull; +use core::sync::atomic::{AtomicBool, Ordering}; + +use alloc::boxed::Box; +use alloc::collections::BTreeMap; +use alloc::vec::Vec; + +use crate::platform::{Pal, Sys, types::*}; +use crate::header::sched::sched_param; +use crate::header::errno::*; +use crate::header::sys_mman; +use crate::ld_so::{linker::Linker, tcb::{Master, Tcb}}; + +use crate::sync::{Mutex, waitval::Waitval}; + +extern "C" fn pthread_init() { +} +extern "C" fn pthread_terminate() { +} + +struct Pthread { + waitval: Waitval, + wants_cancel: AtomicBool, + + stack_base: *mut c_void, + stack_size: usize, + + os_tid: OsTid, +} + +#[derive(Clone, Copy, Debug, Ord, Eq, PartialOrd, PartialEq)] +pub struct OsTid { + #[cfg(target_os = "redox")] + context_id: usize, + #[cfg(target_os = "linux")] + thread_id: usize, +} + +unsafe impl Send for Pthread {} +unsafe impl Sync for Pthread {} + +use crate::header::pthread::attr::Attr; + +/// Positive error codes (EINVAL, not -EINVAL). +#[derive(Debug)] +pub struct Errno(pub c_int); + +pub struct Retval(pub *mut c_void); + +struct MmapGuard { page_start: *mut c_void, mmap_size: usize } +impl Drop for MmapGuard { + fn drop(&mut self) { + unsafe { + let _ = Sys::munmap(self.page_start, self.mmap_size); + } + } +} + +pub unsafe fn create(attrs: Option<&pthread_attr_t>, start_routine: extern "C" fn(arg: *mut c_void) -> *mut c_void, arg: *mut c_void) -> Result { + let attrs = attrs.copied().unwrap_or_default(); + + // Create a locked mutex, unlocked by the thread after it has started. + let synchronization_mutex = Box::into_raw(Box::new(Mutex::locked(()))); + + let stack_size = attrs.stacksize.next_multiple_of(Sys::getpagesize()); + + // TODO: Custom stacks + let stack_base = sys_mman::mmap( + core::ptr::null_mut(), + attrs.stacksize, + sys_mman::PROT_READ | sys_mman::PROT_WRITE, + sys_mman::MAP_SHARED | sys_mman::MAP_ANONYMOUS, + -1, + 0, + ); + if stack_base as isize == -1 { + // "Insufficient resources" + return Err(Errno(EAGAIN)); + } + + let pthread = Pthread { + waitval: Waitval::new(), + wants_cancel: AtomicBool::new(false), + stack_base, + stack_size, + os_tid, + }; + let ptr = Box::into_raw(Box::new(pthread)); + + let stack_raii = MmapGuard { page_start: stack_base, mmap_size: stack_size }; + + let stack_end = stack_base.add(stack_size); + let mut stack = stack_end as *mut usize; + { + let mut push = |value: usize| { + stack = stack.sub(1); + stack.write(value); + }; + + push(0); + push(ptr as usize); + + //WARNING: Stack must be 128-bit aligned for SSE + if let Some(tcb) = Tcb::current() { + push(tcb.mspace as usize); + push(tcb.linker_ptr as usize); + push(tcb.masters_len); + push(tcb.masters_ptr as usize); + push(tcb.tls_len); + } else { + push(ALLOCATOR.get_book_keeper()); + push(0); + push(0); + push(0); + push(0); + } + + push(synchronization_mutex as usize); + + push(arg as usize); + push(start_routine as usize); + + push(new_thread_shim as usize); + } + + let id = Sys::pte_clone(stack); + if id < 0 { + return Err(Errno(EAGAIN)); + } + + let _ = (&mut *synchronization_mutex).lock(); + CID_TO_PTHREAD.lock().insert(id, ForceSendSync(ptr.cast())); + + core::mem::forget(stack_raii); + + Ok(ptr.cast()) +} +/// A shim to wrap thread entry points in logic to set up TLS, for example +unsafe extern "C" fn new_thread_shim( + entry_point: unsafe extern "C" fn(*mut c_void) -> *mut c_void, + arg: *mut c_void, + mutex: *const Mutex<()>, + tls_size: usize, + tls_masters_ptr: *mut Master, + tls_masters_len: usize, + tls_linker_ptr: *const Mutex, + tls_mspace: usize, + pthread: *mut Pthread, +) -> ! { + // TODO: Pass less arguments by allocating the TCB from the creator thread. + if !tls_masters_ptr.is_null() { + let tcb = Tcb::new(tls_size).unwrap(); + tcb.masters_ptr = tls_masters_ptr; + tcb.masters_len = tls_masters_len; + tcb.linker_ptr = tls_linker_ptr; + tcb.mspace = tls_mspace; + tcb.copy_masters().unwrap(); + tcb.activate(); + } + PTHREAD_SELF.set(pthread); + + (&*mutex).manual_unlock(); + let retval = entry_point(arg); + + exit_current_thread(Retval(retval)) +} +pub unsafe fn join(thread: &Pthread) -> Result { + // We don't have to return EDEADLK, but unlike e.g. pthread_t lifetime checking, it's a + // relatively easy check. + if core::ptr::eq(thread, current_thread().unwrap_unchecked()) { + return Err(Errno(EDEADLK)); + } + // Waitval starts locked, and is unlocked when the thread finishes. + let retval = *thread.waitval.wait(); + + // TODO: Deinitialization code + + Ok(retval) +} + +pub unsafe fn detach(thread: &Pthread) -> Result<(), Errno> { + todo!() +} + +// Returns option because that's a no-op, but PTHREAD_SELF should always be initialized except in +// early init code. +pub fn current_thread() -> Option<&'static Pthread> { + unsafe { + NonNull::new(PTHREAD_SELF.get()).map(|p| p.as_ref()) + } +} + +pub unsafe fn testcancel() { + // TODO: Ordering + if current_thread().unwrap_unchecked().wants_cancel.load(Ordering::SeqCst) { + todo!("cancel") + } +} + +pub unsafe fn exit_current_thread(retval: Retval) -> ! { + let this = current_thread().unwrap_unchecked(); + this.waitval.post(retval); + + Sys::exit_thread() +} + +pub unsafe fn cancel(thread: &Pthread) -> Result<(), Errno> { + thread.wants_cancel.store(true, Ordering::SeqCst); + crate::header::signal +} + +// TODO: Hash map? +static OS_TID_TO_PTHREAD: Mutex>> = Mutex::new(BTreeMap::new()); + +struct ForceSendSync(T); +unsafe impl Send for ForceSendSync {} +unsafe impl Sync for ForceSendSync {} + +#[thread_local] +static PTHREAD_SELF: Cell<*mut Pthread> = Cell::new(core::ptr::null_mut()); + +pub struct ForkHandlers { + pub prepare: Vec, + pub parent: Vec, + pub child: Vec, +} + +// TODO: Use fork handlers +// TODO: Append-only atomic queue, not because of performance, but because of atomicity. +pub static FORK_HANDLERS: Mutex = Mutex::new(ForkHandlers { + parent: Vec::new(), + child: Vec::new(), + prepare: Vec::new(), +}); diff --git a/src/sync/mod.rs b/src/sync/mod.rs index 0ba9373917..9d0f92a609 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -1,6 +1,7 @@ pub mod mutex; pub mod once; pub mod semaphore; +pub mod waitval; pub use self::{ mutex::{Mutex, MutexGuard}, @@ -13,7 +14,6 @@ use crate::{ platform::{types::*, Pal, Sys}, }; use core::{ - cell::UnsafeCell, ops::Deref, sync::atomic::{self, AtomicI32 as AtomicInt}, }; @@ -30,18 +30,19 @@ enum AttemptStatus { /// Convenient wrapper around the "futex" system call for /// synchronization implementations -struct AtomicLock { - atomic: UnsafeCell, +#[repr(C)] +pub(crate) struct AtomicLock { + pub(crate) atomic: AtomicInt, } impl AtomicLock { pub const fn new(value: c_int) -> Self { Self { - atomic: UnsafeCell::new(AtomicInt::new(value)), + atomic: AtomicInt::new(value), } } pub fn notify_one(&self) { Sys::futex( - unsafe { &mut *self.atomic.get() }.get_mut(), + self.atomic.as_mut_ptr(), FUTEX_WAKE, 1, 0, @@ -49,7 +50,7 @@ impl AtomicLock { } pub fn notify_all(&self) { Sys::futex( - unsafe { &mut *self.atomic.get() }.get_mut(), + self.atomic.as_mut_ptr(), FUTEX_WAKE, c_int::max_value(), 0, @@ -57,7 +58,7 @@ impl AtomicLock { } pub fn wait_if(&self, value: c_int, timeout_opt: Option<×pec>) { Sys::futex( - unsafe { &mut *self.atomic.get() }.get_mut(), + self.atomic.as_mut_ptr(), FUTEX_WAIT, value, timeout_opt.map_or(0, |timeout| timeout as *const timespec as usize), @@ -125,6 +126,6 @@ impl Deref for AtomicLock { type Target = AtomicInt; fn deref(&self) -> &Self::Target { - unsafe { &*self.atomic.get() } + &self.atomic } } diff --git a/src/sync/waitval.rs b/src/sync/waitval.rs new file mode 100644 index 0000000000..8f83ce5e3b --- /dev/null +++ b/src/sync/waitval.rs @@ -0,0 +1,39 @@ +// Semaphores need one post per wait, Once doesn't have separate post and wait, and while mutexes +// wait for releasing the lock, it calls notify_one and needs locking again to access the value. + +use core::cell::UnsafeCell; +use core::mem::MaybeUninit; +use core::sync::atomic::Ordering; + +use super::*; + +pub struct Waitval { + state: AtomicLock, + value: UnsafeCell>, +} + +unsafe impl Send for Waitval {} +unsafe impl Sync for Waitval {} + +impl Waitval { + pub const fn new() -> Self { + Self { + state: AtomicLock::new(0), + value: UnsafeCell::new(MaybeUninit::uninit()), + } + } + + pub unsafe fn post(&self, value: T) { + self.value.get().write(MaybeUninit::new(value)); + self.state.store(1, Ordering::Release); + self.state.notify_all(); + } + + pub fn wait(&self) -> &T { + while self.state.load(Ordering::Acquire) == 0 { + self.state.wait_if(0, None); + } + + unsafe { (*self.value.get()).assume_init_ref() } + } +}