Skip to content

RFC-002: Уровень реализации IO для типов ресурсов на основе libuv

Справка:

Аннотация

В данном документе определяется уровень реализации IO в YaoXiang: предоставление кроссплатформенных возможностей IO на основе libuv в качестве базовой реализации системы типов ресурсов из RFC-024.

Основное назначение:

RFC-024: Определение типов ресурсов (FilePath, HttpUrl, DBUrl, Console)
    ↓ использует
RFC-002: Реализация IO типов ресурсов (на основе libuv)
    ↓ нижний уровень
libuv: Кроссплатформенный движок IO (цикл событий + пул потоков)

Чем это НЕ является:

  • ❌ Не «прозрачная асинхронность» — пользователь явно управляет параллелизмом через блоки spawn
  • ❌ Не «автоматическая асинхронизация» — операции IO требуют явного вызова в блоке spawn
  • ❌ Не «разработчику не нужно заботиться о деталях низкого уровня» — система типов ресурсов обеспечивает безопасность параллелизма

Чем это ЯВЛЯЕТСЯ:

  • ✅ Уровень реализации IO для типов ресурсов (FilePath, HttpUrl, DBUrl, Console)
  • ✅ Унификация кроссплатформенного IO (libuv обрабатывает различия Windows/Linux/macOS)
  • ✅ Архитектура с общим циклом событий (один цикл событий libuv обрабатывает весь IO)
  • ✅ Интеграция с системой типов ресурсов RFC-024

Мотивация

Зачем нужен libuv?

В RFC-024 определена система типов ресурсов:

  • FilePath — путь в файловой системе
  • HttpUrl — HTTP-Endpoint
  • DBUrl — подключение к базе данных
  • Console — стандартный вывод

Этим типам ресурсов необходима низкоуровневая реализация IO. libuv предоставляет:

ПотребностьЧто предоставляет libuv
Кроссплатформенный IOЕдиный API для Windows/Linux/macOS
Асинхронные возможностиОбщий цикл событий, весь IO всех worker'ов обрабатывается централизованно
Пул потоковВыделенный пул потоков для блокирующих операций
Безопасность параллелизмаОднопоточный цикл событий, естественное отсутствие гонок

Связь с RFC-024

┌─────────────────────────────────────────────────────────┐
│  RFC-024: Модель параллелизма                           │
│  - Блоки spawn {} (явный параллелизм)                   │
│  - Определения типов ресурсов (FilePath, HttpUrl,       │
│    DBUrl, Console)                                      │
│  - Обнаружение конфликтов ресурсов (одинаковый путь     │
│    автоматически сериализуется)                         │
└─────────────────────────────────────────────────────────┘
                          ↓ использует
┌─────────────────────────────────────────────────────────┐
│  RFC-002: Реализация IO типов ресурсов                  │
│  - FilePath → файловый IO libuv                         │
│  - HttpUrl → сетевой IO libuv                           │
│  - DBUrl → пул подключений к БД                         │
│  - Console → сериализация стандартного вывода           │
└─────────────────────────────────────────────────────────┘
                          ↓ нижний уровень
┌─────────────────────────────────────────────────────────┐
│  libuv: Кроссплатформенный движок IO                    │
│  - Цикл событий                                         │
│  - Пул потоков                                          │
│  - Унифицированный кроссплатформенный API               │
└─────────────────────────────────────────────────────────┘

Предложение

1. Архитектура libuv

1.1 Архитектура с общим циклом событий

┌─────────────────────────────────────────────────────────┐
│                    Runtime                              │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐    │
│  │  Worker 0   │  │  Worker 1   │  │  Worker N   │    │
│  │  Вычисления │  │  Вычисления │  │  Вычисления │    │
│  └──────┬──────┘  └──────┬──────┘  └──────┬──────┘    │
│         │                │                │            │
│         └────────────────┼────────────────┘            │
│                          ↓                              │
│  ┌─────────────────────────────────────────────────┐  │
│  │     Цикл событий libuv (выделенный поток)        │  │
│  │     Обработка всех операций IO                   │  │
│  └─────────────────────────────────────────────────┘  │
│                                                         │
└─────────────────────────────────────────────────────────┘

Ключевые характеристики:

  • Один общий цикл событий libuv (запущен в выделенном потоке)
  • Все операции IO от всех worker'ов отправляются в этот общий цикл событий
  • Однопоточный цикл событий естественно избегает гонок
  • Высокая эффективность использования ресурсов — не нужно создавать цикл событий для каждого worker'а

1.2 Механизмы безопасности параллелизма

Характеристика libuvСоответствие в YaoXiangБезопасность параллелизма
Однопоточный цикл событийПоследовательное выполнение внутри блока spawnЕстественное отсутствие гонок
Изоляция пула потоковБлокирующие операции не блокируют главный потокНет общего состояния
Асинхронные колбекиDAG-планировщик управляет зависимостямиДетерминированное выполнение

2. Маппинг IO типов ресурсов

2.1 FilePath → файловый IO libuv

rust
// Модуль std.io (на основе libuv)
pub struct IoModule;

impl StdModule for IoModule {
    fn exports(&self) -> Vec<NativeExport> {
        vec![
            // Операции с файлами → 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 → libuv tty API
            NativeExport::new("print", "std.io.print",
                "(...args) -> ()", native_print),
            NativeExport::new("println", "std.io.println",
                "(...args) -> ()", native_println),
        ]
    }
}

// Реализация файлового IO libuv
fn native_read_file(args: &[RuntimeValue], ctx: &mut NativeContext) -> Result<RuntimeValue, ExecutorError> {
    let path = extract_file_path(args)?;

    // Отправка в цикл событий libuv
    // libuv асинхронное чтение файла
    // Возврат результата
    ctx.uv_loop.fs_read(path)
}

2.2 HttpUrl → сетевой IO libuv

rust
// Модуль std.net (на основе libuv)
pub struct NetModule;

impl StdModule for NetModule {
    fn exports(&self) -> Vec<NativeExport> {
        vec![
            // HTTP-операции → 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),
        ]
    }
}

// Реализация сетевого IO libuv
fn native_http_get(args: &[RuntimeValue], ctx: &mut NativeContext) -> Result<RuntimeValue, ExecutorError> {
    let url = extract_http_url(args)?;

    // Отправка в цикл событий libuv
    // libuv асинхронный HTTP-запрос
    // Возврат результата
    ctx.uv_loop.http_get(url)
}

2.3 DBUrl → пул подключений к БД

rust
// Модуль std.db (на основе libuv)
pub struct DbModule;

impl StdModule for DbModule {
    fn exports(&self) -> Vec<NativeExport> {
        vec![
            // Операции с БД → пул потоков libuv
            NativeExport::new("query", "std.db.query",
                "(url: DBUrl, sql: String) -> Rows", native_query),
        ]
    }
}

// Реализация IO БД libuv
fn native_query(args: &[RuntimeValue], ctx: &mut NativeContext) -> Result<RuntimeValue, ExecutorError> {
    let url = extract_db_url(args)?;
    let sql = extract_sql(args)?;

    // Отправка в пул потоков libuv
    // Запрос к БД выполняется в пуле потоков
    // После завершения колбек уведомляет главный поток
    ctx.uv_loop.db_query(url, sql)
}

2.4 Console → сериализация стандартного вывода

rust
// Операции Console автоматически сериализуются (правила системы типов ресурсов RFC-024)
// Все операции Console выполняются последовательно в одном потоке
fn native_print(args: &[RuntimeValue], ctx: &mut NativeContext) -> Result<RuntimeValue, ExecutorError> {
    let output = format_args(args);

    // Сериализация операций Console
    // libuv tty запись
    ctx.uv_loop.tty_write(output)
}

3. Интеграция с блоками spawn

3.1 С точки зрения пользователя

yaoxiang
# Определения типов ресурсов (RFC-024)
FilePath: Resource
HttpUrl: Resource

# Операции IO (реализация RFC-002)
File.read: (FilePath) -> String
HTTP.get: (HttpUrl) -> Response

# Явный параллелизм пользователя (RFC-024)
(a, b) = spawn {
    read_file("data.txt"),      # Тип ресурса FilePath, низкий уровень libuv
    fetch("http://example.com") # Тип ресурса HttpUrl, низкий уровень libuv
}
# Компилятор: FilePath и HttpUrl не конфликтуют, можно выполнять параллельно

3.2 Компиляционный анализ

Компилятор анализирует блок spawn:
1. Идентификация операций с типами ресурсов
2. Обнаружение конфликтов ресурсов (одинаковый путь/URL автоматически сериализуются)
3. Генерация DAG плана выполнения
4. Маркировка IO-узлов (отправка в libuv)

3.3 Выполнение в runtime

Runtime выполняет блок spawn:
1. Worker 0 отправляет IO-задачу → общий цикл событий
2. Worker 1 отправляет IO-задачу → общий цикл событий
3. Цикл событий унифицированно обрабатывает все IO-операции
4. После завершения IO уведомляется соответствующий Worker
5. Worker продолжает выполнение последующих задач

4. Трёхуровневая архитектура Runtime и libuv

УровеньИспользование libuvАсинхронностьСценарии использования
Embedded RuntimeБез libuvБез асинхронностиWASM, игровые скрипты
Standard RuntimeОбщий цикл событийАсинхронный IOWeb-сервисы, конвейеры данных
Full RuntimeОбщий цикл событийАсинхронный IO + параллелизмНаучные вычисления, крупномасштабный параллелизм

Embedded Runtime: без libuv, синхронное выполнение, без асинхронных возможностей.

Standard Runtime: общий цикл событий libuv, все операции IO обрабатываются асинхронно.

Full Runtime: общий цикл событий libuv, многопоточный параллелизм + асинхронный IO.


Детальное проектирование

1. Структура привязок Rust

rust
// Модуль привязок libuv
pub mod uv {
    // Цикл событий
    pub struct UvLoop {
        loop_handle: *mut uv_loop_t,
    }

    // Операции с файлами
    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>;
    }

    // Сетевые операции
    pub trait NetOps {
        fn http_get(&self, url: &str) -> Result<Response, UvError>;
        fn http_post(&self, url: &str, body: &str) -> Result<Response, UvError>;
    }

    // Операции с БД
    pub trait DbOps {
        fn db_query(&self, url: &str, sql: &str) -> Result<Rows, UvError>;
    }

    // Операции Console
    pub trait ConsoleOps {
        fn tty_write(&self, data: &str) -> Result<(), UvError>;
    }
}

2. Структура модулей стандартной библиотеки

src/std/
├── io.rs          # FilePath IO (на основе libuv)
├── net.rs         # HttpUrl IO (на основе libuv)
├── db.rs          # DBUrl IO (на основе libuv)
├── console.rs     # Console IO (на основе libuv)
└── mod.rs         # Регистрация модулей

3. Интеграция с DAG-планировщиком

rust
// Интерфейс IO-узла (определён в RFC-008)
trait IoScheduler {
    // Отправка IO-задачи, возврат хендла
    fn submit_io(&self, task: IoTask) -> IoHandle;

    // Вызывается libuv при завершении IO, пробуждает DAG-узел
    fn on_io_complete(&self, handle: IoHandle);
}

// Реализация через libuv
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) {
        // Уведомление DAG-планировщика о пробуждении зависимых узлов
        self.dag_scheduler.wake_dependents(handle.node_id);
    }
}

Компромиссы

Преимущества

  1. Единообразие кроссплатформенности: libuv обрабатывает различия Windows/Linux/macOS
  2. Асинхронные возможности IO: общий цикл событий обрабатывает весь IO, не нужен async/await
  3. Безопасность параллелизма: однопоточный цикл событий естественно без гонок
  4. Эффективность использования ресурсов: один цикл событий, низкие затраты памяти
  5. Соответствие RFC-024: система типов ресурсов обеспечивает безопасность параллелизма
  6. Зрелость и стабильность: libuv проверен в Node.js при масштабном использовании

Недостатки

  1. Зависимость от C-библиотеки: требуется привязка к C-библиотеке libuv
  2. Ограничения самозагрузки: после самозагрузки может потребоваться замена на нативную реализацию YaoXiang
  3. Поддержка WASM: требуется дополнительная адаптация

Альтернативные решения

РешениеПочему не выбрано
Rust std::ioСинхронное блокирование, невозможно配合 spawn блоков для асинхронности
tokioСпроектирован для Rust async/await, не соответствует модели явного параллелизма YaoXiang
mioПредоставляет только низкоуровневые асинхронные примитивы, отсутствуют высокоуровневые функции IO
Реализация с нуляСложно и подвержено ошибкам, невозможно сравниться со зрелостью libuv

Стратегия реализации

Фазы

  1. Фаза 1 (v0.3): привязки libuv, базовый файловый IO
  2. Фаза 2 (v0.5): сетевой IO, поддержка HTTP
  3. Фаза 3 (v0.7): IO БД, пул подключений
  4. Фаза 4 (v1.0): адаптация WASM, оптимизация производительности

Зависимости

  • RFC-024 (модель параллелизма) → Завершено
  • RFC-008 (архитектура Runtime) → Завершено
  • RFC-009 (модель владения) → Завершено
  • RFC-011 (система generics) → Завершено

Журнал архитектурных решений

РешениеРешениеПричинаДата
Уровень реализации IOlibuvКроссплатформенность, асинхронность, безопасность параллелизма2025-01-05
НазначениеУровень реализации IO типов ресурсовИнтеграция с системой типов ресурсов RFC-0242026-06-16
Архитектура цикла событийОбщий цикл событийВысокая эффективность ресурсов, избежание повторного создания2026-06-16
Безопасность параллелизмаОднопоточный цикл событийЕстественное отсутствие гонок, соответствие RFC-0242026-06-16
Переработка стандартной библиотекиstd.io/std.net на основе libuvЕдинообразие кроссплатформенности, асинхронные возможности2026-06-16

Открытые вопросы

  • [ ] Схема адаптации libuv в среде WASM
  • [ ] Проектирование пула подключений к БД
  • [ ] Полная реализация HTTP-клиента
  • [ ] Кроссплатформенная согласованность событий файловой системы
  • [ ] Проектирование механизма тайм-аута сетевого IO
  • [ ] Стратегия замены libuv после самозагрузки

Список литературы

Официальная документация YaoXiang

Внешние источники


Жизненный цикл и судьба

СтатусРасположениеОписание
Черновикdocs/design/rfc/draft/На повторном рассмотрении