Skip to content

Commit 4bf4e34

Browse files
committed
refactor(port_std): move SchedState to a new module
1 parent 137561a commit 4bf4e34

File tree

2 files changed

+259
-245
lines changed

2 files changed

+259
-245
lines changed

src/constance_port_std/src/lib.rs

Lines changed: 21 additions & 245 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
use atomic_ref::AtomicRef;
88
use constance::{
99
kernel::{
10-
self, ClearInterruptLineError, EnableInterruptLineError, InterruptNum, InterruptPriority,
10+
ClearInterruptLineError, EnableInterruptLineError, InterruptNum, InterruptPriority,
1111
PendInterruptLineError, Port, PortToKernel, QueryInterruptLineError,
1212
SetInterruptLinePriorityError, TaskCb, UTicks,
1313
},
@@ -16,11 +16,7 @@ use constance::{
1616
};
1717
use once_cell::sync::OnceCell;
1818
use parking_lot::{lock_api::RawMutex, Mutex};
19-
use std::{
20-
cell::Cell,
21-
collections::{BTreeSet, HashMap},
22-
time::Instant,
23-
};
19+
use std::{cell::Cell, time::Instant};
2420

2521
#[cfg(unix)]
2622
#[path = "threading_unix.rs"]
@@ -33,6 +29,7 @@ mod threading;
3329
#[cfg(test)]
3430
mod threading_test;
3531

32+
mod sched;
3633
mod ums;
3734
mod utils;
3835

@@ -74,7 +71,7 @@ pub unsafe trait PortInstance: Kernel + Port<PortTaskState = TaskState> {
7471
/// the corresponding trait methods of `Port*`.
7572
#[doc(hidden)]
7673
pub struct State {
77-
thread_group: OnceCell<ums::ThreadGroup<SchedState>>,
74+
thread_group: OnceCell<ums::ThreadGroup<sched::SchedState>>,
7875
join_handle: Mutex<Option<ums::ThreadGroupJoinHandle>>,
7976
origin: AtomicRef<'static, Instant>,
8077
}
@@ -84,6 +81,10 @@ pub struct TaskState {
8481
tsm: Mutex<Tsm>,
8582
}
8683

84+
impl Init for TaskState {
85+
const INIT: Self = Self::new();
86+
}
87+
8788
/// Task state machine
8889
///
8990
/// These don't exactly align with the task states defined in the kernel.
@@ -98,45 +99,6 @@ enum Tsm {
9899
Running(ums::ThreadId),
99100
}
100101

101-
/// The state of the simulated hardware-based scheduler.
102-
struct SchedState {
103-
/// Interrupt lines.
104-
int_lines: HashMap<InterruptNum, IntLine>,
105-
/// `int_lines.iter().filter(|_,a| a.pended && a.enable)
106-
/// .map(|i,a| (a.priority, i)).collect()`.
107-
pended_lines: BTreeSet<(InterruptPriority, InterruptNum)>,
108-
active_int_handlers: Vec<(InterruptPriority, ums::ThreadId)>,
109-
cpu_lock: bool,
110-
111-
/// The currently-selected task thread.
112-
task_thread: Option<ums::ThreadId>,
113-
114-
/// Garbage can
115-
zombies: Vec<ums::ThreadId>,
116-
}
117-
118-
/// The configuration of an interrupt line.
119-
#[derive(Debug)]
120-
struct IntLine {
121-
priority: InterruptPriority,
122-
start: Option<kernel::cfg::InterruptHandlerFn>,
123-
enable: bool,
124-
pended: bool,
125-
}
126-
127-
impl Init for TaskState {
128-
const INIT: Self = Self::new();
129-
}
130-
131-
impl Init for IntLine {
132-
const INIT: Self = IntLine {
133-
priority: 0,
134-
start: None,
135-
enable: false,
136-
pended: false,
137-
};
138-
}
139-
140102
/// The role of a thread.
141103
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
142104
enum ThreadRole {
@@ -186,7 +148,7 @@ impl TaskState {
186148
// the kernel will never choose this task again. However, the underlying
187149
// UMS thread is still alive. Thus, we need to temporarily override the
188150
// normal scheduling to ensure this thread will run to completion.
189-
lock.scheduler().zombies.push(thread_id);
151+
lock.scheduler().recycle_thread(thread_id);
190152
lock.scheduler().cpu_lock = false;
191153
drop(lock);
192154

@@ -198,192 +160,6 @@ impl TaskState {
198160
}
199161
}
200162

201-
struct BadIntLineError;
202-
203-
impl SchedState {
204-
fn new<System: Kernel>() -> Self {
205-
let mut this = Self {
206-
int_lines: HashMap::new(),
207-
pended_lines: BTreeSet::new(),
208-
active_int_handlers: Vec::new(),
209-
cpu_lock: true,
210-
task_thread: None,
211-
zombies: Vec::new(),
212-
};
213-
214-
for i in 0..NUM_INTERRUPT_LINES {
215-
if let Some(handler) = System::INTERRUPT_HANDLERS.get(i) {
216-
this.int_lines.insert(
217-
i as InterruptNum,
218-
IntLine {
219-
start: Some(handler),
220-
..IntLine::INIT
221-
},
222-
);
223-
}
224-
}
225-
226-
this
227-
}
228-
229-
fn update_line(
230-
&mut self,
231-
i: InterruptNum,
232-
f: impl FnOnce(&mut IntLine),
233-
) -> Result<(), BadIntLineError> {
234-
if i >= NUM_INTERRUPT_LINES {
235-
return Err(BadIntLineError);
236-
}
237-
let line = self.int_lines.entry(i).or_insert_with(|| IntLine::INIT);
238-
self.pended_lines.remove(&(line.priority, i));
239-
f(line);
240-
if line.enable && line.pended {
241-
self.pended_lines.insert((line.priority, i));
242-
}
243-
Ok(())
244-
}
245-
246-
fn is_line_pended(&self, i: InterruptNum) -> Result<bool, BadIntLineError> {
247-
if i >= NUM_INTERRUPT_LINES {
248-
return Err(BadIntLineError);
249-
}
250-
251-
if let Some(line) = self.int_lines.get(&i) {
252-
Ok(line.pended)
253-
} else {
254-
Ok(false)
255-
}
256-
}
257-
}
258-
259-
impl ums::Scheduler for SchedState {
260-
fn choose_next_thread(&mut self) -> Option<ums::ThreadId> {
261-
if let Some(&thread_id) = self.zombies.first() {
262-
// Clean up zombie threads as soon as possible
263-
Some(thread_id)
264-
} else if let Some(&(_, thread_id)) = self.active_int_handlers.last() {
265-
Some(thread_id)
266-
} else if self.cpu_lock {
267-
// CPU Lock owned by a task thread
268-
Some(self.task_thread.unwrap())
269-
} else {
270-
self.task_thread
271-
}
272-
}
273-
274-
fn thread_exited(&mut self, thread_id: ums::ThreadId) {
275-
if let Some(i) = self.zombies.iter().position(|id| *id == thread_id) {
276-
log::trace!("removing the zombie thread {:?}", thread_id);
277-
self.zombies.swap_remove(i);
278-
return;
279-
}
280-
281-
log::warn!("thread_exited: unexpected thread {:?}", thread_id);
282-
}
283-
}
284-
285-
/// Check for any pending interrupts that can be activated under the current
286-
/// condition. If there are one or more of them, activate them and return
287-
/// `true`, in which case the caller should call
288-
/// [`ums::ThreadGroupLockGuard::preempt`] or [`ums::yield_now`].
289-
#[must_use]
290-
fn check_preemption_by_interrupt(
291-
thread_group: &'static ums::ThreadGroup<SchedState>,
292-
lock: &mut ums::ThreadGroupLockGuard<SchedState>,
293-
) -> bool {
294-
let mut activated_any = false;
295-
296-
// Check pending interrupts
297-
loop {
298-
let sched_state = lock.scheduler();
299-
300-
// Find the highest pended priority
301-
let (pri, num) = if let Some(&x) = sched_state.pended_lines.iter().next() {
302-
x
303-
} else {
304-
// No interrupt is pended
305-
break;
306-
};
307-
308-
// Masking by CPU Lock
309-
if sched_state.cpu_lock && is_interrupt_priority_managed(pri) {
310-
log::trace!(
311-
"not handling an interrupt with priority {} because of CPU Lock",
312-
pri
313-
);
314-
break;
315-
}
316-
317-
// Masking by an already active interrupt
318-
if let Some(&(existing_pri, _)) = sched_state.active_int_handlers.last() {
319-
if existing_pri < pri {
320-
log::trace!(
321-
"not handling an interrupt with priority {} because of \
322-
an active interrupt handler with priority {}",
323-
pri,
324-
existing_pri,
325-
);
326-
break;
327-
}
328-
}
329-
330-
// Take the interrupt
331-
sched_state.pended_lines.remove(&(pri, num));
332-
333-
// Find the interrupt handler for `num`. Return
334-
// `default_interrupt_handler` if there's none.
335-
let start = sched_state
336-
.int_lines
337-
.get(&num)
338-
.and_then(|line| line.start)
339-
.unwrap_or(default_interrupt_handler);
340-
341-
let thread_id = lock.spawn(move |thread_id| {
342-
THREAD_ROLE.with(|role| role.set(ThreadRole::Interrupt));
343-
344-
// Safety: The port can call an interrupt handler
345-
unsafe { start() }
346-
347-
let mut lock = thread_group.lock();
348-
349-
// Make this interrupt handler inactive
350-
let (_, popped_thread_id) = lock.scheduler().active_int_handlers.pop().unwrap();
351-
assert_eq!(thread_id, popped_thread_id);
352-
log::trace!(
353-
"an interrupt handler for an interrupt {} (priority = {}) exited",
354-
num,
355-
pri
356-
);
357-
358-
// Make sure this thread will run to completion
359-
lock.scheduler().zombies.push(thread_id);
360-
361-
let _ = check_preemption_by_interrupt(thread_group, &mut lock);
362-
});
363-
364-
log::trace!(
365-
"handling an interrupt {} (priority = {}) with thread {:?}",
366-
num,
367-
pri,
368-
thread_id
369-
);
370-
371-
lock.scheduler().active_int_handlers.push((pri, thread_id));
372-
373-
activated_any = true;
374-
}
375-
376-
activated_any
377-
}
378-
379-
fn is_interrupt_priority_managed(p: InterruptPriority) -> bool {
380-
p >= 0
381-
}
382-
383-
extern "C" fn default_interrupt_handler() {
384-
panic!("Unhandled interrupt");
385-
}
386-
387163
#[allow(clippy::missing_safety_doc)]
388164
impl State {
389165
pub const fn new() -> Self {
@@ -396,7 +172,7 @@ impl State {
396172

397173
pub fn init<System: Kernel>(&self) {
398174
// Create a UMS thread group.
399-
let (thread_group, join_handle) = ums::ThreadGroup::new(SchedState::new::<System>());
175+
let (thread_group, join_handle) = ums::ThreadGroup::new(sched::SchedState::new::<System>());
400176

401177
self.thread_group.set(thread_group).ok().unwrap();
402178
*self.join_handle.lock() = Some(join_handle);
@@ -427,7 +203,7 @@ impl State {
427203
lock.scheduler().cpu_lock = false;
428204

429205
// Start scheduling
430-
assert!(check_preemption_by_interrupt(
206+
assert!(sched::check_preemption_by_interrupt(
431207
self.thread_group.get().unwrap(),
432208
&mut lock
433209
));
@@ -551,7 +327,7 @@ impl State {
551327
assert!(lock.scheduler().cpu_lock);
552328
lock.scheduler().cpu_lock = false;
553329

554-
if check_preemption_by_interrupt(self.thread_group.get().unwrap(), &mut lock) {
330+
if sched::check_preemption_by_interrupt(self.thread_group.get().unwrap(), &mut lock) {
555331
drop(lock);
556332
ums::yield_now();
557333
}
@@ -600,9 +376,9 @@ impl State {
600376
let mut lock = self.thread_group.get().unwrap().lock();
601377
lock.scheduler()
602378
.update_line(num, |line| line.priority = priority)
603-
.map_err(|BadIntLineError| SetInterruptLinePriorityError::BadParam)?;
379+
.map_err(|sched::BadIntLineError| SetInterruptLinePriorityError::BadParam)?;
604380

605-
if check_preemption_by_interrupt(self.thread_group.get().unwrap(), &mut lock) {
381+
if sched::check_preemption_by_interrupt(self.thread_group.get().unwrap(), &mut lock) {
606382
drop(lock);
607383
ums::yield_now();
608384
}
@@ -619,9 +395,9 @@ impl State {
619395
let mut lock = self.thread_group.get().unwrap().lock();
620396
lock.scheduler()
621397
.update_line(num, |line| line.enable = true)
622-
.map_err(|BadIntLineError| EnableInterruptLineError::BadParam)?;
398+
.map_err(|sched::BadIntLineError| EnableInterruptLineError::BadParam)?;
623399

624-
if check_preemption_by_interrupt(self.thread_group.get().unwrap(), &mut lock) {
400+
if sched::check_preemption_by_interrupt(self.thread_group.get().unwrap(), &mut lock) {
625401
drop(lock);
626402
ums::yield_now();
627403
}
@@ -638,7 +414,7 @@ impl State {
638414
(self.thread_group.get().unwrap().lock())
639415
.scheduler()
640416
.update_line(num, |line| line.enable = false)
641-
.map_err(|BadIntLineError| EnableInterruptLineError::BadParam)
417+
.map_err(|sched::BadIntLineError| EnableInterruptLineError::BadParam)
642418
}
643419

644420
pub fn pend_interrupt_line(
@@ -650,9 +426,9 @@ impl State {
650426
let mut lock = self.thread_group.get().unwrap().lock();
651427
lock.scheduler()
652428
.update_line(num, |line| line.pended = true)
653-
.map_err(|BadIntLineError| PendInterruptLineError::BadParam)?;
429+
.map_err(|sched::BadIntLineError| PendInterruptLineError::BadParam)?;
654430

655-
if check_preemption_by_interrupt(self.thread_group.get().unwrap(), &mut lock) {
431+
if sched::check_preemption_by_interrupt(self.thread_group.get().unwrap(), &mut lock) {
656432
drop(lock);
657433
ums::yield_now();
658434
}
@@ -666,7 +442,7 @@ impl State {
666442
(self.thread_group.get().unwrap().lock())
667443
.scheduler()
668444
.update_line(num, |line| line.pended = false)
669-
.map_err(|BadIntLineError| ClearInterruptLineError::BadParam)
445+
.map_err(|sched::BadIntLineError| ClearInterruptLineError::BadParam)
670446
}
671447

672448
pub fn is_interrupt_line_pending(
@@ -676,7 +452,7 @@ impl State {
676452
(self.thread_group.get().unwrap().lock())
677453
.scheduler()
678454
.is_line_pended(num)
679-
.map_err(|BadIntLineError| QueryInterruptLineError::BadParam)
455+
.map_err(|sched::BadIntLineError| QueryInterruptLineError::BadParam)
680456
}
681457

682458
// TODO: Make these customizable to test the kernel under multiple conditions

0 commit comments

Comments
 (0)