// scheduler/queue.mbt
// Priority-aware waiting queue used by all scheduling disciplines.
// Jobs are stored sorted by the chosen discipline at insertion time so
// dequeue is always O(1) and enqueue is O(n) worst-case (acceptable for
// simulation workloads).
///|
/// Available queueing disciplines governing the order in which Jobs are
/// dequeued from the waiting area.
pub(all) enum QueueDiscipline {
/// Jobs are served in the order they arrived (smallest `arrival_time` first).
Fifo
/// Jobs are served in reverse arrival order (newest first).
Lifo
/// Jobs are served in ascending priority order (smallest priority integer wins).
PriorityAscending
/// Jobs are served in descending priority order (largest priority integer wins).
PriorityDescending
/// Shortest-service-time jobs are served first (Shortest Job First).
ShortestJobFirst
/// Longest-service-time jobs are served first.
LongestJobFirst
/// Jobs closest to their deadline are served first (Earliest Deadline First).
EarliestDeadlineFirst
} derive(Debug, Eq)
///|
/// A waiting queue backed by an array sorted according to a chosen discipline.
pub(all) struct JobQueue {
discipline : QueueDiscipline
priv jobs : Array[Job]
}
///|
pub fn JobQueue::new(discipline : QueueDiscipline) -> JobQueue {
{ discipline, jobs: [] }
}
///|
pub fn JobQueue::length(self : JobQueue) -> Int {
self.jobs.length()
}
///|
pub fn JobQueue::is_empty(self : JobQueue) -> Bool {
self.jobs.is_empty()
}
///|
/// Compare two jobs according to the queue's discipline.
/// Returns negative if `a` should be served before `b`.
fn JobQueue::compare_jobs(self : JobQueue, a : Job, b : Job) -> Int {
match self.discipline {
Fifo =>
if a.arrival_time < b.arrival_time {
-1
} else if a.arrival_time > b.arrival_time {
1
} else {
a.id - b.id
}
Lifo =>
if a.arrival_time > b.arrival_time {
-1
} else if a.arrival_time < b.arrival_time {
1
} else {
b.id - a.id
}
PriorityAscending =>
if a.priority < b.priority {
-1
} else if a.priority > b.priority {
1
} else if a.arrival_time < b.arrival_time {
-1
} else if a.arrival_time > b.arrival_time {
1
} else {
a.id - b.id
}
PriorityDescending =>
if a.priority > b.priority {
-1
} else if a.priority < b.priority {
1
} else if a.arrival_time < b.arrival_time {
-1
} else if a.arrival_time > b.arrival_time {
1
} else {
a.id - b.id
}
ShortestJobFirst =>
if a.service_time < b.service_time {
-1
} else if a.service_time > b.service_time {
1
} else {
a.id - b.id
}
LongestJobFirst =>
if a.service_time > b.service_time {
-1
} else if a.service_time < b.service_time {
1
} else {
a.id - b.id
}
EarliestDeadlineFirst => {
let dl_a = match a.deadline {
None => 1.0e300
Some(d) => d
}
let dl_b = match b.deadline {
None => 1.0e300
Some(d) => d
}
if dl_a < dl_b {
-1
} else if dl_a > dl_b {
1
} else {
a.id - b.id
}
}
}
}
///|
/// Enqueue a job in the position determined by the discipline.
pub fn JobQueue::enqueue(self : JobQueue, job : Job) -> Unit {
let mut insert_idx = self.jobs.length()
for i = 0; i < self.jobs.length(); i = i + 1 {
if self.compare_jobs(job, self.jobs[i]) < 0 {
insert_idx = i
break
}
}
self.jobs.insert(insert_idx, job)
}
///|
/// Dequeue the highest-priority job according to the current discipline.
/// Returns `None` if the queue is empty.
pub fn JobQueue::dequeue(self : JobQueue) -> Job? {
if self.jobs.is_empty() {
None
} else {
Some(self.jobs.remove(0))
}
}
///|
/// Peek at the next job to be dequeued without removing it.
pub fn JobQueue::peek(self : JobQueue) -> Job? {
if self.jobs.is_empty() {
None
} else {
Some(self.jobs[0])
}
}
///|
/// Remove a specific job by id. Returns `true` if found and removed.
pub fn JobQueue::remove_by_id(self : JobQueue, job_id : Int) -> Bool {
let mut idx = 0
let mut found = false
while idx < self.jobs.length() {
if self.jobs[idx].id == job_id {
let _ = self.jobs.remove(idx)
found = true
break
} else {
idx = idx + 1
}
}
found
}