Skip to content

RFC-002: libuv-based Resource Type IO Implementation Layer ​

References:

Summary ​

This document defines the IO implementation layer of YaoXiang: providing cross-platform IO capabilities based on libuv, serving as the underlying implementation of the RFC-024 resource type system.

Core Positioning:

RFC-024: Resource Type Definitions (FilePath, HttpUrl, DBUrl, Console)
    ↓ uses
RFC-002: Resource Type IO Implementation (based on libuv)
    ↓ underlying
libuv: Cross-platform IO Engine (Event Loop + Thread Pool)

What it is NOT:

  • ❌ NOT "transparent async" — users explicitly control concurrency through spawn blocks
  • ❌ NOT "automatic asyncification" — IO operations need to be explicitly called within spawn blocks
  • ❌ NOT "developers need not care about underlying details" — the resource type system ensures concurrency safety

What it IS:

  • ✅ IO implementation layer for resource types (FilePath, HttpUrl, DBUrl, Console)
  • ✅ Cross-platform IO unification (libuv handles Windows/Linux/macOS differences)
  • ✅ Shared event loop architecture (a single libuv event loop handles all IO)
  • ✅ Integration with the RFC-024 resource type system

Motivation ​

Why libuv? ​

RFC-024 defines the resource type system:

  • FilePath - Filesystem path
  • HttpUrl - HTTP endpoint
  • DBUrl - Database connection
  • Console - Standard output

These resource types require underlying IO implementations. libuv provides:

NeedWhat libuv provides
Cross-platform IOUnified Windows/Linux/macOS API
Async capabilityShared event loop, all workers' IO centralized
Thread poolDedicated thread pool for blocking operations
Concurrency safetySingle-threaded event loop, inherently race-free

Relationship with RFC-024 ​

┌─────────────────────────────────────────────────────────┐
│  RFC-024: Concurrency Model                              │
│  - spawn {} blocks (explicit concurrency)                │
│  - Resource type definitions (FilePath, HttpUrl, DBUrl, Console) │
│  - Resource conflict detection (same path auto-serialized)│
└─────────────────────────────────────────────────────────┘
                          ↓ uses
┌─────────────────────────────────────────────────────────┐
│  RFC-002: Resource Type IO Implementation                │
│  - FilePath → libuv file IO                              │
│  - HttpUrl → libuv network IO                            │
│  - DBUrl → Database connection pool                      │
│  - Console → Standard output serialization               │
└─────────────────────────────────────────────────────────┘
                          ↓ underlying
┌─────────────────────────────────────────────────────────┐
│  libuv: Cross-platform IO Engine                         │
│  - Event loop                                            │
│  - Thread pool                                           │
│  - Cross-platform unified API                            │
└─────────────────────────────────────────────────────────┘

Proposal ​

1. libuv Architecture ​

1.1 Shared Event Loop Architecture ​

┌─────────────────────────────────────────────────────────┐
│                    Runtime                               │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐    │
│  │  Worker 0   │  │  Worker 1   │  │  Worker N   │    │
│  │  compute    │  │  compute    │  │  compute    │    │
│  └──────┬──────┘  └──────┬──────┘  └──────┬──────┘    │
│         │                │                │            │
│         └────────────────┼────────────────┘            │
│                          ↓                              │
│  ┌─────────────────────────────────────────────────┐  │
│  │       libuv event loop (dedicated thread)        │  │
│  │       handles all IO operations                 │  │
│  └─────────────────────────────────────────────────┘  │
│                                                         │
└─────────────────────────────────────────────────────────┘

Key Features:

  • One shared libuv event loop (running on a dedicated thread)
  • All workers' IO operations are submitted to this shared event loop
  • The single-threaded event loop inherently avoids race conditions
  • Resource-efficient, no need to create an event loop per worker

1.2 Concurrency Safety Mechanism ​

libuv featureYaoXiang counterpartConcurrency safety
Single-threaded loopSequential execution in spawn blocksInherently race-free
Thread pool isolationBlocking ops don't block main threadNo shared state
Async callbacksDAG scheduler manages dependenciesDeterministic execution

2. Resource Type IO Mapping ​

2.1 FilePath → libuv File IO ​

rust
// std.io module (based on libuv)
pub struct IoModule;

impl StdModule for IoModule {
    fn exports(&self) -> Vec<NativeExport> {
        vec![
            // File operations → libuv fs_* API
            NativeExport::new("read_file", "std.io.read_file",
                "(path: FilePath) -> String", native_read_file),
            NativeExport::new("write_file", "std.io.write_file",
                "(path: FilePath, content: String) -> Bool", native_write_file),
            NativeExport::new("append_file", "std.io.append_file",
                "(path: FilePath, content: String) -> Bool", native_append_file),
            // Console operations → libuv tty API
            NativeExport::new("print", "std.io.print",
                "(...args) -> ()", native_print),
            NativeExport::new("println", "std.io.println",
                "(...args) -> ()", native_println),
        ]
    }
}

// libuv file IO implementation
fn native_read_file(args: &[RuntimeValue], ctx: &mut NativeContext) -> Result<RuntimeValue, ExecutorError> {
    let path = extract_file_path(args)?;

    // Submit to libuv event loop
    // libuv reads file asynchronously
    // Return result
    ctx.uv_loop.fs_read(path)
}

2.2 HttpUrl → libuv Network IO ​

rust
// std.net module (based on libuv)
pub struct NetModule;

impl StdModule for NetModule {
    fn exports(&self) -> Vec<NativeExport> {
        vec![
            // HTTP operations → libuv http API
            NativeExport::new("http_get", "std.net.http_get",
                "(url: HttpUrl) -> Response", native_http_get),
            NativeExport::new("http_post", "std.net.http_post",
                "(url: HttpUrl, body: String) -> Response", native_http_post),
        ]
    }
}

// libuv network IO implementation
fn native_http_get(args: &[RuntimeValue], ctx: &mut NativeContext) -> Result<RuntimeValue, ExecutorError> {
    let url = extract_http_url(args)?;

    // Submit to libuv event loop
    // libuv HTTP request asynchronously
    // Return result
    ctx.uv_loop.http_get(url)
}

2.3 DBUrl → Database Connection Pool ​

rust
// std.db module (based on libuv)
pub struct DbModule;

impl StdModule for DbModule {
    fn exports(&self) -> Vec<NativeExport> {
        vec![
            // Database operations → libuv thread pool
            NativeExport::new("query", "std.db.query",
                "(url: DBUrl, sql: String) -> Rows", native_query),
        ]
    }
}

// libuv database IO implementation
fn native_query(args: &[RuntimeValue], ctx: &mut NativeContext) -> Result<RuntimeValue, ExecutorError> {
    let url = extract_db_url(args)?;
    let sql = extract_sql(args)?;

    // Submit to libuv thread pool
    // Database query executes in thread pool
    // Notify main thread via callback on completion
    ctx.uv_loop.db_query(url, sql)
}

2.4 Console → Standard Output Serialization ​

rust
// Console operations auto-serialize (RFC-024 resource type rules)
// All Console operations execute sequentially in the same thread
fn native_print(args: &[RuntimeValue], ctx: &mut NativeContext) -> Result<RuntimeValue, ExecutorError> {
    let output = format_args(args);

    // Console operation serialized
    // libuv tty write
    ctx.uv_loop.tty_write(output)
}

3. Integration with spawn Blocks ​

3.1 User Perspective ​

yaoxiang
# Resource type definitions (RFC-024)
FilePath: Resource
HttpUrl: Resource

# IO operations (RFC-002 implementation)
File.read: (FilePath) -> String
HTTP.get: (HttpUrl) -> Response

# User explicit concurrency (RFC-024)
(a, b) = spawn {
    read_file("data.txt"),      # resource type FilePath, libuv underneath
    fetch("http://example.com") # resource type HttpUrl, libuv underneath
}
# Compiler: FilePath and HttpUrl have no conflict, can run in parallel

3.2 Compile-time Analysis ​

Compiler analyzes spawn block:
1. Identify resource type operations
2. Detect resource conflicts (same path/URL auto-serialized)
3. Generate DAG execution plan
4. Mark IO nodes (submit to libuv)

3.3 Runtime Execution ​

Runtime executes spawn block:
1. Worker 0 submits IO task → shared event loop
2. Worker 1 submits IO task → shared event loop
3. Event loop processes all IO operations uniformly
4. Notify corresponding Worker upon IO completion
5. Worker continues with subsequent tasks

4. Runtime Three-Layer Architecture and libuv ​

Layerlibuv usageAsync capabilityApplicable scenarios
Embedded RuntimeNo libuvNo asyncWASM, game scripts
Standard RuntimeShared event loopIO asyncWeb services, data pipelines
Full RuntimeShared event loopIO async + parallelScientific computing, large-scale parallel

Embedded Runtime: No libuv, immediate execution, no async capability.

Standard Runtime: Shared libuv event loop, all IO operations processed asynchronously.

Full Runtime: Shared libuv event loop, multithreaded parallelism + IO async.


Detailed Design ​

1. Rust Binding Structure ​

rust
// libuv binding module
pub mod uv {
    // Event loop
    pub struct UvLoop {
        loop_handle: *mut uv_loop_t,
    }

    // File operations
    pub trait FileOps {
        fn fs_read(&self, path: &str) -> Result<String, UvError>;
        fn fs_write(&self, path: &str, content: &str) -> Result<(), UvError>;
        fn fs_append(&self, path: &str, content: &str) -> Result<(), UvError>;
    }

    // Network operations
    pub trait NetOps {
        fn http_get(&self, url: &str) -> Result<Response, UvError>;
        fn http_post(&self, url: &str, body: &str) -> Result<Response, UvError>;
    }

    // Database operations
    pub trait DbOps {
        fn db_query(&self, url: &str, sql: &str) -> Result<Rows, UvError>;
    }

    // Console operations
    pub trait ConsoleOps {
        fn tty_write(&self, data: &str) -> Result<(), UvError>;
    }
}

2. Standard Library Module Structure ​

src/std/
├── io.rs          # FilePath IO (based on libuv)
├── net.rs         # HttpUrl IO (based on libuv)
├── db.rs          # DBUrl IO (based on libuv)
├── console.rs     # Console IO (based on libuv)
└── mod.rs         # Module registration

3. Integration with DAG Scheduler ​

rust
// IO node interface (defined in RFC-008)
trait IoScheduler {
    // Submit IO task, return handle
    fn submit_io(&self, task: IoTask) -> IoHandle;

    // Called by libuv upon IO completion, wakes DAG node
    fn on_io_complete(&self, handle: IoHandle);
}

// libuv implementation
impl IoScheduler for UvLoop {
    fn submit_io(&self, task: IoTask) -> IoHandle {
        match task.resource_type {
            ResourceType::FilePath => self.fs_read(task.path),
            ResourceType::HttpUrl => self.http_get(task.url),
            ResourceType::DBUrl => self.db_query(task.url, task.sql),
            ResourceType::Console => self.tty_write(task.data),
        }
    }

    fn on_io_complete(&self, handle: IoHandle) {
        // Notify DAG scheduler to wake downstream nodes
        self.dag_scheduler.wake_dependents(handle.node_id);
    }
}

Trade-offs ​

Advantages ​

  1. Cross-platform unification: libuv handles Windows/Linux/macOS differences
  2. IO async capability: Shared event loop handles all IO, no async/await needed
  3. Concurrency safety: Single-threaded event loop inherently race-free
  4. Resource efficiency: One event loop, low memory overhead
  5. Aligned with RFC-024: Resource type system ensures concurrency safety
  6. Mature and stable: libuv validated at scale by Node.js

Disadvantages ​

  1. C library dependency: Requires binding to libuv C library
  2. Bootstrapping limitation: May need to replace with YaoXiang native implementation after bootstrapping
  3. WASM support: Requires additional adaptation work

Alternatives ​

ApproachWhy not chosen
Rust std::ioSynchronous blocking, cannot coordinate with spawn blocks for async
tokioDesigned for Rust async/await, misaligned with YaoXiang's explicit concurrency model
mioOnly provides raw async primitives, lacks high-level IO features
From-scratch implementationComplex and error-prone, cannot match libuv's maturity

Implementation Strategy ​

Phase Breakdown ​

  1. Phase 1 (v0.3): libuv bindings, basic file IO
  2. Phase 2 (v0.5): Network IO, HTTP support
  3. Phase 3 (v0.7): Database IO, connection pool
  4. Phase 4 (v1.0): WASM adaptation, performance optimization

Dependencies ​

  • RFC-024 (Concurrency Model) → Completed
  • RFC-008 (Runtime Architecture) → Completed
  • RFC-009 (Ownership Model) → Completed
  • RFC-011 (Generics System) → Completed

Design Decision Records ​

DecisionChoiceReasonDate
IO implementation layerlibuvCross-platform, async capability, concurrency safety2025-01-05
PositioningResource type IO implementation layerIntegrates with RFC-024 resource type system2026-06-16
Event loop architectureShared event loopResource-efficient, avoids redundant creation2026-06-16
Concurrency safetySingle-threaded event loopInherently race-free, aligned with RFC-0242026-06-16
Standard library rewritestd.io/std.net based on libuvCross-platform unification, async capability2026-06-16

Open Questions ​

  • [ ] libuv adaptation approach in WASM environment
  • [ ] Database connection pool design
  • [ ] Complete HTTP client implementation
  • [ ] Cross-platform consistency of filesystem events
  • [ ] Network IO timeout mechanism design
  • [ ] Replacement strategy for libuv after bootstrapping

References ​

YaoXiang Official Documentation ​

External References ​


Lifecycle and Disposition ​

StatusLocationDescription
Draftdocs/design/rfc/draft/Under re-review