1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#![cfg_attr(coverage_nightly, feature(coverage_attribute))]

//! Dynamically managed function graph execution.
//!
//! This crate provides a `FnGraph`, where consumers can register a list of
//! functions and their interdependencies. The graph can then return a stream of
//! functions to iterate over either sequentially or concurrently. Any data
//! dependencies required by the functions are guaranteed to not conflict
//! according to borrowing rules.
//!
//! There is additional flexibility that the type of functions is not limited to
//! closures and functions, but any type that implements the [`FnRes`] and
//! [`FnMeta`] traits.
//!
//! # Usage
//!
//! Add the following to `Cargo.toml`
//!
//! ```toml
//! fn_graph = "0.12.0"
//!
//! # Integrate with `fn_meta` / `interruptible` / `resman`
//! fn_graph = { version = "0.12.0", features = ["fn_meta"] }
//! fn_graph = { version = "0.12.0", features = ["interruptible"] }
//! fn_graph = { version = "0.12.0", features = ["resman"] }
//! fn_graph = { version = "0.12.0", features = ["fn_meta", "interruptible", "resman"] }
//! ```
//!
//! # Rationale
//!
//! Support there are three tasks, each represented by a function. Each function
//! needs different data:
//!
//! | Function | Data                 |
//! | -------- | -------------------- |
//! | `f1`     | `&a,     &b`         |
//! | `f2`     | `&a,     &b, &mut c` |
//! | `f3`     | `&mut a, &b, &mut c` |
//!
//! When scheduling parallel execution, it is valid to execute `f1` and `f2` in
//! parallel, since data `a` and `b` are accessed immutably, and `c` is
//! exclusively accessed by `b`. `f3` cannot be executed in parallel with `f1`
//! or `f2` as it requires exclusive access to both `a` and `c`.
//!
//! For a small number of functions and data, manually writing code to
//! schedule function execution is manageable. However, as the number of
//! functions and data increases, so does its complexity, and it is desirable
//! for this boilerplate to be managed by code.
//!
//! # Notes
//!
//! The concept of a runtime managed data-dependency task graph is from
//! [`shred`]; `fn_graph`'s implementation has the following differences:
//!
//! * Different API ergonomics and flexibility trade-offs.
//!
//!     - Takes functions and closures as input instead of `System` impls.
//!
//!     - Parameters are detected from function signature instead of
//!       `SystemData` implementation, but with a limit of 8 parameters.
//!       *(manual `SystemData` implementation has arbitrary limit)*
//!
//!     - Return type is type parameterized instead of `()`.
//!
//! * Instead of grouping functions by stages to manage data access conflicts,
//!   `fn_graph` keeps a dependency graph of logic and data dependencies, and
//!   executes functions when the preceding functions are complete.
//!
//!     This allows for slightly less waiting time for subsequent functions with
//!     data dependencies, as each may begin once its predecessors finish,
//!     whereas a staged approach may contain other functions that are still
//!     executing that prevent functions in the next stage from beginning
//!     execution.
//!
//! ## See Also
//!
//! * [`fn_meta`][`fn_meta`]: Returns metadata about a function at runtime.
//! * [`interruptible`][`interruptible`]: Support for interruptible streams.
//! * [`resman`][`resman`]: Runtime managed resource borrowing.
//! * [`shred`][`shred`]: Shared resource dispatcher.
//!
//! [`fn_meta`]: https://github.com/azriel91/fn_meta
//! [`interruptible`]: https://github.com/azriel91/interruptible
//! [`resman`]: https://github.com/azriel91/resman
//! [`shred`]: https://github.com/amethyst/shred
//!
//! [`FnMeta`]: https://docs.rs/fn_meta/latest/fn_meta/trait.FnMeta.html
//! [`FnRes`]: https://docs.rs/resman/latest/resman/trait.FnRes.html

pub use daggy::{self, WouldCycle};

#[cfg(feature = "resman")]
pub use resman::{self, Resources};
#[cfg(all(feature = "resman", feature = "fn_res"))]
pub use resman::{FnRes, IntoFnRes};

#[cfg(feature = "fn_meta")]
pub use fn_meta::{FnMeta, FnMetaExt, FnMetadata, FnMetadataExt};

pub use crate::{
    data_access::{DataAccess, DataAccessDyn, R, W},
    edge::Edge,
    edge_counts::EdgeCounts,
    edge_id::EdgeId,
    fn_graph::FnGraph,
    fn_graph_builder::FnGraphBuilder,
    fn_id::FnId,
    fn_id_inner::FnIdInner,
    rank::Rank,
    type_ids::TypeIds,
};

#[cfg(feature = "resman")]
pub use crate::data_access::DataBorrow;

mod data_access;
mod edge;
mod edge_counts;
mod edge_id;
mod fn_graph;
mod fn_graph_builder;
mod fn_id;
mod fn_id_inner;
mod rank;
mod type_ids;

#[cfg(feature = "async")]
pub use crate::{
    fn_ref::FnRef, fn_wrapper::FnWrapper, fn_wrapper_mut::FnWrapperMut, stream_opts::StreamOpts,
};
#[cfg(feature = "async")]
pub(crate) use stream_order::StreamOrder;

#[cfg(feature = "async")]
mod fn_ref;
#[cfg(feature = "async")]
mod fn_wrapper;
#[cfg(feature = "async")]
mod fn_wrapper_mut;
#[cfg(feature = "async")]
mod stream_opts;
#[cfg(feature = "async")]
mod stream_order;

#[cfg(feature = "async")]
pub use crate::stream_outcome::{StreamOutcome, StreamOutcomeState};

#[cfg(feature = "async")]
mod stream_outcome;