use alloc::sync::Arc;
use core::{marker::PhantomData, mem::align_of, ops::Deref, pin::Pin, ptr, ptr::NonNull};
use crate::{
runtime::{
RuntimeStatus, TaskSystem,
cpu::{CpuLocal, CpuLocalOwnerBorrow, CpuRemote, IrqGuardToken, RuntimeCpuId},
switch::{
CurrentThreadRef, RuntimeScheduleOrigin, RuntimeSchedulerEntry, RuntimeSchedulerReturn,
},
task_runtime,
},
sched::system::SchedulerRequestScope,
thread::{TaskError, ThreadCore, WaitWakeClaim, WaitWakeDelivery, WakeResult},
};
pub(crate) fn wake_thread_from_current_cpu(
core: &Arc<ThreadCore>,
intent: crate::thread::WakeIntent,
) -> WakeResult {
let Ok(system) = runtime_task_system() else {
return WakeResult::Unavailable;
};
system.wake_thread_from_current_cpu(core, intent)
}
pub(crate) fn wake_wait_claim_from_task(
core: &Arc<ThreadCore>,
claim: &WaitWakeClaim,
intent: crate::thread::WakeIntent,
) -> WaitWakeDelivery {
debug_assert!(!task_runtime::in_hard_irq());
let Ok(system) = runtime_task_system() else {
claim.cancel_selected();
return WaitWakeDelivery::Unavailable;
};
system.wake_wait_claim_from_current_cpu(core, claim, intent)
}
pub(crate) fn runtime_task_system() -> Result<&'static TaskSystem, TaskError> {
let handle = unsafe { task_runtime::task_system_handle() };
task_system_from_handle(handle)
}
fn task_system_from_handle(
handle: crate::runtime::TaskSystemHandle,
) -> Result<&'static TaskSystem, TaskError> {
let raw = handle.into_raw();
validate_handle::<TaskSystem>(raw)?;
Ok(unsafe { &*ptr::with_exposed_provenance::<TaskSystem>(raw) })
}
pub(crate) struct RuntimeCurrentCpu {
cpu: CpuLocalOwnerBorrow<'static>,
_irq: RuntimeIrqGuard,
}
impl Deref for RuntimeCurrentCpu {
type Target = CpuLocal;
fn deref(&self) -> &Self::Target {
&self.cpu
}
}
pub(crate) fn runtime_current_cpu() -> Result<RuntimeCurrentCpu, TaskError> {
let mut irq = RuntimeIrqGuard::enter();
let cpu = irq.claim_current_cpu()?;
Ok(RuntimeCurrentCpu { cpu, _irq: irq })
}
mod runtime_cpu_pin_sealed {
pub trait Sealed {}
}
pub(crate) trait RuntimeCpuPin: runtime_cpu_pin_sealed::Sealed {
fn claim_current_cpu(&mut self) -> Result<CpuLocalOwnerBorrow<'static>, TaskError>;
}
#[derive(Clone, Copy)]
struct RuntimeCpuHandles {
cpu_local: NonNull<CpuLocal>,
cpu_remote: &'static CpuRemote,
}
impl RuntimeCpuHandles {
fn capture() -> Self {
let handles = unsafe { task_runtime::current_cpu_owner_handles() };
unsafe { Self::from_snapshot(handles) }
}
unsafe fn from_snapshot(handles: crate::runtime::cpu::CurrentCpuOwnerHandles) -> Self {
Self {
cpu_local: unsafe {
NonNull::new_unchecked(ptr::with_exposed_provenance_mut::<CpuLocal>(
handles.local().into_raw(),
))
},
cpu_remote: unsafe {
&*ptr::with_exposed_provenance::<CpuRemote>(handles.remote().into_raw())
},
}
}
const fn cpu_id(self) -> RuntimeCpuId {
RuntimeCpuId::new(self.cpu_remote.owner().as_u32())
}
const fn remote(self) -> &'static CpuRemote {
self.cpu_remote
}
fn claim(self) -> Result<CpuLocalOwnerBorrow<'static>, TaskError> {
unsafe { self.cpu_remote.claim_local(self.cpu_local.as_ptr()) }
}
unsafe fn borrow_in_scheduler_frame(self) -> CpuLocalOwnerBorrow<'static> {
unsafe {
self.cpu_remote
.borrow_local_in_scheduler_frame(self.cpu_local)
}
}
}
pub(crate) struct RuntimeCpuOwnerBorrow<'pin> {
cpu: CpuLocalOwnerBorrow<'static>,
_pin: PhantomData<&'pin mut ()>,
}
impl RuntimeCpuOwnerBorrow<'_> {
pub(crate) fn as_mut(&mut self) -> Pin<&mut CpuLocal> {
self.cpu.as_pin_mut()
}
}
impl Deref for RuntimeCpuOwnerBorrow<'_> {
type Target = CpuLocal;
fn deref(&self) -> &Self::Target {
&self.cpu
}
}
pub(crate) fn runtime_current_cpu_mut<'pin>(
pin: &'pin mut impl RuntimeCpuPin,
) -> Result<RuntimeCpuOwnerBorrow<'pin>, TaskError> {
Ok(RuntimeCpuOwnerBorrow {
cpu: pin.claim_current_cpu()?,
_pin: PhantomData,
})
}
pub(crate) fn current_cpu_remote() -> Option<&'static CpuRemote> {
let handle = unsafe { task_runtime::current_cpu_remote_handle() };
cpu_remote_from_handle(handle)
}
fn cpu_remote_from_handle(
handle: crate::runtime::cpu::CpuRemoteHandle,
) -> Option<&'static CpuRemote> {
let raw = handle.into_raw();
if validate_handle::<CpuRemote>(raw).is_err() {
return None;
}
let cpu = unsafe { &*ptr::with_exposed_provenance::<CpuRemote>(raw) };
cpu.is_online().then_some(cpu)
}
fn validate_handle<T>(raw: usize) -> Result<(), TaskError> {
if raw == 0 {
Err(TaskError::NotInitialized)
} else if !raw.is_multiple_of(align_of::<T>()) {
Err(TaskError::InvalidRuntimeHandle)
} else {
Ok(())
}
}
pub(crate) fn validate_schedule_context(origin: RuntimeScheduleOrigin) -> Result<(), TaskError> {
match task_runtime::validate_schedule_context(origin) {
RuntimeStatus::Success => Ok(()),
RuntimeStatus::UnsafeContext => Err(TaskError::UnsafeContext),
status => Err(TaskError::RuntimeFailure(status as u32)),
}
}
pub(crate) fn validate_task_context() -> Result<(), TaskError> {
if task_runtime::in_hard_irq() {
Err(TaskError::UnsafeContext)
} else {
Ok(())
}
}
pub(crate) struct RuntimeIrqGuard {
token: IrqGuardToken,
cpu: RuntimeCpuHandles,
_not_send: PhantomData<*mut ()>,
}
impl RuntimeIrqGuard {
pub(crate) fn enter() -> Self {
let token = crate::runtime::enter_irq_guard(crate::runtime::IrqGuardSource::RuntimeCpu);
Self {
token,
cpu: RuntimeCpuHandles::capture(),
_not_send: PhantomData,
}
}
}
impl runtime_cpu_pin_sealed::Sealed for RuntimeIrqGuard {}
impl RuntimeCpuPin for RuntimeIrqGuard {
fn claim_current_cpu(&mut self) -> Result<CpuLocalOwnerBorrow<'static>, TaskError> {
self.cpu.claim()
}
}
impl Drop for RuntimeIrqGuard {
fn drop(&mut self) {
unsafe { task_runtime::irq_guard_exit(self.token) };
}
}
pub(crate) struct RuntimeSchedulerFrameGuard {
return_to: RuntimeSchedulerReturn,
cpu: RuntimeCpuHandles,
system: &'static TaskSystem,
_not_send: PhantomData<*mut ()>,
}
impl runtime_cpu_pin_sealed::Sealed for RuntimeSchedulerFrameGuard {}
impl RuntimeCpuPin for RuntimeSchedulerFrameGuard {
fn claim_current_cpu(&mut self) -> Result<CpuLocalOwnerBorrow<'static>, TaskError> {
Ok(unsafe { self.cpu.borrow_in_scheduler_frame() })
}
}
impl RuntimeSchedulerFrameGuard {
pub(crate) fn enter(
origin: RuntimeScheduleOrigin,
entry: RuntimeSchedulerEntry,
) -> Result<Self, TaskError> {
let context = task_runtime::scheduler_frame_guard_enter(origin, entry);
let status = context.status();
if status != RuntimeStatus::Success {
return Err(TaskError::UnsafeContext);
}
let system = task_system_from_handle(context.system()).unwrap_or_else(|_| {
task_runtime::fatal_invariant(0x5254_0001, context.system().into_raw())
});
let return_to = match entry {
RuntimeSchedulerEntry::Task
| RuntimeSchedulerEntry::PreemptExit
| RuntimeSchedulerEntry::IrqGuardExit => RuntimeSchedulerReturn::Task,
RuntimeSchedulerEntry::IrqReturn | RuntimeSchedulerEntry::IrqReturnContinuation => {
RuntimeSchedulerReturn::IrqReturn
}
};
Ok(Self {
return_to,
cpu: unsafe { RuntimeCpuHandles::from_snapshot(context.cpu()) },
system,
_not_send: PhantomData,
})
}
pub(crate) fn refresh_current_cpu(&mut self) {
let current = unsafe { task_runtime::current_cpu_id() };
if current != self.cpu.cpu_id() {
self.cpu = RuntimeCpuHandles::capture();
}
}
pub(crate) const fn cpu_id(&self) -> RuntimeCpuId {
self.cpu.cpu_id()
}
pub(crate) const fn task_system(&self) -> &'static TaskSystem {
self.system
}
pub(crate) fn current_thread_publication(
&self,
) -> crate::runtime::switch::CurrentThreadPublication {
task_runtime::current_thread_publication()
}
pub(crate) fn current_thread_ref(&self) -> Result<CurrentThreadRef, TaskError> {
unsafe { self.current_thread_publication().borrow_current() }
}
pub(crate) fn scheduler_request_pending(
&self,
scope: SchedulerRequestScope,
) -> Result<bool, TaskError> {
Ok(self.cpu.remote().scheduler_request_pending(scope))
}
}
impl Drop for RuntimeSchedulerFrameGuard {
fn drop(&mut self) {
let needs_reschedule = self.cpu.remote().needs_immediate_scheduler_work();
let _task_context_safe =
task_runtime::scheduler_frame_guard_exit(self.return_to, needs_reschedule);
}
}