Skip to content

Commit 7a72343

Browse files
committed
fix(task): handle the case where next_running_task == prev_running_task in choose_next_running_task
Fixes the test case `timer_zero_period` failing on `gr_peach`. The following diagram illustrates a simplified execution flow that causes this bug: wait_until_woken_up: [running_task = &task1] task1.state = Waiting leave_cpu_lock() interrupt_handler: make_ready(&task1): task1.state = Ready queue.push_back(&task1) yield_cpu() choose_next_running_task(): next_running_task = queue.pop_front() [next_running_task = &task1] next_running_task.state = Running make_ready(&running_task): running_task.state = Ready queue.push_back(&running_task) running_task = next_running_task yield_cpu() [task1.state = Ready] yield_cpu() [task1.state = Ready] <loop indefinitely> (Conditional branches are omitted. Brackets [] denotes excerpts of current internal states that destine the outcomes of conditional branches.) `choose_next_running_task` did not handle the case where `next_running_task == prev_running_task`. As a result, it transitioned `next_running_task` into Ready state when it's expected to transition it into Running state. The correct execution flow looks like the following: wait_until_woken_up: [running_task = &task1] task1.state = Waiting leave_cpu_lock() interrupt_handler: make_ready(&task1): task1.state = Ready queue.push_back(&task1) yield_cpu() choose_next_running_task(): next_running_task = queue.pop_front() [next_running_task = &task1] next_running_task.state = Running [next_running_task = running_task] yield_cpu() [task1.state = Running] enter_cpu_lock()
1 parent bce1e58 commit 7a72343

File tree

2 files changed

+27
-2
lines changed

2 files changed

+27
-2
lines changed

src/constance/src/kernel/task.rs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,11 @@ pub(super) fn unlock_cpu_and_check_preemption<System: Kernel>(
570570

571571
let prev_task_priority =
572572
if let Some(running_task) = System::state().running_task(lock.borrow_mut()) {
573-
running_task.priority.read(&*lock).to_usize().unwrap()
573+
if *running_task.st.read(&*lock) == TaskSt::Running {
574+
running_task.priority.read(&*lock).to_usize().unwrap()
575+
} else {
576+
usize::MAX
577+
}
574578
} else {
575579
usize::MAX
576580
};
@@ -667,12 +671,18 @@ pub(super) fn choose_next_running_task<System: Kernel>(
667671
// Transition `next_running_task` into the Running state
668672
task.st.replace(&mut *lock, TaskSt::Running);
669673

674+
if ptr_from_option_ref(prev_running_task) == task {
675+
// Skip the remaining steps if `task == prev_running_task`
676+
return;
677+
}
678+
670679
Some(task)
671680
} else {
672681
None
673682
};
674683

675-
// If `prev_running_task` is in the Running state, transition it into Ready
684+
// If `prev_running_task` is in the Running state, transition it into Ready.
685+
// Assumes `prev_running_task != next_running_task`.
676686
if let Some(running_task) = prev_running_task {
677687
match running_task.st.read(&*lock) {
678688
TaskSt::Running => {
@@ -689,6 +699,15 @@ pub(super) fn choose_next_running_task<System: Kernel>(
689699
.replace(&mut *lock, next_running_task);
690700
}
691701

702+
#[inline]
703+
fn ptr_from_option_ref<T>(x: Option<&T>) -> *const T {
704+
if let Some(x) = x {
705+
x
706+
} else {
707+
core::ptr::null()
708+
}
709+
}
710+
692711
/// Transition the currently running task into the Waiting state. Returns when
693712
/// woken up.
694713
///
@@ -723,6 +742,8 @@ pub(super) fn wait_until_woken_up<System: Kernel>(
723742
if *running_task.st.read(&*lock) == TaskSt::Running {
724743
break;
725744
}
745+
746+
assert_eq!(*running_task.st.read(&*lock), TaskSt::Waiting);
726747
}
727748
}
728749

src/constance_test_suite/src/kernel_tests/time_misc.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,5 +122,9 @@ fn task_body<System: Kernel, D: Driver<App<System>>>(_: usize) {
122122
Err(constance::kernel::SleepError::BadParam)
123123
);
124124

125+
// Wait zero microseconds
126+
log::trace!("sleep(0)");
127+
System::sleep(Duration::ZERO).unwrap();
128+
125129
D::success();
126130
}

0 commit comments

Comments
 (0)