async-io-model
Explanations of common asynchronous patterns used in tursodb. Involves IOResult, state machines, re-entrancy pitfalls, CompletionGroup. Always use these patterns in `core` when doing anything IO
Best use case
async-io-model is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Explanations of common asynchronous patterns used in tursodb. Involves IOResult, state machines, re-entrancy pitfalls, CompletionGroup. Always use these patterns in `core` when doing anything IO
Teams using async-io-model should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/async-io-model/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How async-io-model Compares
| Feature / Agent | async-io-model | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Explanations of common asynchronous patterns used in tursodb. Involves IOResult, state machines, re-entrancy pitfalls, CompletionGroup. Always use these patterns in `core` when doing anything IO
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Async I/O Model Guide
Turso uses cooperative yielding with explicit state machines instead of Rust async/await.
## Core Types
```rust
pub enum IOCompletions {
Single(Completion),
}
#[must_use]
pub enum IOResult<T> {
Done(T), // Operation complete, here's the result
IO(IOCompletions), // Need I/O, call me again after completions finish
}
```
Functions returning `IOResult` must be called repeatedly until `Done`.
## Completion and CompletionGroup
A `Completion` tracks a single I/O operation:
```rust
pub struct Completion { /* ... */ }
impl Completion {
pub fn finished(&self) -> bool;
pub fn succeeded(&self) -> bool;
pub fn get_error(&self) -> Option<CompletionError>;
}
```
To wait for multiple I/O operations, use `CompletionGroup`:
```rust
let mut group = CompletionGroup::new(|_| {});
// Add individual completions
group.add(&completion1);
group.add(&completion2);
// Build into single completion that finishes when all complete
let combined = group.build();
io_yield_one!(combined);
```
`CompletionGroup` features:
- Aggregates multiple completions into one
- Calls callback when all complete (or any errors)
- Can nest groups (add a group's completion to another group)
- Cancellable via `group.cancel()`
## Helper Macros
### `return_if_io!`
Unwraps `IOResult`, propagates IO variant up the call stack:
```rust
let result = return_if_io!(some_io_operation());
// Only reaches here if operation returned Done
```
### `io_yield_one!`
Yields a single completion:
```rust
io_yield_one!(completion); // Returns Ok(IOResult::IO(Single(completion)))
```
## State Machine Pattern
Operations that may yield use explicit state enums:
```rust
enum MyOperationState {
Start,
WaitingForRead { page: PageRef },
Processing { data: Vec<u8> },
Done,
}
```
The function loops, matching on state and transitioning:
```rust
fn my_operation(&mut self) -> Result<IOResult<Output>> {
loop {
match &mut self.state {
MyOperationState::Start => {
let (page, completion) = start_read();
self.state = MyOperationState::WaitingForRead { page };
io_yield_one!(completion);
}
MyOperationState::WaitingForRead { page } => {
let data = page.get_contents();
self.state = MyOperationState::Processing { data: data.to_vec() };
// No yield, continue loop
}
MyOperationState::Processing { data } => {
let result = process(data);
self.state = MyOperationState::Done;
return Ok(IOResult::Done(result));
}
MyOperationState::Done => unreachable!(),
}
}
}
```
## Re-Entrancy: The Critical Pitfall
**State mutations before yield points cause bugs on re-entry.**
### Wrong
```rust
fn bad_example(&mut self) -> Result<IOResult<()>> {
self.counter += 1; // Mutates state
return_if_io!(something_that_might_yield()); // If yields, re-entry will increment again!
Ok(IOResult::Done(()))
}
```
If `something_that_might_yield()` returns `IO`, caller waits for completion, then calls `bad_example()` again. `counter` gets incremented twice (or more).
### Correct: Mutate After Yield
```rust
fn good_example(&mut self) -> Result<IOResult<()>> {
return_if_io!(something_that_might_yield());
self.counter += 1; // Only reached once, after IO completes
Ok(IOResult::Done(()))
}
```
### Correct: Use State Machine
```rust
enum State { Start, AfterIO }
fn good_example(&mut self) -> Result<IOResult<()>> {
loop {
match self.state {
State::Start => {
// Don't mutate shared state here
self.state = State::AfterIO;
return_if_io!(something_that_might_yield());
}
State::AfterIO => {
self.counter += 1; // Safe: only entered once
return Ok(IOResult::Done(()));
}
}
}
}
```
## Common Re-Entrancy Bugs
| Pattern | Problem |
|---------|---------|
| `vec.push(x); return_if_io!(...)` | Vec grows on each re-entry |
| `idx += 1; return_if_io!(...)` | Index advances multiple times |
| `map.insert(k,v); return_if_io!(...)` | Duplicate inserts or overwrites |
| `flag = true; return_if_io!(...)` | Usually ok, but check logic |
## State Enum Design
Encode progress in state variants:
```rust
// Good: index is part of state, preserved across yields
enum ProcessState {
Start,
ProcessingItem { idx: usize, items: Vec<Item> },
Done,
}
// Loop advances idx only when transitioning states
ProcessingItem { idx, items } => {
return_if_io!(process_item(&items[idx]));
if idx + 1 < items.len() {
self.state = ProcessingItem { idx: idx + 1, items };
} else {
self.state = Done;
}
}
```
## Turso Implementation
Key files:
- `core/types.rs` - `IOResult`, `IOCompletions`, `return_if_io!`, `return_and_restore_if_io!`
- `core/io/completions.rs` - `Completion`, `CompletionGroup`
- `core/util.rs` - `io_yield_one!` macro
- `core/state_machine.rs` - Generic `StateMachine` wrapper
- `core/storage/btree.rs` - Many state machine examples
- `core/storage/pager.rs` - `CompletionGroup` usage examples
## Testing Async Code
Re-entrancy bugs often only manifest under specific IO timing. Use:
- Deterministic simulation (`testing/simulator/`)
- Whopper concurrent DST (`testing/concurrent-simulator/`)
- Fault injection to force yields at different points
## References
- `docs/manual.md` section on I/ORelated Skills
ios-foundation-models
Use when implementing on-device AI with Apple's Foundation Models framework — prevents context overflow, blocking UI, wrong model use cases, and manual JSON parsing when @Generable should be used. iOS 26+, macOS 26+, iPadOS 26+, ios-visionOS 26+
inter-model-arbitration
Resolves disputes and conflicts between AI models during collaborative tasks
Creating Models
Step-by-step guide to create a new Odoo model with fields, constraints, and methods.
Apple Foundation Models
Use this skill when working with Apple's Foundation Models framework for on-device AI and LLM capabilities in iOS/macOS apps
analyzing-business-models
Analyzes business models including revenue models, unit economics, competitive moats, scalability, and value creation/capture mechanisms using frameworks like Business Model Canvas and strategic analysis. Use when the user requests business model analysis, unit economics review, moat assessment, or wants to understand how a company creates and captures value.
ai-model-cascade
A production-ready pattern for integrating AI models (specifically Google Gemini) with automatic fallback, retry logic, structured output via Zod schemas, and comprehensive error handling. Use when integrating AI/LLM APIs, need automatic fallback when models are overloaded, want type-safe structured responses, or building features requiring reliable AI generation.
asyncredux-flutter-hooks
Integrate AsyncRedux with the flutter_hooks package. Covers adding flutter_hooks_async_redux, using the useSelector hook, and combining hooks with AsyncRedux state management.
async
Regel 10: Async-First mit Symfony Messenger. Use when working with async.
asyncredux-error-handling
Implement comprehensive error handling for actions. Covers the `wrapError()` method for action-level error wrapping, GlobalWrapError for app-wide error transformation, ErrorObserver for logging/monitoring, and the error handling flow (before → reduce → after).
tmdd-threat-modeling
Create and manage TMDD threat models grounded in actual codebase architecture. Use when the user wants to threat-model a system, add a feature, create security threat mappings, run tmdd commands, or work with .tmdd/ YAML files.
sqlmodel-task-models
This skill should be used when defining a robust, type-safe, and async-compatible database schema for the Todo application using SQLModel, ensuring compatibility with Better Auth and optimized for PostgreSQL.
rust-async-patterns
Master Rust async programming with Tokio, async traits, error handling, and concurrent patterns. Use when building async Rust applications, implementing concurrent systems, or debugging async code.