use core::sync::atomic::{AtomicU8, AtomicU64, Ordering};
use crate::task::{UserTaskRef, try_current_user_task, yield_now};
const IDLE: u8 = 0;
const HOLDER_READY: u8 = 1;
const USER_COPY_READY: u8 = 2;
const ADDRESS_SPACE_HELD: u8 = 3;
const USER_COPY_COMPLETED: u8 = 4;
const EAGER_PREPARATION_OBSERVED: u8 = 5;
const USER_COPY_FAULT_OBSERVED: u8 = 6;
static USER_COPY_TEST_STATE: AtomicU8 = AtomicU8::new(IDLE);
static USER_COPY_TEST_ADDRESS_SPACE: AtomicU64 = AtomicU64::new(0);
fn address_space_identity(task: &UserTaskRef) -> u64 {
task.as_thread().proc_data.aspace().id().get()
}
fn belongs_to_armed_test(task: &UserTaskRef, expected_state: u8) -> bool {
USER_COPY_TEST_STATE.load(Ordering::Acquire) == expected_state
&& USER_COPY_TEST_ADDRESS_SPACE.load(Ordering::Relaxed) == address_space_identity(task)
}
pub(crate) fn hold_address_space_until_user_copy() -> bool {
let Ok(Some(task)) = try_current_user_task() else {
return false;
};
let address_space = task.as_thread().proc_data.aspace();
USER_COPY_TEST_ADDRESS_SPACE.store(address_space.id().get(), Ordering::Release);
USER_COPY_TEST_STATE.store(HOLDER_READY, Ordering::Release);
loop {
match USER_COPY_TEST_STATE.load(Ordering::Acquire) {
HOLDER_READY => yield_now(),
USER_COPY_READY => break,
EAGER_PREPARATION_OBSERVED => return true,
_ => return false,
}
}
let _guard = address_space.lock();
if USER_COPY_TEST_STATE
.compare_exchange(
USER_COPY_READY,
ADDRESS_SPACE_HELD,
Ordering::AcqRel,
Ordering::Acquire,
)
.is_err()
{
return false;
}
loop {
match USER_COPY_TEST_STATE.load(Ordering::Acquire) {
ADDRESS_SPACE_HELD => yield_now(),
USER_COPY_COMPLETED | EAGER_PREPARATION_OBSERVED | USER_COPY_FAULT_OBSERVED => {
return true;
}
_ => return false,
}
}
}
pub(crate) fn synchronize_user_copy_with_address_space_holder(task: &UserTaskRef) {
if !belongs_to_armed_test(task, HOLDER_READY) {
return;
}
if USER_COPY_TEST_STATE
.compare_exchange(
HOLDER_READY,
USER_COPY_READY,
Ordering::AcqRel,
Ordering::Acquire,
)
.is_err()
{
return;
}
loop {
match USER_COPY_TEST_STATE.load(Ordering::Acquire) {
USER_COPY_READY => yield_now(),
ADDRESS_SPACE_HELD => return,
_ => return,
}
}
}
pub(crate) fn observe_user_copy_test_state() -> usize {
USER_COPY_TEST_STATE.load(Ordering::Acquire) as usize
}
pub(crate) fn record_eager_user_memory_preparation(task: &UserTaskRef) {
if !belongs_to_armed_test(task, HOLDER_READY) {
return;
}
let _ = USER_COPY_TEST_STATE.compare_exchange(
HOLDER_READY,
EAGER_PREPARATION_OBSERVED,
Ordering::AcqRel,
Ordering::Acquire,
);
}
pub(crate) fn record_faulting_user_copy(task: &UserTaskRef) -> bool {
if !belongs_to_armed_test(task, ADDRESS_SPACE_HELD) {
return false;
}
USER_COPY_TEST_STATE
.compare_exchange(
ADDRESS_SPACE_HELD,
USER_COPY_FAULT_OBSERVED,
Ordering::AcqRel,
Ordering::Acquire,
)
.is_ok()
}
pub(crate) fn record_user_copy_completed(task: &UserTaskRef) {
if !belongs_to_armed_test(task, ADDRESS_SPACE_HELD) {
return;
}
let _ = USER_COPY_TEST_STATE.compare_exchange(
ADDRESS_SPACE_HELD,
USER_COPY_COMPLETED,
Ordering::AcqRel,
Ordering::Acquire,
);
}