<commit_before>//! Scheduler used in Uni.rs

use core::ptr;
use core::mem;

use hal::app;
use hal::{local_irq_disable, local_irq_enable};

use alloc::boxed::Box;

use sync::spin::{InterruptSpinLock, InterruptSpinGuard};
use intrusive::queue::Queue;

use super::{Thread, Builder, ThreadImpl, State};
use super::wait_queue::InternalQueue;

static mut SCHEDULER: SchedulerImpl = SchedulerImpl::new();

pub struct Scheduler;

impl Scheduler {
    /// Mark a thread as ready, and queue it to the ready queue of the
    /// scheduler
    pub fn ready(thread: Thread) {
        unsafe {
            SCHEDULER.ready(thread);
        }
    }

    /// Run the scheduler. The current running process is put back in the
    /// ready queue. The first element of the queue is then executed
    pub fn schedule() {
        unsafe {
            SCHEDULER.schedule(true);
        }
    }

    #[doc(hidden)]
    /// Block the current thread inside a wait queue.
    pub fn block<'a>(queue: InterruptSpinGuard<'a, InternalQueue>) {
        unsafe {
            SCHEDULER.block(queue);
        }
    }

    #[doc(hidden)]
    /// Terminate the execution of the current thread
    pub fn terminate() -> ! {
        unsafe {
            SCHEDULER.terminate();
        }
    }

    /// Spawn a new thread and add it to the scheduler right away
    pub fn spawn<F>(fun: F)
        where F: FnMut() -> (), F: Send + 'static
    {
        Scheduler::ready(Builder::new().spawn(fun));
    }
}

struct SchedulerImpl {
    // This is a little hack here. We are not allowed to have boxes on static
    // mutable variables. Instead we store the raw pointer stored in the Box.
    // We have to be extra careful to rebox it properly when needed,
    // Unique is used to represent ownership
    running: Option<ptr::Unique<ThreadImpl>>,
    ready_list: InterruptSpinLock<Queue<Box<ThreadImpl>, ThreadImpl>>,
    cleanup_queue: InterruptSpinLock<Queue<Box<ThreadImpl>, ThreadImpl>>,
}

impl SchedulerImpl {
    pub const fn new() -> Self {
        SchedulerImpl {
            running: None,
            ready_list: InterruptSpinLock::new(Queue::new()),
            cleanup_queue: InterruptSpinLock::new(Queue::new()),
        }
    }

    /// Mark a thread as ready, and queue it to the ready queue of the
    /// scheduler
    pub fn ready(&self, mut thread: Thread) {
        thread.t_impl.state = State::Ready;

        self.ready_list.lock().enqueue(thread.t_impl);
    }

    unsafe fn cleanup(&mut self) {
        let mut queue = self.cleanup_queue.lock();

        while let Some(t) = queue.dequeue() {
            mem::drop(t);
        }
    }

    // Return the next thread to run. If None is returned then it means that no
    // thread is ready to be scheduled and the current thread was just blocked
    // or terminated
    unsafe fn elect_next(&mut self) -> Option<ptr::Unique<ThreadImpl>> {
        match self.running.take() {
            None => self.ready_list
                        .lock()
                        .dequeue()
                        .and_then(|t| Some(ptr::Unique::new(Box::into_raw(t)))),
            Some(mut r) => {
                let next = self.ready_list.lock().dequeue();

                match next {
                    None => {
                        if r.get().state == State::Running {
                            self.running = Some(r);
                        }

                        None
                    }
                    Some(n) => {
                        if r.get().state == State::Running {
                            r.get_mut().state = State::Ready;

                            self.ready_list
                                .lock()
                                .enqueue(Box::from_raw(r.get_mut()));
                        }

                        Some(ptr::Unique::new(Box::into_raw(n)))
                    }
                }
            }
        }
    }

    unsafe fn switch_to_next(&mut self, mut t: ptr::Unique<ThreadImpl>) -> ! {
        let t_ptr: *const _;

        {
            t_ptr = t.get();
        }

        t.get_mut().state = State::Running;

        self.running = Some(t);

        // Called with interruptions disabled
        local_irq_enable();

        (*t_ptr).context.load();
    }

    pub unsafe fn schedule(&mut self, cleanup: bool) {
        if cleanup {
            // Properly cleanup terminated threads
            self.cleanup();
        }

        if let Some(ref mut r) = self.running {
            // As explained in Context::save() this call will return twice.
            // When this function returns true that means that the thread was
            // just restored and shall continue its execution. This is why we
            // immediately return
            if r.get_mut().context.save() {
                return;
            }
        }

        loop {
            local_irq_disable();

            let next = self.elect_next();

            // The only time elect_next() leaves self.running not None is when
            // the current thread is running and there is no other thread ready
            // to be scheduled. In this case let the current thread continue
            if self.running.is_some() {
                local_irq_enable();
                break;
            }

            match next {
                None => {
                    // If no threads can be run wait for an interruption. An
                    // interruption might wake up a thread for us to run
                    app::block();
                    local_irq_enable();
                }
                Some(next) => {
                    self.switch_to_next(next);
                }
            }
        }
    }

    pub unsafe fn block<'a>(&mut self,
                            mut queue: InterruptSpinGuard<'a, InternalQueue>) {
        if let Some(ref mut r) = self.running {
            r.get_mut().state = State::Blocked;

            queue.enqueue(Box::from_raw(r.get_mut()));

            if r.get_mut().context.save() {
                // We forget the queue because it was dropped when the function
                // was actually blocking the thread. If we are here that means
                // that we were just unblocked (for more info see comment in
                // schedule())
                mem::forget(queue);

                return;
            }<commit_msg>thread/scheduler: Fix some issues detected by clippy<commit_after>//! Scheduler used in Uni.rs

use core::ptr;
use core::mem;

use hal::app;
use hal::{local_irq_disable, local_irq_enable};

use alloc::boxed::Box;

use sync::spin::{InterruptSpinLock, InterruptSpinGuard};
use intrusive::queue::Queue;

use super::{Thread, Builder, ThreadImpl, State};
use super::wait_queue::InternalQueue;

static mut SCHEDULER: SchedulerImpl = SchedulerImpl::new();

pub struct Scheduler;

impl Scheduler {
    /// Mark a thread as ready, and queue it to the ready queue of the
    /// scheduler
    pub fn ready(thread: Thread) {
        unsafe {
            SCHEDULER.ready(thread);
        }
    }

    /// Run the scheduler. The current running process is put back in the
    /// ready queue. The first element of the queue is then executed
    pub fn schedule() {
        unsafe {
            SCHEDULER.schedule(true);
        }
    }

    #[doc(hidden)]
    /// Block the current thread inside a wait queue.
    pub fn block(queue: InterruptSpinGuard<InternalQueue>) {
        unsafe {
            SCHEDULER.block(queue);
        }
    }

    #[doc(hidden)]
    /// Terminate the execution of the current thread
    pub fn terminate() -> ! {
        unsafe {
            SCHEDULER.terminate();
        }
    }

    /// Spawn a new thread and add it to the scheduler right away
    pub fn spawn<F>(fun: F)
        where F: FnMut() -> (), F: Send + 'static
    {
        Scheduler::ready(Builder::new().spawn(fun));
    }
}

struct SchedulerImpl {
    // This is a little hack here. We are not allowed to have boxes on static
    // mutable variables. Instead we store the raw pointer stored in the Box.
    // We have to be extra careful to rebox it properly when needed,
    // Unique is used to represent ownership
    running: Option<ptr::Unique<ThreadImpl>>,
    ready_list: InterruptSpinLock<Queue<Box<ThreadImpl>, ThreadImpl>>,
    cleanup_queue: InterruptSpinLock<Queue<Box<ThreadImpl>, ThreadImpl>>,
}

impl SchedulerImpl {
    pub const fn new() -> Self {
        SchedulerImpl {
            running: None,
            ready_list: InterruptSpinLock::new(Queue::new()),
            cleanup_queue: InterruptSpinLock::new(Queue::new()),
        }
    }

    /// Mark a thread as ready, and queue it to the ready queue of the
    /// scheduler
    pub fn ready(&self, mut thread: Thread) {
        thread.t_impl.state = State::Ready;

        self.ready_list.lock().enqueue(thread.t_impl);
    }

    unsafe fn cleanup(&mut self) {
        let mut queue = self.cleanup_queue.lock();

        while let Some(t) = queue.dequeue() {
            mem::drop(t);
        }
    }

    // Return the next thread to run. If None is returned then it means that no
    // thread is ready to be scheduled and the current thread was just blocked
    // or terminated
    unsafe fn elect_next(&mut self) -> Option<ptr::Unique<ThreadImpl>> {
        match self.running.take() {
            None => self.ready_list
                        .lock()
                        .dequeue()
                        .and_then(|t| Some(ptr::Unique::new(Box::into_raw(t)))),
            Some(mut r) => {
                let next = self.ready_list.lock().dequeue();

                match next {
                    None => {
                        if r.get().state == State::Running {
                            self.running = Some(r);
                        }

                        None
                    }
                    Some(n) => {
                        if r.get().state == State::Running {
                            r.get_mut().state = State::Ready;

                            self.ready_list
                                .lock()
                                .enqueue(Box::from_raw(r.get_mut()));
                        }

                        Some(ptr::Unique::new(Box::into_raw(n)))
                    }
                }
            }
        }
    }

    unsafe fn switch_to_next(&mut self, mut t: ptr::Unique<ThreadImpl>) -> ! {
        let t_ptr: *const _;

        {
            t_ptr = t.get();
        }

        t.get_mut().state = State::Running;

        self.running = Some(t);

        // Called with interruptions disabled
        local_irq_enable();

        (*t_ptr).context.load();
    }

    pub unsafe fn schedule(&mut self, cleanup: bool) {
        if cleanup {
            // Properly cleanup terminated threads
            self.cleanup();
        }

        if let Some(ref mut r) = self.running {
            // As explained in Context::save() this call will return twice.
            // When this function returns true that means that the thread was
            // just restored and shall continue its execution. This is why we
            // immediately return
            if r.get_mut().context.save() {
                return;
            }
        }

        loop {
            local_irq_disable();

            let next = self.elect_next();

            // The only time elect_next() leaves self.running not None is when
            // the current thread is running and there is no other thread ready
            // to be scheduled. In this case let the current thread continue
            if self.running.is_some() {
                local_irq_enable();
                break;
            }

            match next {
                None => {
                    // If no threads can be run wait for an interruption. An
                    // interruption might wake up a thread for us to run
                    app::block();
                    local_irq_enable();
                }
                Some(next) => {
                    self.switch_to_next(next);
                }
            }
        }
    }

    pub unsafe fn block(&mut self,
                        mut queue: InterruptSpinGuard<InternalQueue>) {
        if let Some(ref mut r) = self.running {
            r.get_mut().state = State::Blocked;

            queue.enqueue(Box::from_raw(r.get_mut()));

            if r.get_mut().context.save() {
                // We forget the queue because it was dropped when the function
                // was actually blocking the thread. If we are here that means
                // that we were just unblocked (for more info see comment in
                // schedule())
                mem::forget(queue);

                return;
            }
{'fraction_non_alphanumeric': 0.10333202665621324, 'fraction_numerical': 0.0, 'mean_word_length': 1.5329626687847497, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 55, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '626884', 'n_tokens_mistral': 3437, 'n_tokens_neox': 3266, 'n_words': 1036}