Support futex wait timeout

This commit is contained in:
Jianfeng Jiang
2024-08-01 03:17:49 +00:00
committed by Tate, Hongliang Tian
parent 12325733b0
commit 70505ff4f8
8 changed files with 332 additions and 74 deletions

View File

@ -1,19 +1,27 @@
// SPDX-License-Identifier: MPL-2.0
use core::time::Duration;
use crate::{
prelude::*,
process::posix_thread::futex::{
futex_op_and_flags_from_u32, futex_requeue, futex_wait, futex_wait_bitset, futex_wake,
futex_wake_bitset, FutexOp, FutexTimeout,
futex_wake_bitset, FutexFlags, FutexOp,
},
syscall::SyscallReturn,
time::{
clocks::{MonotonicClock, RealTimeClock},
timer::Timeout,
timespec_t,
wait::TimerBuilder,
},
};
pub fn sys_futex(
futex_addr: Vaddr,
futex_op: i32,
futex_val: u32,
utime_addr: u64,
futex_val: u64,
utime_addr: Vaddr,
futex_new_addr: u64,
bitset: u64,
ctx: &Context,
@ -21,8 +29,8 @@ pub fn sys_futex(
// FIXME: we current ignore futex flags
let (futex_op, futex_flags) = futex_op_and_flags_from_u32(futex_op as _)?;
debug!(
"futex_op = {:?}, futex_flags = {:?}, futex_addr = 0x{:x}",
futex_op, futex_flags, futex_addr
"futex_op = {:?}, futex_flags = {:?}, futex_addr = 0x{:x}, futex_val = 0x{:x}",
futex_op, futex_flags, futex_addr, futex_val
);
let get_futex_val = |val: i32| -> Result<usize> {
@ -32,22 +40,65 @@ pub fn sys_futex(
Ok(val as usize)
};
let get_futex_timeout = |timeout_addr| -> Result<Option<FutexTimeout>> {
let get_futex_timer_builder = |timeout_addr: Vaddr| -> Result<Option<TimerBuilder<'static>>> {
if timeout_addr == 0 {
return Ok(None);
}
// TODO: parse a timeout
todo!()
let timeout = {
let time_spec: timespec_t = CurrentUserSpace::get().read_val(timeout_addr)?;
Duration::try_from(time_spec)?
};
let is_real_time = futex_flags.contains(FutexFlags::FUTEX_CLOCK_REALTIME);
if is_real_time && futex_op == FutexOp::FUTEX_WAIT {
// Ref: <https://github.com/torvalds/linux/commit/4fbf5d6837bf81fd7a27d771358f4ee6c4f243f8>
return_errno_with_message!(Errno::ENOSYS, "FUTEX_WAIT cannot use CLOCK_REALTIME");
}
let timeout = {
// From man(2) futex:
// for FUTEX_WAIT, timeout is interpreted as a relative value.
// This differs from other futex operations,
// where timeout is interpreted as an absolute value.
// To obtain the equivalent of FUTEX_WAIT with an absolute timeout,
// employ FUTEX_WAIT_BITSET with val3 specified as FUTEX_BITSET_MATCH_ANY.
if futex_op == FutexOp::FUTEX_WAIT {
Timeout::After(timeout)
} else {
Timeout::When(timeout)
}
};
let timer_manager = if is_real_time {
debug!("futex timeout = {:?}, clock = CLOCK_REALTIME", timeout);
RealTimeClock::timer_manager()
} else {
debug!("futex timeout = {:?}, clock = CLOCK_MONOTONIC", timeout);
MonotonicClock::timer_manager()
};
Ok(Some(TimerBuilder::new_with_timer_manager(
timeout,
timer_manager,
)))
};
let res = match futex_op {
FutexOp::FUTEX_WAIT => {
let timeout = get_futex_timeout(utime_addr)?;
futex_wait(futex_addr as _, futex_val as _, &timeout).map(|_| 0)
let timer_builder = get_futex_timer_builder(utime_addr)?;
futex_wait(futex_addr as _, futex_val as _, timer_builder, ctx).map(|_| 0)
}
FutexOp::FUTEX_WAIT_BITSET => {
let timeout = get_futex_timeout(utime_addr)?;
futex_wait_bitset(futex_addr as _, futex_val as _, &timeout, bitset as _).map(|_| 0)
let timer_builder = get_futex_timer_builder(utime_addr)?;
futex_wait_bitset(
futex_addr as _,
futex_val as _,
timer_builder,
bitset as _,
ctx,
)
.map(|_| 0)
}
FutexOp::FUTEX_WAKE => {
let max_count = get_futex_val(futex_val as i32)?;
@ -68,8 +119,19 @@ pub fn sys_futex(
)
.map(|nwakes| nwakes as _)
}
_ => panic!("Unsupported futex operations"),
}?;
_ => {
warn!("futex op = {:?}", futex_op);
return_errno_with_message!(Errno::EINVAL, "unsupported futex op");
}
}
.map_err(|e| {
// From Linux manual, Futex returns `ETIMEDOUT` instead of `ETIME`
if e.error() == Errno::ETIME {
Error::with_message(Errno::ETIMEDOUT, "futex wait timeout")
} else {
e
}
})?;
debug!("futex returns, tid= {} ", ctx.posix_thread.tid());
Ok(SyscallReturn::Return(res as _))

View File

@ -7,7 +7,14 @@ use ostd::sync::Waiter;
use super::{clock_gettime::read_clock, ClockId, SyscallReturn};
use crate::{
prelude::*,
time::{clockid_t, timespec_t, TIMER_ABSTIME},
time::{
clockid_t,
clocks::{BootTimeClock, MonotonicClock, RealTimeClock},
timer::Timeout,
timespec_t,
wait::TimerBuilder,
TIMER_ABSTIME,
},
};
pub fn sys_nanosleep(
@ -70,7 +77,7 @@ fn do_clock_nanosleep(
);
let start_time = read_clock(clockid, ctx)?;
let timeout = if is_abs_time {
let duration = if is_abs_time {
if request_time < start_time {
return Ok(SyscallReturn::Return(0));
}
@ -84,18 +91,39 @@ fn do_clock_nanosleep(
// current process. i.e., the signals that should be ignored will not interrupt sleeping thread.
let waiter = Waiter::new_pair().0;
let res = waiter.pause_until_or_timeout(|| None, &timeout);
let timer_manager = {
let clock_id = ClockId::try_from(clockid)?;
match clock_id {
ClockId::CLOCK_BOOTTIME => BootTimeClock::timer_manager(),
ClockId::CLOCK_MONOTONIC => MonotonicClock::timer_manager(),
ClockId::CLOCK_REALTIME => RealTimeClock::timer_manager(),
// FIXME: We should better not expose this prof timer manager.
ClockId::CLOCK_PROCESS_CPUTIME_ID => {
ctx.process.timer_manager().prof_timer().timer_manager()
}
// FIXME: From the manual,
// the CPU clock IDs returned by clock_getcpuclockid(3)
// and pthread_getcpuclockid(3) can also be passed in clockid.
// But it's not covered here.
_ => return_errno_with_message!(Errno::EINVAL, "unknown clockid for clock_nanosleep"),
}
};
let timer_builder =
TimerBuilder::new_with_timer_manager(Timeout::After(duration), timer_manager);
let res = waiter.pause_until_or_timer_timeout(|| None, &timer_builder);
match res {
Err(e) if e.error() == Errno::ETIME => Ok(SyscallReturn::Return(0)),
Err(e) if e.error() == Errno::EINTR => {
let end_time = read_clock(clockid, ctx)?;
if end_time >= start_time + timeout {
if end_time >= start_time + duration {
return Ok(SyscallReturn::Return(0));
}
if remain_timespec_addr != 0 && !is_abs_time {
let remaining_duration = (start_time + timeout) - end_time;
let remaining_duration = (start_time + duration) - end_time;
let remaining_timespec = timespec_t::from(remaining_duration);
ctx.get_user_space()
.write_val(remain_timespec_addr, &remaining_timespec)?;