Of course. I'm writing a async task dependency management framework.
Framework motivation
The motivation for this framework is that our async task dependencies often form a directed acyclic graph (DAG), with the nodes being the tasks and the edges pointing from the depended task to the dependent task.
The DAG is often generated "top-down", meaning that we're most interested in some final outputs, but by constructing the Future
s that'll yield these final outputs, we discover recursively many layers of inter-connected dependent Future
s. To me this can not be easily expressed using .await
and friends (join!
, select!
, etc.).
An primary motivation and use case for this framework is loading the glTF 3D model format.
The task API looks like this:
/// This is an async task and the `node` of the dependency graph.
trait Task {
type Fut: Future<Output = Box<dyn Any>>;
fn set_input(&mut self, index: u8, value: Box<dyn Any>);
fn call(self) -> Self::Futurue;
}
Other APIs that uses Task
/// This is implemented for *FnOnce*.
trait IntoTask {
type Task: Task;
fn into_task(self) -> Self::Task;
}
/// This is a DAG describing task dependencies.
struct Graph { ... }
/// The `Graph` API
impl Graph {
/// `NodeIndex` is the identifier for the task.
pub fn add_task(&mut self, task: impl IntoTask) -> NodeIndex { ... }
/// This method connects the output of `parent` to the `index`th input of `child`.
pub fn add_dependency(
&mut self,
parent: NodeIndex,
child: NodeIndex,
index: u8
) -> Result<(), TypeMismatchError> { ... }
/// The scheduling algorithm.
/// It runs concurrently all tasks that have all its inputs ready.
/// When any one of the task is done (`select_all` style),
/// it checks if any dependent task becomes ready and runs it if so.
pub fn run(&mut self) { ... }
}
The Task
trait cannot be generic because Box<dyn Task>
s will be stored in a container, so it have to use Box<dyn Any>
as inputs and output.
Users of this framework are responsible for ensuring the dependencies are set correctly in the sense that their types match. If not, the API will return an error with the details of the mismatched types.