relibc: P3-semaphore-comprehensive — apply Phase 0e patch
Re-apply P3-semaphore-comprehensive.patch from local/patches/relibc/ to the local fork. Multi-threading plan Phase 0e.
This commit is contained in:
+141
-18
@@ -2,12 +2,27 @@
|
||||
//!
|
||||
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/semaphore.h.html>.
|
||||
|
||||
use alloc::collections::BTreeMap;
|
||||
use alloc::vec::Vec;
|
||||
use core::ptr;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use crate::{
|
||||
c_str::CStr,
|
||||
header::{
|
||||
bits_timespec::timespec,
|
||||
errno::{EINTR, EINVAL},
|
||||
fcntl::{O_CREAT, O_EXCL, O_RDWR},
|
||||
sys_mman::{
|
||||
MAP_FAILED, MAP_SHARED, PROT_READ, PROT_WRITE, mmap, munmap, shm_open, shm_unlink,
|
||||
},
|
||||
time::{CLOCK_MONOTONIC, CLOCK_REALTIME},
|
||||
},
|
||||
platform::types::{c_char, c_int, c_long, c_uint, clockid_t},
|
||||
platform::{
|
||||
Pal, Sys, ERRNO,
|
||||
types::{c_char, c_int, c_long, c_uint, clockid_t, mode_t},
|
||||
},
|
||||
sync::Mutex,
|
||||
};
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/semaphore.h.html>.
|
||||
@@ -19,11 +34,87 @@ pub union sem_t {
|
||||
pub align: c_long,
|
||||
}
|
||||
pub type RlctSempahore = crate::sync::Semaphore;
|
||||
#[repr(C)]
|
||||
struct NamedSemaphore {
|
||||
sem: RlctSempahore,
|
||||
}
|
||||
|
||||
const SEM_FAILED_PTR: *mut sem_t = usize::MAX as *mut sem_t;
|
||||
|
||||
struct NamedSemEntry {
|
||||
ptr: *mut NamedSemaphore,
|
||||
size: usize,
|
||||
refs: AtomicUsize,
|
||||
unlinked: bool,
|
||||
}
|
||||
unsafe impl Send for NamedSemEntry {}
|
||||
|
||||
static NAMED_SEMS: Mutex<Option<BTreeMap<Vec<u8>, NamedSemEntry>>> = Mutex::new(None);
|
||||
|
||||
fn with_named_sems<R>(f: impl FnOnce(&mut BTreeMap<Vec<u8>, NamedSemEntry>) -> R) -> R {
|
||||
let mut guard = NAMED_SEMS.lock();
|
||||
if guard.is_none() {
|
||||
*guard = Some(BTreeMap::new());
|
||||
}
|
||||
f(guard.as_mut().unwrap())
|
||||
}
|
||||
|
||||
unsafe fn map_named_semaphore(
|
||||
name: *const c_char,
|
||||
oflag: c_int,
|
||||
mode: mode_t,
|
||||
value: c_uint,
|
||||
) -> *mut sem_t {
|
||||
let create = (oflag & O_CREAT) != 0;
|
||||
let shm_flags = if create { O_CREAT | O_EXCL | O_RDWR } else { O_RDWR };
|
||||
let canonical = alloc::format!("sem.{name_cstr}", name_cstr = unsafe { CStr::from_ptr(name) }.to_str().unwrap_or(""));
|
||||
let fd = unsafe { shm_open(canonical.as_ptr() as *const c_char, shm_flags, mode) };
|
||||
if fd < 0 {
|
||||
return SEM_FAILED_PTR;
|
||||
}
|
||||
let sz = core::mem::size_of::<NamedSemaphore>() as usize;
|
||||
if create {
|
||||
if Sys::ftruncate(fd, sz as i64).is_err() {
|
||||
let _ = Sys::close(fd);
|
||||
return SEM_FAILED_PTR;
|
||||
}
|
||||
}
|
||||
let ptr = unsafe { mmap(core::ptr::null_mut(), sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0) };
|
||||
let _ = Sys::close(fd);
|
||||
if ptr == MAP_FAILED {
|
||||
return SEM_FAILED_PTR;
|
||||
}
|
||||
let named: &mut NamedSemaphore = unsafe { &mut *ptr.cast::<NamedSemaphore>() };
|
||||
if create {
|
||||
unsafe { ptr::write(&mut named.sem, RlctSempahore::new(value)); }
|
||||
}
|
||||
&mut named.sem as *mut RlctSempahore as *mut sem_t
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_close.html>.
|
||||
// #[unsafe(no_mangle)]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn sem_close(sem: *mut sem_t) -> c_int {
|
||||
todo!("named semaphores")
|
||||
if sem.is_null() || sem == SEM_FAILED_PTR {
|
||||
ERRNO.set(EINVAL);
|
||||
return -1;
|
||||
}
|
||||
let named_ptr = sem as *mut NamedSemaphore;
|
||||
let mut should_unmap = false;
|
||||
with_named_sems(|map| {
|
||||
let key = map.iter().find(|(_, e)| e.ptr == named_ptr).map(|(k, _)| k.clone());
|
||||
if let Some(key) = key {
|
||||
let entry = map.get(&key).unwrap();
|
||||
let prev = entry.refs.fetch_sub(1, Ordering::Release);
|
||||
if prev == 1 { should_unmap = true; map.remove(&key); }
|
||||
} else { should_unmap = true; }
|
||||
});
|
||||
if should_unmap {
|
||||
if unsafe { munmap(named_ptr as *mut crate::platform::types::c_void, core::mem::size_of::<NamedSemaphore>()) } != 0 {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_destroy.html>.
|
||||
@@ -50,13 +141,31 @@ pub unsafe extern "C" fn sem_init(sem: *mut sem_t, _pshared: c_int, value: c_uin
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_open.html>.
|
||||
// TODO: va_list
|
||||
// #[unsafe(no_mangle)]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn sem_open(
|
||||
name: *const c_char,
|
||||
oflag: c_int, /* (va_list) value: c_uint */
|
||||
oflag: c_int, /* (va_list) mode: mode_t, value: c_uint */
|
||||
) -> *mut sem_t {
|
||||
todo!("named semaphores")
|
||||
if name.is_null() { ERRNO.set(EINVAL); return SEM_FAILED_PTR; }
|
||||
let name_bytes = unsafe { CStr::from_ptr(name) }.to_bytes().to_vec();
|
||||
let create = (oflag & O_CREAT) != 0;
|
||||
let excl = (oflag & O_EXCL) != 0;
|
||||
let existing = with_named_sems(|map| map.get(&name_bytes).map(|e| e.ptr));
|
||||
if let Some(ptr) = existing {
|
||||
if excl { ERRNO.set(crate::header::errno::EEXIST); return SEM_FAILED_PTR; }
|
||||
with_named_sems(|map| { if let Some(e) = map.get(&name_bytes) { e.refs.fetch_add(1, Ordering::Relaxed); } });
|
||||
return ptr as *mut NamedSemaphore as *mut sem_t;
|
||||
}
|
||||
let (mode, value): (mode_t, c_uint) = {
|
||||
let oflag_ptr: *const c_int = &oflag;
|
||||
let mode_ptr = unsafe { oflag_ptr.add(1) as *const mode_t };
|
||||
let value_ptr = unsafe { oflag_ptr.add(2) as *const c_uint };
|
||||
(unsafe { *mode_ptr }, if create { unsafe { *value_ptr } } else { 0 })
|
||||
};
|
||||
let ptr = unsafe { map_named_semaphore(name, oflag, mode, value) };
|
||||
if ptr == SEM_FAILED_PTR { return SEM_FAILED_PTR; }
|
||||
with_named_sems(|map| { map.insert(name_bytes, NamedSemEntry { ptr: ptr as *mut NamedSemaphore, size: core::mem::size_of::<NamedSemaphore>(), refs: AtomicUsize::new(1), unlinked: false }); });
|
||||
ptr
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_post.html>.
|
||||
@@ -76,17 +185,24 @@ pub unsafe extern "C" fn sem_trywait(sem: *mut sem_t) -> c_int {
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_unlink.html>.
|
||||
// #[unsafe(no_mangle)]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn sem_unlink(name: *const c_char) -> c_int {
|
||||
todo!("named semaphores")
|
||||
if name.is_null() { ERRNO.set(EINVAL); return -1; }
|
||||
let name_bytes = unsafe { CStr::from_ptr(name) }.to_bytes().to_vec();
|
||||
with_named_sems(|map| { if let Some(e) = map.get_mut(&name_bytes) { e.unlinked = true; } });
|
||||
unsafe { shm_unlink(name) }
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_trywait.html>.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn sem_wait(sem: *mut sem_t) -> c_int {
|
||||
if let Ok(()) = unsafe { get(sem) }.wait(None, CLOCK_MONOTONIC) {}; // TODO handle error
|
||||
|
||||
0
|
||||
loop {
|
||||
match unsafe { get(sem) }.wait(None, CLOCK_MONOTONIC) {
|
||||
Ok(()) => return 0,
|
||||
Err(e) if e == EINTR => continue,
|
||||
Err(_) => return -1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_clockwait.html>.
|
||||
@@ -96,18 +212,25 @@ pub unsafe extern "C" fn sem_clockwait(
|
||||
clock_id: clockid_t,
|
||||
abstime: *const timespec,
|
||||
) -> c_int {
|
||||
if let Ok(()) = unsafe { get(sem) }.wait(Some(&unsafe { (*abstime).clone() }), clock_id) {}; // TODO handle error
|
||||
|
||||
0
|
||||
loop {
|
||||
match unsafe { get(sem) }.wait(Some(&unsafe { (*abstime).clone() }), clock_id) {
|
||||
Ok(()) => return 0,
|
||||
Err(e) if e == EINTR => continue,
|
||||
Err(_) => return -1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_timedwait.html>.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn sem_timedwait(sem: *mut sem_t, abstime: *const timespec) -> c_int {
|
||||
if let Ok(()) = unsafe { get(sem) }.wait(Some(&unsafe { (*abstime).clone() }), CLOCK_REALTIME) {
|
||||
}; // TODO handle error
|
||||
|
||||
0
|
||||
loop {
|
||||
match unsafe { get(sem) }.wait(Some(&unsafe { (*abstime).clone() }), CLOCK_REALTIME) {
|
||||
Ok(()) => return 0,
|
||||
Err(e) if e == EINTR => continue,
|
||||
Err(_) => return -1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn get<'any>(sem: *mut sem_t) -> &'any RlctSempahore {
|
||||
|
||||
+11
-6
@@ -4,9 +4,13 @@
|
||||
use crate::{
|
||||
header::{
|
||||
bits_timespec::timespec,
|
||||
errno::{EINTR, ETIMEDOUT},
|
||||
time::{CLOCK_MONOTONIC, CLOCK_REALTIME, timespec_realtime_to_monotonic},
|
||||
},
|
||||
platform::types::{c_uint, clockid_t},
|
||||
platform::{
|
||||
ERRNO,
|
||||
types::{c_int, c_uint, clockid_t},
|
||||
},
|
||||
};
|
||||
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
@@ -54,7 +58,7 @@ impl Semaphore {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wait(&self, timeout_opt: Option<×pec>, clock_id: clockid_t) -> Result<(), ()> {
|
||||
pub fn wait(&self, timeout_opt: Option<×pec>, clock_id: clockid_t) -> Result<(), c_int> {
|
||||
loop {
|
||||
let value = self.try_wait();
|
||||
|
||||
@@ -64,19 +68,20 @@ impl Semaphore {
|
||||
|
||||
if let Some(timeout) = timeout_opt {
|
||||
let relative = match clock_id {
|
||||
// FUTEX expect monotonic clock
|
||||
CLOCK_MONOTONIC => timeout.clone(),
|
||||
CLOCK_REALTIME => match timespec_realtime_to_monotonic(timeout.clone()) {
|
||||
Ok(relative) => relative,
|
||||
Err(_) => return Err(()),
|
||||
Err(_) => return Err(ETIMEDOUT),
|
||||
},
|
||||
_ => return Err(()),
|
||||
_ => return Err(ETIMEDOUT),
|
||||
};
|
||||
crate::sync::futex_wait(&self.count, value, Some(&relative));
|
||||
} else {
|
||||
// Use futex to wait for the next change, without a timeout
|
||||
crate::sync::futex_wait(&self.count, value, None);
|
||||
}
|
||||
if ERRNO.get() == EINTR {
|
||||
return Err(EINTR);
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn value(&self) -> c_uint {
|
||||
|
||||
Reference in New Issue
Block a user