Refactor the timer module to solve race conditions and support fdinfo for timerfd files
This commit is contained in:
parent
59cd3bd61d
commit
7305d389f0
|
|
@ -14,7 +14,10 @@ use crate::{
|
|||
ipc::{key_t, semaphore::system_v::sem_set::sem_sets, IpcFlags},
|
||||
prelude::*,
|
||||
process::Pid,
|
||||
time::{clocks::JIFFIES_TIMER_MANAGER, timer::Timeout},
|
||||
time::{
|
||||
clocks::JIFFIES_TIMER_MANAGER,
|
||||
timer::{Timeout, TimerGuard},
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Pod)]
|
||||
|
|
@ -170,10 +173,14 @@ pub fn sem_op(
|
|||
if let Some(timeout) = timeout {
|
||||
pending_op.waker = Some(waker.clone());
|
||||
|
||||
let jiffies_timer = JIFFIES_TIMER_MANAGER.get().unwrap().create_timer(move || {
|
||||
waker.wake_up();
|
||||
});
|
||||
jiffies_timer.set_timeout(Timeout::After(timeout));
|
||||
let jiffies_timer =
|
||||
JIFFIES_TIMER_MANAGER
|
||||
.get()
|
||||
.unwrap()
|
||||
.create_timer(move |_guard: TimerGuard| {
|
||||
waker.wake_up();
|
||||
});
|
||||
jiffies_timer.lock().set_timeout(Timeout::After(timeout));
|
||||
} else {
|
||||
pending_op.waker = Some(waker);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ use crate::{
|
|||
Pid,
|
||||
},
|
||||
thread::{Thread, Tid},
|
||||
time::{clocks::ProfClock, Timer, TimerManager},
|
||||
time::{clocks::ProfClock, timer::TimerGuard, Timer, TimerManager},
|
||||
};
|
||||
|
||||
mod builder;
|
||||
|
|
@ -261,7 +261,7 @@ impl PosixThread {
|
|||
/// Creates a timer based on the profiling CPU clock of the current thread.
|
||||
pub fn create_prof_timer<F>(&self, func: F) -> Arc<Timer>
|
||||
where
|
||||
F: Fn() + Send + Sync + 'static,
|
||||
F: Fn(TimerGuard) + Send + Sync + 'static,
|
||||
{
|
||||
self.prof_timer_manager.create_timer(func)
|
||||
}
|
||||
|
|
@ -269,7 +269,7 @@ impl PosixThread {
|
|||
/// Creates a timer based on the user CPU clock of the current thread.
|
||||
pub fn create_virtual_timer<F>(&self, func: F) -> Arc<Timer>
|
||||
where
|
||||
F: Fn() + Send + Sync + 'static,
|
||||
F: Fn(TimerGuard) + Send + Sync + 'static,
|
||||
{
|
||||
self.virtual_timer_manager.create_timer(func)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ use crate::{
|
|||
},
|
||||
time::{
|
||||
clocks::{ProfClock, RealTimeClock},
|
||||
timer::TimerGuard,
|
||||
Timer, TimerManager,
|
||||
},
|
||||
};
|
||||
|
|
@ -99,7 +100,7 @@ pub struct PosixTimerManager {
|
|||
posix_timers: Mutex<Vec<Option<Arc<Timer>>>>,
|
||||
}
|
||||
|
||||
fn create_process_timer_callback(process_ref: &Weak<Process>) -> impl Fn() + Clone {
|
||||
fn create_process_timer_callback(process_ref: &Weak<Process>) -> impl Fn(TimerGuard) + Clone {
|
||||
let current_process = process_ref.clone();
|
||||
let sent_signal = move || {
|
||||
let signal = KernelSignal::new(SIGALRM);
|
||||
|
|
@ -111,7 +112,7 @@ fn create_process_timer_callback(process_ref: &Weak<Process>) -> impl Fn() + Clo
|
|||
let work_func = Box::new(sent_signal);
|
||||
let work_item = WorkItem::new(work_func);
|
||||
|
||||
move || {
|
||||
move |_guard: TimerGuard| {
|
||||
submit_work_item(
|
||||
work_item.clone(),
|
||||
crate::thread::work_queue::WorkPriority::High,
|
||||
|
|
@ -158,7 +159,7 @@ impl PosixTimerManager {
|
|||
/// Creates a timer based on the profiling CPU clock of the current process.
|
||||
pub fn create_prof_timer<F>(&self, func: F) -> Arc<Timer>
|
||||
where
|
||||
F: Fn() + Send + Sync + 'static,
|
||||
F: Fn(TimerGuard) + Send + Sync + 'static,
|
||||
{
|
||||
self.prof_timer.timer_manager().create_timer(func)
|
||||
}
|
||||
|
|
@ -166,7 +167,7 @@ impl PosixTimerManager {
|
|||
/// Creates a timer based on the user CPU clock of the current process.
|
||||
pub fn create_virtual_timer<F>(&self, func: F) -> Arc<Timer>
|
||||
where
|
||||
F: Fn() + Send + Sync + 'static,
|
||||
F: Fn(TimerGuard) + Send + Sync + 'static,
|
||||
{
|
||||
self.virtual_timer.timer_manager().create_timer(func)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,10 @@ use crate::{
|
|||
prelude::*,
|
||||
process::{posix_thread::AsPosixThread, signal::HandlePendingSignal},
|
||||
thread::AsThread,
|
||||
time::wait::{ManagedTimeout, TimeoutExt},
|
||||
time::{
|
||||
timer::TimerGuard,
|
||||
wait::{ManagedTimeout, TimeoutExt},
|
||||
},
|
||||
};
|
||||
|
||||
/// `Pause` is an extension trait to make [`Waiter`] and [`WaitQueue`] signal aware.
|
||||
|
|
@ -160,7 +163,7 @@ impl Pause for Waiter {
|
|||
fn pause_timeout(&self, timeout: &TimeoutExt<'_>) -> Result<()> {
|
||||
let timer = timeout.check_expired()?.map(|timeout| {
|
||||
let waker = self.waker();
|
||||
timeout.create_timer(move || {
|
||||
timeout.create_timer(move |_guard: TimerGuard| {
|
||||
waker.wake_up();
|
||||
})
|
||||
});
|
||||
|
|
@ -189,11 +192,12 @@ impl Pause for Waiter {
|
|||
}
|
||||
|
||||
if let Some(timer) = timer {
|
||||
if timer.remain().is_zero() {
|
||||
let timer_guard = timer.lock();
|
||||
if timer_guard.remain().is_zero() {
|
||||
return_errno_with_message!(Errno::ETIME, "the time limit is reached");
|
||||
}
|
||||
// If the timeout is not expired, cancel the timer manually.
|
||||
timer.cancel();
|
||||
timer_guard.cancel();
|
||||
}
|
||||
|
||||
if posix_thread_opt
|
||||
|
|
|
|||
|
|
@ -9,8 +9,9 @@ pub fn sys_alarm(seconds: u32, ctx: &Context) -> Result<SyscallReturn> {
|
|||
debug!("seconds = {}", seconds);
|
||||
|
||||
let alarm_timer = ctx.process.timer_manager().alarm_timer();
|
||||
let mut timer_guard = alarm_timer.lock();
|
||||
|
||||
let remaining = alarm_timer.remain();
|
||||
let remaining = timer_guard.remain();
|
||||
let mut remaining_secs = remaining.as_secs();
|
||||
if remaining.subsec_nanos() > 0 {
|
||||
remaining_secs += 1;
|
||||
|
|
@ -18,11 +19,11 @@ pub fn sys_alarm(seconds: u32, ctx: &Context) -> Result<SyscallReturn> {
|
|||
|
||||
if seconds == 0 {
|
||||
// Clear previous timer
|
||||
alarm_timer.cancel();
|
||||
timer_guard.cancel();
|
||||
return Ok(SyscallReturn::Return(remaining_secs as _));
|
||||
}
|
||||
|
||||
alarm_timer.set_timeout(Timeout::After(Duration::from_secs(seconds as u64)));
|
||||
timer_guard.set_timeout(Timeout::After(Duration::from_secs(seconds as u64)));
|
||||
|
||||
Ok(SyscallReturn::Return(remaining_secs as _))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,9 +44,22 @@ pub fn sys_setitimer(
|
|||
ItimerType::ITIMER_PROF => process_timer_manager.prof_timer(),
|
||||
};
|
||||
|
||||
let mut timer_guard = timer.lock();
|
||||
|
||||
let (old_interval, remain) = (timer_guard.interval(), timer_guard.remain());
|
||||
|
||||
timer_guard.set_interval(interval);
|
||||
if expire_time == Duration::ZERO {
|
||||
// Clear previous timer
|
||||
timer_guard.cancel();
|
||||
} else {
|
||||
timer_guard.set_timeout(Timeout::After(expire_time));
|
||||
}
|
||||
|
||||
drop(timer_guard);
|
||||
if old_itimerval_addr > 0 {
|
||||
let old_interval = timeval_t::from(timer.interval());
|
||||
let remain = timeval_t::from(timer.remain());
|
||||
let old_interval = timeval_t::from(old_interval);
|
||||
let remain = timeval_t::from(remain);
|
||||
let old_itimerval = itimerval_t {
|
||||
it_interval: old_interval,
|
||||
it_value: remain,
|
||||
|
|
@ -54,14 +67,6 @@ pub fn sys_setitimer(
|
|||
user_space.write_val(old_itimerval_addr, &old_itimerval)?;
|
||||
}
|
||||
|
||||
timer.set_interval(interval);
|
||||
if expire_time == Duration::ZERO {
|
||||
// Clear previous timer
|
||||
timer.cancel();
|
||||
} else {
|
||||
timer.set_timeout(Timeout::After(expire_time));
|
||||
}
|
||||
|
||||
Ok(SyscallReturn::Return(0))
|
||||
}
|
||||
|
||||
|
|
@ -86,8 +91,14 @@ pub fn sys_getitimer(
|
|||
ItimerType::ITIMER_PROF => process_timer_manager.prof_timer(),
|
||||
};
|
||||
|
||||
let interval = timeval_t::from(timer.interval());
|
||||
let remain = timeval_t::from(timer.remain());
|
||||
let (interval, remain) = {
|
||||
let timer_guard = timer.lock();
|
||||
(
|
||||
timeval_t::from(timer_guard.interval()),
|
||||
timeval_t::from(timer_guard.remain()),
|
||||
)
|
||||
};
|
||||
|
||||
let itimerval = itimerval_t {
|
||||
it_interval: interval,
|
||||
it_value: remain,
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ use crate::{
|
|||
time::{
|
||||
clockid_t,
|
||||
clocks::{BootTimeClock, MonotonicClock, RealTimeClock},
|
||||
timer::TimerGuard,
|
||||
Timer,
|
||||
},
|
||||
};
|
||||
|
|
@ -97,7 +98,7 @@ pub fn sys_timer_create(
|
|||
|
||||
let work_func = sent_signal;
|
||||
let work_item = WorkItem::new(work_func);
|
||||
let func = move || {
|
||||
let func = move |_guard: TimerGuard| {
|
||||
submit_work_item(
|
||||
work_item.clone(),
|
||||
crate::thread::work_queue::WorkPriority::High,
|
||||
|
|
@ -117,7 +118,7 @@ pub fn sys_timer_delete(timer_id: usize, _ctx: &Context) -> Result<SyscallReturn
|
|||
return_errno_with_message!(Errno::EINVAL, "invalid timer ID");
|
||||
};
|
||||
|
||||
timer.cancel();
|
||||
timer.lock().cancel();
|
||||
Ok(SyscallReturn::Return(0))
|
||||
}
|
||||
|
||||
|
|
@ -126,7 +127,7 @@ pub fn sys_timer_delete(timer_id: usize, _ctx: &Context) -> Result<SyscallReturn
|
|||
/// This timer will invoke the given callback function (`func`) when it expires.
|
||||
pub fn create_timer<F>(clockid: clockid_t, func: F, ctx: &Context) -> Result<Arc<Timer>>
|
||||
where
|
||||
F: Fn() + Send + Sync + 'static,
|
||||
F: Fn(TimerGuard) + Send + Sync + 'static,
|
||||
{
|
||||
let process_timer_manager = ctx.process.timer_manager();
|
||||
let timer = if clockid >= 0 {
|
||||
|
|
|
|||
|
|
@ -28,27 +28,32 @@ pub fn sys_timer_settime(
|
|||
return_errno_with_message!(Errno::EINVAL, "invalid timer ID");
|
||||
};
|
||||
|
||||
if old_itimerspec_addr > 0 {
|
||||
let old_interval = timespec_t::from(timer.interval());
|
||||
let remain = timespec_t::from(timer.remain());
|
||||
let old_itimerspec = itimerspec_t {
|
||||
it_interval: old_interval,
|
||||
it_value: remain,
|
||||
};
|
||||
user_space.write_val(old_itimerspec_addr, &old_itimerspec)?;
|
||||
}
|
||||
let mut timer_guard = timer.lock();
|
||||
|
||||
timer.set_interval(interval);
|
||||
let (old_interval, remain) = (timer_guard.interval(), timer_guard.remain());
|
||||
|
||||
timer_guard.set_interval(interval);
|
||||
if expire_time == Duration::ZERO {
|
||||
// Clear previous timer
|
||||
timer.cancel();
|
||||
timer_guard.cancel();
|
||||
} else {
|
||||
let timeout = if (flags & TIMER_ABSTIME) == 0 {
|
||||
Timeout::After(expire_time)
|
||||
} else {
|
||||
Timeout::When(expire_time)
|
||||
};
|
||||
timer.set_timeout(timeout);
|
||||
timer_guard.set_timeout(timeout);
|
||||
}
|
||||
|
||||
drop(timer_guard);
|
||||
if old_itimerspec_addr > 0 {
|
||||
let old_interval = timespec_t::from(old_interval);
|
||||
let remain = timespec_t::from(remain);
|
||||
let old_itimerspec = itimerspec_t {
|
||||
it_interval: old_interval,
|
||||
it_value: remain,
|
||||
};
|
||||
user_space.write_val(old_itimerspec_addr, &old_itimerspec)?;
|
||||
}
|
||||
|
||||
Ok(SyscallReturn::Return(0))
|
||||
|
|
@ -66,8 +71,14 @@ pub fn sys_timer_gettime(
|
|||
return_errno_with_message!(Errno::EINVAL, "invalid timer ID");
|
||||
};
|
||||
|
||||
let interval = timespec_t::from(timer.interval());
|
||||
let remain = timespec_t::from(timer.remain());
|
||||
let (interval, remain) = {
|
||||
let timer_guard = timer.lock();
|
||||
(
|
||||
timespec_t::from(timer_guard.interval()),
|
||||
timespec_t::from(timer_guard.remain()),
|
||||
)
|
||||
};
|
||||
|
||||
let itimerspec = itimerspec_t {
|
||||
it_interval: interval,
|
||||
it_value: remain,
|
||||
|
|
|
|||
|
|
@ -23,11 +23,10 @@ pub fn sys_timerfd_gettime(
|
|||
.downcast_ref::<TimerfdFile>()
|
||||
.ok_or_else(|| Error::with_message(Errno::EINVAL, "the fd is not a timerfd"))?;
|
||||
|
||||
let interval = timespec_t::from(timerfd_file.timer().interval());
|
||||
let remain = timespec_t::from(timerfd_file.timer().remain());
|
||||
let (interval, remain) = timerfd_file.get_time();
|
||||
let itimerspec = itimerspec_t {
|
||||
it_interval: interval,
|
||||
it_value: remain,
|
||||
it_interval: timespec_t::from(interval),
|
||||
it_value: timespec_t::from(remain),
|
||||
};
|
||||
ctx.user_space().write_val(itimerspec_addr, &itimerspec)?;
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ use crate::{
|
|||
prelude::*,
|
||||
time::{
|
||||
itimerspec_t,
|
||||
timer::Timeout,
|
||||
timerfd::{TFDSetTimeFlags, TimerfdFile},
|
||||
timespec_t,
|
||||
},
|
||||
|
|
@ -35,10 +34,10 @@ pub fn sys_timerfd_settime(
|
|||
let interval = Duration::try_from(new_itimerspec.it_interval)?;
|
||||
let expire_time = Duration::try_from(new_itimerspec.it_value)?;
|
||||
|
||||
let timer = timerfd_file.timer();
|
||||
let (old_interval, remain) = timerfd_file.set_time(expire_time, interval, flags);
|
||||
if old_itimerspec_addr > 0 {
|
||||
let old_interval = timespec_t::from(timer.interval());
|
||||
let remain = timespec_t::from(timer.remain());
|
||||
let old_interval = timespec_t::from(old_interval);
|
||||
let remain = timespec_t::from(remain);
|
||||
let old_itimerspec = itimerspec_t {
|
||||
it_interval: old_interval,
|
||||
it_value: remain,
|
||||
|
|
@ -46,24 +45,5 @@ pub fn sys_timerfd_settime(
|
|||
user_space.write_val(old_itimerspec_addr, &old_itimerspec)?;
|
||||
}
|
||||
|
||||
timer.set_interval(interval);
|
||||
timer.cancel();
|
||||
// Clear `ticks` after cancel the timer to ensure that `ticks` is zero
|
||||
// when the timer is rearmed.
|
||||
timerfd_file.clear_ticks();
|
||||
|
||||
if expire_time != Duration::ZERO {
|
||||
if flags.contains(TFDSetTimeFlags::TFD_TIMER_CANCEL_ON_SET) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
let timeout = if flags.contains(TFDSetTimeFlags::TFD_TIMER_ABSTIME) {
|
||||
Timeout::When(expire_time)
|
||||
} else {
|
||||
Timeout::After(expire_time)
|
||||
};
|
||||
timer.set_timeout(timeout);
|
||||
}
|
||||
|
||||
Ok(SyscallReturn::Return(0))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use core::{
|
|||
time::Duration,
|
||||
};
|
||||
|
||||
use ostd::sync::SpinLock;
|
||||
use ostd::sync::{LocalIrqDisabled, SpinLock, SpinLockGuard};
|
||||
|
||||
use super::Clock;
|
||||
|
||||
|
|
@ -31,84 +31,79 @@ pub enum Timeout {
|
|||
/// its `interval` field with [`Timer::set_interval`]. By doing this,
|
||||
/// the timer will use the interval time to configure a new timing after expiration.
|
||||
pub struct Timer {
|
||||
interval: SpinLock<Duration>,
|
||||
inner: SpinLock<TimerInner>,
|
||||
timer_manager: Arc<TimerManager>,
|
||||
registered_callback: Box<dyn Fn() + Send + Sync>,
|
||||
timer_callback: SpinLock<Weak<TimerCallback>>,
|
||||
registered_callback: Box<dyn Fn(TimerGuard) + Send + Sync>,
|
||||
}
|
||||
|
||||
impl Timer {
|
||||
/// Create a `Timer` instance from a [`TimerManager`].
|
||||
/// This timer will be managed by the `TimerManager`.
|
||||
#[derive(Default)]
|
||||
struct TimerInner {
|
||||
interval: Duration,
|
||||
timer_callback: Weak<TimerCallback>,
|
||||
}
|
||||
|
||||
/// A guard that provides exclusive access to a `Timer`.
|
||||
pub struct TimerGuard<'a> {
|
||||
inner: SpinLockGuard<'a, TimerInner, LocalIrqDisabled>,
|
||||
timer: &'a Arc<Timer>,
|
||||
}
|
||||
|
||||
impl TimerGuard<'_> {
|
||||
/// Sets the interval time for this timer.
|
||||
///
|
||||
/// Note that if the callback instructions involves sleep, users should put these instructions
|
||||
/// into something like `WorkQueue` to avoid sleeping during system timer interruptions.
|
||||
fn new<F>(registered_callback: F, timer_manager: Arc<TimerManager>) -> Arc<Self>
|
||||
where
|
||||
F: Fn() + Send + Sync + 'static,
|
||||
{
|
||||
Arc::new(Self {
|
||||
interval: SpinLock::new(Duration::ZERO),
|
||||
timer_manager,
|
||||
registered_callback: Box::new(registered_callback),
|
||||
timer_callback: SpinLock::new(Weak::default()),
|
||||
})
|
||||
}
|
||||
|
||||
/// Set the interval time for this timer.
|
||||
/// The timer will be reset with the interval time upon expiration.
|
||||
pub fn set_interval(&self, interval: Duration) {
|
||||
*self.interval.disable_irq().lock() = interval;
|
||||
pub fn set_interval(&mut self, interval: Duration) {
|
||||
self.inner.interval = interval;
|
||||
}
|
||||
|
||||
/// Cancel the current timer's set timeout callback.
|
||||
pub fn cancel(&self) {
|
||||
let timer_callback = self.timer_callback.disable_irq().lock();
|
||||
if let Some(timer_callback) = timer_callback.upgrade() {
|
||||
timer_callback.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the timer with a timeout.
|
||||
/// Sets the timer with a timeout.
|
||||
///
|
||||
/// The registered callback function of this timer will be invoked
|
||||
/// when reaching timeout. If the timer has a valid interval, this timer
|
||||
/// will be set again with the interval when reaching timeout.
|
||||
pub fn set_timeout(self: &Arc<Self>, timeout: Timeout) {
|
||||
pub fn set_timeout(&mut self, timeout: Timeout) {
|
||||
let expired_time = match timeout {
|
||||
Timeout::After(timeout) => {
|
||||
let now = self.timer_manager.clock.read_time();
|
||||
let now = self.timer.timer_manager.clock.read_time();
|
||||
now + timeout
|
||||
}
|
||||
Timeout::When(timeout) => timeout,
|
||||
};
|
||||
|
||||
let timer_weak = Arc::downgrade(self);
|
||||
let new_timer_callback = Arc::new(TimerCallback::new(
|
||||
expired_time,
|
||||
Box::new(move || interval_timer_callback(&timer_weak)),
|
||||
));
|
||||
let timer_weak = Arc::downgrade(self.timer);
|
||||
let new_timer_callback = Arc::new(TimerCallback::new(expired_time, timer_weak));
|
||||
|
||||
let mut timer_callback = self.timer_callback.disable_irq().lock();
|
||||
if let Some(timer_callback) = timer_callback.upgrade() {
|
||||
if let Some(timer_callback) = self.inner.timer_callback.upgrade() {
|
||||
timer_callback.cancel();
|
||||
}
|
||||
*timer_callback = Arc::downgrade(&new_timer_callback);
|
||||
self.timer_manager.insert(new_timer_callback);
|
||||
|
||||
self.inner.timer_callback = Arc::downgrade(&new_timer_callback);
|
||||
self.timer.timer_manager.insert(new_timer_callback);
|
||||
}
|
||||
|
||||
/// Return the current expired time of this timer.
|
||||
/// Cancels the currently set `TimerCallback`.
|
||||
///
|
||||
/// Once cancelled, the current `TimerCallback` will not be triggered again.
|
||||
pub fn cancel(&self) {
|
||||
if let Some(timer_callback) = self.inner.timer_callback.upgrade() {
|
||||
timer_callback.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the current expired time of this timer.
|
||||
pub fn expired_time(&self) -> Duration {
|
||||
let timer_callback = self.timer_callback.disable_irq().lock().upgrade();
|
||||
timer_callback.map_or(Duration::ZERO, |timer_callback| timer_callback.expired_time)
|
||||
let timer_callback = self.inner.timer_callback.upgrade();
|
||||
timer_callback
|
||||
.and_then(|callback| (!callback.is_cancelled()).then_some(callback.expired_time))
|
||||
.unwrap_or(Duration::ZERO)
|
||||
}
|
||||
|
||||
/// Return the remain time to expiration of this timer.
|
||||
/// Returns the remain time to expiration of this timer.
|
||||
///
|
||||
/// If the timer has not been set, this method
|
||||
/// will return `Duration::ZERO`.
|
||||
pub fn remain(&self) -> Duration {
|
||||
let now = self.timer_manager.clock.read_time();
|
||||
let now = self.timer.timer_manager.clock.read_time();
|
||||
let expired_time = self.expired_time();
|
||||
if expired_time > now {
|
||||
expired_time - now
|
||||
|
|
@ -117,27 +112,41 @@ impl Timer {
|
|||
}
|
||||
}
|
||||
|
||||
/// Return a reference to the [`TimerManager`] which manages
|
||||
/// the current timer.
|
||||
pub fn timer_manager(&self) -> &Arc<TimerManager> {
|
||||
&self.timer_manager
|
||||
}
|
||||
|
||||
/// Returns the interval time of the current timer.
|
||||
pub fn interval(&self) -> Duration {
|
||||
*self.interval.disable_irq().lock()
|
||||
self.inner.interval
|
||||
}
|
||||
}
|
||||
|
||||
fn interval_timer_callback(timer: &Weak<Timer>) {
|
||||
let Some(timer) = timer.upgrade() else {
|
||||
return;
|
||||
};
|
||||
impl Timer {
|
||||
/// Creates a `Timer` instance from a [`TimerManager`].
|
||||
/// This timer will be managed by the `TimerManager`.
|
||||
///
|
||||
/// Note that if the callback instructions involves sleep, users should put these instructions
|
||||
/// into something like `WorkQueue` to avoid sleeping during system timer interruptions.
|
||||
fn new<F>(registered_callback: F, timer_manager: Arc<TimerManager>) -> Arc<Self>
|
||||
where
|
||||
F: Fn(TimerGuard) + Send + Sync + 'static,
|
||||
{
|
||||
Arc::new(Self {
|
||||
inner: SpinLock::new(TimerInner::default()),
|
||||
timer_manager,
|
||||
registered_callback: Box::new(registered_callback),
|
||||
})
|
||||
}
|
||||
|
||||
(timer.registered_callback)();
|
||||
let interval = timer.interval.disable_irq().lock();
|
||||
if *interval != Duration::ZERO {
|
||||
timer.set_timeout(Timeout::After(*interval));
|
||||
/// Locks the timer and returns a [`TimerGuard`] for exclusive access.
|
||||
pub fn lock(self: &Arc<Self>) -> TimerGuard<'_> {
|
||||
TimerGuard {
|
||||
inner: self.inner.disable_irq().lock(),
|
||||
timer: self,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the [`TimerManager`] which manages
|
||||
/// the current timer.
|
||||
pub fn timer_manager(&self) -> &Arc<TimerManager> {
|
||||
&self.timer_manager
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -152,7 +161,7 @@ pub struct TimerManager {
|
|||
}
|
||||
|
||||
impl TimerManager {
|
||||
/// Create a `TimerManager` instance from a clock.
|
||||
/// Creates a `TimerManager` instance from a clock.
|
||||
pub fn new(clock: Arc<dyn Clock>) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
clock,
|
||||
|
|
@ -183,8 +192,9 @@ impl TimerManager {
|
|||
.push(timer_callback);
|
||||
}
|
||||
|
||||
/// Check the managed timers, and if any have timed out,
|
||||
/// call the corresponding callback functions.
|
||||
/// Checks and processes the managed timers.
|
||||
///
|
||||
/// If any of the timers have timed out, call the corresponding callback functions.
|
||||
pub fn process_expired_timers(&self) {
|
||||
let callbacks = {
|
||||
let mut timeout_list = self.timer_callbacks.disable_irq().lock();
|
||||
|
|
@ -208,14 +218,14 @@ impl TimerManager {
|
|||
};
|
||||
|
||||
for callback in callbacks {
|
||||
(callback.callback)();
|
||||
callback.call();
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an [`Timer`], which will be managed by this `TimerManager`.
|
||||
/// Creates an [`Timer`], which will be managed by this `TimerManager`.
|
||||
pub fn create_timer<F>(self: &Arc<Self>, function: F) -> Arc<Timer>
|
||||
where
|
||||
F: Fn() + Send + Sync + 'static,
|
||||
F: Fn(TimerGuard) + Send + Sync + 'static,
|
||||
{
|
||||
Timer::new(function, self.clone())
|
||||
}
|
||||
|
|
@ -224,30 +234,52 @@ impl TimerManager {
|
|||
/// A `TimerCallback` can be used to execute a timer callback function.
|
||||
struct TimerCallback {
|
||||
expired_time: Duration,
|
||||
callback: Box<dyn Fn() + Send + Sync>,
|
||||
timer: Weak<Timer>,
|
||||
is_cancelled: AtomicBool,
|
||||
}
|
||||
|
||||
impl TimerCallback {
|
||||
/// Create an instance of `TimerCallback`.
|
||||
fn new(timeout: Duration, callback: Box<dyn Fn() + Send + Sync>) -> Self {
|
||||
/// Creates an instance of `TimerCallback`.
|
||||
fn new(timeout: Duration, timer: Weak<Timer>) -> Self {
|
||||
Self {
|
||||
expired_time: timeout,
|
||||
callback,
|
||||
timer,
|
||||
is_cancelled: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
/// Cancel a `TimerCallback`. If the callback function has not been called,
|
||||
/// Cancels a `TimerCallback`. If the callback function has not been called,
|
||||
/// it will never be called again.
|
||||
fn cancel(&self) {
|
||||
self.is_cancelled.store(true, Ordering::Release);
|
||||
}
|
||||
|
||||
// Return whether the `TimerCallback` is cancelled.
|
||||
// Returns whether the `TimerCallback` is cancelled.
|
||||
fn is_cancelled(&self) -> bool {
|
||||
self.is_cancelled.load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
fn call(&self) {
|
||||
let Some(timer) = self.timer.upgrade() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut timer_guard = timer.lock();
|
||||
|
||||
if self.is_cancelled() {
|
||||
// The callback is cancelled.
|
||||
return;
|
||||
}
|
||||
|
||||
let interval = timer_guard.interval();
|
||||
if interval != Duration::ZERO {
|
||||
timer_guard.set_timeout(Timeout::After(interval));
|
||||
}
|
||||
|
||||
// Pass the `timer_guard` guard to the callback, allowing it to prevent race conditions.
|
||||
// The callback may choose to use the guard or drop it if not needed.
|
||||
(timer.registered_callback)(timer_guard);
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for TimerCallback {
|
||||
|
|
|
|||
|
|
@ -1,27 +1,40 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use core::sync::atomic::{AtomicU64, Ordering};
|
||||
use core::{
|
||||
fmt::Display,
|
||||
sync::atomic::{AtomicU32, AtomicU64, Ordering},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use atomic_integer_wrapper::define_atomic_version_of_integer_like_type;
|
||||
|
||||
use super::clockid_t;
|
||||
use crate::{
|
||||
events::IoEvents,
|
||||
fs::{
|
||||
file_handle::FileLike,
|
||||
file_table::FdFlags,
|
||||
path::RESERVED_MOUNT_ID,
|
||||
pseudofs::anon_inodefs_shared_inode,
|
||||
utils::{CreationFlags, Inode, StatusFlags},
|
||||
},
|
||||
prelude::*,
|
||||
process::signal::{PollHandle, Pollable, Pollee},
|
||||
syscall::create_timer,
|
||||
time::Timer,
|
||||
time::{
|
||||
timer::{Timeout, TimerGuard},
|
||||
Timer,
|
||||
},
|
||||
};
|
||||
|
||||
/// A file-like object representing a timer that can be used with file descriptors.
|
||||
pub struct TimerfdFile {
|
||||
clockid: clockid_t,
|
||||
timer: Arc<Timer>,
|
||||
ticks: Arc<AtomicU64>,
|
||||
pollee: Pollee,
|
||||
flags: SpinLock<TFDFlags>,
|
||||
flags: AtomicTFDFlags,
|
||||
settime_flags: AtomicTFDSetTimeFlags,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
|
|
@ -32,6 +45,28 @@ bitflags! {
|
|||
}
|
||||
}
|
||||
|
||||
// Required by `define_atomic_version_of_integer_like_type`.
|
||||
impl TryFrom<u32> for TFDFlags {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: u32) -> Result<Self> {
|
||||
Self::from_bits(value).ok_or_else(|| Error::with_message(Errno::EINVAL, "invalid TFDFlags"))
|
||||
}
|
||||
}
|
||||
|
||||
// Required by `define_atomic_version_of_integer_like_type`.
|
||||
impl From<TFDFlags> for u32 {
|
||||
fn from(value: TFDFlags) -> Self {
|
||||
value.bits()
|
||||
}
|
||||
}
|
||||
|
||||
define_atomic_version_of_integer_like_type!(TFDFlags, try_from = true, {
|
||||
/// An atomic version of `TFDFlags`.
|
||||
#[derive(Debug, Default)]
|
||||
struct AtomicTFDFlags(AtomicU32);
|
||||
});
|
||||
|
||||
bitflags! {
|
||||
/// The flags used for timerfd settime operations.
|
||||
pub struct TFDSetTimeFlags: u32 {
|
||||
|
|
@ -40,6 +75,29 @@ bitflags! {
|
|||
}
|
||||
}
|
||||
|
||||
// Required by `define_atomic_version_of_integer_like_type`.
|
||||
impl TryFrom<u32> for TFDSetTimeFlags {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: u32) -> Result<Self> {
|
||||
Self::from_bits(value)
|
||||
.ok_or_else(|| Error::with_message(Errno::EINVAL, "invalid TFDSetTimeFlags"))
|
||||
}
|
||||
}
|
||||
|
||||
// Required by `define_atomic_version_of_integer_like_type`.
|
||||
impl From<TFDSetTimeFlags> for u32 {
|
||||
fn from(value: TFDSetTimeFlags) -> Self {
|
||||
value.bits()
|
||||
}
|
||||
}
|
||||
|
||||
define_atomic_version_of_integer_like_type!(TFDSetTimeFlags, try_from = true, {
|
||||
/// An atomic version of `TFDSetTimeFlags`.
|
||||
#[derive(Debug, Default)]
|
||||
struct AtomicTFDSetTimeFlags(AtomicU32);
|
||||
});
|
||||
|
||||
impl TimerfdFile {
|
||||
/// Creates a new `TimerfdFile` instance.
|
||||
pub fn new(clockid: clockid_t, flags: TFDFlags, ctx: &Context) -> Result<Self> {
|
||||
|
|
@ -50,37 +108,78 @@ impl TimerfdFile {
|
|||
let ticks = ticks.clone();
|
||||
let pollee = pollee.clone();
|
||||
|
||||
let expired_fn = move || {
|
||||
ticks.fetch_add(1, Ordering::Release);
|
||||
let expired_fn = move |_guard: TimerGuard| {
|
||||
ticks.fetch_add(1, Ordering::Relaxed);
|
||||
pollee.notify(IoEvents::IN);
|
||||
};
|
||||
create_timer(clockid, expired_fn, ctx)
|
||||
}?;
|
||||
|
||||
Ok(TimerfdFile {
|
||||
clockid,
|
||||
timer,
|
||||
ticks,
|
||||
pollee,
|
||||
flags: SpinLock::new(flags),
|
||||
flags: AtomicTFDFlags::new(flags),
|
||||
settime_flags: AtomicTFDSetTimeFlags::default(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Gets the associated timer.
|
||||
pub fn timer(&self) -> &Arc<Timer> {
|
||||
&self.timer
|
||||
// Sets the timer's timeout and interval.
|
||||
//
|
||||
// The remaining time and old interval are saved before the settings are applied, and then
|
||||
// returned afterwards.
|
||||
pub fn set_time(
|
||||
&self,
|
||||
expire_time: Duration,
|
||||
interval: Duration,
|
||||
flags: TFDSetTimeFlags,
|
||||
) -> (Duration, Duration) {
|
||||
let mut timer_guard = self.timer.lock();
|
||||
|
||||
let (old_interval, remain) = (timer_guard.interval(), timer_guard.remain());
|
||||
|
||||
timer_guard.set_interval(interval);
|
||||
|
||||
// Cancel the timer and clear the ticks counter.
|
||||
timer_guard.cancel();
|
||||
self.ticks.store(0, Ordering::Relaxed);
|
||||
|
||||
if expire_time != Duration::ZERO {
|
||||
if flags.contains(TFDSetTimeFlags::TFD_TIMER_CANCEL_ON_SET) {
|
||||
// TODO: Currently this flag has no effect since the system time cannot be changed.
|
||||
// Once add the support for modifying the system time, the corresponding logics for
|
||||
// this flag need to be implemented.
|
||||
warn!("TFD_TIMER_CANCEL_ON_SET is not implemented yet and has no effect");
|
||||
}
|
||||
|
||||
let timeout = if flags.contains(TFDSetTimeFlags::TFD_TIMER_ABSTIME) {
|
||||
Timeout::When(expire_time)
|
||||
} else {
|
||||
Timeout::After(expire_time)
|
||||
};
|
||||
|
||||
timer_guard.set_timeout(timeout);
|
||||
self.settime_flags.store(flags, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
(old_interval, remain)
|
||||
}
|
||||
|
||||
/// Clears the tick count.
|
||||
pub fn clear_ticks(&self) {
|
||||
self.ticks.store(0, Ordering::Release);
|
||||
/// Gets the timer's remaining time and interval.
|
||||
pub fn get_time(&self) -> (Duration, Duration) {
|
||||
let timer_guard = self.timer.lock();
|
||||
(timer_guard.interval(), timer_guard.remain())
|
||||
}
|
||||
|
||||
fn is_nonblocking(&self) -> bool {
|
||||
self.flags.lock().contains(TFDFlags::TFD_NONBLOCK)
|
||||
self.flags
|
||||
.load(Ordering::Relaxed)
|
||||
.contains(TFDFlags::TFD_NONBLOCK)
|
||||
}
|
||||
|
||||
fn try_read(&self, writer: &mut VmWriter) -> Result<()> {
|
||||
let ticks = self.ticks.fetch_and(0, Ordering::AcqRel);
|
||||
let ticks = self.ticks.fetch_and(0, Ordering::Relaxed);
|
||||
|
||||
if ticks == 0 {
|
||||
return_errno_with_message!(Errno::EAGAIN, "the counter is zero");
|
||||
|
|
@ -94,7 +193,7 @@ impl TimerfdFile {
|
|||
fn check_io_events(&self) -> IoEvents {
|
||||
let mut events = IoEvents::empty();
|
||||
|
||||
if self.ticks.load(Ordering::Acquire) != 0 {
|
||||
if self.ticks.load(Ordering::Relaxed) != 0 {
|
||||
events |= IoEvents::IN;
|
||||
}
|
||||
|
||||
|
|
@ -139,13 +238,15 @@ impl FileLike for TimerfdFile {
|
|||
}
|
||||
|
||||
fn set_status_flags(&self, new_flags: StatusFlags) -> Result<()> {
|
||||
let mut flags = self.flags.lock();
|
||||
|
||||
if new_flags.contains(StatusFlags::O_NONBLOCK) {
|
||||
*flags |= TFDFlags::TFD_NONBLOCK;
|
||||
} else {
|
||||
*flags &= !TFDFlags::TFD_NONBLOCK;
|
||||
}
|
||||
self.flags
|
||||
.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |flags| {
|
||||
if new_flags.contains(StatusFlags::O_NONBLOCK) {
|
||||
Some(flags | TFDFlags::TFD_NONBLOCK)
|
||||
} else {
|
||||
Some(flags & !TFDFlags::TFD_NONBLOCK)
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -153,4 +254,57 @@ impl FileLike for TimerfdFile {
|
|||
fn inode(&self) -> &Arc<dyn Inode> {
|
||||
anon_inodefs_shared_inode()
|
||||
}
|
||||
|
||||
fn dump_proc_fdinfo(self: Arc<Self>, fd_flags: FdFlags) -> Box<dyn Display> {
|
||||
struct FdInfo {
|
||||
flags: u32,
|
||||
ino: u64,
|
||||
clockid: i32,
|
||||
ticks: u64,
|
||||
settime_flags: u32,
|
||||
it_value: Duration,
|
||||
it_interval: Duration,
|
||||
}
|
||||
|
||||
impl Display for FdInfo {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
writeln!(f, "pos:\t{}", 0)?;
|
||||
writeln!(f, "flags:\t0{:o}", self.flags)?;
|
||||
// TODO: This should be the mount ID of the pseudo filesystem.
|
||||
writeln!(f, "mnt_id:\t{}", RESERVED_MOUNT_ID)?;
|
||||
writeln!(f, "ino:\t{}", self.ino)?;
|
||||
writeln!(f, "clockid: {}", self.clockid)?;
|
||||
writeln!(f, "ticks: {}", self.ticks)?;
|
||||
writeln!(f, "settime flags: 0{:o}", self.settime_flags)?;
|
||||
writeln!(
|
||||
f,
|
||||
"it_value: ({}, {})",
|
||||
self.it_value.as_secs(),
|
||||
self.it_value.subsec_nanos()
|
||||
)?;
|
||||
writeln!(
|
||||
f,
|
||||
"it_interval: ({}, {})",
|
||||
self.it_interval.as_secs(),
|
||||
self.it_interval.subsec_nanos()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
let mut flags = self.status_flags().bits() | self.access_mode() as u32;
|
||||
if fd_flags.contains(FdFlags::CLOEXEC) {
|
||||
flags |= CreationFlags::O_CLOEXEC.bits();
|
||||
}
|
||||
|
||||
let timer_guard = self.timer.lock();
|
||||
Box::new(FdInfo {
|
||||
flags,
|
||||
ino: self.inode().ino(),
|
||||
clockid: self.clockid,
|
||||
ticks: self.ticks.load(Ordering::Relaxed),
|
||||
settime_flags: self.settime_flags.load(Ordering::Relaxed).bits(),
|
||||
it_value: timer_guard.expired_time(),
|
||||
it_interval: timer_guard.interval(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use core::time::Duration;
|
|||
use ostd::sync::{WaitQueue, Waiter};
|
||||
|
||||
use super::{clocks::JIFFIES_TIMER_MANAGER, timer::Timeout, Timer, TimerManager};
|
||||
use crate::prelude::*;
|
||||
use crate::{prelude::*, time::timer::TimerGuard};
|
||||
|
||||
/// A trait that provide the timeout related function for [`Waiter`] and [`WaitQueue`]`.
|
||||
pub trait WaitTimeout {
|
||||
|
|
@ -159,10 +159,10 @@ impl<'a> ManagedTimeout<'a> {
|
|||
/// Creates a timer for the timeout.
|
||||
pub fn create_timer<F>(&self, callback: F) -> Arc<Timer>
|
||||
where
|
||||
F: Fn() + Send + Sync + 'static,
|
||||
F: Fn(TimerGuard) + Send + Sync + 'static,
|
||||
{
|
||||
let timer = self.manager.create_timer(callback);
|
||||
timer.set_timeout(self.timeout.clone());
|
||||
timer.lock().set_timeout(self.timeout.clone());
|
||||
timer
|
||||
}
|
||||
}
|
||||
|
|
@ -183,7 +183,7 @@ impl WaitTimeout for Waiter {
|
|||
|
||||
let timer = timeout.map(|timeout| {
|
||||
let waker = self.waker();
|
||||
timeout.create_timer(move || {
|
||||
timeout.create_timer(move |_guard: TimerGuard| {
|
||||
waker.wake_up();
|
||||
})
|
||||
});
|
||||
|
|
@ -194,7 +194,7 @@ impl WaitTimeout for Waiter {
|
|||
move || {
|
||||
if timer
|
||||
.as_ref()
|
||||
.is_some_and(|timer| timer.remain() == Duration::ZERO)
|
||||
.is_some_and(|timer| timer.lock().remain() == Duration::ZERO)
|
||||
{
|
||||
return_errno_with_message!(Errno::ETIME, "the time limit is reached");
|
||||
}
|
||||
|
|
@ -212,7 +212,7 @@ impl WaitTimeout for Waiter {
|
|||
.as_ref()
|
||||
.is_err_and(|e: &Error| e.error() == Errno::ETIME)
|
||||
{
|
||||
timer.cancel();
|
||||
timer.lock().cancel();
|
||||
}
|
||||
|
||||
res
|
||||
|
|
|
|||
|
|
@ -27,7 +27,11 @@ use spin::Once;
|
|||
|
||||
use crate::{
|
||||
syscall::ClockId,
|
||||
time::{clocks::MonotonicClock, timer::Timeout, SystemTime, START_TIME},
|
||||
time::{
|
||||
clocks::MonotonicClock,
|
||||
timer::{Timeout, TimerGuard},
|
||||
SystemTime, START_TIME,
|
||||
},
|
||||
vm::vmo::{Vmo, VmoOptions},
|
||||
};
|
||||
|
||||
|
|
@ -336,7 +340,7 @@ fn update_vdso_high_res_instant(instant: Instant, instant_cycles: u64) {
|
|||
}
|
||||
|
||||
/// Updates instants with respect to coarse-resolution clocks in vDSO data.
|
||||
fn update_vdso_coarse_res_instant() {
|
||||
fn update_vdso_coarse_res_instant(_guard: TimerGuard) {
|
||||
let instant = Instant::from(read_monotonic_time());
|
||||
VDSO.get().unwrap().update_coarse_res_instant(instant);
|
||||
}
|
||||
|
|
@ -368,8 +372,9 @@ pub(super) fn init_in_first_kthread() {
|
|||
let coarse_instant_timer = ManuallyDrop::new(
|
||||
MonotonicClock::timer_manager().create_timer(update_vdso_coarse_res_instant),
|
||||
);
|
||||
coarse_instant_timer.set_interval(Duration::from_millis(100));
|
||||
coarse_instant_timer.set_timeout(Timeout::After(Duration::from_millis(100)));
|
||||
let mut timer_guard = coarse_instant_timer.lock();
|
||||
timer_guard.set_interval(Duration::from_millis(100));
|
||||
timer_guard.set_timeout(Timeout::After(Duration::from_millis(100)));
|
||||
}
|
||||
|
||||
/// Returns the vDSO VMO.
|
||||
|
|
|
|||
Loading…
Reference in New Issue