Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
tursodatabase avatar

Async Io Model

  • 988 installs
  • 23.7k repo stars
  • Updated August 5, 2026
  • tursodatabase/turso

async-io-model is a Turso contributor skill that explains cooperative IOResult state machines, CompletionGroup usage, and re-entrancy rules for non-blocking database core I/O in Rust.

About

async-io-model is a Turso database skill describing the engine's cooperative asynchronous I/O model used in the Rust core instead of async/await. Functions return IOResult with Done or IO variants and must be called repeatedly until completion, coordinating through Completion and CompletionGroup types. The guide covers re-entrancy pitfalls and mandates these patterns for any IO work inside tursodb core. Developers contributing to Turso or embedding its engine reach for async-io-model before writing storage, network, or syscall paths that must yield cooperatively. The skill prevents subtle bugs from mixing conventional Rust async with Turso's explicit state-machine I/O loop. Treat async-io-model as required reading for core patches touching IOResult-returning functions.

  • Explains IOResult cooperative yielding pattern that replaces Rust async/await
  • Defines Completion and CompletionGroup for waiting on multiple I/O operations
  • Details re-entrancy pitfalls and state machine requirements
  • Shows how to nest CompletionGroups and handle cancellation
  • Mandates these patterns for all IO code inside the core module

Async Io Model by the numbers

  • 988 all-time installs (skills.sh)
  • +38 installs in the week ending Aug 4, 2026 (Skillselion tracking)
  • Ranked #401 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
  • Security screen: MEDIUM risk (skills.sh audit)
  • Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/tursodatabase/turso --skill async-io-model

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs988
repo stars23.7k
Security audit3 / 3 scanners passed
Last updatedAugust 5, 2026
Repositorytursodatabase/turso

How does Turso core handle async I/O without async/await?

Correctly implement non-blocking I/O patterns when contributing to or extending the Turso database core.

Who is it for?

Rust developers contributing to Turso core who must implement cooperative I/O with IOResult and CompletionGroup patterns.

Skip if: Application developers using Turso client SDKs only, or Rust codebases that rely on standard async/await Tokio patterns.

When should I use this skill?

The user edits Turso core Rust, mentions IOResult, CompletionGroup, or cooperative I/O state machines in tursodb.

What you get

Correct IOResult-driven functions, CompletionGroup wiring, and re-entrancy-safe cooperative I/O in Turso core Rust code.

  • IOResult-compliant core functions
  • CompletionGroup-integrated IO paths

Files

SKILL.mdMarkdownGitHub ↗

Async I/O Model Guide

Turso uses cooperative yielding with explicit state machines instead of Rust async/await.

Core Types

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:

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:

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:

let result = return_if_io!(some_io_operation());
// Only reaches here if operation returned Done

io_yield_one!

Yields a single completion:

io_yield_one!(completion);  // Returns Ok(IOResult::IO(Single(completion)))

State Machine Pattern

Operations that may yield use explicit state enums:

enum MyOperationState {
    Start,
    WaitingForRead { page: PageRef },
    Processing { data: Vec<u8> },
    Done,
}

The function loops, matching on state and transitioning:

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

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

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

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

PatternProblem
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:

// 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

Related skills

How it compares

Use async-io-model for Turso engine IOResult patterns; standard Tokio async guides do not match tursodb core conventions.

FAQ

Does Turso core use Rust async/await?

Turso core uses cooperative yielding with explicit IOResult state machines and CompletionGroup instead of async/await; functions return Done or IO and must be re-called until complete.

When is async-io-model required?

async-io-model must guide any IO implementation inside tursodb core, enforcing IOResult patterns and documenting re-entrancy pitfalls for Completion-based cooperative execution.

Is Async Io Model safe to install?

skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.

Backend & APIsbackendintegrations

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.