-
Notifications
You must be signed in to change notification settings - Fork 87
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
278 additions
and
3 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
use ahash::RandomState; | ||
use core::sync::atomic::{AtomicU32, Ordering::SeqCst}; | ||
use hashbrown::{hash_map::Entry, HashMap}; | ||
|
||
use crate::{ | ||
arch::kernel::{percore::core_scheduler, processor::get_timer_ticks}, | ||
errno::{EAGAIN, EINVAL, ETIMEDOUT}, | ||
scheduler::task::TaskHandlePriorityQueue, | ||
}; | ||
|
||
use super::spinlock::SpinlockIrqSave; | ||
|
||
// TODO: Replace with a concurrent hashmap. | ||
static PARKING_LOT: SpinlockIrqSave<HashMap<usize, TaskHandlePriorityQueue, RandomState>> = | ||
SpinlockIrqSave::new(HashMap::with_hasher(RandomState::with_seeds(0, 0, 0, 0))); | ||
|
||
bitflags! { | ||
pub struct Flags: u32 { | ||
/// Use a relative timeout | ||
const RELATIVE = 0b01; | ||
} | ||
} | ||
|
||
/// If the value at address matches the expected value, park the current thread until it is either | ||
/// woken up with `futex_wake` (returns 0) or the specified timeout elapses (returns -ETIMEDOUT). | ||
/// | ||
/// The timeout is given in microseconds. If [`Flags::RELATIVE`] is given, it is interpreted as | ||
/// relative to the current time. Otherwise it is understood to be an absolute time | ||
/// (see `get_timer_ticks`). | ||
pub fn futex_wait(address: &AtomicU32, expected: u32, timeout: Option<u64>, flags: Flags) -> i32 { | ||
let mut parking_lot = PARKING_LOT.lock(); | ||
// Check the futex value after locking the parking lot so that all changes are observed. | ||
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(address.as_mut_ptr().addr()) | ||
.or_default() | ||
.push(handle); | ||
drop(parking_lot); | ||
|
||
loop { | ||
scheduler.reschedule(); | ||
|
||
// Try to remove ourselves from the waiting queue. | ||
let mut parking_lot = PARKING_LOT.lock(); | ||
let mut wakeup = true; | ||
if let Entry::Occupied(mut queue) = parking_lot.entry(address.as_mut_ptr().addr()) { | ||
// If we are not in the waking queue, this must have been a wakeup. | ||
wakeup = !queue.get_mut().remove(handle); | ||
if queue.get().is_empty() { | ||
queue.remove(); | ||
} | ||
}; | ||
|
||
if wakeup { | ||
return 0; | ||
} else if wakeup_time.is_some_and(|&t| t <= get_timer_ticks()) { | ||
// If the current time is past the wakeup time, the operation timed out. | ||
return -ETIMEDOUT; | ||
} | ||
|
||
// A spurious wakeup occurred, sleep again. | ||
scheduler.block_current_task(wakeup_time); | ||
} | ||
} | ||
|
||
/// Wake `count` threads waiting on the futex at address. Returns the number of threads | ||
/// woken up (saturates to `i32::MAX`). If `count` is `i32::MAX`, wake up all matching | ||
/// waiting threads. If `count` is negative, returns -EINVAL. | ||
pub 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(address.as_mut_ptr().addr()) { | ||
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
//! Synchronization primitives | ||
pub mod futex; | ||
pub mod recmutex; | ||
pub mod semaphore; | ||
pub mod spinlock; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
use core::sync::atomic::AtomicU32; | ||
|
||
use crate::{ | ||
errno::EINVAL, | ||
synch::futex::{self as synch, Flags}, | ||
timespec, timespec_to_microseconds, | ||
}; | ||
|
||
/// Like `synch::futex_wait`, but does extra sanity checks and takes a `timespec`. | ||
/// | ||
/// Returns -EINVAL if | ||
/// * `address` is null | ||
/// * `timeout` is negative | ||
/// * `flags` contains unknown flags | ||
extern "C" fn __sys_futex_wait( | ||
address: *mut u32, | ||
expected: u32, | ||
timeout: *const timespec, | ||
flags: u32, | ||
) -> i32 { | ||
if address.is_null() { | ||
return -EINVAL; | ||
} | ||
|
||
let address = unsafe { &*(address as *const AtomicU32) }; | ||
let timeout = if timeout.is_null() { | ||
None | ||
} else { | ||
match timespec_to_microseconds(unsafe { timeout.read() }) { | ||
t @ Some(_) => t, | ||
None => return -EINVAL, | ||
} | ||
}; | ||
let flags = match Flags::from_bits(flags) { | ||
Some(flags) => flags, | ||
None => return -EINVAL, | ||
}; | ||
|
||
synch::futex_wait(address, expected, timeout, flags) | ||
} | ||
|
||
#[no_mangle] | ||
pub extern "C" fn sys_futex_wait( | ||
address: *mut u32, | ||
expected: u32, | ||
timeout: *const timespec, | ||
flags: u32, | ||
) -> i32 { | ||
kernel_function!(__sys_futex_wait(address, expected, timeout, flags)) | ||
} | ||
|
||
/// Like `synch::futex_wake`, but does extra sanity checks. | ||
/// | ||
/// Returns -EINVAL if `address` is null. | ||
extern "C" fn __sys_futex_wake(address: *mut u32, count: i32) -> i32 { | ||
if address.is_null() { | ||
return -EINVAL; | ||
} | ||
|
||
let address = unsafe { &*(address as *const AtomicU32) }; | ||
synch::futex_wake(address, count) | ||
} | ||
|
||
#[no_mangle] | ||
pub extern "C" fn sys_futex_wake(address: *mut u32, count: i32) -> i32 { | ||
kernel_function!(__sys_futex_wake(address, count)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.