transfer commit
This commit is contained in:
51
src/scheduler/generator_task.rs
Normal file
51
src/scheduler/generator_task.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
use std::{ops::{Generator, GeneratorState}, pin::Pin};
|
||||
|
||||
use super::{Task, Nice, TaskState};
|
||||
|
||||
pub struct GeneratorTask<G: Generator<(), Yield = ()>> {
|
||||
nice: Nice,
|
||||
generator: Pin<Box<G>>
|
||||
}
|
||||
|
||||
impl<G> GeneratorTask<G> where G: Generator<(), Yield = ()> {
|
||||
fn new(nice: Nice, generator: G) -> Self { Self {
|
||||
nice,
|
||||
generator: Box::pin(generator)
|
||||
} }
|
||||
}
|
||||
|
||||
impl<G> Task for GeneratorTask<G>
|
||||
where G: Generator<(), Yield = ()> {
|
||||
type Result = G::Return;
|
||||
|
||||
fn run_once(&mut self) -> super::TaskState<Self::Result> {
|
||||
match self.generator.as_mut().resume(()) {
|
||||
GeneratorState::Yielded(()) => super::TaskState::Yield,
|
||||
GeneratorState::Complete(r) => super::TaskState::Complete(r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Task for Pin<Box<T>> where T: Generator<(), Yield = ()> {
|
||||
type Result = T::Return;
|
||||
|
||||
fn run_once(&mut self) -> super::TaskState<Self::Result> {
|
||||
match self.as_mut().resume(()) {
|
||||
GeneratorState::Yielded(()) => TaskState::Yield,
|
||||
GeneratorState::Complete(r) => TaskState::Complete(r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! subtask {
|
||||
($g:tt) => { {
|
||||
let task = $g;
|
||||
loop {
|
||||
match task.run_once() {
|
||||
TaskState::Yield => yield;
|
||||
TaskState::Complete(r) => break r;
|
||||
}
|
||||
}
|
||||
} };
|
||||
}
|
||||
47
src/scheduler/mod.rs
Normal file
47
src/scheduler/mod.rs
Normal file
@@ -0,0 +1,47 @@
|
||||
mod generator_task;
|
||||
mod task_pair;
|
||||
mod task_vec;
|
||||
|
||||
pub type Nice = u16;
|
||||
pub type Priority = i32;
|
||||
|
||||
pub enum TaskState<R> {
|
||||
Yield,
|
||||
Complete(R)
|
||||
}
|
||||
|
||||
pub trait Task {
|
||||
type Result;
|
||||
|
||||
fn run_once(&mut self) -> TaskState<Self::Result>;
|
||||
|
||||
fn run_n_times(&mut self, count: u64) -> TaskState<Self::Result> {
|
||||
for _ in 0..count {
|
||||
if let r@TaskState::Complete(_) = self.run_once() {
|
||||
return r
|
||||
}
|
||||
}
|
||||
return TaskState::Yield
|
||||
}
|
||||
|
||||
fn run_to_completion(&mut self) -> Self::Result {
|
||||
loop { if let TaskState::Complete(r) = self.run_once() {return r} }
|
||||
}
|
||||
|
||||
fn boxed<'a>(self) -> TaskBox<'a, Self::Result> where Self: 'a + Sized { Box::new(self) }
|
||||
}
|
||||
|
||||
pub type TaskBox<'a, T> = Box<dyn Task<Result = T> + 'a>;
|
||||
|
||||
impl<'a, R> Task for TaskBox<'a, R> {
|
||||
type Result = R;
|
||||
|
||||
fn run_once(&mut self) -> TaskState<Self::Result> { self.as_mut().run_once() }
|
||||
fn run_n_times(&mut self, count: u64) -> TaskState<Self::Result> {
|
||||
self.as_mut().run_n_times(count)
|
||||
}
|
||||
|
||||
fn run_to_completion(&mut self) -> Self::Result {
|
||||
self.as_mut().run_to_completion()
|
||||
}
|
||||
}
|
||||
3
src/scheduler/notes.md
Normal file
3
src/scheduler/notes.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Purpose
|
||||
|
||||
Type expressions are trees. Any single branch could terminate the solver and any branch may be nonterminating, therefore all of them must be run concurrently. Thread-based concurrency isn't an option because a compiler must be perfectly deterministic. It is also beneficial to have fine-grained control over the relative priority of different tasks.
|
||||
67
src/scheduler/task_pair.rs
Normal file
67
src/scheduler/task_pair.rs
Normal file
@@ -0,0 +1,67 @@
|
||||
use crate::utils::translate::process;
|
||||
|
||||
use super::{Task, Nice, Priority, TaskState};
|
||||
|
||||
enum TaskPairState<T: Task, U: Task> {
|
||||
Empty,
|
||||
Left(T, U::Result),
|
||||
Right(T::Result, U),
|
||||
Both(T, U)
|
||||
}
|
||||
|
||||
pub struct TaskPair<T: Task, U: Task> {
|
||||
l_nice: Nice,
|
||||
r_nice: Nice,
|
||||
state: TaskPairState<T, U>,
|
||||
tally: Priority,
|
||||
}
|
||||
|
||||
impl<T: Task, U: Task> TaskPair<T, U> {
|
||||
pub fn new(l_nice: Nice, left: T, r_nice: Nice, right: U) -> Self {
|
||||
Self {
|
||||
l_nice, r_nice,
|
||||
tally: 0,
|
||||
state: TaskPairState::Both(left, right)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Task, U: Task> Task for TaskPair<T, U> {
|
||||
type Result = (T::Result, U::Result);
|
||||
|
||||
fn run_once(&mut self) -> TaskState<Self::Result> {
|
||||
let TaskPair{ state, tally, l_nice, r_nice } = self;
|
||||
let ret = process(state, |s| match s {
|
||||
TaskPairState::Empty => panic!("Generator completed and empty"),
|
||||
TaskPairState::Left(mut l_task, r_res) => {
|
||||
match l_task.run_once() {
|
||||
TaskState::Complete(r) => (TaskPairState::Empty, TaskState::Complete((r, r_res))),
|
||||
TaskState::Yield => (TaskPairState::Left(l_task, r_res), TaskState::Yield),
|
||||
}
|
||||
}
|
||||
TaskPairState::Right(l_res, mut r_task) => {
|
||||
match r_task.run_once() {
|
||||
TaskState::Complete(r) => (TaskPairState::Empty, TaskState::Complete((l_res, r))),
|
||||
TaskState::Yield => (TaskPairState::Right(l_res, r_task), TaskState::Yield),
|
||||
}
|
||||
}
|
||||
TaskPairState::Both(mut l_task, mut r_task) => {
|
||||
let state = if 0 <= *tally {
|
||||
*tally -= *l_nice as Priority;
|
||||
match l_task.run_once() {
|
||||
TaskState::Complete(r) => TaskPairState::Right(r, r_task),
|
||||
TaskState::Yield => TaskPairState::Both(l_task, r_task),
|
||||
}
|
||||
} else {
|
||||
*tally += *r_nice as Priority;
|
||||
match r_task.run_once() {
|
||||
TaskState::Complete(r) => TaskPairState::Left(l_task, r),
|
||||
TaskState::Yield => TaskPairState::Both(l_task, r_task),
|
||||
}
|
||||
};
|
||||
(state, TaskState::Yield)
|
||||
}
|
||||
});
|
||||
ret
|
||||
}
|
||||
}
|
||||
107
src/scheduler/task_vec.rs
Normal file
107
src/scheduler/task_vec.rs
Normal file
@@ -0,0 +1,107 @@
|
||||
use std::iter;
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
use super::{Task, Nice, TaskState};
|
||||
|
||||
const NORMALIZATION_THRESHOLD:Nice = Nice::MAX / 4;
|
||||
|
||||
struct TaskEntry<T: Task> {
|
||||
nice: Nice,
|
||||
position: usize,
|
||||
tally: Nice,
|
||||
task: T
|
||||
}
|
||||
|
||||
struct TaskVec<T: Task> {
|
||||
results: Vec<Option<T::Result>>,
|
||||
task_heap: Vec<Option<TaskEntry<T>>>,
|
||||
}
|
||||
|
||||
impl<T: Task> TaskVec<T> {
|
||||
pub fn new(tasks: Vec<(Nice, T)>) -> Self {
|
||||
let mut results = Vec::with_capacity(tasks.len());
|
||||
results.resize_with(tasks.len(), || None);
|
||||
let task_heap = tasks.into_iter().enumerate()
|
||||
.map(|(position, (nice, task))| Some(TaskEntry{ nice, task, position, tally: 1 }))
|
||||
.collect_vec();
|
||||
Self { results, task_heap }
|
||||
}
|
||||
|
||||
fn entry(&self, i: usize) -> Option<&TaskEntry<T>> {
|
||||
if self.task_heap.len() <= i {None}
|
||||
else {self.task_heap[i].as_ref()}
|
||||
}
|
||||
fn entry_mut(&mut self, i: usize) -> Option<&mut TaskEntry<T>> {
|
||||
if self.task_heap.len() <= i {None}
|
||||
else {self.task_heap[i].as_mut()}
|
||||
}
|
||||
fn tally(&self, i: usize) -> Nice {
|
||||
self.task_heap[i].as_ref().map(|e| e.tally).unwrap_or(0)
|
||||
}
|
||||
fn swap(&mut self, a: usize, b: usize) {
|
||||
self.task_heap.swap(a, b);
|
||||
}
|
||||
fn iter_mut(&mut self) -> impl Iterator<Item = &mut TaskEntry<T>> {
|
||||
self.task_heap.iter_mut().filter_map(|e| e.as_mut())
|
||||
}
|
||||
|
||||
fn normalize(&mut self) {
|
||||
let shrink_count = self.task_heap.iter().rev().take_while(|e| e.is_none()).count();
|
||||
let new_len = self.task_heap.len() - shrink_count;
|
||||
self.task_heap.splice(0..new_len, iter::empty());
|
||||
let head = self.entry_mut(0);
|
||||
let offset = if let Some(e) = head {
|
||||
let offset = e.tally - 1;
|
||||
if offset < NORMALIZATION_THRESHOLD {return}
|
||||
e.tally = 1;
|
||||
offset
|
||||
} else {return};
|
||||
for entry in self.iter_mut() { entry.tally -= offset }
|
||||
}
|
||||
|
||||
fn sink(&mut self, i: usize) {
|
||||
let lchi = 2*i + 1;
|
||||
let rchi = 2*i + 2;
|
||||
let t = self.tally(i);
|
||||
let lcht = if let Some(e) = self.entry(lchi) {e.tally} else {
|
||||
if self.tally(rchi) < t {
|
||||
self.swap(rchi, i);
|
||||
self.sink(rchi);
|
||||
}
|
||||
return
|
||||
};
|
||||
let rcht = if let Some(e) = self.entry(rchi) {e.tally} else {
|
||||
if self.tally(lchi) < t {
|
||||
self.swap(lchi, i);
|
||||
self.sink(lchi);
|
||||
}
|
||||
return
|
||||
};
|
||||
let mchi = {
|
||||
if rcht < t && rcht < lcht {rchi}
|
||||
else if lcht < t && lcht < rcht {lchi}
|
||||
else {return}
|
||||
};
|
||||
self.swap(i, mchi);
|
||||
self.sink(mchi);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Task> Task for TaskVec<T> {
|
||||
fn run_once(&mut self) -> super::TaskState<Self::Result> {
|
||||
let head = &mut self.task_heap[0];
|
||||
let head_entry = head.as_mut().expect("All completed, cannot run further");
|
||||
head_entry.tally += head_entry.nice;
|
||||
match head_entry.task.run_once() {
|
||||
TaskState::Complete(r) => {
|
||||
self.results[head_entry.position] = Some(r);
|
||||
*head = None;
|
||||
self.sink(0);
|
||||
if self.entry(0).is_some() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user