rusty_mos/process/
scheduler.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//! Do the schedule job.

use crate::process::envs::{env_run, EnvStatus, CUR_ENV_IDX, ENVS_DATA, ENV_SCHE_LIST, NENV};
use core::sync::atomic::{AtomicU32, Ordering::SeqCst};

/// Record the env's rest time-slice for scheduling.
static ENV_REST_COUNT: AtomicU32 = AtomicU32::new(0);

/// Schedule the envs. If `yield`, the current env will be moved to the tail
/// of the schedule list. Otherwise, the strategy will judge the rest time the
/// env will enjoy.
///
/// We use the priority to represent the time-slice count of a env. If the count
/// run out, the next env will be selected.
///
/// # Return
/// The function is a *no-return* function. [env_run] will run the selected env.
///
/// # Panic
///
/// The list is empty when we should pick one env to run. Only in this
/// situation, a panic will be raised.
///
/// # Safety
/// Actually, the list and the current env pointer **SHALL** be valid.
#[no_mangle]
pub fn schedule(r#yield: bool) -> ! {
    let mut env = CUR_ENV_IDX.load(SeqCst);
    if r#yield
        || ENV_REST_COUNT.load(SeqCst) == 0
        || env == NENV
        || ENVS_DATA.borrow().0[env].status != EnvStatus::Runnable
    {
        if env != NENV && ENVS_DATA.borrow().0[env].status == EnvStatus::Runnable {
            ENV_SCHE_LIST.borrow_mut().remove(env);
            ENV_SCHE_LIST.borrow_mut().insert_tail(env);
        }
        if ENV_SCHE_LIST.borrow().empty() {
            panic!("Schedule queue is empty. Terminated!");
        }
        env = ENV_SCHE_LIST.borrow().peek_head().unwrap();
        ENV_REST_COUNT.store(ENVS_DATA.borrow().0[env].priority, SeqCst);
    }

    let _count = ENV_REST_COUNT.fetch_sub(1, SeqCst);

    env_run(env);
}