mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-29 17:48:11 +08:00
Make every edge a shared payload pointer with an owning Arc
This commit is contained in:
@@ -119,6 +119,28 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<Input, N> GNode<Input> for std::sync::Arc<N>
|
||||||
|
where
|
||||||
|
N: GNode<Input> + ?Sized,
|
||||||
|
{
|
||||||
|
type Output = N::Output;
|
||||||
|
|
||||||
|
fn eval(&self, input: &Input) -> GPoll<Self::Output> {
|
||||||
|
(**self).eval(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extent(&self, input: &Input) -> GPoll<Extent> {
|
||||||
|
(**self).extent(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_batch<'a>(&self, input: &'a Input, range: Range<u64>, scratch: Option<&'a mut [MaybeUninit<Self::Output>]>) -> BatchStatus<'a, Self::Output>
|
||||||
|
where
|
||||||
|
Input: InjectIndex + Copy,
|
||||||
|
{
|
||||||
|
(**self).eval_batch(input, range, scratch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct StatusCell {
|
pub struct StatusCell {
|
||||||
finality: Cell<Finality>,
|
finality: Cell<Finality>,
|
||||||
error: Cell<Option<GraphError>>,
|
error: Cell<Option<GraphError>>,
|
||||||
|
|||||||
@@ -83,8 +83,61 @@ pub enum ConstructionError {
|
|||||||
Type { expected: Type, found: Type },
|
Type { expected: Type, found: Type },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct SharedEdge<N: ?Sized> {
|
||||||
|
ptr: std::ptr::NonNull<N>,
|
||||||
|
own: std::sync::Arc<N>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N: ?Sized> SharedEdge<N> {
|
||||||
|
pub fn new(own: std::sync::Arc<N>) -> Self {
|
||||||
|
Self {
|
||||||
|
ptr: std::ptr::NonNull::from(&*own),
|
||||||
|
own,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn share(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
ptr: self.ptr,
|
||||||
|
own: self.own.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Input, N> GNode<Input> for SharedEdge<N>
|
||||||
|
where
|
||||||
|
N: GNode<Input> + ?Sized,
|
||||||
|
{
|
||||||
|
type Output = N::Output;
|
||||||
|
|
||||||
|
fn eval(&self, input: &Input) -> crate::gpoll::GPoll<Self::Output> {
|
||||||
|
// SAFETY: `own` keeps the payload alive for `self`'s lifetime and Arc
|
||||||
|
// payloads are address stable.
|
||||||
|
unsafe { self.ptr.as_ref() }.eval(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extent(&self, input: &Input) -> crate::gpoll::GPoll<crate::gpoll::Extent> {
|
||||||
|
// SAFETY: as in eval.
|
||||||
|
unsafe { self.ptr.as_ref() }.extent(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_batch<'a>(
|
||||||
|
&self,
|
||||||
|
input: &'a Input,
|
||||||
|
range: std::ops::Range<u64>,
|
||||||
|
scratch: Option<&'a mut [std::mem::MaybeUninit<Self::Output>]>,
|
||||||
|
) -> crate::gnode::BatchStatus<'a, Self::Output>
|
||||||
|
where
|
||||||
|
Input: crate::context::InjectIndex + Copy,
|
||||||
|
{
|
||||||
|
// SAFETY: as in eval.
|
||||||
|
unsafe { self.ptr.as_ref() }.eval_batch(input, range, scratch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct EdgeHandle {
|
pub struct EdgeHandle {
|
||||||
node: Box<dyn std::any::Any>,
|
node: Box<dyn std::any::Any>,
|
||||||
|
share: fn(&dyn std::any::Any) -> Box<dyn std::any::Any>,
|
||||||
ty: Type,
|
ty: Type,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,39 +148,48 @@ impl std::fmt::Debug for EdgeHandle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl EdgeHandle {
|
impl EdgeHandle {
|
||||||
pub fn new<T: 'static>(node: Box<ErasedGNode<T>>) -> Self {
|
pub fn new<T: 'static>(node: std::sync::Arc<ErasedGNode<T>>) -> Self {
|
||||||
Self::new_erased(node, concrete!(T))
|
Self::new_erased(node, concrete!(T))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_ref<T: 'static>(node: Box<ErasedLendGNode<T>>) -> Self {
|
pub fn new_ref<T: 'static>(node: std::sync::Arc<ErasedLendGNode<T>>) -> Self {
|
||||||
Self::new_erased(node, Type::Ref(Box::new(concrete!(T))))
|
Self::new_erased(node, Type::Ref(Box::new(concrete!(T))))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_erased<N: ?Sized>(node: Box<N>, ty: Type) -> Self
|
pub fn new_erased<N: ?Sized + 'static>(node: std::sync::Arc<N>, ty: Type) -> Self {
|
||||||
where
|
Self {
|
||||||
Box<N>: std::any::Any,
|
node: Box::new(SharedEdge::new(node)),
|
||||||
{
|
share: |edge| Box::new(edge.downcast_ref::<SharedEdge<N>>().expect("share hook matches the stored edge type").share()),
|
||||||
Self { node: Box::new(node), ty }
|
ty,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ty(&self) -> &Type {
|
pub fn ty(&self) -> &Type {
|
||||||
&self.ty
|
&self.ty
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn downcast<T: 'static>(self) -> Result<Box<ErasedGNode<T>>, ConstructionError> {
|
pub fn duplicate(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
node: (self.share)(&*self.node),
|
||||||
|
share: self.share,
|
||||||
|
ty: self.ty.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn downcast<T: 'static>(self) -> Result<SharedEdge<ErasedGNode<T>>, ConstructionError> {
|
||||||
self.downcast_erased(concrete!(T))
|
self.downcast_erased(concrete!(T))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn downcast_lend<T: 'static>(self) -> Result<Box<ErasedLendGNode<T>>, ConstructionError> {
|
pub fn downcast_lend<T: 'static>(self) -> Result<SharedEdge<ErasedLendGNode<T>>, ConstructionError> {
|
||||||
self.downcast_erased(Type::Ref(Box::new(concrete!(T))))
|
self.downcast_erased(Type::Ref(Box::new(concrete!(T))))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn downcast_erased<N: ?Sized>(self, expected: Type) -> Result<Box<N>, ConstructionError>
|
pub fn downcast_erased<N: ?Sized + 'static>(self, expected: Type) -> Result<SharedEdge<N>, ConstructionError> {
|
||||||
where
|
|
||||||
Box<N>: std::any::Any,
|
|
||||||
{
|
|
||||||
let found = self.ty;
|
let found = self.ty;
|
||||||
self.node.downcast::<Box<N>>().map(|node| *node).map_err(|_| ConstructionError::Type { expected, found })
|
self.node
|
||||||
|
.downcast::<SharedEdge<N>>()
|
||||||
|
.map(|edge| *edge)
|
||||||
|
.map_err(|_| ConstructionError::Type { expected, found })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -397,6 +459,18 @@ mod tests {
|
|||||||
use crate::arena::Arena;
|
use crate::arena::Arena;
|
||||||
use crate::context::{Ctx, EvalScope, ExtractArena};
|
use crate::context::{Ctx, EvalScope, ExtractArena};
|
||||||
use crate::gpoll::GPoll;
|
use crate::gpoll::GPoll;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::atomic::{AtomicU32, Ordering};
|
||||||
|
|
||||||
|
struct CountingNode(AtomicU32);
|
||||||
|
|
||||||
|
impl<Input> GNode<Input> for CountingNode {
|
||||||
|
type Output = u32;
|
||||||
|
|
||||||
|
fn eval(&self, _input: &Input) -> GPoll<u32> {
|
||||||
|
GPoll::Final(self.0.fetch_add(1, Ordering::Relaxed) + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct ValueNode<T>(T);
|
struct ValueNode<T>(T);
|
||||||
|
|
||||||
@@ -452,9 +526,9 @@ mod tests {
|
|||||||
let scope = scope_fixture(&generations, &arena);
|
let scope = scope_fixture(&generations, &arena);
|
||||||
let ctx = ContextImpl::root(&scope);
|
let ctx = ContextImpl::root(&scope);
|
||||||
|
|
||||||
let lending = EdgeHandle::new_ref(Box::new(LendNode("held".to_string())) as Box<ErasedLendGNode<String>>);
|
let lending = EdgeHandle::new_ref(Arc::new(LendNode("held".to_string())) as Arc<ErasedLendGNode<String>>);
|
||||||
let upstream = lending.downcast_lend::<String>().unwrap();
|
let upstream = lending.downcast_lend::<String>().unwrap();
|
||||||
let node: Box<ErasedSplitEdge> = Box::new(SplitNode { content: upstream });
|
let node: Arc<ErasedSplitEdge> = Arc::new(SplitNode { content: upstream });
|
||||||
let handle = EdgeHandle::new_erased(node, concrete!(SplitBorrow<'static>));
|
let handle = EdgeHandle::new_erased(node, concrete!(SplitBorrow<'static>));
|
||||||
assert_eq!(*handle.ty(), concrete!(SplitBorrow<'static>));
|
assert_eq!(*handle.ty(), concrete!(SplitBorrow<'static>));
|
||||||
|
|
||||||
@@ -574,7 +648,7 @@ mod tests {
|
|||||||
let mut args = args.into_iter();
|
let mut args = args.into_iter();
|
||||||
let value = args.next().ok_or(ConstructionError::Arity { expected: 1, got: 0 })?.downcast::<String>()?;
|
let value = args.next().ok_or(ConstructionError::Arity { expected: 1, got: 0 })?.downcast::<String>()?;
|
||||||
drop(value);
|
drop(value);
|
||||||
Ok(EdgeHandle::new(Box::new(ValueNode(0u32)) as Box<ErasedGNode<u32>>))
|
Ok(EdgeHandle::new(Arc::new(ValueNode(0u32)) as Arc<ErasedGNode<u32>>))
|
||||||
}
|
}
|
||||||
let entry = RegistryEntry {
|
let entry = RegistryEntry {
|
||||||
io: NodeIoRecord {
|
io: NodeIoRecord {
|
||||||
@@ -584,12 +658,12 @@ mod tests {
|
|||||||
constructor: construct_strlen,
|
constructor: construct_strlen,
|
||||||
};
|
};
|
||||||
|
|
||||||
let owned = EdgeHandle::new(Box::new(ValueNode("typed".to_string())) as Box<ErasedGNode<String>>);
|
let owned = EdgeHandle::new(Arc::new(ValueNode("typed".to_string())) as Arc<ErasedGNode<String>>);
|
||||||
assert!(construct(&entry, vec![owned]).is_ok());
|
assert!(construct(&entry, vec![owned]).is_ok());
|
||||||
|
|
||||||
assert_eq!(construct(&entry, vec![]).unwrap_err(), ConstructionError::Arity { expected: 1, got: 0 });
|
assert_eq!(construct(&entry, vec![]).unwrap_err(), ConstructionError::Arity { expected: 1, got: 0 });
|
||||||
|
|
||||||
let mistyped = EdgeHandle::new(Box::new(ValueNode(1.0f64)) as Box<ErasedGNode<f64>>);
|
let mistyped = EdgeHandle::new(Arc::new(ValueNode(1.0f64)) as Arc<ErasedGNode<f64>>);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
construct(&entry, vec![mistyped]).unwrap_err(),
|
construct(&entry, vec![mistyped]).unwrap_err(),
|
||||||
ConstructionError::Type {
|
ConstructionError::Type {
|
||||||
@@ -598,7 +672,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
let lent = EdgeHandle::new_ref(Box::new(LendNode("typed".to_string())) as Box<ErasedLendGNode<String>>);
|
let lent = EdgeHandle::new_ref(Arc::new(LendNode("typed".to_string())) as Arc<ErasedLendGNode<String>>);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
construct(&entry, vec![lent]).unwrap_err(),
|
construct(&entry, vec![lent]).unwrap_err(),
|
||||||
ConstructionError::Type {
|
ConstructionError::Type {
|
||||||
@@ -607,4 +681,24 @@ mod tests {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn duplicated_edges_share_one_instance_and_outlive_each_other() {
|
||||||
|
let arena = Arena::new(1024);
|
||||||
|
let generations = [];
|
||||||
|
let scope = scope_fixture(&generations, &arena);
|
||||||
|
let ctx = ContextImpl::root(&scope);
|
||||||
|
|
||||||
|
let handle = EdgeHandle::new(Arc::new(CountingNode(AtomicU32::new(0))) as Arc<ErasedGNode<u32>>);
|
||||||
|
let duplicate = handle.duplicate();
|
||||||
|
assert_eq!(*duplicate.ty(), concrete!(u32));
|
||||||
|
|
||||||
|
let first = handle.downcast::<u32>().unwrap();
|
||||||
|
let second = duplicate.downcast::<u32>().unwrap();
|
||||||
|
assert_eq!(first.eval(&ctx), GPoll::Final(1));
|
||||||
|
assert_eq!(second.eval(&ctx), GPoll::Final(2));
|
||||||
|
|
||||||
|
drop(first);
|
||||||
|
assert_eq!(second.eval(&ctx), GPoll::Final(3));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -597,8 +597,8 @@ fn entries_tokens(parsed: &ParsedNodeFn, struct_name: &Ident, data_field_generic
|
|||||||
|
|
||||||
let entries = rows.iter().map(|row| {
|
let entries = rows.iter().map(|row| {
|
||||||
let types = row.iter();
|
let types = row.iter();
|
||||||
let boxed_types = row.iter().map(|ty| quote!(::std::boxed::Box<gcore::registry::ErasedGNode<#ty>>));
|
let edge_types = row.iter().map(|ty| quote!(gcore::registry::SharedEdge<gcore::registry::ErasedGNode<#ty>>));
|
||||||
let output = quote!(<#struct_name<#(#boxed_types),*> as gcore::gnode::GNode<gcore::context::ContextImpl<'static>>>::Output);
|
let output = quote!(<#struct_name<#(#edge_types),*> as gcore::gnode::GNode<gcore::context::ContextImpl<'static>>>::Output);
|
||||||
let downcasts = names.iter().zip(row.iter()).map(|(name, ty)| {
|
let downcasts = names.iter().zip(row.iter()).map(|(name, ty)| {
|
||||||
quote!(let #name = inputs.next().unwrap().downcast::<#ty>()?;)
|
quote!(let #name = inputs.next().unwrap().downcast::<#ty>()?;)
|
||||||
});
|
});
|
||||||
@@ -614,7 +614,7 @@ fn entries_tokens(parsed: &ParsedNodeFn, struct_name: &Ident, data_field_generic
|
|||||||
}
|
}
|
||||||
let mut inputs = inputs.into_iter();
|
let mut inputs = inputs.into_iter();
|
||||||
#(#downcasts)*
|
#(#downcasts)*
|
||||||
Ok(gcore::registry::EdgeHandle::new(::std::boxed::Box::new(#struct_name::new(#(#names),*)) as ::std::boxed::Box<gcore::registry::ErasedGNode<#output>>))
|
Ok(gcore::registry::EdgeHandle::new(::std::sync::Arc::new(#struct_name::new(#(#names),*)) as ::std::sync::Arc<gcore::registry::ErasedGNode<#output>>))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -219,8 +219,8 @@ mod tests {
|
|||||||
let scope = scope_fixture(&generations, &arena);
|
let scope = scope_fixture(&generations, &arena);
|
||||||
let ctx = ContextImpl::root(&scope);
|
let ctx = ContextImpl::root(&scope);
|
||||||
|
|
||||||
let edge = EdgeHandle::new(Box::new(CountingNode(AtomicU32::new(0))) as Box<ErasedGNode<u32>>);
|
let edge = EdgeHandle::new(Arc::new(CountingNode(AtomicU32::new(0))) as Arc<ErasedGNode<u32>>);
|
||||||
let memoized = EdgeHandle::new(Box::new(MemoizeNode::new(edge.downcast::<u32>().unwrap())) as Box<ErasedGNode<u32>>);
|
let memoized = EdgeHandle::new(Arc::new(MemoizeNode::new(edge.downcast::<u32>().unwrap())) as Arc<ErasedGNode<u32>>);
|
||||||
let stacked = MemoizeNode::new(memoized.downcast::<u32>().unwrap());
|
let stacked = MemoizeNode::new(memoized.downcast::<u32>().unwrap());
|
||||||
|
|
||||||
assert_eq!(stacked.eval(&ctx), GPoll::Final(1));
|
assert_eq!(stacked.eval(&ctx), GPoll::Final(1));
|
||||||
@@ -234,8 +234,8 @@ mod tests {
|
|||||||
let scope = scope_fixture(&generations, &arena);
|
let scope = scope_fixture(&generations, &arena);
|
||||||
let ctx = ContextImpl::root(&scope);
|
let ctx = ContextImpl::root(&scope);
|
||||||
|
|
||||||
let edge = EdgeHandle::new(Box::new(ValueNode("lent out".to_string())) as Box<ErasedGNode<String>>);
|
let edge = EdgeHandle::new(Arc::new(ValueNode("lent out".to_string())) as Arc<ErasedGNode<String>>);
|
||||||
let lending = EdgeHandle::new_ref(Box::new(FrameMemoNode::new(edge.downcast::<String>().unwrap())) as Box<ErasedLendGNode<String>>);
|
let lending = EdgeHandle::new_ref(Arc::new(FrameMemoNode::new(edge.downcast::<String>().unwrap())) as Arc<ErasedLendGNode<String>>);
|
||||||
assert_eq!(*lending.ty(), Type::Ref(Box::new(concrete!(String))));
|
assert_eq!(*lending.ty(), Type::Ref(Box::new(concrete!(String))));
|
||||||
|
|
||||||
let node = lending.downcast_lend::<String>().unwrap();
|
let node = lending.downcast_lend::<String>().unwrap();
|
||||||
|
|||||||
@@ -1070,6 +1070,7 @@ mod graphene_test {
|
|||||||
use core_types::gpoll::{Finality, GPoll};
|
use core_types::gpoll::{Finality, GPoll};
|
||||||
use core_types::registry::{EdgeHandle, ErasedGNode, construct};
|
use core_types::registry::{EdgeHandle, ErasedGNode, construct};
|
||||||
use std::mem::MaybeUninit;
|
use std::mem::MaybeUninit;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
struct SourceNode<T>(T);
|
struct SourceNode<T>(T);
|
||||||
|
|
||||||
@@ -1128,8 +1129,8 @@ mod graphene_test {
|
|||||||
let ctx = ContextImpl::root(&scope);
|
let ctx = ContextImpl::root(&scope);
|
||||||
|
|
||||||
let entries = logical_or_entries();
|
let entries = logical_or_entries();
|
||||||
let value = EdgeHandle::new(Box::new(SourceNode(true)) as Box<ErasedGNode<bool>>);
|
let value = EdgeHandle::new(Arc::new(SourceNode(true)) as Arc<ErasedGNode<bool>>);
|
||||||
let other_value = EdgeHandle::new(Box::new(SourceNode(false)) as Box<ErasedGNode<bool>>);
|
let other_value = EdgeHandle::new(Arc::new(SourceNode(false)) as Arc<ErasedGNode<bool>>);
|
||||||
let wired = construct(&entries[0], vec![value, other_value]).unwrap().downcast::<bool>().unwrap();
|
let wired = construct(&entries[0], vec![value, other_value]).unwrap().downcast::<bool>().unwrap();
|
||||||
|
|
||||||
assert_eq!(GNode::eval(&wired, &ctx), GPoll::Final(true));
|
assert_eq!(GNode::eval(&wired, &ctx), GPoll::Final(true));
|
||||||
@@ -1148,8 +1149,8 @@ mod graphene_test {
|
|||||||
assert_eq!(entries[3].io.inputs, vec![core_types::concrete!(DVec2), core_types::concrete!(DVec2)]);
|
assert_eq!(entries[3].io.inputs, vec![core_types::concrete!(DVec2), core_types::concrete!(DVec2)]);
|
||||||
assert_eq!(entries[3].io.output, core_types::concrete!(DVec2));
|
assert_eq!(entries[3].io.output, core_types::concrete!(DVec2));
|
||||||
|
|
||||||
let augend = EdgeHandle::new(Box::new(SourceNode(1.5f64)) as Box<ErasedGNode<f64>>);
|
let augend = EdgeHandle::new(Arc::new(SourceNode(1.5f64)) as Arc<ErasedGNode<f64>>);
|
||||||
let addend = EdgeHandle::new(Box::new(SourceNode(2.5f64)) as Box<ErasedGNode<f64>>);
|
let addend = EdgeHandle::new(Arc::new(SourceNode(2.5f64)) as Arc<ErasedGNode<f64>>);
|
||||||
let wired = construct(&entries[0], vec![augend, addend]).unwrap().downcast::<f64>().unwrap();
|
let wired = construct(&entries[0], vec![augend, addend]).unwrap().downcast::<f64>().unwrap();
|
||||||
|
|
||||||
assert_eq!(GNode::eval(&wired, &ctx), GPoll::Final(4.0));
|
assert_eq!(GNode::eval(&wired, &ctx), GPoll::Final(4.0));
|
||||||
|
|||||||
Reference in New Issue
Block a user