Async Io Model

tursodatabase/turso/.claude/skills/async-io-model

作者 tursodatabaseff97ec42cdef无许可证24K 个星标收录于 2026年10月9日更新于 2026年10月8日仓库今天更新

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

AI 生成的概览

讲解 Turso 的协作式异步 I/O 模型:IOResult、状态机、重入陷阱与 CompletionGroup。

功能
该技能提供 tursodb 代码库中异步 I/O 模式的参考说明,其中以显式状态机的协作式让出取代 Rust async/await。它记录了 IOResult、IOCompletions 等核心类型,Completion 与 CompletionGroup 抽象,以及 return_if_io!、io_yield_one! 等辅助宏。内容还涵盖状态机设计、在让出点之前修改状态所导致的常见重入缺陷,以及异步代码的测试方法。它产出的是说明与代码示例,而非可执行产物。
适用场景
在编写或审查 tursodb core 中与 I/O 相关的代码时使用,尤其是在处理 IOResult、completion 或状态机时。它也适用于诊断因让出点之前修改状态而引起的重入缺陷。该技能指出,在 core 中进行任何 IO 操作时都应使用这些模式。
运行要求
不需要脚本或工具,仅为说明与参考资料。假定读者熟悉 Rust 与 tursodb 代码库,其中引用了 core/types.rs、core/io/completions.rs 等仓库文件。

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 completionsgroup.add(&completion1);group.add(&completion2);
// Build into single completion that finishes when all completelet 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

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:

rust
// Good: index is part of state, preserved across yieldsenum ProcessState {    Start,    ProcessingItem { idx: usize, items: Vec<Item> },    Done,}
// Loop advances idx only when transitioning statesProcessingItem { 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/turso位于.claude/skills/async-io-model提交ff97ec4

许可证: 无许可证

内容归原作者所有。SourceWeft 从公开仓库中收录这些内容。

举报或申请下架