use core::sync::atomic::AtomicU32;
use core::sync::atomic::Ordering::SeqCst;
use ahash::RandomState;
use hashbrown::hash_map::Entry;
use hashbrown::HashMap;
use hermit_sync::InterruptTicketMutex;
use crate::arch::kernel::core_local::core_scheduler;
use crate::arch::kernel::processor::get_timer_ticks;
use crate::errno::{EAGAIN, EINVAL, ETIMEDOUT};
use crate::scheduler::task::TaskHandlePriorityQueue;
static PARKING_LOT: InterruptTicketMutex<HashMap<usize, TaskHandlePriorityQueue, RandomState>> =
InterruptTicketMutex::new(HashMap::with_hasher(RandomState::with_seeds(0, 0, 0, 0)));
bitflags! {
pub struct Flags: u32 {
const RELATIVE = 0b01;
}
}
fn addr(addr: &AtomicU32) -> usize {
let ptr: *const _ = addr;
ptr.addr()
}
pub(crate) fn futex_wait(
address: &AtomicU32,
expected: u32,
timeout: Option<u64>,
flags: Flags,
) -> i32 {
let mut parking_lot = PARKING_LOT.lock();
if address.load(SeqCst) != expected {
return -EAGAIN;
}
let wakeup_time = if flags.contains(Flags::RELATIVE) {
timeout.and_then(|t| get_timer_ticks().checked_add(t))
} else {
timeout
};
let scheduler = core_scheduler();
scheduler.block_current_task(wakeup_time);
let handle = scheduler.get_current_task_handle();
parking_lot.entry(addr(address)).or_default().push(handle);
drop(parking_lot);
loop {
scheduler.reschedule();
let mut parking_lot = PARKING_LOT.lock();
if matches!(wakeup_time, Some(t) if t <= get_timer_ticks()) {
let mut wakeup = true;
if let Entry::Occupied(mut queue) = parking_lot.entry(addr(address)) {
wakeup = !queue.get_mut().remove(handle);
if queue.get().is_empty() {
queue.remove();
}
}
if wakeup {
return 0;
} else {
return -ETIMEDOUT;
}
} else {
let wakeup = !matches!(parking_lot
.get(&addr(address)), Some(queue) if queue.contains(handle));
if wakeup {
return 0;
} else {
scheduler.block_current_task(wakeup_time);
}
}
drop(parking_lot);
}
}
pub(crate) fn futex_wait_and_set(
address: &AtomicU32,
expected: u32,
timeout: Option<u64>,
flags: Flags,
new_value: u32,
) -> i32 {
let mut parking_lot = PARKING_LOT.lock();
if address.swap(new_value, SeqCst) != expected {
return -EAGAIN;
}
let wakeup_time = if flags.contains(Flags::RELATIVE) {
timeout.and_then(|t| get_timer_ticks().checked_add(t))
} else {
timeout
};
let scheduler = core_scheduler();
scheduler.block_current_task(wakeup_time);
let handle = scheduler.get_current_task_handle();
parking_lot.entry(addr(address)).or_default().push(handle);
drop(parking_lot);
loop {
scheduler.reschedule();
let mut parking_lot = PARKING_LOT.lock();
if matches!(wakeup_time, Some(t) if t <= get_timer_ticks()) {
let mut wakeup = true;
if let Entry::Occupied(mut queue) = parking_lot.entry(addr(address)) {
wakeup = !queue.get_mut().remove(handle);
if queue.get().is_empty() {
queue.remove();
}
}
if wakeup {
return 0;
} else {
return -ETIMEDOUT;
}
} else {
let wakeup = !matches!(parking_lot
.get(&addr(address)), Some(queue) if queue.contains(handle));
if wakeup {
return 0;
} else {
scheduler.block_current_task(wakeup_time);
}
}
drop(parking_lot);
}
}
pub(crate) fn futex_wake(address: &AtomicU32, count: i32) -> i32 {
if count < 0 {
return -EINVAL;
}
let mut parking_lot = PARKING_LOT.lock();
let mut queue = match parking_lot.entry(addr(address)) {
Entry::Occupied(entry) => entry,
Entry::Vacant(_) => return 0,
};
let scheduler = core_scheduler();
let mut woken = 0;
while woken != count || count == i32::MAX {
match queue.get_mut().pop() {
Some(handle) => scheduler.custom_wakeup(handle),
None => break,
}
woken = woken.saturating_add(1);
}
if queue.get().is_empty() {
queue.remove();
}
woken
}
pub(crate) fn futex_wake_or_set(address: &AtomicU32, count: i32, new_value: u32) -> i32 {
if count < 0 {
return -EINVAL;
}
let mut parking_lot = PARKING_LOT.lock();
let mut queue = match parking_lot.entry(addr(address)) {
Entry::Occupied(entry) => entry,
Entry::Vacant(_) => {
address.store(new_value, SeqCst);
return 0;
}
};
let scheduler = core_scheduler();
let mut woken = 0;
while woken != count || count == i32::MAX {
match queue.get_mut().pop() {
Some(handle) => scheduler.custom_wakeup(handle),
None => break,
}
woken = woken.saturating_add(1);
}
if queue.get().is_empty() {
queue.remove();
}
if woken == 0 {
address.store(new_value, SeqCst);
}
woken
}