What is Crossflow?
Crossflow is a general-purpose Rust library for reactive and async programming. It simplifies the challenges of implementing highly async software systems that may have parallel activities with interdependencies, conditional branching, and/or cycles. Its specialty is creating event-driven multi-agent state machines.
Implemented in Rust on the Bevy ECS, crossflow has high performance and guaranteed-safe parallelism, making it suitable for highly responsive low-level state machines, just as well as high-level visually programmed device orchestrators.
Services
The basic unit of work in crossflow is encapsulated by a service. Services take an input message and eventually yield an output message—perhaps immediately or perhaps after some long-running routine has finished.
In crossflow services are defined by Bevy Systems that take an input and produce an output. As Bevy Systems, the crossflow services can integrate into an application by interacting with the Bevy World through entities, components, and resources of Bevy’s ECS. This allows services to have enormous versatility in how they are implemented, while still being highly parallelizable, memory-safe, and efficient.
A service can be executed by sending it a request at any time using this line of code:
#![allow(unused)]
fn main() {
let outcome = commands.request(input, service).outcome();
}
This line is non-blocking, meaning the service will be executed concurrently with the rest of the application’s activity. The outcome is a receiver that can be polled—or better yet awaited in an async context—until the service has sent its response or has been cancelled.
Workflows
Best practice when creating complex systems is to encapsulate services into the simplest possible building blocks and assemble those blocks into increasingly sophisticated structures. This is where workflows come in.
Workflows allow you to assemble services into a directed graph—cycles are allowed—that form a more complex behavior, feeding the output of each service as input to another service. Workflows are excellent for defining state machines that have async state transitions or that have lots of parallel activity that needs to be managed and synchronized.
When you create a workflow you will ultimately be creating yet another service that can be treated exactly the same as a service created using a Bevy System. This workflow-based service can even be used as a node inside of another workflow. In other words, you can build hierarchical workflows.
Execution
You can run as many service requests as you want at the same time for as many
services or workflows as you want, including making multiple simultaneous requests
for the same service or workflow. Each time request is called, a new
“session” will be spawned that executes the workflow or service independently from
any other that is running. However, if any of the services or workflows interact
with “the world” (either the Bevy World, the actual physical world, or
some external resource) then those services or workflows may indirectly interact
with each other.
Tip
Check out the live web demo to get a sense for what a workflow might look like.
Try passing in
[20, 30]as the request message and run the workflow to see the message get split and calculated.
Getting Started
To get you started with crossflow, the next chapter will teach you how to spawn a basic service. After that you will see how to run a service, then how to assemble services into a workflow (which is itself a service) and execute it.
To learn the fundamental concepts around what a “workflow” is in the crossflow library, see the Introduction to Workflows chapter.