ax-task 0.8.1

OS-independent IRQ-safe SMP task scheduling core
Documentation
use super::*;
use crate::runtime::task_runtime;

const RUNQUEUE_SEQUENCE_EXHAUSTED_INVARIANT: u32 = 0x5251_0001;

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) enum SequenceAllocationError {
    Exhausted,
}

impl RunQueue {
    pub(super) fn queued_thread_including_current(
        &self,
        id: ThreadId,
    ) -> Option<QueuedThreadSnapshot> {
        match self.membership_class(id)? {
            QueueMembershipClass::Stop => self.stop.as_ref().map(QueuedThreadSnapshot::from),
            QueueMembershipClass::Deadline(key) => {
                self.deadline.get(key).map(QueuedThreadSnapshot::from)
            }
            QueueMembershipClass::DeadlineThrottled => {
                self.deadline.throttled(id).map(QueuedThreadSnapshot::from)
            }
            QueueMembershipClass::Realtime(key) => self.rt.get(key).map(QueuedThreadSnapshot::from),
            QueueMembershipClass::Fair => {
                self.fair.find_first_matching(&mut |thread| thread.id == id)
            }
        }
    }

    pub(super) fn contains(&self, id: ThreadId) -> bool {
        self.membership_class(id).is_some()
    }

    pub(super) fn membership_class(&self, id: ThreadId) -> Option<QueueMembershipClass> {
        self.membership
            .get(id.slot() as usize)
            .and_then(|membership| *membership)
            .filter(|membership| membership.generation == id.generation())
            .map(|membership| membership.class)
    }

    pub(super) fn register_membership(&mut self, id: ThreadId, class: QueueMembershipClass) {
        let slot = id.slot() as usize;
        assert!(
            self.membership.len() > slot,
            "thread construction must prepare owner rq membership"
        );
        assert!(
            self.membership[slot]
                .replace(QueueMembership {
                    generation: id.generation(),
                    class,
                })
                .is_none(),
            "runqueue membership must be unique"
        );
    }

    pub(super) fn unregister_membership(&mut self, id: ThreadId) {
        let membership = self
            .membership
            .get_mut(id.slot() as usize)
            .and_then(Option::take)
            .expect("queued thread must retain owner membership until removal");
        assert_eq!(membership.generation, id.generation());
    }

    pub(super) fn replace_membership_class(&mut self, id: ThreadId, class: QueueMembershipClass) {
        let membership = self
            .membership
            .get_mut(id.slot() as usize)
            .and_then(Option::as_mut)
            .expect("queued thread must retain owner membership during rekey");
        assert_eq!(membership.generation, id.generation());
        membership.class = class;
    }

    pub(super) fn allocate_sequence(&mut self) -> u64 {
        self.try_allocate_sequence()
            .unwrap_or_else(|error| match error {
                SequenceAllocationError::Exhausted => task_runtime::fatal_invariant(
                    RUNQUEUE_SEQUENCE_EXHAUSTED_INVARIANT,
                    self.next_sequence as usize,
                ),
            })
    }

    pub(super) fn try_allocate_sequence(&mut self) -> Result<u64, SequenceAllocationError> {
        let sequence = self.next_sequence;
        let next = sequence
            .checked_add(1)
            .ok_or(SequenceAllocationError::Exhausted)?;
        self.next_sequence = next;
        Ok(sequence)
    }
}