201 lines
6.5 KiB
Rust
201 lines
6.5 KiB
Rust
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
use alloc::{
|
|
boxed::Box,
|
|
sync::{Arc, Weak},
|
|
vec::Vec,
|
|
};
|
|
use core::time::Duration;
|
|
|
|
use id_alloc::IdAlloc;
|
|
use ostd::{
|
|
arch::{
|
|
timer::{self, TIMER_FREQ},
|
|
x86::trap::is_kernel_interrupted,
|
|
},
|
|
sync::Mutex,
|
|
};
|
|
|
|
use super::Process;
|
|
use crate::{
|
|
process::{
|
|
posix_thread::PosixThreadExt,
|
|
signal::{constants::SIGALRM, signals::kernel::KernelSignal},
|
|
},
|
|
thread::{
|
|
work_queue::{submit_work_item, work_item::WorkItem},
|
|
Thread,
|
|
},
|
|
time::{
|
|
clocks::{ProfClock, RealTimeClock},
|
|
Timer, TimerManager,
|
|
},
|
|
};
|
|
|
|
/// Updates the CPU time recorded in the CPU clocks of current Process.
|
|
///
|
|
/// This function will be invoked at the system timer interrupt, and
|
|
/// invoke the callbacks of expired timers which are based on the updated
|
|
/// CPU clock.
|
|
fn update_cpu_time() {
|
|
let current_thread = Thread::current();
|
|
if let Some(posix_thread) = current_thread.as_posix_thread() {
|
|
let process = posix_thread.process();
|
|
let timer_manager = process.timer_manager();
|
|
let jiffies_interval = Duration::from_millis(1000 / TIMER_FREQ);
|
|
// Based on whether the timer interrupt occurs in kernel mode or user mode,
|
|
// the function will add the duration of one timer interrupt interval to the
|
|
// corresponding CPU clocks.
|
|
if is_kernel_interrupted() {
|
|
posix_thread
|
|
.prof_clock()
|
|
.kernel_clock()
|
|
.add_time(jiffies_interval);
|
|
process
|
|
.prof_clock()
|
|
.kernel_clock()
|
|
.add_time(jiffies_interval);
|
|
} else {
|
|
posix_thread
|
|
.prof_clock()
|
|
.user_clock()
|
|
.add_time(jiffies_interval);
|
|
process.prof_clock().user_clock().add_time(jiffies_interval);
|
|
timer_manager
|
|
.virtual_timer()
|
|
.timer_manager()
|
|
.process_expired_timers();
|
|
}
|
|
timer_manager
|
|
.prof_timer()
|
|
.timer_manager()
|
|
.process_expired_timers();
|
|
posix_thread.process_expired_timers();
|
|
}
|
|
}
|
|
|
|
/// Registers a function to update the CPU clock in processes and
|
|
/// threads during the system timer interrupt.
|
|
pub(super) fn init() {
|
|
timer::register_callback(update_cpu_time);
|
|
}
|
|
|
|
/// Represents timer resources and utilities for a POSIX process.
|
|
pub struct PosixTimerManager {
|
|
/// A real-time countdown timer, measuring in wall clock time.
|
|
alarm_timer: Arc<Timer>,
|
|
/// A timer based on user CPU clock.
|
|
virtual_timer: Arc<Timer>,
|
|
/// A timer based on the profiling clock.
|
|
prof_timer: Arc<Timer>,
|
|
/// An ID allocator to allocate unique timer IDs.
|
|
id_allocator: Mutex<IdAlloc>,
|
|
/// A container managing all POSIX timers created by `timer_create()` syscall
|
|
/// within the process context.
|
|
posix_timers: Mutex<Vec<Option<Arc<Timer>>>>,
|
|
}
|
|
|
|
fn create_process_timer_callback(process_ref: &Weak<Process>) -> impl Fn() + Clone {
|
|
let current_process = process_ref.clone();
|
|
let sent_signal = move || {
|
|
let signal = KernelSignal::new(SIGALRM);
|
|
if let Some(process) = current_process.upgrade() {
|
|
process.enqueue_signal(signal);
|
|
}
|
|
};
|
|
|
|
let work_func = Box::new(sent_signal);
|
|
let work_item = Arc::new(WorkItem::new(work_func));
|
|
|
|
move || {
|
|
submit_work_item(
|
|
work_item.clone(),
|
|
crate::thread::work_queue::WorkPriority::High,
|
|
);
|
|
}
|
|
}
|
|
|
|
impl PosixTimerManager {
|
|
pub(super) fn new(prof_clock: &Arc<ProfClock>, process_ref: &Weak<Process>) -> Self {
|
|
const MAX_NUM_OF_POSIX_TIMERS: usize = 10000;
|
|
|
|
let callback = create_process_timer_callback(process_ref);
|
|
|
|
let alarm_timer = RealTimeClock::timer_manager().create_timer(callback.clone());
|
|
|
|
let virtual_timer =
|
|
TimerManager::new(prof_clock.user_clock().clone()).create_timer(callback.clone());
|
|
let prof_timer = TimerManager::new(prof_clock.clone()).create_timer(callback);
|
|
|
|
Self {
|
|
alarm_timer,
|
|
virtual_timer,
|
|
prof_timer,
|
|
id_allocator: Mutex::new(IdAlloc::with_capacity(MAX_NUM_OF_POSIX_TIMERS)),
|
|
posix_timers: Mutex::new(Vec::new()),
|
|
}
|
|
}
|
|
|
|
/// Gets the alarm timer of the corresponding process.
|
|
pub fn alarm_timer(&self) -> &Arc<Timer> {
|
|
&self.alarm_timer
|
|
}
|
|
|
|
/// Gets the virtual timer of the corresponding process.
|
|
pub fn virtual_timer(&self) -> &Arc<Timer> {
|
|
&self.virtual_timer
|
|
}
|
|
|
|
/// Gets the profiling timer of the corresponding process.
|
|
pub fn prof_timer(&self) -> &Arc<Timer> {
|
|
&self.prof_timer
|
|
}
|
|
|
|
/// 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,
|
|
{
|
|
self.prof_timer.timer_manager().create_timer(func)
|
|
}
|
|
|
|
/// 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,
|
|
{
|
|
self.virtual_timer.timer_manager().create_timer(func)
|
|
}
|
|
|
|
/// Adds a POSIX timer to the managed `posix_timers`, and allocate a timer ID for this timer.
|
|
/// Return the timer ID.
|
|
pub fn add_posix_timer(&self, posix_timer: Arc<Timer>) -> usize {
|
|
let mut timers = self.posix_timers.lock();
|
|
// Holding the lock of `posix_timers` is required to operate the `id_allocator`.
|
|
let timer_id = self.id_allocator.lock().alloc().unwrap();
|
|
if timers.len() < timer_id + 1 {
|
|
timers.resize(timer_id + 1, None);
|
|
}
|
|
// The ID allocated is not used by any other timers so this index in `timers`
|
|
// must be `None`.
|
|
timers[timer_id] = Some(posix_timer);
|
|
timer_id
|
|
}
|
|
|
|
/// Finds a POSIX timer by the input `timer_id`.
|
|
pub fn find_posix_timer(&self, timer_id: usize) -> Option<Arc<Timer>> {
|
|
self.posix_timers.lock()[timer_id].clone()
|
|
}
|
|
|
|
/// Removes the POSIX timer with the ID `timer_id`.
|
|
pub fn remove_posix_timer(&self, timer_id: usize) -> Option<Arc<Timer>> {
|
|
let mut timers = self.posix_timers.lock();
|
|
let timer = timers[timer_id].take();
|
|
if timer.is_some() {
|
|
// Holding the lock of `posix_timers` is required to operate the `id_allocator`.
|
|
self.id_allocator.lock().free(timer_id);
|
|
}
|
|
timer
|
|
}
|
|
}
|