Separated orchid-host and orchid-extension
This is an architectural change that allows me to implment specifics first and generalize along observed symmetries in orchid-base
This commit is contained in:
42
Cargo.lock
generated
42
Cargo.lock
generated
@@ -4,11 +4,12 @@ version = 3
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ahash"
|
name = "ahash"
|
||||||
version = "0.8.9"
|
version = "0.8.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d713b3834d76b85304d4d525563c1276e2e30dc97cc67bfb4585a4a29fc2c89f"
|
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
"getrandom",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"version_check",
|
"version_check",
|
||||||
"zerocopy",
|
"zerocopy",
|
||||||
@@ -164,10 +165,21 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "getrandom"
|
||||||
version = "0.14.3"
|
version = "0.2.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
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 = [
|
dependencies = [
|
||||||
"ahash",
|
"ahash",
|
||||||
"allocator-api2",
|
"allocator-api2",
|
||||||
@@ -273,14 +285,28 @@ dependencies = [
|
|||||||
name = "orchid-extension"
|
name = "orchid-extension"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
"hashbrown",
|
||||||
|
"itertools",
|
||||||
"orchid-api",
|
"orchid-api",
|
||||||
"orchid-api-traits",
|
"orchid-api-traits",
|
||||||
"orchid-base",
|
"orchid-base",
|
||||||
|
"ordered-float",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "orchid-host"
|
name = "orchid-host"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"derive_destructure",
|
||||||
|
"hashbrown",
|
||||||
|
"itertools",
|
||||||
|
"lazy_static",
|
||||||
|
"orchid-api",
|
||||||
|
"orchid-api-traits",
|
||||||
|
"orchid-base",
|
||||||
|
"ordered-float",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "orchid-std"
|
name = "orchid-std"
|
||||||
@@ -498,6 +524,12 @@ dependencies = [
|
|||||||
"winapi-util",
|
"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]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::collections::HashMap;
|
|||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::ops::Range;
|
use std::ops::{Range, RangeInclusive};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -24,8 +24,8 @@ pub trait Encode {
|
|||||||
vec
|
vec
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub trait Coding: Encode + Decode {}
|
pub trait Coding: Encode + Decode + Clone {}
|
||||||
impl<T: Encode + Decode> Coding for T {}
|
impl<T: Encode + Decode + Clone> Coding for T {}
|
||||||
|
|
||||||
macro_rules! num_impl {
|
macro_rules! num_impl {
|
||||||
($number:ty, $size:expr) => {
|
($number:ty, $size:expr) => {
|
||||||
@@ -81,7 +81,7 @@ nonzero_impl!(std::num::NonZeroI32);
|
|||||||
nonzero_impl!(std::num::NonZeroI64);
|
nonzero_impl!(std::num::NonZeroI64);
|
||||||
nonzero_impl!(std::num::NonZeroI128);
|
nonzero_impl!(std::num::NonZeroI128);
|
||||||
|
|
||||||
impl<'a, T: Encode> Encode for &'a T {
|
impl<'a, T: Encode + 'a> Encode for &'a T {
|
||||||
fn encode<W: Write>(&self, write: &mut W) { (**self).encode(write) }
|
fn encode<W: Write>(&self, write: &mut W) { (**self).encode(write) }
|
||||||
}
|
}
|
||||||
impl<T: Decode + FloatCore> Decode for NotNan<T> {
|
impl<T: Decode + FloatCore> Decode for NotNan<T> {
|
||||||
@@ -233,16 +233,25 @@ impl<T: Decode, const N: usize> Decode for [T; N] {
|
|||||||
impl<T: Encode, const N: usize> Encode for [T; N] {
|
impl<T: Encode, const N: usize> Encode for [T; N] {
|
||||||
fn encode<W: Write>(&self, write: &mut W) { self.iter().for_each(|t| t.encode(write)) }
|
fn encode<W: Write>(&self, write: &mut W) { self.iter().for_each(|t| t.encode(write)) }
|
||||||
}
|
}
|
||||||
impl<T: Decode> Decode for Range<T> {
|
|
||||||
fn decode<R: Read>(read: &mut R) -> Self { T::decode(read)..T::decode(read) }
|
macro_rules! two_end_range {
|
||||||
}
|
($this:ident, $name:tt, $op:tt, $start:expr, $end:expr) => {
|
||||||
impl<T: Encode> Encode for Range<T> {
|
impl<T: Decode> Decode for $name<T> {
|
||||||
fn encode<W: Write>(&self, write: &mut W) {
|
fn decode<R: Read>(read: &mut R) -> Self { T::decode(read) $op T::decode(read) }
|
||||||
self.start.encode(write);
|
}
|
||||||
self.end.encode(write);
|
impl<T: Encode> Encode for $name<T> {
|
||||||
|
fn encode<W: Write>(&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 {
|
macro_rules! smart_ptr {
|
||||||
($name:tt) => {
|
($name:tt) => {
|
||||||
impl<T: Decode> Decode for $name<T> {
|
impl<T: Decode> Decode for $name<T> {
|
||||||
|
|||||||
@@ -9,4 +9,4 @@ pub use hierarchy::{
|
|||||||
Base, Extends, HierarchyRole, InHierarchy, Subtype, TLBool, TLFalse, TLTrue, UnderRoot,
|
Base, Extends, HierarchyRole, InHierarchy, Subtype, TLBool, TLFalse, TLTrue, UnderRoot,
|
||||||
UnderRootImpl,
|
UnderRootImpl,
|
||||||
};
|
};
|
||||||
pub use relations::{MsgSet, Request};
|
pub use relations::{Channel, MsgSet, Request};
|
||||||
|
|||||||
@@ -5,9 +5,12 @@ pub trait Request: Coding + Sized + Send + 'static {
|
|||||||
fn respond(&self, rep: Self::Response) -> Vec<u8> { rep.enc_vec() }
|
fn respond(&self, rep: Self::Response) -> Vec<u8> { rep.enc_vec() }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait MsgSet {
|
pub trait Channel: 'static {
|
||||||
type InReq: Coding + Sized + Send + 'static;
|
type Req: Coding + Sized + Send + 'static;
|
||||||
type InNot: Coding + Sized + Send + 'static;
|
type Notif: Coding + Sized + Send + 'static;
|
||||||
type OutReq: Coding + Sized + Send + 'static;
|
}
|
||||||
type OutNot: Coding + Sized + Send + 'static;
|
|
||||||
|
pub trait MsgSet {
|
||||||
|
type In: Channel;
|
||||||
|
type Out: Channel;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use std::ops::RangeInclusive;
|
||||||
|
|
||||||
use orchid_api_derive::{Coding, Hierarchy};
|
use orchid_api_derive::{Coding, Hierarchy};
|
||||||
use orchid_api_traits::Request;
|
use orchid_api_traits::Request;
|
||||||
|
|
||||||
@@ -16,11 +18,18 @@ pub enum ParserReq {
|
|||||||
|
|
||||||
pub type LexerId = u16;
|
pub type LexerId = u16;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Coding)]
|
||||||
|
pub struct Lexer {
|
||||||
|
id: LexerId,
|
||||||
|
drop: bool,
|
||||||
|
notify_chars: Option<Vec<RangeInclusive<char>>>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Coding, Hierarchy)]
|
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||||
#[extends(ParserReq, HostExtReq)]
|
#[extends(ParserReq, HostExtReq)]
|
||||||
pub struct MkLexer(pub SysId, pub TStr);
|
pub struct MkLexer(pub SysId, pub TStr);
|
||||||
impl Request for MkLexer {
|
impl Request for MkLexer {
|
||||||
type Response = LexerId;
|
type Response = Lexer;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Coding, Hierarchy)]
|
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||||
@@ -50,6 +59,7 @@ impl Request for SubLex {
|
|||||||
type Response = SubLex;
|
type Response = SubLex;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ParseLine {
|
#[derive(Clone, Debug, Coding)]
|
||||||
|
pub struct LexerDrop;
|
||||||
|
|
||||||
}
|
pub struct ParseLine {}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ use std::io::{Read, Write};
|
|||||||
|
|
||||||
use derive_more::{From, TryInto};
|
use derive_more::{From, TryInto};
|
||||||
use orchid_api_derive::{Coding, Hierarchy};
|
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};
|
use crate::{atom, expr, intern, parser, system, tree};
|
||||||
|
|
||||||
@@ -77,12 +77,18 @@ pub enum ExtHostReq {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Notifications sent from the extension to the host
|
/// Notifications sent from the extension to the host
|
||||||
#[derive(Coding, From, TryInto)]
|
#[derive(Debug, Clone, Coding, From, TryInto)]
|
||||||
#[allow(clippy::enum_variant_names)]
|
#[allow(clippy::enum_variant_names)]
|
||||||
pub enum ExtHostNotif {
|
pub enum ExtHostNotif {
|
||||||
Expr(expr::ExprNotif),
|
Expr(expr::ExprNotif),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ExtHostChannel;
|
||||||
|
impl Channel for ExtHostChannel {
|
||||||
|
type Notif = ExtHostNotif;
|
||||||
|
type Req = ExtHostReq;
|
||||||
|
}
|
||||||
|
|
||||||
/// Requests running from the host to the extension
|
/// Requests running from the host to the extension
|
||||||
#[derive(Clone, Debug, Coding, Hierarchy)]
|
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||||
#[extendable]
|
#[extendable]
|
||||||
@@ -101,25 +107,28 @@ pub enum HostExtReq {
|
|||||||
pub enum HostExtNotif {
|
pub enum HostExtNotif {
|
||||||
SystemDrop(system::SystemDrop),
|
SystemDrop(system::SystemDrop),
|
||||||
AtomDrop(atom::AtomDrop),
|
AtomDrop(atom::AtomDrop),
|
||||||
|
LexerDrop(parser::LexerDrop),
|
||||||
/// The host can assume that after this notif is sent, a correctly written
|
/// The host can assume that after this notif is sent, a correctly written
|
||||||
/// extension will eventually exit.
|
/// extension will eventually exit.
|
||||||
Exit,
|
Exit,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct HostExtChannel;
|
||||||
|
impl Channel for HostExtChannel {
|
||||||
|
type Notif = HostExtNotif;
|
||||||
|
type Req = HostExtReq;
|
||||||
|
}
|
||||||
|
|
||||||
/// Message set viewed from the extension's perspective
|
/// Message set viewed from the extension's perspective
|
||||||
pub struct ExtMsgSet;
|
pub struct ExtMsgSet;
|
||||||
impl MsgSet for ExtMsgSet {
|
impl MsgSet for ExtMsgSet {
|
||||||
type InNot = HostExtNotif;
|
type In = HostExtChannel;
|
||||||
type InReq = HostExtReq;
|
type Out = ExtHostChannel;
|
||||||
type OutNot = ExtHostNotif;
|
|
||||||
type OutReq = ExtHostReq;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Message Set viewed from the host's perspective
|
/// Message Set viewed from the host's perspective
|
||||||
pub struct HostMsgSet;
|
pub struct HostMsgSet;
|
||||||
impl MsgSet for HostMsgSet {
|
impl MsgSet for HostMsgSet {
|
||||||
type InNot = ExtHostNotif;
|
type In = ExtHostChannel;
|
||||||
type InReq = ExtHostReq;
|
type Out = HostExtChannel;
|
||||||
type OutNot = HostExtNotif;
|
|
||||||
type OutReq = HostExtReq;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ pub type SysDeclId = u16;
|
|||||||
pub type SysId = u16;
|
pub type SysId = u16;
|
||||||
|
|
||||||
/// Details about a system provided by this library
|
/// Details about a system provided by this library
|
||||||
#[derive(Coding)]
|
#[derive(Debug, Clone, Coding)]
|
||||||
pub struct SystemDecl {
|
pub struct SystemDecl {
|
||||||
/// ID of the system, unique within the library
|
/// ID of the system, unique within the library
|
||||||
pub id: SysDeclId,
|
pub id: SysDeclId,
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ use orchid_api::expr::{Clause, Expr};
|
|||||||
use orchid_api::location::Location;
|
use orchid_api::location::Location;
|
||||||
|
|
||||||
use super::traits::{GenClause, Generable};
|
use super::traits::{GenClause, Generable};
|
||||||
use crate::expr::RtExpr;
|
|
||||||
use crate::host::AtomHand;
|
|
||||||
use crate::intern::{deintern, intern};
|
use crate::intern::{deintern, intern};
|
||||||
|
|
||||||
fn safely_reinterpret<In: 'static, Out: 'static>(x: In) -> Result<Out, In> {
|
fn safely_reinterpret<In: 'static, Out: 'static>(x: In) -> Result<Out, In> {
|
||||||
@@ -24,7 +22,7 @@ impl GenClause for Expr {
|
|||||||
fn generate<T: super::traits::Generable>(&self, ctx: T::Ctx<'_>, pop: &impl Fn() -> T) -> T {
|
fn generate<T: super::traits::Generable>(&self, ctx: T::Ctx<'_>, pop: &impl Fn() -> T) -> T {
|
||||||
match &self.clause {
|
match &self.clause {
|
||||||
Clause::Arg(arg) => T::arg(ctx, deintern(*arg).as_str()),
|
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::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::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)),
|
Clause::Seq(n1, n2) => T::seq(ctx, |c| n1.generate(c, pop), |c| n2.generate(c, pop)),
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
//! Various elemental components to build expression trees that all implement
|
//! Various elemental components to build expression trees that all implement
|
||||||
//! [GenClause].
|
//! [GenClause].
|
||||||
|
|
||||||
|
use orchid_api::atom::Atom;
|
||||||
|
|
||||||
use super::traits::{GenClause, Generable};
|
use super::traits::{GenClause, Generable};
|
||||||
use crate::host::AtomHand;
|
|
||||||
|
|
||||||
/// A trivial atom
|
/// A trivial atom
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct SysAtom(pub AtomHand);
|
pub struct SysAtom(pub Atom);
|
||||||
impl GenClause for SysAtom {
|
impl GenClause for SysAtom {
|
||||||
fn generate<T: Generable>(&self, ctx: T::Ctx<'_>, _: &impl Fn() -> T) -> T {
|
fn generate<T: Generable>(&self, ctx: T::Ctx<'_>, _: &impl Fn() -> T) -> T {
|
||||||
T::atom(ctx, self.0.clone())
|
T::atom(ctx, self.0.clone())
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use std::cell::RefCell;
|
|||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use crate::host::AtomHand;
|
use orchid_api::atom::Atom;
|
||||||
|
|
||||||
/// Representations of the Orchid expression tree that can describe basic
|
/// Representations of the Orchid expression tree that can describe basic
|
||||||
/// language elements.
|
/// language elements.
|
||||||
@@ -13,7 +13,7 @@ pub trait Generable: Sized + 'static {
|
|||||||
/// Context information defined by parents. Generators just forward this.
|
/// Context information defined by parents. Generators just forward this.
|
||||||
type Ctx<'a>: Sized;
|
type Ctx<'a>: Sized;
|
||||||
/// Wrap external data.
|
/// 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
|
/// Generate a reference to a constant
|
||||||
fn constant<'a>(ctx: Self::Ctx<'_>, name: impl IntoIterator<Item = &'a str>) -> Self;
|
fn constant<'a>(ctx: Self::Ctx<'_>, name: impl IntoIterator<Item = &'a str>) -> Self;
|
||||||
/// Generate a function call given the function and its argument
|
/// Generate a function call given the function and its argument
|
||||||
|
|||||||
@@ -4,13 +4,13 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use dyn_clone::{clone_box, DynClone};
|
use dyn_clone::{clone_box, DynClone};
|
||||||
|
use orchid_api::atom::Atom;
|
||||||
use orchid_api::expr::Expr;
|
use orchid_api::expr::Expr;
|
||||||
use trait_set::trait_set;
|
use trait_set::trait_set;
|
||||||
|
|
||||||
use super::tpl;
|
use super::tpl;
|
||||||
use super::traits::{Gen, GenClause};
|
use super::traits::{Gen, GenClause};
|
||||||
use crate::combine::Combine;
|
use crate::combine::Combine;
|
||||||
use crate::host::AtomHand;
|
|
||||||
use crate::tree::{ModEntry, ModMember, TreeConflict};
|
use crate::tree::{ModEntry, ModMember, TreeConflict};
|
||||||
|
|
||||||
trait_set! {
|
trait_set! {
|
||||||
@@ -64,14 +64,14 @@ pub fn ent<K: AsRef<str>>(
|
|||||||
|
|
||||||
/// Describe an [Atomic]
|
/// Describe an [Atomic]
|
||||||
#[must_use]
|
#[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]
|
/// 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
|
/// The unarray is used to trick rustfmt into breaking the atom into a block
|
||||||
/// without breaking this call into a block
|
/// without breaking this call into a block
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn atom_ent<K: AsRef<str>>(key: K, [atom]: [AtomHand; 1]) -> (K, ConstTree) {
|
pub fn atom_ent<K: AsRef<str>>(key: K, [atom]: [Atom; 1]) -> (K, ConstTree) {
|
||||||
(key, atom_leaf(atom))
|
(key, atom_leaf(atom))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
pub mod boxed_iter;
|
pub mod boxed_iter;
|
||||||
pub mod child;
|
pub mod msg;
|
||||||
pub mod clone;
|
pub mod clone;
|
||||||
pub mod combine;
|
pub mod combine;
|
||||||
pub mod event;
|
pub mod event;
|
||||||
pub mod expr;
|
// pub mod gen;
|
||||||
pub mod gen;
|
|
||||||
pub mod intern;
|
pub mod intern;
|
||||||
pub mod location;
|
pub mod location;
|
||||||
pub mod name;
|
pub mod name;
|
||||||
|
|||||||
16
orchid-base/src/msg.rs
Normal file
16
orchid-base/src/msg.rs
Normal file
@@ -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<Vec<u8>> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
@@ -6,13 +6,13 @@ use std::sync::{Arc, Mutex};
|
|||||||
|
|
||||||
use dyn_clone::{clone_box, DynClone};
|
use dyn_clone::{clone_box, DynClone};
|
||||||
use hashbrown::HashMap;
|
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;
|
use trait_set::trait_set;
|
||||||
|
|
||||||
trait_set! {
|
trait_set! {
|
||||||
pub trait SendFn<T: MsgSet> = for<'a> FnMut(&'a [u8], ReqNot<T>) + DynClone + Send + 'static;
|
pub trait SendFn<T: MsgSet> = for<'a> FnMut(&'a [u8], ReqNot<T>) + DynClone + Send + 'static;
|
||||||
pub trait ReqFn<T: MsgSet> = FnMut(RequestHandle<T>) + Send + 'static;
|
pub trait ReqFn<T: MsgSet> = FnMut(RequestHandle<T>) + Send + 'static;
|
||||||
pub trait NotifFn<T: MsgSet> = for<'a> FnMut(T::InNot, ReqNot<T>) + Send + Sync + 'static;
|
pub trait NotifFn<T: MsgSet> = for<'a> FnMut(<T::In as Channel>::Notif, ReqNot<T>) + Send + Sync + 'static;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_id(message: &[u8]) -> (u64, &[u8]) {
|
fn get_id(message: &[u8]) -> (u64, &[u8]) {
|
||||||
@@ -21,14 +21,14 @@ fn get_id(message: &[u8]) -> (u64, &[u8]) {
|
|||||||
|
|
||||||
pub struct RequestHandle<T: MsgSet> {
|
pub struct RequestHandle<T: MsgSet> {
|
||||||
id: u64,
|
id: u64,
|
||||||
message: T::InReq,
|
message: <T::In as Channel>::Req,
|
||||||
send: Box<dyn SendFn<T>>,
|
send: Box<dyn SendFn<T>>,
|
||||||
parent: ReqNot<T>,
|
parent: ReqNot<T>,
|
||||||
fulfilled: AtomicBool,
|
fulfilled: AtomicBool,
|
||||||
}
|
}
|
||||||
impl<MS: MsgSet> RequestHandle<MS> {
|
impl<MS: MsgSet> RequestHandle<MS> {
|
||||||
pub fn reqnot(&self) -> ReqNot<MS> { self.parent.clone() }
|
pub fn reqnot(&self) -> ReqNot<MS> { self.parent.clone() }
|
||||||
pub fn req(&self) -> &MS::InReq { &self.message }
|
pub fn req(&self) -> &<MS::In as Channel>::Req { &self.message }
|
||||||
fn respond(&self, response: &impl Encode) {
|
fn respond(&self, response: &impl Encode) {
|
||||||
assert!(!self.fulfilled.swap(true, Ordering::Relaxed), "Already responded");
|
assert!(!self.fulfilled.swap(true, Ordering::Relaxed), "Already responded");
|
||||||
let mut buf = (!self.id).to_be_bytes().to_vec();
|
let mut buf = (!self.id).to_be_bytes().to_vec();
|
||||||
@@ -78,21 +78,21 @@ impl<T: MsgSet> ReqNot<T> {
|
|||||||
let mut g = self.0.lock().unwrap();
|
let mut g = self.0.lock().unwrap();
|
||||||
let (id, payload) = get_id(&message[..]);
|
let (id, payload) = get_id(&message[..]);
|
||||||
if id == 0 {
|
if id == 0 {
|
||||||
(g.notif)(T::InNot::decode(&mut &payload[..]), self.clone())
|
(g.notif)(<T::In as Channel>::Notif::decode(&mut &payload[..]), self.clone())
|
||||||
} else if 0 < id.bitand(1 << 63) {
|
} else if 0 < id.bitand(1 << 63) {
|
||||||
let sender = g.responses.remove(&!id).expect("Received response for invalid message");
|
let sender = g.responses.remove(&!id).expect("Received response for invalid message");
|
||||||
sender.send(message).unwrap();
|
sender.send(message).unwrap();
|
||||||
} else {
|
} else {
|
||||||
let send = clone_box(&*g.send);
|
let send = clone_box(&*g.send);
|
||||||
let message = T::InReq::decode(&mut &payload[..]);
|
let message = <T::In as Channel>::Req::decode(&mut &payload[..]);
|
||||||
(g.req)(RequestHandle { id, message, send, fulfilled: false.into(), parent: self.clone() })
|
(g.req)(RequestHandle { id, message, send, fulfilled: false.into(), parent: self.clone() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn notify<N: Coding + Into<T::OutNot>>(&self, notif: N) {
|
pub fn notify<N: Coding + Into<<T::Out as Channel>::Notif>>(&self, notif: N) {
|
||||||
let mut send = clone_box(&*self.0.lock().unwrap().send);
|
let mut send = clone_box(&*self.0.lock().unwrap().send);
|
||||||
let mut buf = vec![0; 8];
|
let mut buf = vec![0; 8];
|
||||||
let msg: T::OutNot = notif.into();
|
let msg: <T::Out as Channel>::Notif = notif.into();
|
||||||
msg.encode(&mut buf);
|
msg.encode(&mut buf);
|
||||||
send(&buf, self.clone())
|
send(&buf, self.clone())
|
||||||
}
|
}
|
||||||
@@ -112,7 +112,7 @@ impl<'a, T> DynRequester for MappedRequester<'a, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: MsgSet> DynRequester for ReqNot<T> {
|
impl<T: MsgSet> DynRequester for ReqNot<T> {
|
||||||
type Transfer = T::OutReq;
|
type Transfer = <T::Out as Channel>::Req;
|
||||||
fn raw_request(&self, req: Self::Transfer) -> RawReply {
|
fn raw_request(&self, req: Self::Transfer) -> RawReply {
|
||||||
let mut g = self.0.lock().unwrap();
|
let mut g = self.0.lock().unwrap();
|
||||||
let id = g.id;
|
let id = g.id;
|
||||||
@@ -156,23 +156,27 @@ mod test {
|
|||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use orchid_api_derive::Coding;
|
use orchid_api_derive::Coding;
|
||||||
use orchid_api_traits::Request;
|
use orchid_api_traits::{Channel, Request};
|
||||||
|
|
||||||
use super::{MsgSet, ReqNot};
|
use super::{MsgSet, ReqNot};
|
||||||
use crate::{clone, reqnot::Requester as _};
|
use crate::{clone, reqnot::Requester as _};
|
||||||
|
|
||||||
#[derive(Coding, Debug, PartialEq)]
|
#[derive(Clone, Debug, Coding, PartialEq)]
|
||||||
pub struct TestReq(u8);
|
pub struct TestReq(u8);
|
||||||
impl Request for TestReq {
|
impl Request for TestReq {
|
||||||
type Response = u8;
|
type Response = u8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct TestChan;
|
||||||
|
impl Channel for TestChan {
|
||||||
|
type Notif = u8;
|
||||||
|
type Req = TestReq;
|
||||||
|
}
|
||||||
|
|
||||||
pub struct TestMsgSet;
|
pub struct TestMsgSet;
|
||||||
impl MsgSet for TestMsgSet {
|
impl MsgSet for TestMsgSet {
|
||||||
type InNot = u8;
|
type In = TestChan;
|
||||||
type InReq = TestReq;
|
type Out = TestChan;
|
||||||
type OutNot = u8;
|
|
||||||
type OutReq = TestReq;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
ahash = "0.8.11"
|
||||||
|
hashbrown = "0.14.5"
|
||||||
|
itertools = "0.12.1"
|
||||||
orchid-api = { version = "0.1.0", path = "../orchid-api" }
|
orchid-api = { version = "0.1.0", path = "../orchid-api" }
|
||||||
orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
|
orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
|
||||||
orchid-base = { version = "0.1.0", path = "../orchid-base" }
|
orchid-base = { version = "0.1.0", path = "../orchid-base" }
|
||||||
|
ordered-float = "4.2.0"
|
||||||
|
|||||||
137
orchid-extension/src/data.rs
Normal file
137
orchid-extension/src/data.rs
Normal file
@@ -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: SystemDepCard> {
|
||||||
|
_t: PhantomData<T>,
|
||||||
|
id: SysId,
|
||||||
|
reqnot: ReqNot<ExtMsgSet>,
|
||||||
|
}
|
||||||
|
impl<T: SystemDepCard> SystemHandle<T> {
|
||||||
|
fn new(id: SysId, reqnot: ReqNot<ExtMsgSet>) -> Self { Self { _t: PhantomData, id, reqnot } }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait System: Send {
|
||||||
|
fn consts(&self) -> Expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SystemParams<Ctor: SystemCtor + ?Sized> {
|
||||||
|
pub deps: <Ctor::Deps as DepSet>::Sat,
|
||||||
|
pub id: SysId,
|
||||||
|
pub reqnot: ReqNot<ExtMsgSet>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<ExtMsgSet>) -> Self::Sat;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: SystemDepCard> DepSet for T {
|
||||||
|
type Sat = SystemHandle<Self>;
|
||||||
|
fn report(names: &mut impl FnMut(&'static str)) { names(T::NAME) }
|
||||||
|
fn create(take: &mut impl FnMut() -> SysId, reqnot: ReqNot<ExtMsgSet>) -> 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<Self>) -> Box<dyn System>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait DynSystemCtor: Send {
|
||||||
|
fn decl(&self) -> SystemDecl;
|
||||||
|
fn new_system(&self, new: &NewSystem, reqnot: ReqNot<ExtMsgSet>) -> Box<dyn System>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: SystemCtor + 'static> 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::<T>().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<ExtMsgSet>) -> Box<dyn System> {
|
||||||
|
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<Box<dyn DynSystemCtor>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<ExtMsgSet>) -> 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
|
||||||
|
}
|
||||||
@@ -1,19 +1,23 @@
|
|||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
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::proto::{ExtMsgSet, ExtensionHeader, HostExtNotif, HostExtReq, HostHeader};
|
||||||
use orchid_api_traits::{Decode, Encode};
|
use orchid_api_traits::{Decode, Encode};
|
||||||
use orchid_base::child::{recv_parent_msg, send_parent_msg};
|
|
||||||
use orchid_base::clone;
|
use orchid_base::clone;
|
||||||
use orchid_base::intern::{init_replica, sweep_replica};
|
use orchid_base::intern::{init_replica, sweep_replica};
|
||||||
use orchid_base::reqnot::{ReqNot, Requester};
|
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()[..]);
|
HostHeader::decode(&mut &recv_parent_msg().unwrap()[..]);
|
||||||
let mut buf = Vec::new();
|
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();
|
send_parent_msg(&buf).unwrap();
|
||||||
let exiting = Arc::new(AtomicBool::new(false));
|
let exiting = Arc::new(AtomicBool::new(false));
|
||||||
let rn = ReqNot::<ExtMsgSet>::new(
|
let rn = ReqNot::<ExtMsgSet>::new(
|
||||||
@@ -22,11 +26,17 @@ pub fn main(data: &mut ExtensionData) {
|
|||||||
HostExtNotif::Exit => exiting.store(true, Ordering::Relaxed),
|
HostExtNotif::Exit => exiting.store(true, Ordering::Relaxed),
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
}),
|
}),
|
||||||
|req| match req.req() {
|
clone!(systems; move |req| match req.req() {
|
||||||
HostExtReq::Ping(ping) => req.handle(ping, &()),
|
HostExtReq::Ping(ping) => req.handle(ping, &()),
|
||||||
HostExtReq::Sweep(sweep) => req.handle(sweep, &sweep_replica()),
|
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!(),
|
_ => todo!(),
|
||||||
},
|
}),
|
||||||
);
|
);
|
||||||
init_replica(rn.clone().map());
|
init_replica(rn.clone().map());
|
||||||
while !exiting.load(Ordering::Relaxed) {
|
while !exiting.load(Ordering::Relaxed) {
|
||||||
|
|||||||
@@ -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<SystemHandle>,
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SystemCtor {
|
|
||||||
deps: Vec<String>,
|
|
||||||
make: Box<dyn FnMut(SystemParams) -> System>,
|
|
||||||
name: String,
|
|
||||||
prio: NotNan<f64>,
|
|
||||||
dependencies: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ExtensionData {
|
|
||||||
systems: Vec<SystemCtor>
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1 +1,3 @@
|
|||||||
pub mod entrypoint;
|
pub mod entrypoint;
|
||||||
|
pub mod data;
|
||||||
|
pub mod msg;
|
||||||
|
|||||||
6
orchid-extension/src/msg.rs
Normal file
6
orchid-extension/src/msg.rs
Normal file
@@ -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<Vec<u8>> { recv_msg(&mut io::stdin().lock()) }
|
||||||
@@ -6,3 +6,11 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[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"
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
use std::io::{self, Read, Write};
|
use std::io;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use std::{mem, process};
|
use std::{mem, process};
|
||||||
|
|
||||||
|
use orchid_base::msg::{recv_msg, send_msg};
|
||||||
|
|
||||||
pub struct SharedChild {
|
pub struct SharedChild {
|
||||||
child: process::Child,
|
child: process::Child,
|
||||||
stdin: Mutex<process::ChildStdin>,
|
stdin: Mutex<process::ChildStdin>,
|
||||||
@@ -24,21 +26,3 @@ impl SharedChild {
|
|||||||
impl Drop for SharedChild {
|
impl Drop for SharedChild {
|
||||||
fn drop(&mut self) { mem::drop(self.child.kill()) }
|
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<Vec<u8>> {
|
|
||||||
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<Vec<u8>> { recv_msg(&mut io::stdin().lock()) }
|
|
||||||
@@ -2,10 +2,10 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
|||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
use orchid_api::expr::ExprTicket;
|
use orchid_api::expr::ExprTicket;
|
||||||
|
|
||||||
use crate::host::AtomHand;
|
use crate::extension::AtomHand;
|
||||||
use lazy_static::lazy_static;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct RtExpr {
|
pub struct RtExpr {
|
||||||
@@ -36,6 +36,6 @@ impl Drop for RtExpr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static!{
|
lazy_static! {
|
||||||
static ref KNOWN_EXPRS: RwLock<HashMap<ExprTicket, RtExpr>> = RwLock::default();
|
static ref KNOWN_EXPRS: RwLock<HashMap<ExprTicket, RtExpr>> = RwLock::default();
|
||||||
}
|
}
|
||||||
|
|||||||
234
orchid-host/src/extension.rs
Normal file
234
orchid-host/src/extension.rs
Normal file
@@ -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<u8>,
|
||||||
|
}
|
||||||
|
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<AtomData>);
|
||||||
|
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<u8>) -> Vec<u8> {
|
||||||
|
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<process::Child>,
|
||||||
|
reqnot: ReqNot<HostMsgSet>,
|
||||||
|
systems: Vec<SystemCtor>,
|
||||||
|
}
|
||||||
|
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<ExtensionData>);
|
||||||
|
impl Extension {
|
||||||
|
pub fn new(mut cmd: process::Command) -> io::Result<Self> {
|
||||||
|
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<ExtensionData> = 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<Item = &SystemCtor> { self.0.systems.iter() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SystemCtor {
|
||||||
|
decl: SystemDecl,
|
||||||
|
ext: Weak<ExtensionData>,
|
||||||
|
}
|
||||||
|
impl SystemCtor {
|
||||||
|
pub fn name(&self) -> &str { &self.decl.name }
|
||||||
|
pub fn priority(&self) -> NotNan<f64> { self.decl.priority }
|
||||||
|
pub fn depends(&self) -> impl ExactSizeIterator<Item = &str> {
|
||||||
|
self.decl.depends.iter().map(|s| &**s)
|
||||||
|
}
|
||||||
|
pub fn run<'a>(&self, depends: impl IntoIterator<Item = &'a System>) -> 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<HashMap<u16, System>> = RwLock::default();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(destructure)]
|
||||||
|
pub struct SystemInstData {
|
||||||
|
exprs: RwLock<HashMap<ExprTicket, (AtomicU32, RtExpr)>>,
|
||||||
|
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<SystemInstData>);
|
||||||
|
impl System {
|
||||||
|
fn resolve(id: u16) -> Option<System> { 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())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,234 +1,3 @@
|
|||||||
use std::io::Write as _;
|
pub mod child;
|
||||||
use std::sync::atomic::{AtomicU16, AtomicU32, Ordering};
|
pub mod expr;
|
||||||
use std::sync::{Arc, Mutex, RwLock, Weak};
|
pub mod extension;
|
||||||
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<u8>,
|
|
||||||
}
|
|
||||||
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<AtomData>);
|
|
||||||
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<u8>) -> Vec<u8> {
|
|
||||||
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<process::Child>,
|
|
||||||
reqnot: ReqNot<HostMsgSet>,
|
|
||||||
systems: Vec<SystemCtor>,
|
|
||||||
}
|
|
||||||
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<ExtensionData>);
|
|
||||||
impl Extension {
|
|
||||||
pub fn new(mut cmd: process::Command) -> io::Result<Self> {
|
|
||||||
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<ExtensionData> = 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<Item = &SystemCtor> { self.0.systems.iter() }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SystemCtor {
|
|
||||||
decl: SystemDecl,
|
|
||||||
ext: Weak<ExtensionData>,
|
|
||||||
}
|
|
||||||
impl SystemCtor {
|
|
||||||
pub fn name(&self) -> &str { &self.decl.name }
|
|
||||||
pub fn priority(&self) -> NotNan<f64> { self.decl.priority }
|
|
||||||
pub fn depends(&self) -> impl ExactSizeIterator<Item = &str> {
|
|
||||||
self.decl.depends.iter().map(|s| &**s)
|
|
||||||
}
|
|
||||||
pub fn run<'a>(&self, depends: impl IntoIterator<Item = &'a System>) -> 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<HashMap<u16, System>> = RwLock::default();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(destructure)]
|
|
||||||
pub struct SystemInstData {
|
|
||||||
exprs: RwLock<HashMap<ExprTicket, (AtomicU32, RtExpr)>>,
|
|
||||||
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<SystemInstData>);
|
|
||||||
impl System {
|
|
||||||
fn resolve(id: u16) -> Option<System> { 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())
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user