mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-28 03:43:23 +00:00
Support futex wait timeout
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
12325733b0
commit
70505ff4f8
@ -9,7 +9,7 @@ use ostd::{
|
||||
};
|
||||
use spin::Once;
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{prelude::*, time::wait::TimerBuilder};
|
||||
|
||||
type FutexBitSet = u32;
|
||||
type FutexBucketRef = Arc<Mutex<FutexBucket>>;
|
||||
@ -19,21 +19,38 @@ const FUTEX_FLAGS_MASK: u32 = 0xFFFF_FFF0;
|
||||
const FUTEX_BITSET_MATCH_ANY: FutexBitSet = 0xFFFF_FFFF;
|
||||
|
||||
/// do futex wait
|
||||
pub fn futex_wait(futex_addr: u64, futex_val: i32, timeout: &Option<FutexTimeout>) -> Result<()> {
|
||||
futex_wait_bitset(futex_addr as _, futex_val, timeout, FUTEX_BITSET_MATCH_ANY)
|
||||
pub fn futex_wait(
|
||||
futex_addr: u64,
|
||||
futex_val: i32,
|
||||
timer_builder: Option<TimerBuilder>,
|
||||
ctx: &Context,
|
||||
) -> Result<()> {
|
||||
futex_wait_bitset(
|
||||
futex_addr as _,
|
||||
futex_val,
|
||||
timer_builder,
|
||||
FUTEX_BITSET_MATCH_ANY,
|
||||
ctx,
|
||||
)
|
||||
}
|
||||
|
||||
/// do futex wait bitset
|
||||
/// Does futex wait bitset
|
||||
pub fn futex_wait_bitset(
|
||||
futex_addr: Vaddr,
|
||||
futex_val: i32,
|
||||
timeout: &Option<FutexTimeout>,
|
||||
timer_builder: Option<TimerBuilder>,
|
||||
bitset: FutexBitSet,
|
||||
ctx: &Context,
|
||||
) -> Result<()> {
|
||||
debug!(
|
||||
"futex_wait_bitset addr: {:#x}, val: {}, timeout: {:?}, bitset: {:#x}",
|
||||
futex_addr, futex_val, timeout, bitset
|
||||
"futex_wait_bitset addr: {:#x}, val: {}, bitset: {:#x}",
|
||||
futex_addr, futex_val, bitset
|
||||
);
|
||||
|
||||
if bitset == 0 {
|
||||
return_errno_with_message!(Errno::EINVAL, "at least one bit should be set");
|
||||
}
|
||||
|
||||
let futex_key = FutexKey::new(futex_addr, bitset);
|
||||
let (futex_item, waiter) = FutexItem::create(futex_key);
|
||||
|
||||
@ -41,7 +58,7 @@ pub fn futex_wait_bitset(
|
||||
// lock futex bucket ref here to avoid data race
|
||||
let mut futex_bucket = futex_bucket_ref.lock();
|
||||
|
||||
if !futex_key.load_val().is_ok_and(|val| val == futex_val) {
|
||||
if !futex_key.load_val(ctx).is_ok_and(|val| val == futex_val) {
|
||||
return_errno_with_message!(
|
||||
Errno::EAGAIN,
|
||||
"futex value does not match or load_val failed"
|
||||
@ -53,18 +70,15 @@ pub fn futex_wait_bitset(
|
||||
// drop lock
|
||||
drop(futex_bucket);
|
||||
|
||||
// TODO: wait on the futex item with a timeout.
|
||||
waiter.wait();
|
||||
|
||||
Ok(())
|
||||
waiter.pause_timer_timeout(timer_builder.as_ref())
|
||||
}
|
||||
|
||||
/// do futex wake
|
||||
/// Does futex wake
|
||||
pub fn futex_wake(futex_addr: Vaddr, max_count: usize) -> Result<usize> {
|
||||
futex_wake_bitset(futex_addr, max_count, FUTEX_BITSET_MATCH_ANY)
|
||||
}
|
||||
|
||||
/// Do futex wake with bitset
|
||||
/// Does futex wake with bitset
|
||||
pub fn futex_wake_bitset(
|
||||
futex_addr: Vaddr,
|
||||
max_count: usize,
|
||||
@ -75,6 +89,10 @@ pub fn futex_wake_bitset(
|
||||
futex_addr, max_count, bitset
|
||||
);
|
||||
|
||||
if bitset == 0 {
|
||||
return_errno_with_message!(Errno::EINVAL, "at least one bit should be set");
|
||||
}
|
||||
|
||||
let futex_key = FutexKey::new(futex_addr, bitset);
|
||||
let (_, futex_bucket_ref) = get_futex_bucket(futex_key);
|
||||
let mut futex_bucket = futex_bucket_ref.lock();
|
||||
@ -84,7 +102,7 @@ pub fn futex_wake_bitset(
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Do futex requeue
|
||||
/// Does futex requeue
|
||||
pub fn futex_requeue(
|
||||
futex_addr: Vaddr,
|
||||
max_nwakes: usize,
|
||||
@ -153,15 +171,6 @@ pub fn init() {
|
||||
FUTEX_BUCKETS.call_once(|| FutexBucketVec::new(get_bucket_count()));
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FutexTimeout {}
|
||||
|
||||
impl FutexTimeout {
|
||||
pub fn new() -> Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
struct FutexBucketVec {
|
||||
vec: Vec<FutexBucketRef>,
|
||||
}
|
||||
@ -330,10 +339,10 @@ impl FutexKey {
|
||||
Self { addr, bitset }
|
||||
}
|
||||
|
||||
pub fn load_val(&self) -> Result<i32> {
|
||||
pub fn load_val(&self, ctx: &Context) -> Result<i32> {
|
||||
// FIXME: how to implement a atomic load?
|
||||
warn!("implement an atomic load");
|
||||
CurrentUserSpace::get().read_val(self.addr)
|
||||
ctx.get_user_space().read_val(self.addr)
|
||||
}
|
||||
|
||||
pub fn addr(&self) -> Vaddr {
|
||||
|
@ -6,7 +6,10 @@ use ostd::sync::{WaitQueue, Waiter};
|
||||
|
||||
use super::sig_mask::SigMask;
|
||||
use crate::{
|
||||
prelude::*, process::posix_thread::PosixThreadExt, thread::Thread, time::wait::WaitTimeout,
|
||||
prelude::*,
|
||||
process::posix_thread::PosixThreadExt,
|
||||
thread::Thread,
|
||||
time::wait::{TimerBuilder, WaitTimeout},
|
||||
};
|
||||
|
||||
/// `Pause` is an extension trait to make [`Waiter`] and [`WaitQueue`] signal aware.
|
||||
@ -33,7 +36,7 @@ pub trait Pause: WaitTimeout {
|
||||
where
|
||||
F: FnMut() -> Option<R>,
|
||||
{
|
||||
self.pause_until_or_timeout_opt(cond, None)
|
||||
self.pause_until_or_timer_timeout_opt(cond, None)
|
||||
}
|
||||
|
||||
/// Pauses the execution of the current thread until the `cond` is met ( i.e., `cond()`
|
||||
@ -56,7 +59,18 @@ pub trait Pause: WaitTimeout {
|
||||
return cond()
|
||||
.ok_or_else(|| Error::with_message(Errno::ETIME, "the time limit is reached"));
|
||||
}
|
||||
self.pause_until_or_timeout_opt(cond, Some(timeout))
|
||||
let timer_builder = TimerBuilder::new(timeout);
|
||||
self.pause_until_or_timer_timeout_opt(cond, Some(&timer_builder))
|
||||
}
|
||||
|
||||
/// Similar to [`Pause::pause_until_or_timeout`].
|
||||
///
|
||||
/// The only difference is that the timeout is against the user-specified clock.
|
||||
fn pause_until_or_timer_timeout<F, R>(&self, cond: F, timer_builder: &TimerBuilder) -> Result<R>
|
||||
where
|
||||
F: FnMut() -> Option<R>,
|
||||
{
|
||||
self.pause_until_or_timer_timeout_opt(cond, Some(timer_builder))
|
||||
}
|
||||
|
||||
/// Pauses the execution of the current thread until the `cond` is met ( i.e., `cond()`
|
||||
@ -71,14 +85,31 @@ pub trait Pause: WaitTimeout {
|
||||
///
|
||||
/// [`ETIME`]: crate::error::Errno::ETIME
|
||||
/// [`EINTR`]: crate::error::Errno::EINTR
|
||||
#[doc(hidden)]
|
||||
fn pause_until_or_timeout_opt<F, R>(&self, cond: F, timeout: Option<&Duration>) -> Result<R>
|
||||
fn pause_until_or_timer_timeout_opt<F, R>(
|
||||
&self,
|
||||
cond: F,
|
||||
timer_builder: Option<&TimerBuilder>,
|
||||
) -> Result<R>
|
||||
where
|
||||
F: FnMut() -> Option<R>;
|
||||
|
||||
/// Pauses the execution of the current thread until being woken up,
|
||||
/// or some signals are received by the current thread or process.
|
||||
/// If the input `timeout` is set, the pausing will finish when the `timeout` is expired.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If `timeout` is expired before woken up or some signals are received,
|
||||
/// this method will return [`Errno::ETIME`].
|
||||
fn pause_timer_timeout(&self, timer_builder: Option<&TimerBuilder>) -> Result<()>;
|
||||
}
|
||||
|
||||
impl Pause for Waiter {
|
||||
fn pause_until_or_timeout_opt<F, R>(&self, mut cond: F, timeout: Option<&Duration>) -> Result<R>
|
||||
fn pause_until_or_timer_timeout_opt<F, R>(
|
||||
&self,
|
||||
mut cond: F,
|
||||
timer_builder: Option<&TimerBuilder>,
|
||||
) -> Result<R>
|
||||
where
|
||||
F: FnMut() -> Option<R>,
|
||||
{
|
||||
@ -88,9 +119,12 @@ impl Pause for Waiter {
|
||||
|
||||
let current_thread = self.task().data().downcast_ref::<Arc<Thread>>();
|
||||
|
||||
let Some(posix_thread) = current_thread.and_then(|thread| thread.as_posix_thread()) else {
|
||||
if let Some(timeout) = timeout {
|
||||
return self.wait_until_or_timeout(cond, timeout);
|
||||
let Some(posix_thread) = current_thread
|
||||
.as_ref()
|
||||
.and_then(|thread| thread.as_posix_thread())
|
||||
else {
|
||||
if let Some(timer_builder) = timer_builder {
|
||||
return self.wait_until_or_timer_timeout(cond, timer_builder);
|
||||
} else {
|
||||
return self.wait_until_or_cancelled(cond, || Ok(()));
|
||||
}
|
||||
@ -107,18 +141,54 @@ impl Pause for Waiter {
|
||||
};
|
||||
|
||||
posix_thread.set_signalled_waker(self.waker());
|
||||
let res = if let Some(timeout) = timeout {
|
||||
self.wait_until_or_timeout_cancelled(cond, cancel_cond, timeout)
|
||||
let res = if let Some(timer_builder) = timer_builder {
|
||||
self.wait_until_or_timer_timeout_cancelled(cond, cancel_cond, timer_builder)
|
||||
} else {
|
||||
self.wait_until_or_cancelled(cond, cancel_cond)
|
||||
};
|
||||
posix_thread.clear_signalled_waker();
|
||||
res
|
||||
}
|
||||
|
||||
fn pause_timer_timeout(&self, timer_builder: Option<&TimerBuilder>) -> Result<()> {
|
||||
let timer = timer_builder.map(|timer_builder| {
|
||||
let waker = self.waker();
|
||||
timer_builder.fire(move || {
|
||||
waker.wake_up();
|
||||
})
|
||||
});
|
||||
|
||||
let current_thread = self.task().data().downcast_ref::<Arc<Thread>>();
|
||||
|
||||
if let Some(posix_thread) = current_thread
|
||||
.as_ref()
|
||||
.and_then(|thread| thread.as_posix_thread())
|
||||
{
|
||||
posix_thread.set_signalled_waker(self.waker());
|
||||
self.wait();
|
||||
posix_thread.clear_signalled_waker();
|
||||
} else {
|
||||
self.wait();
|
||||
}
|
||||
|
||||
if let Some(timer) = timer {
|
||||
if timer.remain().is_zero() {
|
||||
return_errno_with_message!(Errno::ETIME, "the timeout is reached");
|
||||
}
|
||||
// If the timeout is not expired, cancel the timer manually.
|
||||
timer.cancel();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Pause for WaitQueue {
|
||||
fn pause_until_or_timeout_opt<F, R>(&self, mut cond: F, timeout: Option<&Duration>) -> Result<R>
|
||||
fn pause_until_or_timer_timeout_opt<F, R>(
|
||||
&self,
|
||||
mut cond: F,
|
||||
timer_builder: Option<&TimerBuilder>,
|
||||
) -> Result<R>
|
||||
where
|
||||
F: FnMut() -> Option<R>,
|
||||
{
|
||||
@ -131,7 +201,11 @@ impl Pause for WaitQueue {
|
||||
self.enqueue(waiter.waker());
|
||||
cond()
|
||||
};
|
||||
waiter.pause_until_or_timeout_opt(cond, timeout)
|
||||
waiter.pause_until_or_timer_timeout_opt(cond, timer_builder)
|
||||
}
|
||||
|
||||
fn pause_timer_timeout(&self, _timer_builder: Option<&TimerBuilder>) -> Result<()> {
|
||||
panic!("`pause_timer_timeout` can only be used on `Waiter`");
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user