
A comprehensive C# coroutine-based kernel library for .NET that provides cooperative multitasking and flow control primitives. Flow enables developers to build complex async workflows using coroutines, futures, barriers, triggers, and timers.


// Create a kernel and basic coroutine
var kernel = Create.Kernel();
var coro = kernel.Factory.Coroutine(MyCoroutine);
kernel.Root.Add(coro);
// Step the kernel in your update loop
kernel.Step(); // or kernel.Update(deltaTime)
The library is organized into logical folders:
CsharpFlow/
├── Interfaces/ # All interface definitions (26 files)
│ ├── IKernel.cs # Core execution engine
│ ├── IGenerator.cs # Base execution units
│ ├── ITransient.cs # Lifetime management
│ ├── IFactory.cs # Object creation
│ └── Flow control interfaces (IBarrier, ITrigger, IFuture, etc.)
├── Impl/ # Implementation classes (25 files)
│ ├── Kernel.cs # Execution engine implementation
│ ├── Generator.cs # Base generator logic
│ ├── Factory.cs # Object factory
│ └── Flow control implementations
├── Logger/ # Logging subsystem
├── TestFlow/ # Comprehensive test suite
└── Properties/ # Assembly metadata
Interfaces/IKernel.cs, Impl/Kernel.cs)The central execution engine that manages time and steps all active generators. Supports both delta-time and fixed-step execution.
Interfaces/IGenerator.cs, Impl/Generator.cs)Base execution units including coroutines, subroutines, and sequences. Support suspension, resumption, and event-driven completion.
Interfaces/IFactory.cs, Impl/Factory.cs)Comprehensive object creation system with 40+ factory methods for all flow control types.
Interfaces/IBarrier.cs) - Wait for multiple operations to completeInterfaces/ITrigger.cs) - Execute when any of multiple conditions are metInterfaces/IFuture.cs) - Represent values that will be available in the futureInterfaces/ITimer.cs) - Schedule time-based executionInterfaces/ISequence.cs) - Execute operations in order
Current documentation is available at GamaSutra (note: formatting may be inconsistent). The original detailed post was published on AltDevBlogADay but is no longer available.
The tests are located in TestFlow/Editor/ for Unity compatibility. The test suite covers:
private void CreateHeartbeat()
{
New.PeriodicTimer(TimeSpan.FromMinutes(2)).Elapsed += tr =>
{
Get<UserCount>("user/alive").Then(result =>
{
if (result.Succeeded(out var val))
{
_activeUsers.Value = val.Num;
Info($"{val.Num} users online.");
}
});
};
}
public void GameLoop()
{
Root.Add(
New.Sequence(
New.Coroutine(StartGame).Named("StartGame"),
New.While(() => !_gameOver,
New.Coroutine(PlayerTurn).Named("Turn")),
New.Coroutine(EndGame).Named("EndGame")
).Named("GameLoop")
);
}
private IEnumerator StartGame(IGenerator self)
{
var start = New.Sequence(
New.Barrier(
WhitePlayer.StartGame(),
BlackPlayer.StartGame()
).Named("Init Game"),
New.Barrier(
WhitePlayer.DrawInitialCards(),
BlackPlayer.DrawInitialCards()
).Named("Deal Cards"),
New.Barrier(
New.TimedBarrier(
TimeSpan.FromSeconds(Parameters.MulliganTimer),
WhitePlayer.AcceptCards(),
BlackPlayer.AcceptCards()
).Named("Mulligan"),
New.Sequence(
WhitePlayer.PlaceKing(),
BlackPlayer.PlaceKing()
).Named("Place Kings")
).Named("Preceedings")
).Named("Start Game");
start.Completed += (tr) => Info("StartGame completed");
yield return start;
}
The .Named() extension method enables debugging and tracing. The library provides extensive runtime visualization to monitor kernel execution in real-time.
Flow uses a composition-based approach combining various flow control primitives. Below are the architectural diagrams for the major systems:
graph TB
K[Kernel] --> R[Root Node]
K --> F[Factory]
K --> T[TimeFrame]
K --> L[Logger]
F --> |Creates| G[Generators]
F --> |Creates| TR[Transients]
F --> |Creates| FC[Flow Controls]
R --> |Contains| N[Node Collection]
N --> |Steps| G
G --> CO[Coroutines]
G --> SU[Subroutines]
G --> SE[Sequences]
FC --> BA[Barriers]
FC --> TI[Triggers]
FC --> FU[Futures]
FC --> TM[Timers]
graph LR
subgraph "Sequential Flow"
SEQ[Sequence] --> |Step 1| A[Operation A]
A --> |Step 2| B[Operation B]
B --> |Step 3| C[Operation C]
end
subgraph "Parallel Flow"
BAR[Barrier] --> PA[Operation A]
BAR --> PB[Operation B]
BAR --> PC[Operation C]
PA --> |All Complete| DONE[Continue]
PB --> DONE
PC --> DONE
end
subgraph "Conditional Flow"
TRIG[Trigger] --> TA[Operation A]
TRIG --> TB[Operation B]
TRIG --> TC[Operation C]
TA --> |Any Complete| CONT[Continue]
TB --> CONT
TC --> CONT
end
stateDiagram-v2
[*] --> Created: Factory.Create()
Created --> Running: Resume()
Running --> Suspended: Suspend()
Suspended --> Running: Resume()
Running --> Completed: Complete()
Completed --> [*]
Running --> Stepping: Step()
Stepping --> Running: Continue
Stepping --> Suspended: yield return
Stepping --> Completed: End of method
sequenceDiagram
participant K as Kernel
participant B as Barrier
participant A as Task A
participant C as Task B
participant D as Task C
K->>B: Create Barrier
K->>A: Add Task A
K->>C: Add Task B
K->>D: Add Task C
loop Step Execution
K->>B: Step()
B->>A: Step()
B->>C: Step()
B->>D: Step()
end
A->>B: Complete()
C->>B: Complete()
D->>B: Complete()
B->>K: All Tasks Complete
sequenceDiagram
participant P as Producer
participant F as Future<T>
participant C as Consumer
C->>F: Create Future
C->>F: ResumeAfter(future)
Note over C: Consumer Suspended
P->>F: Set Value
F->>F: Available = true
F->>C: Resume Consumer
C->>F: Get Value
graph TD
T[Timer Created] --> S[Start Timer]
S --> W{Wait for Interval}
W --> |Time Elapsed| E[Trigger Elapsed Event]
E --> OS{OneShot Timer?}
OS --> |Yes| C[Complete]
OS --> |No| W
C --> D[Timer Destroyed]
subgraph "Periodic Timer"
P[Periodic] --> W2{Wait for Interval}
W2 --> |Time Elapsed| E2[Trigger Elapsed Event]
E2 --> W2
end
This architecture eliminates the need to manually track state across update calls in game loops or async workflows.
graph TD
subgraph "Complex Game Loop"
GL[Game Loop] --> INIT[Initialize]
INIT --> MENU[Main Menu]
MENU --> |Start Game| GAME[Game Session]
GAME --> TURN[Player Turn]
TURN --> AI[AI Turn]
AI --> CHECK{Game Over?}
CHECK --> |No| TURN
CHECK --> |Yes| SCORE[Show Score]
SCORE --> MENU
end
subgraph "Async Resource Loading"
LOAD[Load Resources] --> PAR[Parallel Loading]
PAR --> TEX[Load Textures]
PAR --> AUD[Load Audio]
PAR --> DAT[Load Data]
TEX --> BARRIER[Wait All]
AUD --> BARRIER
DAT --> BARRIER
BARRIER --> START[Start Game]
end
sequenceDiagram
participant P as Producer
participant C as Channel<T>
participant C1 as Consumer 1
participant C2 as Consumer 2
P->>C: Write(value1)
P->>C: Write(value2)
C1->>C: Read()
C->>C1: Return value1
C2->>C: Read()
C->>C2: Return value2
Note over C: Channel manages buffering and synchronization
stateDiagram-v2
[*] --> Running: Start
Running --> Error: Exception Thrown
Running --> Completed: Normal Completion
Error --> Retry: Retry Policy
Retry --> Running: Attempt Again
Retry --> Failed: Max Retries
Completed --> [*]
Failed --> [*]
Error --> Fallback: Fallback Strategy
Fallback --> Completed: Fallback Success
Fallback --> Failed: Fallback Failed
Flow folder into your Unity project’s Assets folder.asmdef files
Flow.sln solutionFlow.dll in your project
Alternatively, you can include the source files directly in your project.
The Verbose() logging method evaluates all arguments even when the verbosity level would prevent output. Use caution with expensive operations:
Verbosity = 10;
Verbose(15, $"Result: {ExpensiveFunction()}"); // ExpensiveFunction() still executes!
For performance-critical code, check verbosity levels before logging:
if (Verbosity >= 15)
Verbose(15, $"Result: {ExpensiveFunction()}");