use crate::{non_named::{self, InitOnce, Semaphore as _},
SemaphoreRef};
use core::{cell::UnsafeCell,
ffi::{c_int, c_uint},
marker::PhantomPinned,
mem::MaybeUninit,
pin::Pin};
#[must_use]
#[derive(Debug)]
pub struct Semaphore {
inner: MaybeUninit<UnsafeCell<libc::sem_t>>,
init_once: InitOnce,
_pinned: PhantomPinned,
}
unsafe impl Sync for Semaphore {}
impl Semaphore {
const SINGLE_PROCESS_PRIVATE: c_int = 0;
const MULTI_PROCESS_SHARED: c_int = 1;
#[inline]
pub const fn uninit() -> Self {
Self {
inner: MaybeUninit::uninit(),
init_once: InitOnce::new(),
_pinned: PhantomPinned,
}
}
#[inline]
#[allow(clippy::missing_panics_doc, clippy::same_name_method, clippy::unwrap_in_result)]
pub fn init_with(
self: Pin<&Self>,
is_shared: bool,
sem_count: c_uint,
) -> Result<SemaphoreRef<'_>, bool> {
let r = self.init_once.call_once(|| {
let sem: *mut libc::sem_t = UnsafeCell::raw_get(MaybeUninit::as_ptr(&self.inner));
let r = unsafe {
libc::sem_init(
sem,
if is_shared {
Semaphore::MULTI_PROCESS_SHARED
} else {
Semaphore::SINGLE_PROCESS_PRIVATE
},
sem_count,
)
};
if r == 0 { Ok(()) } else { Err(()) }
});
match r {
#[allow(clippy::expect_used)]
Some(Ok(())) => Ok(self.sem_ref().expect("the `Semaphore` is ready")),
Some(Err(())) => Err(false),
None => Err(true),
}
}
fn ready_ref(self: Pin<&Self>) -> Option<Pin<&'_ UnsafeCell<libc::sem_t>>> {
#![allow(clippy::if_then_some_else_none)]
if self.init_once.is_ready() {
fn project_inner_init(it: &Semaphore) -> &UnsafeCell<libc::sem_t> {
let sem = &it.inner;
unsafe { MaybeUninit::assume_init_ref(sem) }
}
let sem = unsafe { Pin::map_unchecked(self, project_inner_init) };
Some(sem)
} else {
None
}
}
}
impl non_named::Sealed for Semaphore {}
impl non_named::Semaphore for Semaphore {
#[inline]
fn init_with(self: Pin<&Self>, sem_count: c_uint) -> Result<SemaphoreRef<'_>, bool> {
Semaphore::init_with(self, false, sem_count)
}
#[inline]
fn sem_ref(self: Pin<&Self>) -> Result<SemaphoreRef<'_>, ()> {
self.ready_ref()
.map(|sem| {
unsafe { SemaphoreRef::unnamed(sem) }
})
.ok_or(())
}
}
impl Default for Semaphore {
#[inline]
fn default() -> Self { Self::uninit() }
}
impl Drop for Semaphore {
#[inline]
fn drop(&mut self) {
fn pinned_drop(this: Pin<&mut Semaphore>) {
if let Some(sem) = this.into_ref().ready_ref() {
let r = unsafe { libc::sem_destroy(sem.get()) };
debug_assert_eq!(r, 0, "the semaphore is valid with no waiters");
}
}
pinned_drop(unsafe { Pin::new_unchecked(self) });
}
}
#[cfg(doctest)]
mod compile_fail_tests {
fn must_pin() {}
fn lifetime_enforced() {}
}