diff --git a/Cargo.lock b/Cargo.lock index fc42f65..1683f6a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,11 +4,12 @@ version = 3 [[package]] name = "ahash" -version = "0.8.9" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d713b3834d76b85304d4d525563c1276e2e30dc97cc67bfb4585a4a29fc2c89f" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", + "getrandom", "once_cell", "version_check", "zerocopy", @@ -164,10 +165,21 @@ dependencies = [ ] [[package]] -name = "hashbrown" -version = "0.14.3" +name = "getrandom" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", "allocator-api2", @@ -273,14 +285,28 @@ dependencies = [ name = "orchid-extension" version = "0.1.0" dependencies = [ + "ahash", + "hashbrown", + "itertools", "orchid-api", "orchid-api-traits", "orchid-base", + "ordered-float", ] [[package]] name = "orchid-host" version = "0.1.0" +dependencies = [ + "derive_destructure", + "hashbrown", + "itertools", + "lazy_static", + "orchid-api", + "orchid-api-traits", + "orchid-base", + "ordered-float", +] [[package]] name = "orchid-std" @@ -498,6 +524,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "winapi" version = "0.3.9" diff --git a/orchid-api-traits/src/coding.rs b/orchid-api-traits/src/coding.rs index 6e2c2b5..0ab623a 100644 --- a/orchid-api-traits/src/coding.rs +++ b/orchid-api-traits/src/coding.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use std::hash::Hash; use std::io::{Read, Write}; use std::iter; -use std::ops::Range; +use std::ops::{Range, RangeInclusive}; use std::rc::Rc; use std::sync::Arc; @@ -24,8 +24,8 @@ pub trait Encode { vec } } -pub trait Coding: Encode + Decode {} -impl Coding for T {} +pub trait Coding: Encode + Decode + Clone {} +impl Coding for T {} macro_rules! num_impl { ($number:ty, $size:expr) => { @@ -81,7 +81,7 @@ nonzero_impl!(std::num::NonZeroI32); nonzero_impl!(std::num::NonZeroI64); nonzero_impl!(std::num::NonZeroI128); -impl<'a, T: Encode> Encode for &'a T { +impl<'a, T: Encode + 'a> Encode for &'a T { fn encode(&self, write: &mut W) { (**self).encode(write) } } impl Decode for NotNan { @@ -233,16 +233,25 @@ impl Decode for [T; N] { impl Encode for [T; N] { fn encode(&self, write: &mut W) { self.iter().for_each(|t| t.encode(write)) } } -impl Decode for Range { - fn decode(read: &mut R) -> Self { T::decode(read)..T::decode(read) } -} -impl Encode for Range { - fn encode(&self, write: &mut W) { - self.start.encode(write); - self.end.encode(write); + +macro_rules! two_end_range { + ($this:ident, $name:tt, $op:tt, $start:expr, $end:expr) => { + impl Decode for $name { + fn decode(read: &mut R) -> Self { T::decode(read) $op T::decode(read) } + } + impl Encode for $name { + fn encode(&self, write: &mut W) { + let $this = self; + ($start).encode(write); + ($end).encode(write); + } + } } } +two_end_range!(x, Range, .., x.start, x.end); +two_end_range!(x, RangeInclusive, ..=, x.start(), x.end()); + macro_rules! smart_ptr { ($name:tt) => { impl Decode for $name { diff --git a/orchid-api-traits/src/lib.rs b/orchid-api-traits/src/lib.rs index 7b7d539..be92cf0 100644 --- a/orchid-api-traits/src/lib.rs +++ b/orchid-api-traits/src/lib.rs @@ -9,4 +9,4 @@ pub use hierarchy::{ Base, Extends, HierarchyRole, InHierarchy, Subtype, TLBool, TLFalse, TLTrue, UnderRoot, UnderRootImpl, }; -pub use relations::{MsgSet, Request}; +pub use relations::{Channel, MsgSet, Request}; diff --git a/orchid-api-traits/src/relations.rs b/orchid-api-traits/src/relations.rs index b50595f..f311ded 100644 --- a/orchid-api-traits/src/relations.rs +++ b/orchid-api-traits/src/relations.rs @@ -5,9 +5,12 @@ pub trait Request: Coding + Sized + Send + 'static { fn respond(&self, rep: Self::Response) -> Vec { rep.enc_vec() } } -pub trait MsgSet { - type InReq: Coding + Sized + Send + 'static; - type InNot: Coding + Sized + Send + 'static; - type OutReq: Coding + Sized + Send + 'static; - type OutNot: Coding + Sized + Send + 'static; +pub trait Channel: 'static { + type Req: Coding + Sized + Send + 'static; + type Notif: Coding + Sized + Send + 'static; +} + +pub trait MsgSet { + type In: Channel; + type Out: Channel; } diff --git a/orchid-api/src/parser.rs b/orchid-api/src/parser.rs index fbff40a..9ec4ff8 100644 --- a/orchid-api/src/parser.rs +++ b/orchid-api/src/parser.rs @@ -1,3 +1,5 @@ +use std::ops::RangeInclusive; + use orchid_api_derive::{Coding, Hierarchy}; use orchid_api_traits::Request; @@ -16,11 +18,18 @@ pub enum ParserReq { pub type LexerId = u16; +#[derive(Clone, Debug, Coding)] +pub struct Lexer { + id: LexerId, + drop: bool, + notify_chars: Option>>, +} + #[derive(Clone, Debug, Coding, Hierarchy)] #[extends(ParserReq, HostExtReq)] pub struct MkLexer(pub SysId, pub TStr); impl Request for MkLexer { - type Response = LexerId; + type Response = Lexer; } #[derive(Clone, Debug, Coding, Hierarchy)] @@ -50,6 +59,7 @@ impl Request for SubLex { type Response = SubLex; } -pub struct ParseLine { +#[derive(Clone, Debug, Coding)] +pub struct LexerDrop; -} +pub struct ParseLine {} diff --git a/orchid-api/src/proto.rs b/orchid-api/src/proto.rs index 236d7e4..e663f3f 100644 --- a/orchid-api/src/proto.rs +++ b/orchid-api/src/proto.rs @@ -26,7 +26,7 @@ use std::io::{Read, Write}; use derive_more::{From, TryInto}; use orchid_api_derive::{Coding, Hierarchy}; -use orchid_api_traits::{read_exact, write_exact, Decode, Encode, MsgSet, Request}; +use orchid_api_traits::{read_exact, write_exact, Channel, Decode, Encode, MsgSet, Request}; use crate::{atom, expr, intern, parser, system, tree}; @@ -77,12 +77,18 @@ pub enum ExtHostReq { } /// Notifications sent from the extension to the host -#[derive(Coding, From, TryInto)] +#[derive(Debug, Clone, Coding, From, TryInto)] #[allow(clippy::enum_variant_names)] pub enum ExtHostNotif { Expr(expr::ExprNotif), } +pub struct ExtHostChannel; +impl Channel for ExtHostChannel { + type Notif = ExtHostNotif; + type Req = ExtHostReq; +} + /// Requests running from the host to the extension #[derive(Clone, Debug, Coding, Hierarchy)] #[extendable] @@ -101,25 +107,28 @@ pub enum HostExtReq { pub enum HostExtNotif { SystemDrop(system::SystemDrop), AtomDrop(atom::AtomDrop), + LexerDrop(parser::LexerDrop), /// The host can assume that after this notif is sent, a correctly written /// extension will eventually exit. Exit, } +pub struct HostExtChannel; +impl Channel for HostExtChannel { + type Notif = HostExtNotif; + type Req = HostExtReq; +} + /// Message set viewed from the extension's perspective pub struct ExtMsgSet; impl MsgSet for ExtMsgSet { - type InNot = HostExtNotif; - type InReq = HostExtReq; - type OutNot = ExtHostNotif; - type OutReq = ExtHostReq; + type In = HostExtChannel; + type Out = ExtHostChannel; } /// Message Set viewed from the host's perspective pub struct HostMsgSet; impl MsgSet for HostMsgSet { - type InNot = ExtHostNotif; - type InReq = ExtHostReq; - type OutNot = HostExtNotif; - type OutReq = HostExtReq; + type In = ExtHostChannel; + type Out = HostExtChannel; } diff --git a/orchid-api/src/system.rs b/orchid-api/src/system.rs index b29e1d1..79cc070 100644 --- a/orchid-api/src/system.rs +++ b/orchid-api/src/system.rs @@ -11,7 +11,7 @@ pub type SysDeclId = u16; pub type SysId = u16; /// Details about a system provided by this library -#[derive(Coding)] +#[derive(Debug, Clone, Coding)] pub struct SystemDecl { /// ID of the system, unique within the library pub id: SysDeclId, diff --git a/orchid-base/src/gen/impls.rs b/orchid-base/src/gen/impls.rs index 9793130..2b420ee 100644 --- a/orchid-base/src/gen/impls.rs +++ b/orchid-base/src/gen/impls.rs @@ -5,8 +5,6 @@ use orchid_api::expr::{Clause, Expr}; use orchid_api::location::Location; use super::traits::{GenClause, Generable}; -use crate::expr::RtExpr; -use crate::host::AtomHand; use crate::intern::{deintern, intern}; fn safely_reinterpret(x: In) -> Result { @@ -24,7 +22,7 @@ impl GenClause for Expr { fn generate(&self, ctx: T::Ctx<'_>, pop: &impl Fn() -> T) -> T { match &self.clause { Clause::Arg(arg) => T::arg(ctx, deintern(*arg).as_str()), - Clause::Atom(atom) => T::atom(ctx, AtomHand::from_api(atom.clone())), + Clause::Atom(atom) => T::atom(ctx, atom.clone()), Clause::Call(f, x) => T::apply(ctx, |c| f.generate(c, pop), |c| x.generate(c, pop)), Clause::Lambda(arg, b) => T::lambda(ctx, deintern(*arg).as_str(), |ctx| b.generate(ctx, pop)), Clause::Seq(n1, n2) => T::seq(ctx, |c| n1.generate(c, pop), |c| n2.generate(c, pop)), diff --git a/orchid-base/src/gen/tpl.rs b/orchid-base/src/gen/tpl.rs index 69c362b..f063f28 100644 --- a/orchid-base/src/gen/tpl.rs +++ b/orchid-base/src/gen/tpl.rs @@ -1,12 +1,13 @@ //! Various elemental components to build expression trees that all implement //! [GenClause]. +use orchid_api::atom::Atom; + use super::traits::{GenClause, Generable}; -use crate::host::AtomHand; /// A trivial atom #[derive(Clone, Debug)] -pub struct SysAtom(pub AtomHand); +pub struct SysAtom(pub Atom); impl GenClause for SysAtom { fn generate(&self, ctx: T::Ctx<'_>, _: &impl Fn() -> T) -> T { T::atom(ctx, self.0.clone()) diff --git a/orchid-base/src/gen/traits.rs b/orchid-base/src/gen/traits.rs index bbb33c7..668270b 100644 --- a/orchid-base/src/gen/traits.rs +++ b/orchid-base/src/gen/traits.rs @@ -5,7 +5,7 @@ use std::cell::RefCell; use std::collections::VecDeque; use std::fmt; -use crate::host::AtomHand; +use orchid_api::atom::Atom; /// Representations of the Orchid expression tree that can describe basic /// language elements. @@ -13,7 +13,7 @@ pub trait Generable: Sized + 'static { /// Context information defined by parents. Generators just forward this. type Ctx<'a>: Sized; /// Wrap external data. - fn atom(ctx: Self::Ctx<'_>, a: AtomHand) -> Self; + fn atom(ctx: Self::Ctx<'_>, a: Atom) -> Self; /// Generate a reference to a constant fn constant<'a>(ctx: Self::Ctx<'_>, name: impl IntoIterator) -> Self; /// Generate a function call given the function and its argument diff --git a/orchid-base/src/gen/tree.rs b/orchid-base/src/gen/tree.rs index a2aa3cd..9451cce 100644 --- a/orchid-base/src/gen/tree.rs +++ b/orchid-base/src/gen/tree.rs @@ -4,13 +4,13 @@ use std::fmt; use dyn_clone::{clone_box, DynClone}; +use orchid_api::atom::Atom; use orchid_api::expr::Expr; use trait_set::trait_set; use super::tpl; use super::traits::{Gen, GenClause}; use crate::combine::Combine; -use crate::host::AtomHand; use crate::tree::{ModEntry, ModMember, TreeConflict}; trait_set! { @@ -64,14 +64,14 @@ pub fn ent>( /// Describe an [Atomic] #[must_use] -pub fn atom_leaf(atom: AtomHand) -> ConstTree { leaf(tpl::SysAtom(atom)) } +pub fn atom_leaf(atom: Atom) -> ConstTree { leaf(tpl::SysAtom(atom)) } /// Describe an [Atomic] which appears as an entry in a [ConstTree::tree] /// /// The unarray is used to trick rustfmt into breaking the atom into a block /// without breaking this call into a block #[must_use] -pub fn atom_ent>(key: K, [atom]: [AtomHand; 1]) -> (K, ConstTree) { +pub fn atom_ent>(key: K, [atom]: [Atom; 1]) -> (K, ConstTree) { (key, atom_leaf(atom)) } diff --git a/orchid-base/src/lib.rs b/orchid-base/src/lib.rs index 3765909..8784840 100644 --- a/orchid-base/src/lib.rs +++ b/orchid-base/src/lib.rs @@ -1,10 +1,9 @@ pub mod boxed_iter; -pub mod child; +pub mod msg; pub mod clone; pub mod combine; pub mod event; -pub mod expr; -pub mod gen; +// pub mod gen; pub mod intern; pub mod location; pub mod name; diff --git a/orchid-base/src/msg.rs b/orchid-base/src/msg.rs new file mode 100644 index 0000000..85c29c3 --- /dev/null +++ b/orchid-base/src/msg.rs @@ -0,0 +1,16 @@ +use std::io; + +pub fn send_msg(write: &mut impl io::Write, msg: &[u8]) -> io::Result<()> { + write.write_all(&(u32::try_from(msg.len()).unwrap()).to_be_bytes())?; + write.write_all(msg)?; + write.flush() +} + +pub fn recv_msg(read: &mut impl io::Read) -> io::Result> { + let mut len = [0u8; 4]; + read.read_exact(&mut len)?; + let len = u32::from_be_bytes(len); + let mut msg = vec![0u8; len as usize]; + read.read_exact(&mut msg)?; + Ok(msg) +} diff --git a/orchid-base/src/reqnot.rs b/orchid-base/src/reqnot.rs index ff360a7..5b57539 100644 --- a/orchid-base/src/reqnot.rs +++ b/orchid-base/src/reqnot.rs @@ -6,13 +6,13 @@ use std::sync::{Arc, Mutex}; use dyn_clone::{clone_box, DynClone}; use hashbrown::HashMap; -use orchid_api_traits::{Coding, Decode, Encode, MsgSet, Request}; +use orchid_api_traits::{Channel, Coding, Decode, Encode, MsgSet, Request}; use trait_set::trait_set; trait_set! { pub trait SendFn = for<'a> FnMut(&'a [u8], ReqNot) + DynClone + Send + 'static; pub trait ReqFn = FnMut(RequestHandle) + Send + 'static; - pub trait NotifFn = for<'a> FnMut(T::InNot, ReqNot) + Send + Sync + 'static; + pub trait NotifFn = for<'a> FnMut(::Notif, ReqNot) + Send + Sync + 'static; } fn get_id(message: &[u8]) -> (u64, &[u8]) { @@ -21,14 +21,14 @@ fn get_id(message: &[u8]) -> (u64, &[u8]) { pub struct RequestHandle { id: u64, - message: T::InReq, + message: ::Req, send: Box>, parent: ReqNot, fulfilled: AtomicBool, } impl RequestHandle { pub fn reqnot(&self) -> ReqNot { self.parent.clone() } - pub fn req(&self) -> &MS::InReq { &self.message } + pub fn req(&self) -> &::Req { &self.message } fn respond(&self, response: &impl Encode) { assert!(!self.fulfilled.swap(true, Ordering::Relaxed), "Already responded"); let mut buf = (!self.id).to_be_bytes().to_vec(); @@ -78,21 +78,21 @@ impl ReqNot { let mut g = self.0.lock().unwrap(); let (id, payload) = get_id(&message[..]); if id == 0 { - (g.notif)(T::InNot::decode(&mut &payload[..]), self.clone()) + (g.notif)(::Notif::decode(&mut &payload[..]), self.clone()) } else if 0 < id.bitand(1 << 63) { let sender = g.responses.remove(&!id).expect("Received response for invalid message"); sender.send(message).unwrap(); } else { let send = clone_box(&*g.send); - let message = T::InReq::decode(&mut &payload[..]); + let message = ::Req::decode(&mut &payload[..]); (g.req)(RequestHandle { id, message, send, fulfilled: false.into(), parent: self.clone() }) } } - pub fn notify>(&self, notif: N) { + pub fn notify::Notif>>(&self, notif: N) { let mut send = clone_box(&*self.0.lock().unwrap().send); let mut buf = vec![0; 8]; - let msg: T::OutNot = notif.into(); + let msg: ::Notif = notif.into(); msg.encode(&mut buf); send(&buf, self.clone()) } @@ -112,7 +112,7 @@ impl<'a, T> DynRequester for MappedRequester<'a, T> { } impl DynRequester for ReqNot { - type Transfer = T::OutReq; + type Transfer = ::Req; fn raw_request(&self, req: Self::Transfer) -> RawReply { let mut g = self.0.lock().unwrap(); let id = g.id; @@ -156,23 +156,27 @@ mod test { use std::sync::{Arc, Mutex}; use orchid_api_derive::Coding; - use orchid_api_traits::Request; + use orchid_api_traits::{Channel, Request}; use super::{MsgSet, ReqNot}; use crate::{clone, reqnot::Requester as _}; - #[derive(Coding, Debug, PartialEq)] + #[derive(Clone, Debug, Coding, PartialEq)] pub struct TestReq(u8); impl Request for TestReq { type Response = u8; } + pub struct TestChan; + impl Channel for TestChan { + type Notif = u8; + type Req = TestReq; + } + pub struct TestMsgSet; impl MsgSet for TestMsgSet { - type InNot = u8; - type InReq = TestReq; - type OutNot = u8; - type OutReq = TestReq; + type In = TestChan; + type Out = TestChan; } #[test] diff --git a/orchid-extension/Cargo.toml b/orchid-extension/Cargo.toml index e8fda3d..d5c24a6 100644 --- a/orchid-extension/Cargo.toml +++ b/orchid-extension/Cargo.toml @@ -6,6 +6,10 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +ahash = "0.8.11" +hashbrown = "0.14.5" +itertools = "0.12.1" orchid-api = { version = "0.1.0", path = "../orchid-api" } orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" } orchid-base = { version = "0.1.0", path = "../orchid-base" } +ordered-float = "4.2.0" diff --git a/orchid-extension/src/data.rs b/orchid-extension/src/data.rs new file mode 100644 index 0000000..04a442d --- /dev/null +++ b/orchid-extension/src/data.rs @@ -0,0 +1,137 @@ +use std::any::TypeId; +use std::hash::{Hash, Hasher}; +use std::marker::PhantomData; + +use itertools::Itertools; +use orchid_api::expr::Expr; +use orchid_api::proto::ExtMsgSet; +use orchid_api::system::{NewSystem, SysId, SystemDecl}; +use orchid_api_traits::Coding; +use orchid_base::reqnot::ReqNot; +use ordered_float::NotNan; + +pub struct SystemHandle { + _t: PhantomData, + id: SysId, + reqnot: ReqNot, +} +impl SystemHandle { + fn new(id: SysId, reqnot: ReqNot) -> Self { Self { _t: PhantomData, id, reqnot } } +} + +pub trait System: Send { + fn consts(&self) -> Expr; +} + +pub struct SystemParams { + pub deps: ::Sat, + pub id: SysId, + pub reqnot: ReqNot, +} + +pub trait SystemDepCard { + type IngressReq: Coding; + type IngressNotif: Coding; + const NAME: &'static str; +} + +pub trait DepSet { + type Sat; + fn report(names: &mut impl FnMut(&'static str)); + fn create(take: &mut impl FnMut() -> SysId, reqnot: ReqNot) -> Self::Sat; +} + +impl DepSet for T { + type Sat = SystemHandle; + fn report(names: &mut impl FnMut(&'static str)) { names(T::NAME) } + fn create(take: &mut impl FnMut() -> SysId, reqnot: ReqNot) -> Self::Sat { + SystemHandle::new(take(), reqnot) + } +} + +pub trait SystemCtor: Send { + type Deps: DepSet; + const NAME: &'static str; + const VERSION: f64; + #[allow(clippy::new_ret_no_self)] + fn new(params: SystemParams) -> Box; +} + +pub trait DynSystemCtor: Send { + fn decl(&self) -> SystemDecl; + fn new_system(&self, new: &NewSystem, reqnot: ReqNot) -> Box; +} + +impl DynSystemCtor for T { + fn decl(&self) -> SystemDecl { + // Version is equivalent to priority for all practical purposes + let priority = NotNan::new(T::VERSION).unwrap(); + // aggregate depends names + let mut depends = Vec::new(); + T::Deps::report(&mut |n| depends.push(n.to_string())); + // generate definitely unique id + let mut ahash = ahash::AHasher::default(); + TypeId::of::().hash(&mut ahash); + let id = (ahash.finish().to_be_bytes().into_iter().tuples()) + .map(|(l, b)| u16::from_be_bytes([l, b])) + .fold(0, |a, b| a ^ b); + SystemDecl { name: T::NAME.to_string(), depends, id, priority } + } + fn new_system(&self, new: &NewSystem, reqnot: ReqNot) -> Box { + let mut ids = new.depends.iter().copied(); + T::new(SystemParams { + deps: T::Deps::create(&mut || ids.next().unwrap(), reqnot.clone()), + id: new.id, + reqnot, + }) + } +} + +pub struct ExtensionData { + pub systems: Vec>, +} + +mod dep_set_tuple_impls { + use orchid_api::proto::ExtMsgSet; + use orchid_api::system::SysId; + use orchid_base::reqnot::ReqNot; + + use super::DepSet; + + macro_rules! dep_set_tuple_impl { + ($($name:ident),*) => { + impl<$( $name :DepSet ),*> DepSet for ( $( $name , )* ) { + type Sat = ( $( $name ::Sat , )* ); + fn report(names: &mut impl FnMut(&'static str)) { + $( + $name ::report(names); + )* + } + fn create(take: &mut impl FnMut() -> SysId, reqnot: ReqNot) -> Self::Sat { + ( + $( + $name ::create(take, reqnot.clone()), + )* + ) + } + } + }; + } + + dep_set_tuple_impl!(A); + dep_set_tuple_impl!(A, B); // 2 + dep_set_tuple_impl!(A, B, C); + dep_set_tuple_impl!(A, B, C, D); // 4 + dep_set_tuple_impl!(A, B, C, D, E); + dep_set_tuple_impl!(A, B, C, D, E, F); + dep_set_tuple_impl!(A, B, C, D, E, F, G); + dep_set_tuple_impl!(A, B, C, D, E, F, G, H); // 8 + dep_set_tuple_impl!(A, B, C, D, E, F, G, H, I); + dep_set_tuple_impl!(A, B, C, D, E, F, G, H, I, J); + dep_set_tuple_impl!(A, B, C, D, E, F, G, H, I, J, K); + dep_set_tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L); // 12 + dep_set_tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M); + dep_set_tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N); + dep_set_tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O); + dep_set_tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P); // 16 +} diff --git a/orchid-extension/src/entrypoint.rs b/orchid-extension/src/entrypoint.rs index 7cf6453..5c00708 100644 --- a/orchid-extension/src/entrypoint.rs +++ b/orchid-extension/src/entrypoint.rs @@ -1,19 +1,23 @@ use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; +use hashbrown::HashMap; +use itertools::Itertools; use orchid_api::proto::{ExtMsgSet, ExtensionHeader, HostExtNotif, HostExtReq, HostHeader}; use orchid_api_traits::{Decode, Encode}; -use orchid_base::child::{recv_parent_msg, send_parent_msg}; use orchid_base::clone; use orchid_base::intern::{init_replica, sweep_replica}; use orchid_base::reqnot::{ReqNot, Requester}; -pub struct ExtensionData {} +use crate::data::ExtensionData; +use crate::msg::{recv_parent_msg, send_parent_msg}; -pub fn main(data: &mut ExtensionData) { +pub fn main(data: ExtensionData) { HostHeader::decode(&mut &recv_parent_msg().unwrap()[..]); let mut buf = Vec::new(); - ExtensionHeader { systems: vec![] }.encode(&mut buf); + let decls = data.systems.iter().map(|sys| sys.decl()).collect_vec(); + let systems = Arc::new(Mutex::new(HashMap::new())); + ExtensionHeader { systems: decls.clone() }.encode(&mut buf); send_parent_msg(&buf).unwrap(); let exiting = Arc::new(AtomicBool::new(false)); let rn = ReqNot::::new( @@ -22,11 +26,17 @@ pub fn main(data: &mut ExtensionData) { HostExtNotif::Exit => exiting.store(true, Ordering::Relaxed), _ => todo!(), }), - |req| match req.req() { + clone!(systems; move |req| match req.req() { HostExtReq::Ping(ping) => req.handle(ping, &()), HostExtReq::Sweep(sweep) => req.handle(sweep, &sweep_replica()), + HostExtReq::NewSystem(new_sys) => { + let i = decls.iter().enumerate().find(|(_, s)| s.id == new_sys.system).unwrap().0; + let system = data.systems[i].new_system(new_sys, req.reqnot()); + systems.lock().unwrap().insert(new_sys.id, system); + req.handle(new_sys, &()) + }, _ => todo!(), - }, + }), ); init_replica(rn.clone().map()); while !exiting.load(Ordering::Relaxed) { diff --git a/orchid-extension/src/extension.rs b/orchid-extension/src/extension.rs deleted file mode 100644 index a1b0244..0000000 --- a/orchid-extension/src/extension.rs +++ /dev/null @@ -1,30 +0,0 @@ -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Arc; - -use orchid_api::proto::{ExtMsgSet, ExtensionHeader, HostExtNotif, HostExtReq, HostHeader}; -use orchid_api_traits::{Decode, Encode}; -use ordered_float::NotNan; - -use crate::child::{recv_parent_msg, send_parent_msg}; -use crate::clone; -use crate::intern::{init_replica, sweep_replica}; -use crate::reqnot::{ReqNot, Requester as _}; - -pub struct SystemParams { - deps: Vec, - -} - -pub struct SystemCtor { - deps: Vec, - make: Box System>, - name: String, - prio: NotNan, - dependencies: Vec, -} - -pub struct ExtensionData { - systems: Vec -} - - diff --git a/orchid-extension/src/lib.rs b/orchid-extension/src/lib.rs index f5ecc5a..74a206f 100644 --- a/orchid-extension/src/lib.rs +++ b/orchid-extension/src/lib.rs @@ -1 +1,3 @@ pub mod entrypoint; +pub mod data; +pub mod msg; diff --git a/orchid-extension/src/msg.rs b/orchid-extension/src/msg.rs new file mode 100644 index 0000000..05fa165 --- /dev/null +++ b/orchid-extension/src/msg.rs @@ -0,0 +1,6 @@ +use std::io; + +use orchid_base::msg::{recv_msg, send_msg}; + +pub fn send_parent_msg(msg: &[u8]) -> io::Result<()> { send_msg(&mut io::stdout().lock(), msg) } +pub fn recv_parent_msg() -> io::Result> { recv_msg(&mut io::stdin().lock()) } diff --git a/orchid-host/Cargo.toml b/orchid-host/Cargo.toml index 1c3667f..2165415 100644 --- a/orchid-host/Cargo.toml +++ b/orchid-host/Cargo.toml @@ -6,3 +6,11 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +derive_destructure = "1.0.0" +hashbrown = "0.14.5" +itertools = "0.12.1" +lazy_static = "1.4.0" +orchid-api = { version = "0.1.0", path = "../orchid-api" } +orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" } +orchid-base = { version = "0.1.0", path = "../orchid-base" } +ordered-float = "4.2.0" diff --git a/orchid-base/src/child.rs b/orchid-host/src/child.rs similarity index 56% rename from orchid-base/src/child.rs rename to orchid-host/src/child.rs index 319915c..c3d731a 100644 --- a/orchid-base/src/child.rs +++ b/orchid-host/src/child.rs @@ -1,7 +1,9 @@ -use std::io::{self, Read, Write}; +use std::io; use std::sync::Mutex; use std::{mem, process}; +use orchid_base::msg::{recv_msg, send_msg}; + pub struct SharedChild { child: process::Child, stdin: Mutex, @@ -24,21 +26,3 @@ impl SharedChild { impl Drop for SharedChild { fn drop(&mut self) { mem::drop(self.child.kill()) } } - -pub fn send_msg(write: &mut impl Write, msg: &[u8]) -> io::Result<()> { - write.write_all(&(u32::try_from(msg.len()).unwrap()).to_be_bytes())?; - write.write_all(msg)?; - write.flush() -} - -pub fn recv_msg(read: &mut impl Read) -> io::Result> { - let mut len = [0u8; 4]; - read.read_exact(&mut len)?; - let len = u32::from_be_bytes(len); - let mut msg = vec![0u8; len as usize]; - read.read_exact(&mut msg)?; - Ok(msg) -} - -pub fn send_parent_msg(msg: &[u8]) -> io::Result<()> { send_msg(&mut io::stdout().lock(), msg) } -pub fn recv_parent_msg() -> io::Result> { recv_msg(&mut io::stdin().lock()) } diff --git a/orchid-host/src/expr.rs b/orchid-host/src/expr.rs index 5a272c1..71e4cd3 100644 --- a/orchid-host/src/expr.rs +++ b/orchid-host/src/expr.rs @@ -2,10 +2,10 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, RwLock}; use hashbrown::HashMap; +use lazy_static::lazy_static; use orchid_api::expr::ExprTicket; -use crate::host::AtomHand; -use lazy_static::lazy_static; +use crate::extension::AtomHand; #[derive(Clone, Debug)] pub struct RtExpr { @@ -36,6 +36,6 @@ impl Drop for RtExpr { } } -lazy_static!{ +lazy_static! { static ref KNOWN_EXPRS: RwLock> = RwLock::default(); } diff --git a/orchid-host/src/extension.rs b/orchid-host/src/extension.rs new file mode 100644 index 0000000..7ee0239 --- /dev/null +++ b/orchid-host/src/extension.rs @@ -0,0 +1,234 @@ +use std::io::Write as _; +use std::sync::atomic::{AtomicU16, AtomicU32, Ordering}; +use std::sync::{Arc, Mutex, RwLock, Weak}; +use std::{fmt, io, process, thread}; + +use derive_destructure::destructure; +use hashbrown::HashMap; +use itertools::Itertools; +use lazy_static::lazy_static; +use orchid_api::atom::{Atom, AtomDrop, AtomSame, CallRef, FinalCall, Fwd, Fwded}; +use orchid_api::expr::{Acquire, Expr, ExprNotif, ExprTicket, Release, Relocate}; +use orchid_api::intern::IntReq; +use orchid_api::proto::{ + ExtHostNotif, ExtHostReq, ExtensionHeader, HostExtNotif, HostHeader, HostMsgSet, +}; +use orchid_api::system::{NewSystem, SysDeclId, SysId, SystemDecl, SystemDrop}; +use orchid_api::tree::{GetConstTree, TreeModule}; +use orchid_api_traits::{Decode, Encode}; +use orchid_base::clone; +use orchid_base::intern::{deintern, intern}; +use orchid_base::reqnot::{ReqNot, Requester as _}; +use ordered_float::NotNan; + +use crate::expr::RtExpr; + +#[derive(Debug, destructure)] +pub struct AtomData { + owner: System, + drop: bool, + data: Vec, +} +impl AtomData { + fn api(self) -> Atom { + let (owner, drop, data) = self.destructure(); + Atom { data, drop, owner: owner.0.id } + } + fn api_ref(&self) -> Atom { + Atom { data: self.data.clone(), drop: self.drop, owner: self.owner.0.id } + } +} +impl Drop for AtomData { + fn drop(&mut self) { + self.owner.0.ext.0.reqnot.notify(AtomDrop(Atom { + owner: self.owner.0.id, + data: self.data.clone(), + drop: true, + })) + } +} + +#[derive(Clone, Debug)] +pub struct AtomHand(Arc); +impl AtomHand { + pub fn from_api(Atom { data, drop, owner }: Atom) -> Self { + let owner = System::resolve(owner).expect("Atom owned by non-existing system"); + Self(Arc::new(AtomData { data, drop, owner })) + } + pub fn call(self, arg: RtExpr) -> Expr { + let owner_sys = self.0.owner.clone(); + let ext = &owner_sys.0.ext; + let ticket = owner_sys.give_expr(arg.canonicalize(), || arg); + match Arc::try_unwrap(self.0) { + Ok(data) => ext.0.reqnot.request(FinalCall(data.api(), ticket)), + Err(hand) => ext.0.reqnot.request(CallRef(hand.api_ref(), ticket)), + } + } + pub fn same(&self, other: &AtomHand) -> bool { + let owner = self.0.owner.0.id; + if other.0.owner.0.id != owner { + return false; + } + self.0.owner.0.ext.0.reqnot.request(AtomSame(self.0.api_ref(), other.0.api_ref())) + } + pub fn req(&self, req: Vec) -> Vec { + self.0.owner.0.ext.0.reqnot.request(Fwded(self.0.api_ref(), req)) + } + pub fn api_ref(&self) -> Atom { self.0.api_ref() } +} + +/// Data held about an Extension. This is refcounted within [Extension]. It's +/// important to only ever access parts of this struct through the [Arc] because +/// the components reference each other through [Weak]s of it, and will panic if +/// upgrading fails. +#[derive(destructure)] +pub struct ExtensionData { + child: Mutex, + reqnot: ReqNot, + systems: Vec, +} +impl Drop for ExtensionData { + fn drop(&mut self) { self.reqnot.notify(HostExtNotif::Exit) } +} + +fn acq_expr(sys: SysId, extk: ExprTicket) { + (System::resolve(sys).expect("Expr acq'd by invalid system")) + .give_expr(extk, || RtExpr::resolve(extk).expect("Invalid expr acq'd")); +} + +fn rel_expr(sys: SysId, extk: ExprTicket) { + let sys = System::resolve(sys).unwrap(); + let mut exprs = sys.0.exprs.write().unwrap(); + exprs.entry(extk).and_replace_entry_with(|_, (rc, rt)| { + (0 < rc.fetch_sub(1, Ordering::Relaxed)).then_some((rc, rt)) + }); +} + +#[derive(Clone)] +pub struct Extension(Arc); +impl Extension { + pub fn new(mut cmd: process::Command) -> io::Result { + let mut child = cmd.stdin(process::Stdio::piped()).stdout(process::Stdio::piped()).spawn()?; + HostHeader.encode(child.stdin.as_mut().unwrap()); + let eh = ExtensionHeader::decode(child.stdout.as_mut().unwrap()); + Ok(Self(Arc::new_cyclic(|weak| ExtensionData { + child: Mutex::new(child), + reqnot: ReqNot::new( + clone!(weak; move |sfn, _| { + let arc: Arc = weak.upgrade().unwrap(); + let mut g = arc.child.lock().unwrap(); + g.stdin.as_mut().unwrap().write_all(sfn).unwrap(); + }), + |notif, _| match notif { + ExtHostNotif::Expr(expr) => match expr { + ExprNotif::Acquire(Acquire(sys, extk)) => acq_expr(sys, extk), + ExprNotif::Release(Release(sys, extk)) => rel_expr(sys, extk), + ExprNotif::Relocate(Relocate { inc, dec, expr }) => { + acq_expr(inc, expr); + rel_expr(dec, expr); + }, + }, + }, + |req| match req.req() { + ExtHostReq::Ping(ping) => req.handle(ping, &()), + ExtHostReq::IntReq(intreq) => match intreq { + IntReq::InternStr(s) => req.handle(s, &intern(&**s.0).marker()), + IntReq::InternStrv(v) => req.handle(v, &intern(&*v.0).marker()), + IntReq::ExternStr(si) => req.handle(si, &deintern(si.0).arc()), + IntReq::ExternStrv(vi) => + req.handle(vi, &Arc::new(deintern(vi.0).iter().map(|t| t.marker()).collect_vec())), + }, + ExtHostReq::Fwd(fw @ Fwd(atom, _body)) => { + let sys = System::resolve(atom.owner).unwrap(); + thread::spawn(clone!(fw; move || { + req.handle(&fw, &sys.0.ext.0.reqnot.request(Fwded(fw.0.clone(), fw.1.clone()))) + })); + }, + _ => todo!(), + }, + ), + systems: eh.systems.into_iter().map(|decl| SystemCtor { decl, ext: weak.clone() }).collect(), + }))) + } + pub fn systems(&self) -> impl Iterator { self.0.systems.iter() } +} + +pub struct SystemCtor { + decl: SystemDecl, + ext: Weak, +} +impl SystemCtor { + pub fn name(&self) -> &str { &self.decl.name } + pub fn priority(&self) -> NotNan { self.decl.priority } + pub fn depends(&self) -> impl ExactSizeIterator { + self.decl.depends.iter().map(|s| &**s) + } + pub fn run<'a>(&self, depends: impl IntoIterator) -> System { + let mut inst_g = SYSTEM_INSTS.write().unwrap(); + let depends = depends.into_iter().map(|si| si.0.id).collect_vec(); + debug_assert_eq!(depends.len(), self.decl.depends.len(), "Wrong number of deps provided"); + let ext = self.ext.upgrade().expect("SystemCtor should be freed before Extension"); + static NEXT_ID: AtomicU16 = AtomicU16::new(0); + let id = NEXT_ID.fetch_add(1, Ordering::Relaxed); + let () = ext.reqnot.request(NewSystem { depends, id, system: self.decl.id }); + let data = System(Arc::new(SystemInstData { + decl_id: self.decl.id, + ext: Extension(ext), + exprs: RwLock::default(), + id, + })); + inst_g.insert(id, data.clone()); + data + } +} + +lazy_static! { + static ref SYSTEM_INSTS: RwLock> = RwLock::default(); +} + +#[derive(destructure)] +pub struct SystemInstData { + exprs: RwLock>, + ext: Extension, + decl_id: SysDeclId, + id: u16, +} +impl Drop for SystemInstData { + fn drop(&mut self) { + self.ext.0.reqnot.notify(SystemDrop(self.id)); + if let Ok(mut g) = SYSTEM_INSTS.write() { + g.remove(&self.id); + } + } +} +#[derive(Clone)] +pub struct System(Arc); +impl System { + fn resolve(id: u16) -> Option { SYSTEM_INSTS.read().unwrap().get(&id).cloned() } + fn give_expr(&self, ticket: ExprTicket, get_expr: impl FnOnce() -> RtExpr) -> ExprTicket { + let mut exprs = self.0.exprs.write().unwrap(); + exprs + .entry(ticket) + .and_modify(|(c, _)| { + c.fetch_add(1, Ordering::Relaxed); + }) + .or_insert((AtomicU32::new(1), get_expr())); + ticket + } + + pub fn const_tree(&self) -> TreeModule { self.0.ext.0.reqnot.request(GetConstTree(self.0.id)) } +} +impl fmt::Debug for System { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let ctor = (self.0.ext.0.systems.iter().find(|c| c.decl.id == self.0.decl_id)) + .expect("System instance with no associated constructor"); + write!(f, "System({} @ {} #{}, ", ctor.decl.name, ctor.decl.priority, self.0.id)?; + match self.0.exprs.read() { + Err(_) => write!(f, "expressions unavailable"), + Ok(r) => { + let rc: u32 = r.values().map(|v| v.0.load(Ordering::Relaxed)).sum(); + write!(f, "{rc} refs to {} exprs", r.len()) + }, + } + } +} diff --git a/orchid-host/src/lib.rs b/orchid-host/src/lib.rs index 1a9c018..a706912 100644 --- a/orchid-host/src/lib.rs +++ b/orchid-host/src/lib.rs @@ -1,234 +1,3 @@ -use std::io::Write as _; -use std::sync::atomic::{AtomicU16, AtomicU32, Ordering}; -use std::sync::{Arc, Mutex, RwLock, Weak}; -use std::{fmt, io, process, thread}; - -use derive_destructure::destructure; -use hashbrown::HashMap; -use itertools::Itertools; -use lazy_static::lazy_static; -use orchid_api::atom::{Atom, AtomDrop, AtomSame, CallRef, FinalCall, Fwd, Fwded}; -use orchid_api::expr::{Acquire, Expr, ExprNotif, ExprTicket, Release, Relocate}; -use orchid_api::intern::IntReq; -use orchid_api::proto::{ - ExtHostNotif, ExtHostReq, ExtensionHeader, HostExtNotif, HostHeader, HostMsgSet, -}; -use orchid_api::system::{NewSystem, SysDeclId, SysId, SystemDecl, SystemDrop}; -use orchid_api::tree::{GetConstTree, TreeModule}; -use orchid_api_traits::{Decode, Encode}; -use ordered_float::NotNan; - -use crate::clone; -use crate::expr::RtExpr; -use crate::intern::{deintern, intern}; -use crate::reqnot::{ReqNot, Requester as _}; - -#[derive(Debug, destructure)] -pub struct AtomData { - owner: System, - drop: bool, - data: Vec, -} -impl AtomData { - fn api(self) -> Atom { - let (owner, drop, data) = self.destructure(); - Atom { data, drop, owner: owner.0.id } - } - fn api_ref(&self) -> Atom { - Atom { data: self.data.clone(), drop: self.drop, owner: self.owner.0.id } - } -} -impl Drop for AtomData { - fn drop(&mut self) { - self.owner.0.ext.0.reqnot.notify(AtomDrop(Atom { - owner: self.owner.0.id, - data: self.data.clone(), - drop: true, - })) - } -} - -#[derive(Clone, Debug)] -pub struct AtomHand(Arc); -impl AtomHand { - pub fn from_api(Atom { data, drop, owner }: Atom) -> Self { - let owner = System::resolve(owner).expect("Atom owned by non-existing system"); - Self(Arc::new(AtomData { data, drop, owner })) - } - pub fn call(self, arg: RtExpr) -> Expr { - let owner_sys = self.0.owner.clone(); - let ext = &owner_sys.0.ext; - let ticket = owner_sys.give_expr(arg.canonicalize(), || arg); - match Arc::try_unwrap(self.0) { - Ok(data) => ext.0.reqnot.request(FinalCall(data.api(), ticket)), - Err(hand) => ext.0.reqnot.request(CallRef(hand.api_ref(), ticket)), - } - } - pub fn same(&self, other: &AtomHand) -> bool { - let owner = self.0.owner.0.id; - if other.0.owner.0.id != owner { - return false; - } - self.0.owner.0.ext.0.reqnot.request(AtomSame(self.0.api_ref(), other.0.api_ref())) - } - pub fn req(&self, req: Vec) -> Vec { - self.0.owner.0.ext.0.reqnot.request(Fwded(self.0.api_ref(), req)) - } - pub fn api_ref(&self) -> Atom { self.0.api_ref() } -} - -/// Data held about an Extension. This is refcounted within [Extension]. It's -/// important to only ever access parts of this struct through the [Arc] because -/// the components reference each other through [Weak]s of it, and will panic if -/// upgrading fails. -#[derive(destructure)] -pub struct ExtensionData { - child: Mutex, - reqnot: ReqNot, - systems: Vec, -} -impl Drop for ExtensionData { - fn drop(&mut self) { self.reqnot.notify(HostExtNotif::Exit) } -} - -fn acq_expr(sys: SysId, extk: ExprTicket) { - (System::resolve(sys).expect("Expr acq'd by invalid system")) - .give_expr(extk, || RtExpr::resolve(extk).expect("Invalid expr acq'd")); -} - -fn rel_expr(sys: SysId, extk: ExprTicket) { - let sys = System::resolve(sys).unwrap(); - let mut exprs = sys.0.exprs.write().unwrap(); - exprs.entry(extk).and_replace_entry_with(|_, (rc, rt)| { - (0 < rc.fetch_sub(1, Ordering::Relaxed)).then_some((rc, rt)) - }); -} - -#[derive(Clone)] -pub struct Extension(Arc); -impl Extension { - pub fn new(mut cmd: process::Command) -> io::Result { - let mut child = cmd.stdin(process::Stdio::piped()).stdout(process::Stdio::piped()).spawn()?; - HostHeader.encode(child.stdin.as_mut().unwrap()); - let eh = ExtensionHeader::decode(child.stdout.as_mut().unwrap()); - Ok(Self(Arc::new_cyclic(|weak| ExtensionData { - child: Mutex::new(child), - reqnot: ReqNot::new( - clone!(weak; move |sfn, _| { - let arc: Arc = weak.upgrade().unwrap(); - let mut g = arc.child.lock().unwrap(); - g.stdin.as_mut().unwrap().write_all(sfn).unwrap(); - }), - |notif, _| match notif { - ExtHostNotif::Expr(expr) => match expr { - ExprNotif::Acquire(Acquire(sys, extk)) => acq_expr(sys, extk), - ExprNotif::Release(Release(sys, extk)) => rel_expr(sys, extk), - ExprNotif::Relocate(Relocate { inc, dec, expr }) => { - acq_expr(inc, expr); - rel_expr(dec, expr); - }, - }, - }, - |req| match req.req() { - ExtHostReq::Ping(ping) => req.handle(ping, &()), - ExtHostReq::IntReq(intreq) => match intreq { - IntReq::InternStr(s) => req.handle(s, &intern(&**s.0).marker()), - IntReq::InternStrv(v) => req.handle(v, &intern(&*v.0).marker()), - IntReq::ExternStr(si) => req.handle(si, &deintern(si.0).arc()), - IntReq::ExternStrv(vi) => - req.handle(vi, &Arc::new(deintern(vi.0).iter().map(|t| t.marker()).collect_vec())), - }, - ExtHostReq::Fwd(fw @ Fwd(atom, _body)) => { - let sys = System::resolve(atom.owner).unwrap(); - thread::spawn(clone!(fw; move || { - req.handle(&fw, &sys.0.ext.0.reqnot.request(Fwded(fw.0.clone(), fw.1.clone()))) - })); - }, - _ => todo!(), - }, - ), - systems: eh.systems.into_iter().map(|decl| SystemCtor { decl, ext: weak.clone() }).collect(), - }))) - } - pub fn systems(&self) -> impl Iterator { self.0.systems.iter() } -} - -pub struct SystemCtor { - decl: SystemDecl, - ext: Weak, -} -impl SystemCtor { - pub fn name(&self) -> &str { &self.decl.name } - pub fn priority(&self) -> NotNan { self.decl.priority } - pub fn depends(&self) -> impl ExactSizeIterator { - self.decl.depends.iter().map(|s| &**s) - } - pub fn run<'a>(&self, depends: impl IntoIterator) -> System { - let mut inst_g = SYSTEM_INSTS.write().unwrap(); - let depends = depends.into_iter().map(|si| si.0.id).collect_vec(); - debug_assert_eq!(depends.len(), self.decl.depends.len(), "Wrong number of deps provided"); - let ext = self.ext.upgrade().expect("SystemCtor should be freed before Extension"); - static NEXT_ID: AtomicU16 = AtomicU16::new(0); - let id = NEXT_ID.fetch_add(1, Ordering::Relaxed); - let () = ext.reqnot.request(NewSystem { depends, id, system: self.decl.id }); - let data = System(Arc::new(SystemInstData { - decl_id: self.decl.id, - ext: Extension(ext), - exprs: RwLock::default(), - id, - })); - inst_g.insert(id, data.clone()); - data - } -} - -lazy_static! { - static ref SYSTEM_INSTS: RwLock> = RwLock::default(); -} - -#[derive(destructure)] -pub struct SystemInstData { - exprs: RwLock>, - ext: Extension, - decl_id: SysDeclId, - id: u16, -} -impl Drop for SystemInstData { - fn drop(&mut self) { - self.ext.0.reqnot.notify(SystemDrop(self.id)); - if let Ok(mut g) = SYSTEM_INSTS.write() { - g.remove(&self.id); - } - } -} -#[derive(Clone)] -pub struct System(Arc); -impl System { - fn resolve(id: u16) -> Option { SYSTEM_INSTS.read().unwrap().get(&id).cloned() } - fn give_expr(&self, ticket: ExprTicket, get_expr: impl FnOnce() -> RtExpr) -> ExprTicket { - let mut exprs = self.0.exprs.write().unwrap(); - exprs - .entry(ticket) - .and_modify(|(c, _)| { - c.fetch_add(1, Ordering::Relaxed); - }) - .or_insert((AtomicU32::new(1), get_expr())); - ticket - } - - pub fn const_tree(&self) -> TreeModule { self.0.ext.0.reqnot.request(GetConstTree(self.0.id)) } -} -impl fmt::Debug for System { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let ctor = (self.0.ext.0.systems.iter().find(|c| c.decl.id == self.0.decl_id)) - .expect("System instance with no associated constructor"); - write!(f, "System({} @ {} #{}, ", ctor.decl.name, ctor.decl.priority, self.0.id)?; - match self.0.exprs.read() { - Err(_) => write!(f, "expressions unavailable"), - Ok(r) => { - let rc: u32 = r.values().map(|v| v.0.load(Ordering::Relaxed)).sum(); - write!(f, "{rc} refs to {} exprs", r.len()) - }, - } - } -} +pub mod child; +pub mod expr; +pub mod extension;