use alloc::{boxed::Box, string::String};
use core::{
fmt,
sync::atomic::{AtomicBool, AtomicI32, Ordering},
};
use crate::{
runtime::{
context::runtime_task_system,
delivery::inbox::{InboxKind, InboxNode},
lock::PreemptTicketLock,
task_runtime,
},
sync::WaitQueue,
thread::{TaskError, ThreadHandle},
};
#[must_use = "prepare must be published or cancelled"]
pub struct PreparedThread {
handle: Option<ThreadHandle>,
}
impl PreparedThread {
pub(crate) fn new(handle: ThreadHandle) -> Self {
Self {
handle: Some(handle),
}
}
pub fn thread_handle(&self) -> ThreadHandle {
self.handle
.as_ref()
.expect("unconsumed preparation")
.clone()
}
pub fn stage(mut self) -> Result<StagedThread, TaskError> {
let handle = self.handle.as_ref().expect("unconsumed preparation");
runtime_task_system()?.stage_new_thread(handle)?;
Ok(StagedThread {
handle: self.handle.take(),
})
}
pub fn publish(self) -> Result<ThreadHandle, TaskError> {
Ok(self.stage()?.activate())
}
}
impl Drop for PreparedThread {
fn drop(&mut self) {
if let Some(handle) = self.handle.take() {
cancel_new_thread(handle);
}
}
}
#[must_use = "staged publication must be activated or cancelled"]
pub struct StagedThread {
handle: Option<ThreadHandle>,
}
impl StagedThread {
pub fn thread_handle(&self) -> ThreadHandle {
self.handle.as_ref().expect("unconsumed stage").clone()
}
pub fn activate(mut self) -> ThreadHandle {
let mut irq = crate::runtime::context::RuntimeIrqGuard::enter();
let mut cpu = crate::runtime::context::runtime_current_cpu_mut(&mut irq)
.expect("activation requires an installed owner CPU");
let handle = self.handle.as_ref().expect("unconsumed stage");
runtime_task_system()
.expect("staged system remains installed")
.activate_staged_thread(cpu.as_mut(), handle);
self.handle.take().expect("committed stage")
}
}
impl Drop for StagedThread {
fn drop(&mut self) {
if let Some(handle) = self.handle.take() {
cancel_new_thread(handle);
}
}
}
pub(crate) struct ThreadExecution {
pub(crate) cancellation_node: InboxNode,
entry: PreemptTicketLock<Option<Box<dyn FnOnce() + Send + 'static>>>,
completion: WaitQueue,
completed: AtomicBool,
exit_code: AtomicI32,
name: String,
}
impl fmt::Debug for ThreadExecution {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ThreadExecution")
.field("name", &self.name)
.finish_non_exhaustive()
}
}
impl ThreadExecution {
pub(crate) fn new(entry: Box<dyn FnOnce() + Send + 'static>, name: String) -> Self {
Self {
cancellation_node: InboxNode::new(InboxKind::Reclaim),
entry: PreemptTicketLock::new(Some(entry)),
completion: WaitQueue::new(),
completed: AtomicBool::new(false),
exit_code: AtomicI32::new(0),
name,
}
}
pub(crate) fn finish(&self) {
let unused_entry = self.entry.lock().take();
drop(unused_entry);
if !self.completed.swap(true, Ordering::AcqRel) {
self.completion.notify_all();
}
}
}
impl ThreadHandle {
pub fn extension(&self) -> Option<crate::thread::ThreadExtensionBorrow<'_>> {
self.extension_view()
.map(|view| crate::thread::ThreadExtensionBorrow::new(view, self))
}
pub fn wait(&self) -> Result<i32, TaskError> {
if crate::thread::current::current_thread_id()? == self.id() {
return Err(TaskError::InvalidConfiguration);
}
let execution = self
.core
.execution
.as_ref()
.ok_or(TaskError::InvalidConfiguration)?;
execution
.completion
.try_wait_until(|| execution.completed.load(Ordering::Acquire))?;
Ok(execution.exit_code.load(Ordering::Acquire))
}
pub fn join(self) -> Result<i32, TaskError> {
let code = self.wait()?;
match runtime_task_system()?.reap_thread_handle(self) {
Ok(()) => (),
Err(error)
if matches!(
error.task_error(),
TaskError::ThreadBusy | TaskError::NotExited
) =>
{
drop(error.into_retry_handle())
}
Err(error) => return Err(error.task_error()),
}
Ok(code)
}
pub fn detach(self) {
drop(self);
}
}
pub fn exit_current(exit_code: i32) -> ! {
let permit = crate::thread::current::prepare_current_exit()
.unwrap_or_else(|error| task_runtime::fatal_invariant(15, error_code(error)));
let core = crate::thread::current::current_thread_core_arc()
.unwrap_or_else(|error| task_runtime::fatal_invariant(10, error_code(error)));
if let Some(execution) = core.execution.as_ref() {
execution.exit_code.store(exit_code, Ordering::Relaxed);
execution.finish();
}
drop(core);
crate::thread::current::commit_current_exit(permit)
}
pub(crate) unsafe extern "C" fn thread_entry() -> ! {
unsafe { crate::runtime::switch::finish_initial_context_switch() }
.unwrap_or_else(|error| task_runtime::fatal_invariant(9, error_code(error)));
let core = crate::thread::current::current_thread_core_arc()
.unwrap_or_else(|error| task_runtime::fatal_invariant(10, error_code(error)));
let entry = core
.execution
.as_ref()
.expect("thread entry requires execution state")
.entry
.lock()
.take()
.expect("thread entry runs once");
drop(core);
entry();
exit_current(0)
}
fn cancel_new_thread(handle: ThreadHandle) {
let system = runtime_task_system().expect("prepared task system remains installed");
system.publish_thread_cancellation(&handle.core);
drop(handle);
}
const fn error_code(error: TaskError) -> usize {
match error {
TaskError::NotInitialized => 1,
TaskError::InvalidRuntimeHandle => 2,
TaskError::NoRunnableThread => 3,
TaskError::UnsafeContext => 4,
_ => 255,
}
}