Skip to main content
skill Claude Code CX Codex Cursor GitHub Copilot

async-io-model — Claude Code skill: what it does, how to install

Teaches the agent Turso's cooperative async I/O patterns: explicit state machines, IOResult, and avoiding re-entrancy bugs.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add tursodatabase/turso --skill async-io-model -a claude-code
Open on GitHub

What async-io-model does

Turso avoids Rust's native async/await inside its database engine, using explicit state machine enums and an IOResult type instead. This skill loads those exact patterns into the agent context. It covers tracking pending disk operations through Completion, aggregating batches via CompletionGroup, and driving control loops with return_if_io! and io_yield_one!.

The critical pitfall covered in this guide is re-entrancy. Mutating data structures like advancing an index or calling push before return_if_io! causes duplicate work because callers re-enter the function once I/O completes. The rules direct the agent to mutate state only after yield points or to encode progress directly into state enum variants.

These conventions apply to Turso's core directory, and developers test them using deterministic simulation under testing/simulator/. For applications running on standard Tokio runtimes, these patterns are irrelevant.

What it helps with

  • convert asynchronous Rust logic into explicit state machines using IOResult
  • detect state mutations placed before return_if_io! yield points
  • aggregate concurrent I/O operations with CompletionGroup
  • structure state enum variants to preserve iteration progress across yields

Example prompts for the agent

  • Refactor this I/O loop into an explicit state machine using IOResult
  • Audit this storage function for re-entrancy bugs around return_if_io!
  • Combine these Completion operations using CompletionGroup

How to install async-io-model

1Skills CLI (Vercel)

npx skills add tursodatabase/turso --skill async-io-model -a claude-code

2Manually: put the files in place

~/.claude/skills/async-io-model/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/async-io-model/

From the same set10

The whole set →
code-quality tursodatabase Correctness rules, Rust patterns, and anti-over-engineering guidance for coding agents working in your repo. Claude Code CX Codex Cursor +1 1,800installs storage-format tursodatabase A reference for the SQLite file format: header, pages, B-trees, cells, overflow, freelist, tied to Turso's storage code. Claude Code CX Codex Cursor +1 1,300installs testing tursodatabase Skill teaches you to write tests for SQL and Rust: choose between .sqltest, TCL and integration formats, run test suites with the right commands. Claude Code CX Codex Cursor +1 1,300installs debugging tursodatabase A skill for tracking down tursodb bugs: compare bytecode against SQLite, turn on trace logs, run ThreadSanitizer, replay a seed and measure stack use. Claude Code CX Codex Cursor +1 1,300installs index-knowledge tursodatabase Skill automatically creates hierarchical AGENTS.md documentation for a codebase. Analyzes project structure, scores directory complexity, and generates files with instructions for AI agents. Claude Code CX Codex Cursor +1 1,300installs mvcc tursodatabase Architectural reference for experimental MVCC in Turso: row versioning, snapshot isolation, and known memory limits. Useful when working on database internals. Claude Code CX Codex Cursor +1 1,200installs differential-fuzzer tursodatabase This skill guides Turso differential fuzzer runs and investigates differences against SQLite. Use it when generated SQL may expose correctness bugs. Claude Code CX Codex Cursor +1 1,200installs transaction-correctness tursodatabase Explains how transactions work in Turso: WAL mechanics, checkpointing, crash recovery, and concurrency rules. Claude Code CX Codex Cursor +1 1,200installs

Similar

scaffolding-oracle-to-postgres-migration-test-project official github Creates an empty xUnit integration test project for Oracle in a .NET solution. Use it in Phase 3 before writing Oracle baseline integration tests. GitHub Copilot 1,700installs bigquery-troubleshooting official google This skill investigates broken, slow, or costly BigQuery jobs. Use it to isolate bottlenecks, trace symptoms to causes, and choose remediation steps. Claude Code Gemini CLI CX Codex 1,400installs bigquery-optimization official google This skill helps reduce BigQuery costs, tune storage, and improve SQL queries. Use it for capacity planning and table layout changes. Claude Code Gemini CLI CX Codex 1,400installs agent-memory-systems sickn33 Skill for designing agent memory with per-user and per-tenant isolation. Build RAG, conversation history, and vector stores so that one client's data stays separate from another. Claude Code CX Codex Cursor +1 2,000installs database-lookup K-Dense-AI Queries 80 documented public database APIs with explicit filters, pagination, and provenance. It is useful when scientific or financial facts require reproducible citations. Claude Code CX Codex Cursor +1 2,100installs seaborn K-Dense-AI A skill that gets your coding agent building seaborn plots from pandas DataFrames: distributions, regressions, pairplots and heatmaps, written against the current API. Claude Code CX Codex Cursor +1 2,100installs xlsx official anthropics Creates, reads, edits, and converts spreadsheet files. Use it when the requested deliverable is an Excel workbook or another tabular file. Claude Code Claude.ai 187,223installs google-agents-cli-observability official Google This skill guides you through tracing and monitoring AI agents, including ADK agents. Use it when you need to inspect agent execution or monitor a deployed agent. Claude Code CX Codex Cursor +1 509,900installs

Questions about async-io-model

How to install async-io-model in Claude Code?

Run npx skills add tursodatabase/turso --skill async-io-model -a claude-code in your terminal.

What does async-io-model do?

It equips the coding agent with Turso's core asynchronous model, replacing Rust async/await with IOResult, completion tracking, and cooperative state loops.

Why are mutations before return_if_io! problematic?

If an operation yields IO, the caller pauses and invokes the function again later. Any mutation executed before that yield runs twice, corrupting collections and indices.

Source text

SKILL.md · MIT
---
name: async-io-model
description: 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
---
# 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/O

© tursodatabase · MIT · tursodatabase/turso

See also

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor