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-эндпоинт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![
// Файловые операции → API libuv fs_*
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 → API libuv tty
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-операции → API libuv http
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
// Запись в tty через libuv
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 | Общий цикл событий | Асинхронный IO | Веб-сервисы, конвейеры данных |
| 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 # IO для FilePath (на базе libuv)
├── net.rs # IO для HttpUrl (на базе libuv)
├── db.rs # IO для DBUrl (на базе libuv)
├── console.rs # IO для Console (на базе 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);
}
}Компромиссы
Преимущества
- Кроссплатформенная унификация: libuv обрабатывает различия Windows/Linux/macOS
- Асинхронный IO: общий цикл событий обрабатывает все IO без необходимости в async/await
- Безопасность параллелизма: однопоточный цикл событий естественным образом исключает гонки
- Эффективность по ресурсам: один цикл событий, малые накладные расходы памяти
- Соответствие RFC-024: система типов ресурсов обеспечивает безопасность параллелизма
- Зрелость и стабильность: libuv проверен масштабным использованием в Node.js
Недостатки
- Зависимость от C-библиотеки: требуется привязка к C-библиотеке libuv
- Ограничения самозагрузки: после самозагрузки может потребоваться замена на нативную реализацию YaoXiang
- Поддержка WASM: требуется дополнительная работа по адаптации
Альтернативные варианты
| Вариант | Почему не выбран |
|---|---|
| Rust std::io | Синхронный блокирующий, несовместим со spawn-блоками для асинхронности |
| tokio | Спроектирован для Rust async/await, не соответствует модели явного параллелизма YaoXiang |
| mio | Предоставляет только низкоуровневые асинхронные примитивы, без высокоуровневого IO |
| Реализация с нуля | Сложно и подвержено ошибкам, не сравнимо со зрелостью libuv |
Стратегия реализации
Этапы
- Этап 1 (v0.3): привязки libuv, базовый файловый IO
- Этап 2 (v0.5): сетевой IO, поддержка HTTP
- Этап 3 (v0.7): IO БД, пул соединений
- Этап 4 (v1.0): адаптация для WASM, оптимизация производительности
Зависимости
- RFC-024 (модель параллелизма) → завершён
- RFC-008 (архитектура Runtime) → завершён
- RFC-009 (модель владения) → завершён
- RFC-011 (система обобщений) → завершён
Журнал проектных решений
| Решение | Выбор | Причина | Дата |
|---|---|---|---|
| Слой реализации IO | libuv | Кроссплатформенность, асинхронность, безопасность параллелизма | 2025-01-05 |
| Позиционирование | Слой реализации IO для типов ресурсов | Интеграция с системой типов ресурсов RFC-024 | 2026-06-16 |
| Архитектура цикла событий | Общий цикл событий | Высокая эффективность, избежание дублирования | 2026-06-16 |
| Безопасность параллелизма | Однопоточный цикл событий | Естественное отсутствие гонок, соответствие RFC-024 | 2026-06-16 |
| Переписывание стандартной библиотеки | std.io/std.net на базе libuv | Кроссплатформенная унификация, асинхронность | 2026-06-16 |
Открытые вопросы
- [ ] Схема адаптации libuv для окружения WASM
- [ ] Проектирование пула соединений с БД
- [ ] Полная реализация HTTP-клиента
- [ ] Кроссплатформенная согласованность событий файловой системы
- [ ] Проектирование механизма тайм-аутов для сетевого IO
- [ ] Стратегия замены libuv после самозагрузки
Список литературы
Официальная документация YaoXiang
- RFC-024 Модель параллелизма
- RFC-008 Архитектура Runtime
- RFC-009 Модель владения
- Спецификация модели параллелизма
Внешние ссылки
Жизненный цикл и расположение
| Состояние | Расположение | Описание |
|---|---|---|
| Черновик | docs/design/rfc/draft/ | На повторном рассмотрении |
