Fixed a tricky type erasure bug in the scheduler

This commit is contained in:
2023-10-12 14:36:59 +01:00
parent af3e9f67fa
commit cb395da894
32 changed files with 147 additions and 125 deletions

View File

@@ -18,6 +18,7 @@ use crate::interpreted::{Clause, ExprInst};
use crate::interpreter::HandlerTable;
use crate::pipeline::file_loader::embed_to_map;
use crate::systems::codegen::call;
use crate::systems::stl::Numeric;
use crate::utils::poller::{PollEvent, Poller};
use crate::utils::unwrap_or;
use crate::{ConstTree, Interner};
@@ -28,8 +29,8 @@ struct Timer {
delay: NotNan<f64>,
}
pub fn set_timer(recurring: bool, delay: NotNan<f64>) -> XfnResult<Clause> {
Ok(init_cps(2, Timer { recurring, delay }))
pub fn set_timer(recurring: bool, delay: Numeric) -> XfnResult<Clause> {
Ok(init_cps(2, Timer { recurring, delay: delay.as_float() }))
}
#[derive(Clone)]
@@ -38,9 +39,7 @@ impl CancelTimer {
pub fn new(f: impl Fn() + Send + 'static) -> Self {
Self(Arc::new(Mutex::new(f)))
}
pub fn cancel(&self) {
self.0.lock().unwrap()()
}
pub fn cancel(&self) { self.0.lock().unwrap()() }
}
impl Debug for CancelTimer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {

View File

@@ -39,7 +39,8 @@ pub fn tuple(data: impl IntoIterator<Item = ExprInst>) -> Clause {
#[cfg(test)]
mod test {
use crate::{systems::codegen::tuple, foreign::Atomic};
use crate::foreign::Atomic;
use crate::systems::codegen::tuple;
#[test]
fn tuple_printer() {

View File

@@ -2,12 +2,12 @@ use super::flow::IOCmdHandlePack;
use super::instances::{BRead, ReadCmd, SRead, Sink, Source, WriteCmd};
use crate::error::RuntimeError;
use crate::foreign::cps_box::init_cps;
use crate::foreign::{xfn_1ary, xfn_2ary, Atomic, XfnResult, Atom};
use crate::foreign::{xfn_1ary, xfn_2ary, Atom, Atomic, XfnResult};
use crate::interpreted::Clause;
use crate::representations::OrcString;
use crate::systems::scheduler::SharedHandle;
use crate::systems::stl::Binary;
use crate::{ConstTree, Interner, ast};
use crate::{ast, ConstTree, Interner};
type WriteHandle = SharedHandle<Sink>;
type ReadHandle = SharedHandle<Source>;
@@ -62,9 +62,7 @@ pub fn io_bindings<'a>(
]) + ConstTree::Tree(
std_streams
.into_iter()
.map(|(n, at)| {
(i.i(n), ConstTree::clause(ast::Clause::Atom(Atom(at))))
})
.map(|(n, at)| (i.i(n), ConstTree::clause(ast::Clause::Atom(Atom(at)))))
.collect(),
),
)

View File

@@ -5,12 +5,13 @@ use super::Canceller;
use crate::interpreted::ExprInst;
pub type SyncResult<T> = (T, Box<dyn Any + Send>);
/// Output from handlers contains the resource being processed and any Orchid
/// handlers executed as a result of the operation
pub type HandlerRes<T> = (T, Vec<ExprInst>);
pub type SyncOperation<T> =
Box<dyn FnOnce(T, Canceller) -> SyncResult<T> + Send>;
pub type SyncOpResultHandler<T> = Box<
dyn FnOnce(T, Box<dyn Any + Send>, Canceller) -> (T, Vec<ExprInst>)
+ Send,
dyn FnOnce(T, Box<dyn Any + Send>, Canceller) -> (T, Vec<ExprInst>) + Send,
>;
struct SyncQueueItem<T> {
@@ -43,10 +44,7 @@ pub struct BusyState<T> {
}
impl<T> BusyState<T> {
pub fn new<U: 'static + Send>(
handler: impl FnOnce(T, U, Canceller) -> (T, Vec<ExprInst>)
+ Send
+ 'static,
handler: impl FnOnce(T, U, Canceller) -> HandlerRes<T> + Send + 'static,
) -> Self {
BusyState {
handler: Box::new(|t, payload, cancel| {
@@ -65,8 +63,8 @@ impl<T> BusyState<T> {
pub fn enqueue<U: 'static + Send>(
&mut self,
operation: impl FnOnce(T, Canceller) -> (T, U) + Send + 'static,
handler: impl FnOnce(T, U, Canceller) -> (T, Vec<ExprInst>) + Send + 'static,
early_cancel: impl FnOnce(T) -> (T, Vec<ExprInst>) + Send + 'static,
handler: impl FnOnce(T, U, Canceller) -> HandlerRes<T> + Send + 'static,
early_cancel: impl FnOnce(T) -> HandlerRes<T> + Send + 'static,
) -> Option<Canceller> {
if self.seal.is_some() {
return None;
@@ -80,28 +78,31 @@ impl<T> BusyState<T> {
(t, Box::new(r))
}),
handler: Box::new(|t, u, c| {
let u = u.downcast().expect("mismatched handler and operation");
let u: Box<U> = u.downcast().expect("mismatched handler and operation");
handler(t, *u, c)
}),
});
Some(cancelled)
}
pub fn seal(&mut self, recipient: impl FnOnce(T) -> Vec<ExprInst> + Send + 'static) {
pub fn seal(
&mut self,
recipient: impl FnOnce(T) -> Vec<ExprInst> + Send + 'static,
) {
assert!(self.seal.is_none(), "Already sealed");
self.seal = Some(Box::new(recipient))
}
pub fn is_sealed(&self) -> bool { self.seal.is_some() }
pub fn rotate<U: Send + 'static>(
pub fn rotate(
mut self,
instance: T,
result: U,
result: Box<dyn Any + Send>,
cancelled: Canceller,
) -> NextItemReport<T> {
let (mut instance, mut events) =
(self.handler)(instance, Box::new(result), cancelled);
(self.handler)(instance, result, cancelled);
let next_item = loop {
if let Some(candidate) = self.queue.pop_front() {
if candidate.cancelled.is_cancelled() {

View File

@@ -5,5 +5,6 @@ mod busy;
mod canceller;
mod system;
pub use busy::HandlerRes;
pub use canceller::Canceller;
pub use system::{SealedOrTaken, SeqScheduler, SharedHandle, SharedState};

View File

@@ -8,8 +8,8 @@ use hashbrown::HashMap;
use itertools::Itertools;
use trait_set::trait_set;
use super::busy::{BusyState, NextItemReportKind};
use super::Canceller;
use super::busy::{BusyState, NextItemReportKind, SyncOperation};
use super::{Canceller, HandlerRes};
use crate::error::AssertionError;
use crate::facade::{IntoSystem, System};
use crate::foreign::cps_box::{init_cps, CPSBox};
@@ -80,13 +80,10 @@ impl<T> SharedHandle<T> {
/// is to return values synchronously after they have been removed with
/// [SyncHandle::untake].
pub fn untake(&self, value: T) -> Result<(), T> {
take_with_output(
&mut *self.0.lock().unwrap(),
|state| match state {
SharedResource::Taken => (SharedResource::Free(value), Ok(())),
_ => (state, Err(value)),
},
)
take_with_output(&mut *self.0.lock().unwrap(), |state| match state {
SharedResource::Taken => (SharedResource::Free(value), Ok(())),
_ => (state, Err(value)),
})
}
}
impl<T> Clone for SharedHandle<T> {
@@ -144,7 +141,7 @@ pub fn is_taken_error(x: ExprInst) -> XfnResult<bool> {
trait_set! {
/// The part of processing a blocking I/O task that cannot be done on a remote
/// thread, eg. because it accesses other systems or Orchid code.
trait NonSendFn = FnOnce(Box<dyn Any>, SeqScheduler) -> Vec<ExprInst>;
trait NonSendFn = FnOnce(Box<dyn Any + Send>, SeqScheduler) -> Vec<ExprInst>;
}
struct SyncReply {
@@ -198,9 +195,9 @@ impl SeqScheduler {
pub fn schedule<T: Send + 'static, U: Send + 'static>(
&self,
handle: SharedHandle<T>,
operation: impl FnOnce(T, Canceller) -> (T, U) + Sync + Send + 'static,
handler: impl FnOnce(T, U, Canceller) -> (T, Vec<ExprInst>) + Sync + Send + 'static,
early_cancel: impl FnOnce(T) -> (T, Vec<ExprInst>) + Sync + Send + 'static,
operation: impl FnOnce(T, Canceller) -> (T, U) + Send + 'static,
handler: impl FnOnce(T, U, Canceller) -> HandlerRes<T> + Send + 'static,
early_cancel: impl FnOnce(T) -> HandlerRes<T> + Send + 'static,
) -> Result<Canceller, SealedOrTaken> {
take_with_output(&mut *handle.0.lock().unwrap(), {
let handle = handle.clone();
@@ -216,7 +213,11 @@ impl SeqScheduler {
SharedResource::Free(t) => {
let cancelled = Canceller::new();
drop(early_cancel); // cannot possibly be useful
self.submit(t, handle, cancelled.clone(), operation);
let op_erased: SyncOperation<T> = Box::new(|t, c| {
let (t, u) = operation(t, c);
(t, Box::new(u))
});
self.submit(t, handle, cancelled.clone(), op_erased);
(SharedResource::Busy(BusyState::new(handler)), Ok(cancelled))
},
}
@@ -233,9 +234,11 @@ impl SeqScheduler {
) -> Canceller {
let cancelled = Canceller::new();
let canc1 = cancelled.clone();
let opid = self.0.pending.borrow_mut().insert(Box::new(|data, _| {
handler(*data.downcast().expect("This is associated by ID"), canc1)
}));
let opid = self.0.pending.borrow_mut().insert(Box::new(
|data: Box<dyn Any + Send>, _| {
handler(*data.downcast().expect("This is associated by ID"), canc1)
},
));
let canc1 = cancelled.clone();
let mut port = self.0.port.clone();
self.0.pool.submit(Box::new(move || {
@@ -251,18 +254,15 @@ impl SeqScheduler {
handle: SharedHandle<T>,
seal: impl FnOnce(T) -> Vec<ExprInst> + Sync + Send + 'static,
) -> Result<Vec<ExprInst>, SealedOrTaken> {
take_with_output(
&mut *handle.0.lock().unwrap(),
|state| match state {
SharedResource::Busy(mut b) if !b.is_sealed() => {
b.seal(seal);
(SharedResource::Busy(b), Ok(Vec::new()))
},
SharedResource::Busy(_) => (state, Err(SealedOrTaken)),
SharedResource::Taken => (SharedResource::Taken, Err(SealedOrTaken)),
SharedResource::Free(t) => (SharedResource::Taken, Ok(seal(t))),
take_with_output(&mut *handle.0.lock().unwrap(), |state| match state {
SharedResource::Busy(mut b) if !b.is_sealed() => {
b.seal(seal);
(SharedResource::Busy(b), Ok(Vec::new()))
},
)
SharedResource::Busy(_) => (state, Err(SealedOrTaken)),
SharedResource::Taken => (SharedResource::Taken, Err(SealedOrTaken)),
SharedResource::Free(t) => (SharedResource::Taken, Ok(seal(t))),
})
}
/// Asynchronously recursive function to schedule a new task for execution and
@@ -270,18 +270,18 @@ impl SeqScheduler {
/// from the callback passed to the [AsynchSystem] so that if the task is
/// never resolved but the [AsynchSystem] through which the resolving event
/// would arrive is dropped this [SeqScheduler] is also dropped.
fn submit<T: Send + 'static, U: Send + 'static>(
fn submit<T: Send + 'static>(
&self,
t: T,
handle: SharedHandle<T>,
cancelled: Canceller,
operation: impl FnOnce(T, Canceller) -> (T, U) + Send + 'static,
operation: SyncOperation<T>,
) {
// referenced by self until run, references handle
let opid = self.0.pending.borrow_mut().insert(Box::new({
let cancelled = cancelled.clone();
move |data, this| {
let (t, u): (T, U) =
move |data: Box<dyn Any + Send>, this: SeqScheduler| {
let (t, u): (T, Box<dyn Any + Send>) =
*data.downcast().expect("This is associated by ID");
let handle2 = handle.clone();
take_with_output(&mut *handle.0.lock().unwrap(), |state| {

View File

@@ -1,11 +1,10 @@
use crate::foreign::{xfn_1ary, xfn_2ary, XfnResult, Atom};
use super::Numeric;
use crate::error::AssertionError;
use crate::foreign::{xfn_1ary, xfn_2ary, Atom, XfnResult};
use crate::interner::Interner;
use crate::representations::interpreted::Clause;
use crate::error::AssertionError;
use crate::{ConstTree, Location, OrcString};
use super::Numeric;
/// Takes a boolean and two branches, runs the first if the bool is true, the
/// second if it's false.
// Even though it's a ternary function, IfThenElse is implemented as an unary
@@ -23,11 +22,12 @@ pub fn if_then_else(b: bool) -> XfnResult<Clause> {
/// - both are bool,
/// - both are either uint or num
pub fn equals(a: Atom, b: Atom) -> XfnResult<bool> {
let (a, b) = match (a.try_downcast::<OrcString>(), b.try_downcast::<OrcString>()) {
(Ok(a), Ok(b)) => return Ok(a == b),
(Err(a), Err(b)) => (a, b),
_ => return Ok(false),
};
let (a, b) =
match (a.try_downcast::<OrcString>(), b.try_downcast::<OrcString>()) {
(Ok(a), Ok(b)) => return Ok(a == b),
(Err(a), Err(b)) => (a, b),
_ => return Ok(false),
};
match (a.request::<Numeric>(), b.request::<Numeric>()) {
(Some(a), Some(b)) => return Ok(a.as_float() == b.as_float()),
(None, None) => (),

View File

@@ -3,12 +3,12 @@ import std::panic
-- utilities for using lists as pairs
export const fst := \l. (
const fst := \l. (
list::get l 0
(panic "nonempty expected")
\x.x
)
export const snd := \l. (
const snd := \l. (
list::get l 1
(panic "2 elements expected")
\x.x

View File

@@ -4,15 +4,15 @@ mod arithmetic_error;
mod binary;
mod bool;
mod conv;
mod exit_status;
mod inspect;
mod number;
mod panic;
mod state;
mod stl_system;
mod string;
mod exit_status;
pub use arithmetic_error::ArithmeticError;
pub use binary::Binary;
pub use exit_status::ExitStatus;
pub use number::Numeric;
pub use stl_system::StlConfig;
pub use exit_status::ExitStatus;

View File

@@ -4,7 +4,7 @@ use ordered_float::NotNan;
use super::ArithmeticError;
use crate::error::AssertionError;
use crate::foreign::{xfn_2ary, ExternError, ToClause, XfnResult, Atomic};
use crate::foreign::{xfn_2ary, Atomic, ExternError, ToClause, XfnResult};
use crate::interpreted::TryFromExprInst;
use crate::representations::interpreted::{Clause, ExprInst};
use crate::{ConstTree, Interner, Location};