use alloc::sync::Arc;
use crate::{
runtime::{
TaskSystem,
context::{RuntimeIrqGuard, runtime_current_cpu_mut, runtime_task_system},
sync::{
PiMutexClaimOutcome, PiMutexLockResult, PiMutexRef, PiWaitCancelOutcome, PiWaitToken,
},
},
thread::{
CurrentThreadToken, ParkPrepare, TaskError, ThreadCore, ThreadId,
current::{
acquire_blocking_permit, current_thread_core_arc,
park::{cancel_current_park, commit_current_park},
},
},
};
pub(crate) enum PiParkAttempt {
Complete,
Retry,
Prepared(Arc<ThreadCore>, crate::thread::ParkTicket),
}
pub fn pi_mutex_lock_slow(
lock: PiMutexRef<'_>,
current: &CurrentThreadToken,
sequence: u64,
) -> Result<PiMutexLockResult, TaskError> {
let _permit = acquire_blocking_permit()?;
let current_core = current_thread_core_arc()?;
if current_core.id() != current.id() {
return Err(TaskError::InvalidPiState);
}
let system = runtime_task_system()?;
let mut irq = RuntimeIrqGuard::enter();
let mut cpu = runtime_current_cpu_mut(&mut irq)?;
system.drain_owner_control(cpu.as_mut())?;
let mut park = loop {
match system.prepare_current_park(¤t_core)? {
ParkPrepare::Notified => continue,
ParkPrepare::Prepared(ticket) => break ticket,
}
};
match system.pi_mutex_lock_slow(lock, current.id(), sequence) {
Ok(PiMutexLockResult::Acquired) => {
system.cancel_current_park(cpu.as_mut(), ¤t_core, &mut park)?;
Ok(PiMutexLockResult::Acquired)
}
Ok(PiMutexLockResult::Waiting(token)) => {
token.install_prepared_park(park);
Ok(PiMutexLockResult::Waiting(token))
}
Err(error) => {
system.cancel_current_park(cpu.as_mut(), ¤t_core, &mut park)?;
Err(error)
}
}
}
pub fn pi_park_current_once(token: &PiWaitToken) -> Result<(), TaskError> {
let system = runtime_task_system()?;
let prepared = if let Some(ticket) = token.take_prepared_park() {
let current = current_thread_core_arc()?;
if current.id() != token.thread_id().into() || ticket.thread() != current.id() {
return Err(TaskError::InvalidPiState);
}
PiParkAttempt::Prepared(current, ticket)
} else {
if token.can_claim() || token.is_granted() {
return Ok(());
}
prepare_pi_park_attempt(system, token)?
};
let (current, mut ticket) = match prepared {
PiParkAttempt::Complete | PiParkAttempt::Retry => return Ok(()),
PiParkAttempt::Prepared(current, ticket) => (current, ticket),
};
if token.can_claim() || token.is_granted() {
cancel_current_park(¤t, &mut ticket)?;
return Ok(());
}
commit_current_park(¤t, &mut ticket).map(|_| ())
}
pub(crate) fn cancel_prepared_pi_park(token: &PiWaitToken) -> Result<(), TaskError> {
let Some(mut ticket) = token.take_prepared_park() else {
return Ok(());
};
let current = current_thread_core_arc()?;
if current.id() != token.thread_id().into() || ticket.thread() != current.id() {
return Err(TaskError::InvalidPiState);
}
cancel_current_park(¤t, &mut ticket)
}
pub(crate) fn prepare_pi_park_attempt(
system: &TaskSystem,
token: &PiWaitToken,
) -> Result<PiParkAttempt, TaskError> {
let _permit = acquire_blocking_permit()?;
let current = current_thread_core_arc()?;
if current.id() != token.thread_id().into() {
return Err(TaskError::InvalidPiState);
}
let mut irq = RuntimeIrqGuard::enter();
let mut cpu = runtime_current_cpu_mut(&mut irq)?;
system.drain_owner_control(cpu.as_mut())?;
if token.can_claim() || token.is_granted() {
return Ok(PiParkAttempt::Complete);
}
match system.prepare_current_park(¤t)? {
ParkPrepare::Notified => Ok(PiParkAttempt::Retry),
ParkPrepare::Prepared(ticket) => Ok(PiParkAttempt::Prepared(current, ticket)),
}
}
pub fn pi_wait_cancel(token: PiWaitToken) -> Result<(), TaskError> {
let outcome = runtime_task_system()?.pi_wait_try_cancel(&token)?;
cancel_prepared_pi_park(&token)?;
match outcome {
PiWaitCancelOutcome::Cancelled => Ok(()),
PiWaitCancelOutcome::HandoffPending => Err(TaskError::InvalidPiState),
}
}
pub fn pi_wait_try_cancel(token: &PiWaitToken) -> Result<PiWaitCancelOutcome, TaskError> {
runtime_task_system()?.pi_wait_try_cancel(token)
}
pub unsafe fn pi_mutex_release_owned(
lock: PiMutexRef<'_>,
old_owner: ThreadId,
) -> Result<(), TaskError> {
runtime_task_system()?.pi_mutex_release(lock, old_owner)
}
pub fn pi_mutex_claim(
token: &PiWaitToken,
current: &CurrentThreadToken,
) -> Result<PiMutexClaimOutcome, TaskError> {
if current.id() != token.thread_id().into() {
return Err(TaskError::InvalidPiState);
}
runtime_task_system()?.pi_mutex_claim(token)
}
pub fn pi_waiter_is_granted(token: &PiWaitToken) -> bool {
let waiter = unsafe {
token
.provider_waiter()
.cast::<crate::thread::PiWaitState>()
.as_ref()
};
waiter.is_granted(token.generation())
}
pub fn pi_waiter_is_top(token: &PiWaitToken) -> bool {
let waiter = unsafe {
token
.provider_waiter()
.cast::<crate::thread::PiWaitState>()
.as_ref()
};
waiter.is_top(token.generation())
}
pub fn pi_initial_owner_is_on_cpu(token: &PiWaitToken) -> Result<bool, TaskError> {
runtime_task_system()?.pi_initial_owner_is_on_cpu(token)
}
pub unsafe fn pi_drop_wait_handle(wait_handle: *mut ()) {
unsafe { crate::thread::drop_pi_mutex_wait_handle(wait_handle) };
}