Perform more noncontroversial cleanups

This commit is contained in:
Ruihan Li
2024-10-21 22:44:40 +08:00
committed by Tate, Hongliang Tian
parent 9d5350c2ee
commit 7e2e9cebf6
2 changed files with 35 additions and 17 deletions

View File

@ -266,9 +266,8 @@ impl Waker {
} }
fn do_wait(&self) { fn do_wait(&self) {
let has_woken = &self.has_woken; while !self.has_woken.swap(false, Ordering::Acquire) {
while !has_woken.swap(false, Ordering::Acquire) { scheduler::park_current(|| self.has_woken.load(Ordering::Acquire));
scheduler::park_current(has_woken);
} }
} }

View File

@ -8,8 +8,6 @@
mod fifo_scheduler; mod fifo_scheduler;
pub mod info; pub mod info;
use core::sync::atomic::{AtomicBool, Ordering};
use spin::Once; use spin::Once;
use super::{preempt::cpu_local, processor, Task}; use super::{preempt::cpu_local, processor, Task};
@ -114,27 +112,45 @@ pub(crate) fn might_preempt() {
yield_now(); yield_now();
} }
/// Blocks the current task unless `has_woken` is `true`. /// Blocks the current task unless `has_woken()` returns `true`.
pub(crate) fn park_current(has_woken: &AtomicBool) { ///
/// Note that this method may return due to spurious wake events. It's the caller's responsibility
/// to detect them (if necessary).
pub(crate) fn park_current<F>(has_woken: F)
where
F: Fn() -> bool,
{
let mut current = None; let mut current = None;
let mut is_first_try = true; let mut is_first_try = true;
reschedule(&mut |local_rq: &mut dyn LocalRunQueue| {
reschedule(|local_rq: &mut dyn LocalRunQueue| {
if is_first_try { if is_first_try {
if has_woken.load(Ordering::Acquire) { if has_woken() {
return ReschedAction::DoNothing; return ReschedAction::DoNothing;
} }
// Note the race conditions: the current task may be woken after the above `has_woken`
// check, but before the below `dequeue_current` action, we need to make sure that the
// wakeup event isn't lost.
//
// Currently, for the FIFO scheduler, `Scheduler::enqueue` will try to lock `local_rq`
// when the above race condition occurs, so it will wait until we finish calling the
// `dequeue_current` method and nothing bad will happen. This may need to be revisited
// after more complex schedulers are introduced.
current = local_rq.dequeue_current(); current = local_rq.dequeue_current();
local_rq.update_current(UpdateFlags::Wait); local_rq.update_current(UpdateFlags::Wait);
} }
if let Some(next_task) = local_rq.pick_next_current() { if let Some(next_task) = local_rq.pick_next_current() {
if Arc::ptr_eq(current.as_ref().unwrap(), next_task) { if Arc::ptr_eq(current.as_ref().unwrap(), next_task) {
return ReschedAction::DoNothing; return ReschedAction::DoNothing;
} }
ReschedAction::SwitchTo(next_task.clone()) return ReschedAction::SwitchTo(next_task.clone());
} else { }
is_first_try = false; is_first_try = false;
ReschedAction::Retry ReschedAction::Retry
}
}); });
} }
@ -184,7 +200,7 @@ fn set_need_preempt(cpu_id: CpuId) {
/// ///
/// This should only be called if the current is to exit. /// This should only be called if the current is to exit.
pub(super) fn exit_current() -> ! { pub(super) fn exit_current() -> ! {
reschedule(&mut |local_rq: &mut dyn LocalRunQueue| { reschedule(|local_rq: &mut dyn LocalRunQueue| {
let _ = local_rq.dequeue_current(); let _ = local_rq.dequeue_current();
if let Some(next_task) = local_rq.pick_next_current() { if let Some(next_task) = local_rq.pick_next_current() {
ReschedAction::SwitchTo(next_task.clone()) ReschedAction::SwitchTo(next_task.clone())
@ -198,9 +214,8 @@ pub(super) fn exit_current() -> ! {
/// Yields execution. /// Yields execution.
pub(super) fn yield_now() { pub(super) fn yield_now() {
reschedule(&mut |local_rq| { reschedule(|local_rq| {
local_rq.update_current(UpdateFlags::Yield); local_rq.update_current(UpdateFlags::Yield);
if let Some(next_task) = local_rq.pick_next_current() { if let Some(next_task) = local_rq.pick_next_current() {
ReschedAction::SwitchTo(next_task.clone()) ReschedAction::SwitchTo(next_task.clone())
} else { } else {
@ -213,7 +228,7 @@ pub(super) fn yield_now() {
/// user-given closure. /// user-given closure.
/// ///
/// The closure makes the scheduling decision by taking the local runqueue has its input. /// The closure makes the scheduling decision by taking the local runqueue has its input.
fn reschedule<F>(f: &mut F) fn reschedule<F>(mut f: F)
where where
F: FnMut(&mut dyn LocalRunQueue) -> ReschedAction, F: FnMut(&mut dyn LocalRunQueue) -> ReschedAction,
{ {
@ -236,6 +251,10 @@ where
}; };
}; };
// FIXME: At this point, we need to prevent the current task from being scheduled on another
// CPU core. However, we currently have no way to ensure this. This is a soundness hole and
// should be fixed. See <https://github.com/asterinas/asterinas/issues/1471> for details.
cpu_local::clear_need_preempt(); cpu_local::clear_need_preempt();
processor::switch_to_task(next_task); processor::switch_to_task(next_task);
} }