use crate::{
alloc::CBox,
core::{
cstr::nstd_core_cstr_get_null,
optional::{gen_optional, NSTDOptional},
result::NSTDResult,
str::{nstd_core_str_as_cstr, NSTDOptionalStr, NSTDStr},
time::NSTDDuration,
},
heap_ptr::NSTDOptionalHeapPtr,
io::NSTDIOError,
NSTDBool, NSTDUInt,
};
use nstdapi::nstdapi;
use std::thread::{Builder, JoinHandle, Thread, ThreadId};
#[nstdapi]
pub struct NSTDThread {
thread: CBox<JoinHandle<NSTDThreadResult>>,
}
gen_optional!(NSTDOptionalThread, NSTDThread);
#[nstdapi]
pub struct NSTDThreadHandle {
handle: CBox<Thread>,
}
#[nstdapi]
pub struct NSTDThreadID {
id: CBox<ThreadId>,
}
#[nstdapi]
#[derive(Clone, Copy)]
pub struct NSTDThreadDescriptor {
pub name: NSTDOptionalStr,
pub stack_size: NSTDUInt,
}
pub type NSTDThreadResult = NSTDOptionalHeapPtr<'static>;
pub type NSTDOptionalThreadResult = NSTDOptional<NSTDThreadResult>;
pub type NSTDThreadCountResult = NSTDResult<NSTDUInt, NSTDIOError>;
#[nstdapi]
pub unsafe fn nstd_thread_spawn(
#[cfg(not(feature = "capi"))] thread_fn: unsafe fn(NSTDOptionalHeapPtr<'_>) -> NSTDThreadResult,
#[cfg(feature = "capi")] thread_fn: unsafe extern "C" fn(
NSTDOptionalHeapPtr<'_>,
) -> NSTDThreadResult,
data: NSTDOptionalHeapPtr<'static>,
desc: Option<&NSTDThreadDescriptor>,
) -> NSTDOptionalThread {
let mut builder = Builder::new();
if let Some(desc) = desc {
if let NSTDOptional::Some(name) = &desc.name {
let c_name = nstd_core_str_as_cstr(name);
if !nstd_core_cstr_get_null(&c_name).is_null() {
return NSTDOptional::None;
}
builder = builder.name(name.as_str().to_string());
}
if desc.stack_size != 0 {
builder = builder.stack_size(desc.stack_size);
}
}
if let Ok(thread) = builder.spawn(move || thread_fn(data)) {
if let Some(thread) = CBox::new(thread) {
return NSTDOptional::Some(NSTDThread { thread });
}
}
NSTDOptional::None
}
#[inline]
#[nstdapi]
pub fn nstd_thread_current() -> NSTDThreadHandle {
NSTDThreadHandle {
handle: CBox::new(std::thread::current()).expect("failed to allocate for a thread handle"),
}
}
#[inline]
#[nstdapi]
pub fn nstd_thread_handle(thread: &NSTDThread) -> NSTDThreadHandle {
NSTDThreadHandle {
handle: CBox::new(thread.thread.thread().clone())
.expect("failed to allocate for a thread handle"),
}
}
#[inline]
#[nstdapi]
pub fn nstd_thread_is_finished(thread: &NSTDThread) -> NSTDBool {
thread.thread.is_finished()
}
#[inline]
#[nstdapi]
pub unsafe fn nstd_thread_join(thread: NSTDThread) -> NSTDOptionalThreadResult {
thread
.thread
.into_inner()
.join()
.map_or(NSTDOptional::None, NSTDOptional::Some)
}
#[inline]
#[nstdapi]
#[allow(
unused_variables,
clippy::missing_const_for_fn,
clippy::needless_pass_by_value
)]
pub fn nstd_thread_detach(thread: NSTDThread) {}
#[inline]
#[nstdapi]
pub fn nstd_thread_name(handle: &NSTDThreadHandle) -> NSTDOptionalStr {
handle.handle.name().map_or(NSTDOptional::None, |name| {
NSTDOptional::Some(NSTDStr::from_str(name))
})
}
#[inline]
#[nstdapi]
pub fn nstd_thread_id(handle: &NSTDThreadHandle) -> NSTDThreadID {
NSTDThreadID {
id: CBox::new(handle.handle.id()).expect("failed to allocate for a thread ID"),
}
}
#[inline]
#[nstdapi]
#[allow(
unused_variables,
clippy::missing_const_for_fn,
clippy::needless_pass_by_value
)]
pub fn nstd_thread_handle_free(handle: NSTDThreadHandle) {}
#[inline]
#[nstdapi]
pub fn nstd_thread_sleep(duration: NSTDDuration) {
std::thread::sleep(duration.into_duration());
}
#[inline]
#[nstdapi]
pub fn nstd_thread_count() -> NSTDThreadCountResult {
match std::thread::available_parallelism() {
Ok(threads) => NSTDResult::Ok(threads.get()),
Err(err) => NSTDResult::Err(NSTDIOError::from_err(err.kind())),
}
}
#[inline]
#[nstdapi]
#[allow(clippy::missing_const_for_fn)]
pub fn nstd_thread_is_panicking() -> NSTDBool {
#[cfg(panic = "unwind")]
return std::thread::panicking();
#[cfg(panic = "abort")]
return false;
}
#[inline]
#[nstdapi]
pub fn nstd_thread_id_compare(x_id: &NSTDThreadID, y_id: &NSTDThreadID) -> NSTDBool {
*x_id.id == *y_id.id
}
#[inline]
#[nstdapi]
#[allow(
unused_variables,
clippy::missing_const_for_fn,
clippy::needless_pass_by_value
)]
pub fn nstd_thread_id_free(id: NSTDThreadID) {}