2 releases

0.1.1 Nov 23, 2020
0.1.0 Oct 28, 2020

#502 in Concurrency

28 downloads per month

MPL-2.0 license

40KB
739 lines

Dager

Evolution of my last experiment, dagger[], which is the evolution of the first execution graph library asyncgraph. Dagger has an multithreaded executor that uses a theadpool. It is able to execute any execution DAG. However, it is your responibility that the graph makes sense.

Design

The principle of the graph is, that a node gets executed every time all of its inputs are ready. The execution of the node itself is scheduled via an executer that must be started by the user (see the examples). So if it can’t continue the work, the scheduler will automatically pause the thread and try to work on something else.

Each node as an Aggregator "around" it. It waits for all inputs to be ready, executes the node, and sends this nodes output to the next nodes based on the currently set edges of this node.

Safety

Since the graph can change at any time, it can't be as type safe as a statically dispatched graph. Therefore, types are checked every time a edge is added to a node. An error is thrown if an edge has not the same input and output types of the two connecting nodes.

However, if an edge successfully connects, the graph guarantees that the connection will last, and work.

Obviously if you are build some unsound graph, the output will be unsound as well. For instance a graph that gets no input won't produce anything. Similarly a node where not all inputs are set won't fire ever.

Implementing custom nodes

So how hard is it to implement custom nodes? Well intentionally pretty easy, the following code implements float addition. However, this can be easily abstracted over all type T that implement Add as you can see in the examples/math.rs example.

struct AddFloat;
impl Node for FloatAdd{
    type InSig = (f32, f32);
    type OutSig = [f32; 1];

    fn process(&self, input: Self::Inputs) -> Self::Outputs{	
	    [input.0 + input.1]
    }
}

Logging

Since graphs are always difficult, the crate implements the log crate. So you can init a simple logger like simple_logger on startup and get pretty printed warnings from the graph, while its executed.

Building and running

First install Rust and cargo on your platform.

Then execute

cargo build --release

in the root to build the library.

Similar to all rust/cargo projects several examples can be executed by

cargo run --example {example_name}

where {example_name} can be the following:

  • math (some simple calculation example)
  • looping_execution (some graph that gets executed)
  • self_executing (some graph that uses the Executable trait for all nodes that have no inputs)
  • dynamic_graph (a graph that gets changed at runtime to include or exclude some printer node)

Documentation

Documentation can be build locally via

cargo doc --open

Most code is documented.

License

The whole project is licensed under the Mozilla Public License, v. 2.0

A version of this license is included in the repository and a notice at the top of every code-file.

Dependencies

~580KB