Refactor the timer module to solve race conditions and support fdinfo for timerfd files

This commit is contained in:
Chen Chengjun 2025-11-17 02:51:56 +00:00 committed by Tate, Hongliang Tian
parent 59cd3bd61d
commit 7305d389f0
14 changed files with 389 additions and 183 deletions

View File

@ -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);
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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

View File

@ -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 _))
}

View File

@ -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,

View File

@ -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 {

View File

@ -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,

View File

@ -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)?;

View File

@ -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))
}

View File

@ -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 {

View File

@ -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(),
})
}
}

View File

@ -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

View File

@ -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.