Struct Node

Source
pub struct Node { /* private fields */ }
Expand description

A clonable, concurrency-aware handle to a single workflow

Node provides lazy loading semantics where workflows are only loaded from disk when first accessed. It implements efficient cloning through Arc sharing, making it suitable for concurrent access patterns.

§Concurrency Model

Node is designed for concurrent access:

  • Read access: Multiple threads can concurrently read the workflow using get_workflow() or get_workflow_like()
  • Write access: Mutation requires external coordination through the lock manager and should be done through get_workflow_mut()
  • Lazy loading: Thread-safe transitions from unloaded to loaded state using double-checked locking pattern

§Lifecycle

Nodes begin in the Unloaded state and transition to Loaded on first access:

Node::new() ──▶ Unloaded ──get_workflow()──▶ Loaded ──set_workflow()──▶ Loaded

§Examples

Basic usage:

let reference = Reference::new("example");
let node = Node::new(reference);
 
// Node is created in unloaded state
// Workflow loading happens lazily on first access

Cloning for concurrent access:

let node1 = Node::new(reference);
let node2 = node1.clone();
 
// Both nodes share the same internal state
assert_eq!(node1, node2);

The Clone implementation is cheap, as it only increments the reference count of the internal Arc.

Implementations§

Source§

impl Node

Source

pub fn new(reference: Reference) -> Self

Creates a new Node in an Unloaded state

The workflow will remain unloaded until the first access through get_workflow() or get_workflow_mut().

§Arguments
  • reference - The reference pointing to the workflow file location
§Examples
use aimx::Reference;
use aimx::values::Node;
 
let reference = Reference::new("main");
let node = Node::new(reference);
Source

pub fn get_workflow(&self) -> Arc<Workflow>

Gets the workflow for read-only access, loading it from disk if necessary

This method employs a double-checked locking pattern to ensure thread safety:

  1. Acquires read lock to check if workflow is already loaded
  2. If unloaded, releases read lock and acquires write lock
  3. Re-checks state (protected by write lock) and loads workflow if still needed

This ensures that:

  • Concurrent readers never block each other when workflow is loaded
  • Only one thread loads the workflow when transitioning from unloaded to loaded
  • No race conditions during state transition
§Returns

An Arc<Workflow> containing a shared, immutable reference to the workflow

§Panics

This method will panic if the thread is unable to acquire the necessary locks, which typically indicates a deadlock scenario.

Source

pub fn get_workflow_like(&self) -> Arc<dyn WorkflowLike>

Gets the workflow as a trait object implementing WorkflowLike

This provides dynamic dispatch capabilities for workflow operations. The workflow is loaded from disk if not already in memory.

This method is equivalent to calling get_workflow() and casting the result to Arc<dyn WorkflowLike>. It’s provided as a convenience for cases where you need to work with the workflow through its trait interface.

§Returns

An Arc<dyn WorkflowLike> trait object for dynamic workflow operations

§Panics

This method will panic if the thread is unable to acquire the necessary locks, which typically indicates a deadlock scenario.

Source

pub fn get_workflow_mut(&self) -> Workflow

Gets the workflow for mutable access, loading it from disk if necessary

§Important Safety Requirement

The caller must already have write permission from the lock manager before calling this method. This method does not acquire external locks.

This method follows the same double-checked locking pattern as get_workflow() but returns a cloned Workflow suitable for mutation rather than a shared reference.

Unlike get_workflow() which returns a shared reference, this method returns an owned Workflow that can be modified. The internal state still remains loaded, but modifications to the returned workflow won’t affect the node’s state until set_workflow() is called.

§Returns

A cloned Workflow instance that can be modified

§Panics

This method will panic if the thread is unable to acquire the necessary locks, which typically indicates a deadlock scenario.

Source

pub fn set_workflow(&self, workflow: Workflow)

Atomically replaces the workflow of the node with a concrete Workflow

This method atomically updates the node’s state to contain the provided workflow. The previous state (loaded or unloaded) is replaced entirely.

This is typically used after modifying a workflow obtained through get_workflow_mut() to commit the changes back to the node.

§Arguments
  • workflow - The new workflow to store in the node
§Panics

This method will panic if the thread is unable to acquire the necessary write lock, which typically indicates a deadlock scenario.

Trait Implementations§

Source§

impl Clone for Node

Source§

fn clone(&self) -> Node

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Node

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for Node

Source§

fn eq(&self, other: &Self) -> bool

Compares two Node instances for equality based on shared identity

Two Node instances are considered equal if they reference the exact same internal Arc<RwLock<NodeState>>. This means they are clones of each other or reference the same underlying workflow state.

This is not a deep comparison of workflow contents. Two nodes with different Arc instances but identical workflow contents will be considered different, while two cloned nodes sharing the same Arc will be equal.

§Returns

true if both nodes share the same internal state pointer, false otherwise

§Examples
let reference = Reference::new("example");
let node1 = Node::new(reference);
let node2 = node1.clone();
 
// Cloned nodes are equal because they share the same internal state
assert_eq!(node1, node2);
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.

Auto Trait Implementations§

§

impl Freeze for Node

§

impl RefUnwindSafe for Node

§

impl Send for Node

§

impl Sync for Node

§

impl Unpin for Node

§

impl UnwindSafe for Node

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

§

impl<T> PolicyExt for T
where T: ?Sized,

§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] only if self and other return Action::Follow. Read more
§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] if either self or other returns Action::Follow. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

impl<T> ErasedDestructor for T
where T: 'static,