diff --git a/src/header/semaphore/mod.rs b/src/header/semaphore/mod.rs index 0ca2fa9..a334c7e 100644 --- a/src/header/semaphore/mod.rs +++ b/src/header/semaphore/mod.rs @@ -2,12 +2,27 @@ //! //! See . +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 . @@ -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, NamedSemEntry>>> = Mutex::new(None); + +fn with_named_sems(f: impl FnOnce(&mut BTreeMap, 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::() 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::() }; + if create { + unsafe { ptr::write(&mut named.sem, RlctSempahore::new(value)); } + } + &mut named.sem as *mut RlctSempahore as *mut sem_t +} /// See . // #[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::()) } != 0 { + return -1; + } + } + 0 } /// See . @@ -50,13 +141,31 @@ pub unsafe extern "C" fn sem_init(sem: *mut sem_t, _pshared: c_int, value: c_uin } /// See . -// 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::(), refs: AtomicUsize::new(1), unlinked: false }); }); + ptr } /// See . @@ -76,17 +185,24 @@ pub unsafe extern "C" fn sem_trywait(sem: *mut sem_t) -> c_int { } /// See . -// #[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 . #[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 . @@ -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 . #[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 { diff --git a/src/sync/semaphore.rs b/src/sync/semaphore.rs index ce14961..33f7b32 100644 --- a/src/sync/semaphore.rs +++ b/src/sync/semaphore.rs @@ -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 {