Skip to content

Commit fd6ad85

Browse files
committed
feat(port_std): implement Port::pend_tick[_after]
1 parent 0cb5a90 commit fd6ad85

File tree

1 file changed

+99
-6
lines changed
  • src/constance_port_std/src

1 file changed

+99
-6
lines changed

src/constance_port_std/src/lib.rs

Lines changed: 99 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#![feature(const_fn)]
22
#![feature(thread_local)]
33
#![feature(external_doc)]
4+
#![feature(deadline_api)]
45
#![feature(unsafe_block_in_unsafe_fn)] // `unsafe fn` doesn't imply `unsafe {}`
56
#![doc(include = "./lib.md")]
67
#![deny(unsafe_op_in_unsafe_fn)]
@@ -16,7 +17,11 @@ use constance::{
1617
};
1718
use once_cell::sync::OnceCell;
1819
use parking_lot::{lock_api::RawMutex, Mutex};
19-
use std::{cell::Cell, time::Instant};
20+
use std::{
21+
cell::Cell,
22+
sync::mpsc,
23+
time::{Duration, Instant},
24+
};
2025

2126
#[cfg(unix)]
2227
#[path = "threading_unix.rs"]
@@ -53,6 +58,12 @@ pub const INTERRUPT_LINE_DISPATCH: InterruptNum = 1023;
5358
/// The default interrupt priority for [`INTERRUPT_LINE_DISPATCH`].
5459
pub const INTERRUPT_PRIORITY_DISPATCH: InterruptPriority = 16384;
5560

61+
/// The (software) interrupt line used for timer interrupts.
62+
pub const INTERRUPT_LINE_TIMER: InterruptNum = 1022;
63+
64+
/// The default interrupt priority for [`INTERRUPT_LINE_TIMER`].
65+
pub const INTERRUPT_PRIORITY_TIMER: InterruptPriority = 16383;
66+
5667
/// Implemented on a system type by [`use_port!`].
5768
///
5869
/// # Safety
@@ -73,6 +84,7 @@ pub unsafe trait PortInstance: Kernel + Port<PortTaskState = TaskState> {
7384
pub struct State {
7485
thread_group: OnceCell<ums::ThreadGroup<sched::SchedState>>,
7586
join_handle: Mutex<Option<ums::ThreadGroupJoinHandle>>,
87+
timer_cmd_send: Mutex<Option<mpsc::Sender<TimerCmd>>>,
7688
origin: AtomicRef<'static, Instant>,
7789
}
7890

@@ -99,6 +111,10 @@ enum Tsm {
99111
Running(ums::ThreadId),
100112
}
101113

114+
enum TimerCmd {
115+
SetTimeout { at: Instant },
116+
}
117+
102118
/// The role of a thread.
103119
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
104120
enum ThreadRole {
@@ -171,20 +187,50 @@ impl State {
171187
Self {
172188
thread_group: OnceCell::new(),
173189
join_handle: Mutex::const_new(RawMutex::INIT, None),
190+
timer_cmd_send: Mutex::const_new(RawMutex::INIT, None),
174191
origin: AtomicRef::new(None),
175192
}
176193
}
177194

178195
/// Initialize the user-mode scheduling system and boot the kernel.
179196
///
180197
/// Returns when the shutdown initiated by [`shutdown`] completes.
181-
pub fn port_boot<System: Kernel>(&self) {
198+
pub fn port_boot<System: PortInstance>(&self) {
182199
// Create a UMS thread group.
183200
let (thread_group, join_handle) = ums::ThreadGroup::new(sched::SchedState::new::<System>());
184201

185202
self.thread_group.set(thread_group).ok().unwrap();
186203
*self.join_handle.lock() = Some(join_handle);
187204

205+
// Start a timer thread
206+
let (timer_cmd_send, timer_cmd_recv) = mpsc::channel();
207+
log::trace!("starting the timer thread");
208+
let timer_join_handle = std::thread::spawn(move || {
209+
let mut next_deadline = None;
210+
loop {
211+
let recv_result = if let Some(next_deadline) = next_deadline {
212+
timer_cmd_recv.recv_deadline(next_deadline)
213+
} else {
214+
timer_cmd_recv
215+
.recv()
216+
.map_err(|_| mpsc::RecvTimeoutError::Disconnected)
217+
};
218+
match recv_result {
219+
Err(mpsc::RecvTimeoutError::Disconnected) => {
220+
break;
221+
}
222+
Err(mpsc::RecvTimeoutError::Timeout) => {
223+
pend_interrupt_line::<System>(INTERRUPT_LINE_TIMER).unwrap();
224+
next_deadline = None;
225+
}
226+
Ok(TimerCmd::SetTimeout { at }) => {
227+
next_deadline = Some(at);
228+
}
229+
}
230+
}
231+
});
232+
*self.timer_cmd_send.lock() = Some(timer_cmd_send);
233+
188234
// Create the initial UMS worker thread, where the boot phase of the
189235
// kernel runs
190236
let mut lock = self.thread_group.get().unwrap().lock();
@@ -200,11 +246,33 @@ impl State {
200246
lock.scheduler().task_thread = Some(thread_id);
201247
lock.scheduler().recycle_thread(thread_id);
202248
lock.preempt();
249+
250+
// Configure timer interrupt
251+
lock.scheduler()
252+
.update_line(INTERRUPT_LINE_TIMER, |line| {
253+
line.priority = INTERRUPT_PRIORITY_TIMER;
254+
line.enable = true;
255+
line.start = Some(Self::timer_handler::<System>);
256+
})
257+
.ok()
258+
.unwrap();
259+
203260
drop(lock);
204261

205262
// Wait until the thread group shuts down
206263
let join_handle = self.join_handle.lock().take().unwrap();
207-
if let Err(e) = join_handle.join() {
264+
let result = join_handle.join();
265+
266+
// Stop the timer thread.
267+
// `timer_cmd_recv.recv` will return `Err(_)` when we drop the
268+
// corresponding sender (`timer_cmd_send`).
269+
log::trace!("stopping the timer thread");
270+
*self.timer_cmd_send.lock() = None;
271+
timer_join_handle.join().unwrap();
272+
log::trace!("stopped the timer thread");
273+
274+
// Propagate any panic that occured in a worker thread
275+
if let Err(e) = result {
208276
std::panic::resume_unwind(e);
209277
}
210278
}
@@ -222,6 +290,7 @@ impl State {
222290
let mut lock = self.thread_group.get().unwrap().lock();
223291

224292
// Configure PendSV
293+
// TODO: move this (except for `pended = true`) to `port_boot`
225294
lock.scheduler()
226295
.update_line(INTERRUPT_LINE_DISPATCH, |line| {
227296
line.priority = INTERRUPT_PRIORITY_DISPATCH;
@@ -550,12 +619,36 @@ impl State {
550619

551620
pub fn pend_tick_after<System: PortInstance>(&self, tick_count_delta: UTicks) {
552621
expect_worker_thread::<System>();
553-
// TODO
622+
log::trace!("pend_tick_after({:?})", tick_count_delta);
623+
624+
// Calculate when `timer_tick` should be called
625+
let now = Instant::now() + Duration::from_micros(tick_count_delta.into());
626+
627+
// Lock the scheduler because we aren't sure what would happen if
628+
// `Sender::send` was interrupted
629+
let _sched_lock = lock_scheduler::<System>();
630+
631+
let timer_cmd_send = self.timer_cmd_send.lock();
632+
let timer_cmd_send = timer_cmd_send.as_ref().unwrap();
633+
timer_cmd_send
634+
.send(TimerCmd::SetTimeout { at: now })
635+
.unwrap();
554636
}
555637

556-
pub fn pend_tick<System: PortInstance>(&self) {
638+
pub fn pend_tick<System: PortInstance>(&'static self) {
557639
expect_worker_thread::<System>();
558-
// TODO
640+
log::trace!("pend_tick");
641+
642+
self.pend_interrupt_line::<System>(INTERRUPT_LINE_TIMER)
643+
.unwrap();
644+
}
645+
646+
extern "C" fn timer_handler<System: PortInstance>() {
647+
assert_eq!(expect_worker_thread::<System>(), ThreadRole::Interrupt);
648+
log::trace!("timer_handler");
649+
650+
// Safety: CPU Lock inactive, an interrupt context
651+
unsafe { <System as PortToKernel>::timer_tick() };
559652
}
560653
}
561654

0 commit comments

Comments
 (0)