From 7ad4fe96d179cb829784f4d3382abc1fe536b425 Mon Sep 17 00:00:00 2001 From: Lawrence Bethlenfalvy Date: Sun, 12 May 2024 13:30:00 +0200 Subject: [PATCH] Progress mostly on atom repr --- Cargo.lock | 1 + orchid-api/src/expr.rs | 15 ++- orchid-api/src/proto.rs | 6 +- orchid-extension/Cargo.toml | 1 + orchid-extension/src/data.rs | 174 ++++++++++------------------ orchid-extension/src/entrypoint.rs | 6 +- orchid-extension/src/lib.rs | 2 + orchid-extension/src/system.rs | 6 + orchid-extension/src/system_ctor.rs | 114 ++++++++++++++++++ orchid-host/src/extension.rs | 24 ++-- 10 files changed, 212 insertions(+), 137 deletions(-) create mode 100644 orchid-extension/src/system.rs create mode 100644 orchid-extension/src/system_ctor.rs diff --git a/Cargo.lock b/Cargo.lock index 1683f6a..9040634 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -286,6 +286,7 @@ name = "orchid-extension" version = "0.1.0" dependencies = [ "ahash", + "derive_destructure", "hashbrown", "itertools", "orchid-api", diff --git a/orchid-api/src/expr.rs b/orchid-api/src/expr.rs index 4055d55..031357f 100644 --- a/orchid-api/src/expr.rs +++ b/orchid-api/src/expr.rs @@ -4,7 +4,7 @@ use orchid_api_traits::Request; use crate::atom::Atom; use crate::intern::{TStr, TStrv}; use crate::location::Location; -use crate::proto::ExtHostReq; +use crate::proto::{ExtHostNotif, ExtHostReq}; use crate::system::SysId; /// An arbitrary ID associated with an expression on the host side. Incoming @@ -25,7 +25,8 @@ pub type ExprTicket = u64; /// /// This can be called with a foreign system to signal that an owned reference /// is being passed, though [Relocate] may be a better fit. -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding)] +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)] +#[extends(ExprNotif, ExtHostNotif)] pub struct Acquire(pub SysId, pub ExprTicket); /// Release a reference either previously acquired through either [Acquire] @@ -33,13 +34,15 @@ pub struct Acquire(pub SysId, pub ExprTicket); /// acquired an expression is counted, and it is the system's responsibility to /// ensure that acquires and releases pair up. Behaviour in case of excessive /// freeing is not defined. -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding)] +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)] +#[extends(ExprNotif, ExtHostNotif)] pub struct Release(pub SysId, pub ExprTicket); /// Decrement the reference count for one system and increment it for another, /// to indicate passing an owned reference. Equivalent to [Acquire] followed by /// [Release]. -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding)] +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)] +#[extends(ExprNotif, ExtHostNotif)] pub struct Relocate { pub dec: SysId, pub inc: SysId, @@ -91,7 +94,9 @@ pub enum ExprReq { Inspect(Inspect), } -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding)] +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)] +#[extends(ExtHostNotif)] +#[extendable] pub enum ExprNotif { Acquire(Acquire), Release(Release), diff --git a/orchid-api/src/proto.rs b/orchid-api/src/proto.rs index e663f3f..25f556b 100644 --- a/orchid-api/src/proto.rs +++ b/orchid-api/src/proto.rs @@ -24,7 +24,6 @@ use std::io::{Read, Write}; -use derive_more::{From, TryInto}; use orchid_api_derive::{Coding, Hierarchy}; use orchid_api_traits::{read_exact, write_exact, Channel, Decode, Encode, MsgSet, Request}; @@ -77,10 +76,11 @@ pub enum ExtHostReq { } /// Notifications sent from the extension to the host -#[derive(Debug, Clone, Coding, From, TryInto)] +#[derive(Debug, Clone, Coding, Hierarchy)] #[allow(clippy::enum_variant_names)] +#[extendable] pub enum ExtHostNotif { - Expr(expr::ExprNotif), + ExprNotif(expr::ExprNotif), } pub struct ExtHostChannel; diff --git a/orchid-extension/Cargo.toml b/orchid-extension/Cargo.toml index d5c24a6..3ce2f8e 100644 --- a/orchid-extension/Cargo.toml +++ b/orchid-extension/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] ahash = "0.8.11" +derive_destructure = "1.0.0" hashbrown = "0.14.5" itertools = "0.12.1" orchid-api = { version = "0.1.0", path = "../orchid-api" } diff --git a/orchid-extension/src/data.rs b/orchid-extension/src/data.rs index 04a442d..2287960 100644 --- a/orchid-extension/src/data.rs +++ b/orchid-extension/src/data.rs @@ -1,14 +1,13 @@ -use std::any::TypeId; -use std::hash::{Hash, Hasher}; use std::marker::PhantomData; +use std::ops::Deref; -use itertools::Itertools; -use orchid_api::expr::Expr; +use derive_destructure::destructure; +use orchid_api::atom::{Atom, Fwd}; +use orchid_api::expr::{ExprTicket, Release}; 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; +use orchid_api::system::SysId; +use orchid_api_traits::{Coding, Decode, Request}; +use orchid_base::reqnot::{ReqNot, Requester}; pub struct SystemHandle { _t: PhantomData, @@ -16,122 +15,69 @@ pub struct SystemHandle { reqnot: ReqNot, } impl SystemHandle { - fn new(id: SysId, reqnot: ReqNot) -> Self { Self { _t: PhantomData, id, reqnot } } + pub(crate) fn new(id: SysId, reqnot: ReqNot) -> Self { + Self { _t: PhantomData, id, reqnot } + } + pub fn id(&self) -> SysId { self.id } + pub fn wrap_atom>( + &self, + api: Atom, + ticket: ExprTicket, + ) -> Result, Atom> { + if api.owner == self.id { + Ok(OwnedAtom { ticket, sys: self.clone(), value: T::decode_atom(&api), api }) + } else { + Err(api) + } + } +} +impl Clone for SystemHandle { + fn clone(&self) -> Self { Self { reqnot: self.reqnot.clone(), _t: PhantomData, id: self.id } } } -pub trait System: Send { - fn consts(&self) -> Expr; +pub trait Atomic: 'static { + type Owner: SystemDepCard; + type Req: Coding; + const HAS_DROP: bool; } -pub struct SystemParams { - pub deps: ::Sat, - pub id: SysId, - pub reqnot: ReqNot, -} - -pub trait SystemDepCard { - type IngressReq: Coding; - type IngressNotif: Coding; +pub trait SystemDepCard: 'static { const NAME: &'static str; + /// Decode an atom from binary representation. + /// + /// This is held in the dep card because there is no global type tag on the + /// atom, so by the logic of the binary coding algorithm the value has to be a + /// concrete type, probably an enum of the viable types. + fn decode_atom>(api: &Atom) -> A; } -pub trait DepSet { - type Sat; - fn report(names: &mut impl FnMut(&'static str)); - fn create(take: &mut impl FnMut() -> SysId, reqnot: ReqNot) -> Self::Sat; +#[derive(destructure)] +pub struct OwnedAtom { + sys: SystemHandle, + ticket: ExprTicket, + api: Atom, + value: A, } - -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) +impl OwnedAtom { + /// Unpack the object, returning the held atom and expr ticket. This is in + /// contrast to dropping the atom which releases the expr ticket. + pub fn unpack(self) -> (A, ExprTicket, Atom) { + let (_, ticket, api, value) = self.destructure(); + (value, ticket, api) + } + pub fn ticket(&self) -> ExprTicket { self.ticket } + pub fn request + Request>(&self, req: R) -> R::Response { + R::Response::decode(&mut &self.sys.reqnot.request(Fwd(self.api.clone(), req.enc_vec()))[..]) } } - -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; +impl Deref for OwnedAtom { + type Target = A; + fn deref(&self) -> &Self::Target { &self.value } } - -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, - }) +impl Drop for OwnedAtom { + fn drop(&mut self) { + if A::HAS_DROP { + self.sys.reqnot.notify(Release(self.sys.id(), self.ticket)) + } } } - -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 5c00708..88aae97 100644 --- a/orchid-extension/src/entrypoint.rs +++ b/orchid-extension/src/entrypoint.rs @@ -9,8 +9,12 @@ use orchid_base::clone; use orchid_base::intern::{init_replica, sweep_replica}; use orchid_base::reqnot::{ReqNot, Requester}; -use crate::data::ExtensionData; use crate::msg::{recv_parent_msg, send_parent_msg}; +use crate::system_ctor::DynSystemCtor; + +pub struct ExtensionData { + pub systems: Vec>, +} pub fn main(data: ExtensionData) { HostHeader::decode(&mut &recv_parent_msg().unwrap()[..]); diff --git a/orchid-extension/src/lib.rs b/orchid-extension/src/lib.rs index 74a206f..4b5a6c1 100644 --- a/orchid-extension/src/lib.rs +++ b/orchid-extension/src/lib.rs @@ -1,3 +1,5 @@ pub mod entrypoint; pub mod data; pub mod msg; +pub mod system_ctor; +pub mod system; diff --git a/orchid-extension/src/system.rs b/orchid-extension/src/system.rs new file mode 100644 index 0000000..25471a9 --- /dev/null +++ b/orchid-extension/src/system.rs @@ -0,0 +1,6 @@ +use orchid_api::expr::Expr; + +pub trait System: Send { + fn consts(&self) -> Expr; +} + diff --git a/orchid-extension/src/system_ctor.rs b/orchid-extension/src/system_ctor.rs new file mode 100644 index 0000000..2a7b91a --- /dev/null +++ b/orchid-extension/src/system_ctor.rs @@ -0,0 +1,114 @@ +use std::any::TypeId; +use std::hash::{Hash as _, Hasher as _}; + +use itertools::Itertools as _; +use orchid_api::proto::ExtMsgSet; +use orchid_api::system::{NewSystem, SysId, SystemDecl}; +use orchid_base::reqnot::ReqNot; +use ordered_float::NotNan; + +use crate::data::{SystemDepCard, SystemHandle}; +use crate::system::System; + +pub struct SystemParams { + pub deps: ::Sat, + pub id: SysId, + pub reqnot: ReqNot, +} + +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 + 'static { + 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 + 'static { + 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, + }) + } +} + +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-host/src/extension.rs b/orchid-host/src/extension.rs index 7ee0239..f8a09a8 100644 --- a/orchid-host/src/extension.rs +++ b/orchid-host/src/extension.rs @@ -120,24 +120,20 @@ impl Extension { 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); - }, + ExtHostNotif::ExprNotif(ExprNotif::Acquire(Acquire(sys, extk))) => acq_expr(sys, extk), + ExtHostNotif::ExprNotif(ExprNotif::Release(Release(sys, extk))) => rel_expr(sys, extk), + ExtHostNotif::ExprNotif(ExprNotif::Relocate(Relocate { dec, inc, 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::IntReq(IntReq::InternStr(s)) => req.handle(s, &intern(&**s.0).marker()), + ExtHostReq::IntReq(IntReq::InternStrv(v)) => req.handle(v, &intern(&*v.0).marker()), + ExtHostReq::IntReq(IntReq::ExternStr(si)) => req.handle(si, &deintern(si.0).arc()), + ExtHostReq::IntReq(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 || {