Skip to content

Commit e3e56ae

Browse files
committed
feat(kernel): add and implement Kernel::{boost_priority, unboost_priority}
1 parent 6574d01 commit e3e56ae

File tree

9 files changed

+296
-9
lines changed

9 files changed

+296
-9
lines changed

src/constance/src/kernel.rs

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
//! The RTOS kernel
22
use atomic_ref::AtomicRef;
33
use core::{
4-
borrow::BorrowMut, fmt, mem::forget, num::NonZeroUsize, ops::Range, sync::atomic::Ordering,
4+
borrow::BorrowMut,
5+
fmt,
6+
mem::forget,
7+
num::NonZeroUsize,
8+
ops::Range,
9+
sync::atomic::{AtomicBool, Ordering},
510
};
611

712
use crate::utils::{intrusive_list::StaticListHead, BinUInteger, Init, PrioBitmap};
@@ -51,6 +56,35 @@ pub trait Kernel: Port + KernelCfg2 + Sized + 'static {
5156
/// Return a flag indicating whether CPU Lock is currently active.
5257
fn has_cpu_lock() -> bool;
5358

59+
/// Activate [Priority Boost].
60+
///
61+
/// Returns [`BadContext`] if Priority Boost is already active, the
62+
/// calling context is not a task context, or CPU Lock is active.
63+
///
64+
/// [Priority Boost]: crate#system-states
65+
/// [`BadContext`]: CpuLockError::BadContext
66+
fn boost_priority() -> Result<(), BoostPriorityError>;
67+
68+
/// Deactivate [Priority Boost].
69+
///
70+
/// Returns [`BadContext`] if Priority Boost is already inactive, the
71+
/// calling context is not a task context, or CPU Lock is active.
72+
///
73+
/// [Priority Boost]: crate#system-states
74+
/// [`BadContext`]: CpuLockError::BadContext
75+
///
76+
/// # Safety
77+
///
78+
/// Priority Boost is useful for creating a critical section. By making this
79+
/// method `unsafe`, safe code is prevented from interfering with a critical
80+
/// section.
81+
unsafe fn unboost_priority() -> Result<(), BoostPriorityError>;
82+
83+
/// Return a flag indicating whether [Priority Boost] is currently active.
84+
///
85+
/// [Priority Boost]: crate#system-states
86+
fn is_priority_boost_active() -> bool;
87+
5488
/// Terminate the current task, putting it into a Dormant state.
5589
///
5690
/// The kernel (to be precise, the port) makes an implicit call to this
@@ -106,6 +140,18 @@ impl<T: Port + KernelCfg2 + 'static> Kernel for T {
106140
Self::is_cpu_lock_active()
107141
}
108142

143+
fn boost_priority() -> Result<(), BoostPriorityError> {
144+
state::boost_priority::<Self>()
145+
}
146+
147+
unsafe fn unboost_priority() -> Result<(), BoostPriorityError> {
148+
state::unboost_priority::<Self>()
149+
}
150+
151+
fn is_priority_boost_active() -> bool {
152+
Self::state().priority_boost.load(Ordering::Relaxed)
153+
}
154+
109155
unsafe fn exit_task() -> Result<!, ExitTaskError> {
110156
// Safety: Just forwarding the function call
111157
unsafe { exit_current_task::<Self>() }
@@ -399,6 +445,9 @@ pub struct State<
399445
/// Invariant: `task_ready_bitmap[i].first.is_some() ==
400446
/// task_ready_queue.get(i)`
401447
task_ready_queue: utils::CpuLockCell<System, TaskReadyQueue>,
448+
449+
/// `true` if Priority Boost is active.
450+
priority_boost: AtomicBool,
402451
}
403452

404453
impl<
@@ -413,6 +462,7 @@ impl<
413462
running_task: AtomicRef::new(None),
414463
task_ready_bitmap: Init::INIT,
415464
task_ready_queue: Init::INIT,
465+
priority_boost: AtomicBool::new(false),
416466
};
417467
}
418468

@@ -429,6 +479,7 @@ impl<
429479
.field("running_task", &self.running_task)
430480
.field("task_ready_bitmap", &self.task_ready_bitmap)
431481
.field("task_ready_queue", &self.task_ready_queue)
482+
.field("priority_boost", &self.priority_boost)
432483
.finish()
433484
}
434485
}

src/constance/src/kernel/error.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,19 @@ define_error! {
215215
}
216216
}
217217

218+
define_error! {
219+
/// Error type for [`Kernel::boost_priority`] and
220+
/// [`Kernel::unboost_priority`].
221+
///
222+
/// [`Kernel::boost_priority`]: super::Kernel::boost_priority
223+
/// [`Kernel::unboost_priority`]: super::Kernel::unboost_priority
224+
pub enum BoostPriorityError: BadContextError {
225+
/// Priority Boost is already active or inactive, the current
226+
/// context is not a task context, or CPU Lock is active.
227+
BadContext,
228+
}
229+
}
230+
218231
define_error! {
219232
/// Error type for wait operations such as [`EventGroup::wait`].
220233
///

src/constance/src/kernel/state.rs

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,45 @@
1-
use super::{BadContextError, Kernel};
1+
use core::sync::atomic::Ordering;
2+
3+
use super::{task, utils, BadContextError, BoostPriorityError, Kernel};
24

35
/// If the current context is not waitable, return `Err(BadContext)`.
46
pub(super) fn expect_waitable_context<System: Kernel>() -> Result<(), BadContextError> {
5-
if System::is_interrupt_context() {
7+
if System::is_interrupt_context() || System::is_priority_boost_active() {
68
Err(BadContextError::BadContext)
79
} else {
8-
// TODO: priority boost
10+
Ok(())
11+
}
12+
}
13+
14+
/// Implements `Kernel::boost_priority`.
15+
pub(super) fn boost_priority<System: Kernel>() -> Result<(), BoostPriorityError> {
16+
if System::is_cpu_lock_active()
17+
|| System::is_interrupt_context()
18+
|| System::is_priority_boost_active()
19+
{
20+
Err(BoostPriorityError::BadContext)
21+
} else {
22+
System::state()
23+
.priority_boost
24+
.store(true, Ordering::Relaxed);
25+
Ok(())
26+
}
27+
}
28+
29+
/// Implements `Kernel::unboost_priority`.
30+
pub(super) fn unboost_priority<System: Kernel>() -> Result<(), BoostPriorityError> {
31+
if System::is_interrupt_context() || !System::is_priority_boost_active() {
32+
Err(BoostPriorityError::BadContext)
33+
} else {
34+
// Acquire CPU Lock after checking other states so that
35+
// `drop_in_place(&mut lock)` doesn't get emitted twice
36+
let lock = utils::lock_cpu()?;
37+
System::state()
38+
.priority_boost
39+
.store(false, Ordering::Relaxed);
40+
41+
// Check pending preemption
42+
task::unlock_cpu_and_check_preemption::<System>(lock);
943
Ok(())
1044
}
1145
}

src/constance/src/kernel/task.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,11 @@ pub(super) unsafe fn exit_current_task<System: Kernel>() -> Result<!, ExitTaskEr
357357
utils::assume_cpu_lock::<System>()
358358
};
359359

360+
// If Priority Boost is active, deacrivate it.
361+
System::state()
362+
.priority_boost
363+
.store(false, Ordering::Release);
364+
360365
// Transition the current task to Dormant
361366
let running_task = System::state().running_task().unwrap();
362367
assert_eq!(*running_task.st.read(&*lock), TaskSt::Running);
@@ -482,6 +487,16 @@ pub(super) unsafe fn make_ready<System: Kernel>(
482487
/// System services that transition a task into a Ready state should call
483488
/// this before returning to the caller.
484489
pub(super) fn unlock_cpu_and_check_preemption<System: Kernel>(lock: utils::CpuLockGuard<System>) {
490+
// If Priority Boost is active, treat the currently running task as the
491+
// highest-priority task.
492+
if System::is_priority_boost_active() {
493+
debug_assert_eq!(
494+
*System::state().running_task().unwrap().st.read(&*lock),
495+
TaskSt::Running
496+
);
497+
return;
498+
}
499+
485500
let prev_task_priority = if let Some(running_task) = System::state().running_task() {
486501
running_task.priority.to_usize().unwrap()
487502
} else {
@@ -508,6 +523,17 @@ pub(super) fn unlock_cpu_and_check_preemption<System: Kernel>(lock: utils::CpuLo
508523
pub(super) fn choose_next_running_task<System: Kernel>(
509524
mut lock: utils::CpuLockGuardBorrowMut<System>,
510525
) {
526+
// If Priority Boost is active, treat the currently running task as the
527+
// highest-priority task.
528+
if System::is_priority_boost_active() {
529+
// Blocking system calls aren't allowed when Priority Boost is active
530+
debug_assert_eq!(
531+
*System::state().running_task().unwrap().st.read(&*lock),
532+
TaskSt::Running
533+
);
534+
return;
535+
}
536+
511537
// The priority of `running_task`
512538
let prev_running_task = System::state().running_task();
513539
let prev_task_priority = if let Some(running_task) = prev_running_task {

src/constance/src/lib.md

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -139,16 +139,13 @@ A system can be in some of the system states described in this section at any po
139139

140140
Like a lock guard of a mutex, CPU Lock can be thought of as something to be “owned” by a current thread. This conception allows it to be seamlessly integrated with Rust's vocabulary and mental model around the ownership model.
141141

142-
**Priority Boost** temporarily raises the effective priority of the current task to higher than any values possible in normal circumstances. Priority Boost can only be activated or deactivated in a task context. Potentially blocking system services are disallowed when Priority Boost is active, and will return [`BadContext`].
142+
**Priority Boost** temporarily raises the effective priority of the current task to higher than any values possible in normal circumstances. Priority Boost can only be activated or deactivated in a task context. Potentially blocking system services are disallowed when Priority Boost is active, and will return [`BadContext`]. Application code can use [`boost_priority`] to activate Priority Boost.
143143

144+
[`boost_priority`]: crate::kernel::Kernel::boost_priority
144145
[`BadContext`]: crate::kernel::ResultCode::BadContext
145146

146147
<div class="admonition-follows"></div>
147148

148-
> **To be implemented:** Supporting priority boost
149-
150-
<div class="admonition-follows"></div>
151-
152149
> **Relation to Other Specifications:** Inspired from [the μITRON4.0 specification](http://www.ertl.jp/ITRON/SPEC/mitron4-e.html). CPU Lock and Priority Boost correspond to a CPU locked state and a dispatching state from μITRON4.0, respectively. In contrast to this specification, both concepts are denoted by proper nouns in the Constance RTOS. This means phrases like “when the CPU is locked” are not allowed.
153150
>
154151
> CPU Lock corresponds to `SuspendOSInterrupts` and `ResumeOSInterrupts` from the OSEK/VDX specification.
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//! Activates and deactivates Priority Boost.
2+
use constance::{kernel::Task, prelude::*};
3+
4+
use super::Driver;
5+
6+
#[derive(Debug)]
7+
pub struct App<System> {
8+
task: Task<System>,
9+
}
10+
11+
impl<System: Kernel> App<System> {
12+
constance::configure! {
13+
pub const fn new<D: Driver<Self>>(_: &mut CfgBuilder<System>) -> Self {
14+
let task = new! { Task<_>, start = task_body::<System, D>, priority = 0, active = true };
15+
16+
App { task }
17+
}
18+
}
19+
}
20+
21+
fn task_body<System: Kernel, D: Driver<App<System>>>(_: usize) {
22+
assert!(!System::is_priority_boost_active());
23+
24+
// Activate Priority Boost
25+
System::boost_priority().unwrap();
26+
27+
// Can't do it again because it's already acquired
28+
assert!(System::is_priority_boost_active());
29+
assert_eq!(
30+
System::boost_priority(),
31+
Err(constance::kernel::BoostPriorityError::BadContext),
32+
);
33+
assert!(System::is_priority_boost_active());
34+
35+
// -------------------------------------------------------------------
36+
37+
// Try acquiring CPU Lock while Priority Boost being active
38+
System::acquire_cpu_lock().unwrap();
39+
assert!(System::has_cpu_lock());
40+
unsafe { System::release_cpu_lock() }.unwrap();
41+
42+
// Blocking operations are disallowed
43+
assert_eq!(
44+
System::park(),
45+
Err(constance::kernel::ParkError::BadContext),
46+
);
47+
48+
// -------------------------------------------------------------------
49+
50+
// Deactivate Priority Boost
51+
unsafe { System::unboost_priority() }.unwrap();
52+
53+
// Can't do it again because it's already deactivated
54+
assert!(!System::is_priority_boost_active());
55+
assert_eq!(
56+
unsafe { System::unboost_priority() },
57+
Err(constance::kernel::BoostPriorityError::BadContext),
58+
);
59+
assert!(!System::is_priority_boost_active());
60+
61+
// -------------------------------------------------------------------
62+
63+
// Acquire CPU Lock, and see that Priority Boost doesn't activate in it
64+
System::acquire_cpu_lock().unwrap();
65+
assert_eq!(
66+
System::boost_priority(),
67+
Err(constance::kernel::BoostPriorityError::BadContext),
68+
);
69+
unsafe { System::release_cpu_lock() }.unwrap();
70+
71+
assert!(!System::is_priority_boost_active());
72+
73+
D::success();
74+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
//! Sequence the execution of tasks using the parking mechanism. Priority Boost
2+
//! is used to temporarily suppress preemption.
3+
use constance::{
4+
kernel::{Hunk, Task},
5+
prelude::*,
6+
};
7+
8+
use super::Driver;
9+
use crate::utils::SeqTracker;
10+
11+
pub struct App<System> {
12+
task2: Task<System>,
13+
seq: Hunk<System, SeqTracker>,
14+
}
15+
16+
impl<System: Kernel> App<System> {
17+
constance::configure! {
18+
pub const fn new<D: Driver<Self>>(_: &mut CfgBuilder<System>) -> Self {
19+
new! { Task<_>, start = task1_body::<System, D>, priority = 2, active = true };
20+
let task2 = new! { Task<_>, start = task2_body::<System, D>, priority = 1, active = true };
21+
22+
let seq = new! { Hunk<_, SeqTracker> };
23+
24+
App { task2, seq }
25+
}
26+
}
27+
}
28+
29+
fn task1_body<System: Kernel, D: Driver<App<System>>>(_: usize) {
30+
D::app().seq.expect_and_replace(1, 2);
31+
32+
// Activate Boost Priority
33+
System::boost_priority().unwrap();
34+
35+
// Boost Priority is active, so `task2` can't preempt yet even though it
36+
// has a higher priority
37+
D::app().task2.unpark_exact().unwrap();
38+
39+
D::app().seq.expect_and_replace(2, 3);
40+
41+
// Deactive Boost Priority. Now `task2` can preempt and run to completion
42+
unsafe { System::unboost_priority() }.unwrap();
43+
44+
D::app().seq.expect_and_replace(4, 5);
45+
46+
D::success();
47+
}
48+
49+
fn task2_body<System: Kernel, D: Driver<App<System>>>(_: usize) {
50+
D::app().seq.expect_and_replace(0, 1);
51+
52+
System::park().unwrap(); // blocks, switching to `task1`
53+
54+
D::app().seq.expect_and_replace(3, 4);
55+
56+
// `task2` is done, now go back to `task1`
57+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//! Checks that Boost Priority is deactivated when a task completes.
2+
use constance::{kernel::Task, prelude::*};
3+
4+
use super::Driver;
5+
6+
pub struct App<System> {
7+
task2: Task<System>,
8+
}
9+
10+
impl<System: Kernel> App<System> {
11+
constance::configure! {
12+
pub const fn new<D: Driver<Self>>(_: &mut CfgBuilder<System>) -> Self {
13+
new! { Task<_>, start = task1_body::<System, D>, priority = 2, active = true };
14+
let task2 = new! { Task<_>, start = task2_body::<System, D>, priority = 1 };
15+
16+
App { task2 }
17+
}
18+
}
19+
}
20+
21+
fn task1_body<System: Kernel, D: Driver<App<System>>>(_: usize) {
22+
// Activate `task2` twice
23+
D::app().task2.activate().unwrap();
24+
D::app().task2.activate().unwrap();
25+
D::success();
26+
}
27+
28+
fn task2_body<System: Kernel, D: Driver<App<System>>>(_: usize) {
29+
// Activate Priority Boost. This should succeed in both runs because
30+
// it's automatically deactivated on each run.
31+
System::boost_priority().unwrap();
32+
}

0 commit comments

Comments
 (0)