# cordis-c **Repository Path**: mengdj/cordis-c ## Basic Information - **Project Name**: cordis-c - **Description**: No description available - **Primary Language**: C++ - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-10-12 - **Last Updated**: 2026-10-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # cordis-c Cordis 核心运行时的 C++20 实现,用于组合具有依赖关系和确定生命周期的插件。 核心能力: - `Context`:插件、服务、副作用和事件的统一入口 - `Fiber`:独立的插件运行实例与生命周期单元 - `EffectScope`:自动记录资源,并在卸载时逆序清理 - 类型化服务:`provide()` / `require()` - 响应式依赖:服务出现时加载 Consumer,消失或替换时自动卸载并重载 - Isolation Domain:同类型服务可以在不同上下文中相互隔离 - 类型事件:监听器自动归属 Fiber,卸载时自动解绑 - 跨线程调度:所有运行时状态由单一调度线程串行修改 - 同步和异步关闭:`dispose()` / `dispose_async()` / `shutdown()` / `shutdown_async()` ## 环境要求 - CMake 3.20 或更高版本 - 支持 C++20 的编译器 - Visual Studio 2022 / MSVC - GCC 10+ - Clang 12+ - 线程库,由 CMake 的 `Threads::Threads` 自动配置 ## 构建与测试 ### 通用 CMake ```bash cmake -S . -B build cmake --build build ctest --test-dir build --output-on-failure ``` ### Visual Studio ```bash cmake -S . -B build -G "Visual Studio 17 2022" -A x64 cmake --build build --config Release ctest --test-dir build -C Release --output-on-failure ``` 在其他 CMake 项目中使用: ```cmake add_subdirectory(path/to/cordis-c) target_link_libraries(your_target PRIVATE cordis) ``` 源文件中包含: ```cpp #include ``` ## 快速开始 ```cpp #include #include #include struct Database { void query() const { std::cout << "query\n"; } }; int main() { cordis::Runtime runtime; auto root = runtime.root(); auto provider = root.plugin([](cordis::Context ctx) { ctx.provide(std::make_shared()); }); auto consumer = root.plugin( {cordis::service_key()}, [](cordis::Context ctx) { ctx.require()->query(); }); runtime.shutdown(); } ``` 每个非根插件只能访问自己显式声明的服务依赖。下面的调用会被拒绝: ```cpp root.plugin([](cordis::Context ctx) { ctx.require(); // 未在 required 列表中声明 }); ``` ## 插件与 Fiber `Context::plugin()` 创建一个 Fiber,并立即返回 `FiberHandle`: ```cpp auto fiber = root.plugin([](cordis::Context ctx) { // 插件启动逻辑 }); if (fiber.state() == cordis::FiberState::active) { // 插件已启动 } ``` Fiber 可能处于以下状态: ```text pending -> loading -> active -> failed active -> unloading -> pending active -> unloading -> disposed ``` - `pending`:依赖尚未就绪 - `loading`:正在运行插件启动函数 - `active`:插件已启动 - `failed`:启动函数抛出异常 - `unloading`:正在清理资源 - `disposed`:永久卸载,不再重新启动 插件启动失败可通过句柄读取: ```cpp if (fiber.state() == cordis::FiberState::failed) { auto error = fiber.failure(); } ``` 父 Fiber 卸载时,其子 Fiber 会按逆创建顺序自动卸载。 ## 副作用与自动清理 使用 `ctx.effect()` 注册需要撤销的资源: ```cpp ctx.effect([] { auto resource = acquire_resource(); return [resource] { release_resource(resource); }; }); ``` Fiber 卸载时: 1. 子 Fiber 按逆创建顺序卸载 2. effects 按逆注册顺序清理 3. 单个 disposer 抛出异常时,剩余 disposer 仍会继续执行 4. 清理异常收集在 `DisposeReport::errors` 中 5. 同一个 disposer 最多执行一次 ```cpp auto report = fiber.dispose(); if (!report.success()) { for (const auto& error : report.errors) { try { std::rethrow_exception(error); } catch (const std::exception& exception) { std::cerr << exception.what() << '\n'; } } } ``` 注册监听器、服务和子插件也会自动绑定到当前 Fiber,无需再手动添加对应 disposer。 ## 响应式依赖 Consumer 在依赖缺失时保持 `pending`: ```cpp auto consumer = root.plugin( {cordis::service_key()}, [](cordis::Context ctx) { auto database = ctx.require(); }); ``` 随后注册 Provider: ```cpp auto provider = root.plugin([](cordis::Context ctx) { ctx.provide(std::make_shared()); }); ``` 此时 Consumer 自动启动。Provider 被卸载后: ```cpp provider.dispose(); ``` 运行时会: 1. 将服务标记为不可用 2. 卸载依赖该服务的 Consumer 3. 清理 Consumer 的 effects 4. 清理 Provider 5. 将 Consumer 保持在 `pending` 以后出现新的 `Database` Provider 时,Consumer 会使用新的 Provider identity 自动重载。 同一个 isolation domain 中,同类型服务只能有一个 Provider。 ## Isolation Domain ### 匿名隔离域 每次无参数调用都会创建一个新域: ```cpp auto isolated = root.isolate(); isolated.plugin([](cordis::Context ctx) { ctx.provide(std::make_shared()); }); ``` 默认 Context 中的 `Database` 与该隔离域中的 `Database` 互不影响。 ### 命名共享域 相同 Runtime、相同服务类型和相同名称会映射到同一个域: ```cpp auto tenant_provider = root.isolate("tenant-a"); auto tenant_consumer = root.isolate("tenant-a"); auto another_tenant = root.isolate("tenant-b"); ``` `tenant_provider` 与 `tenant_consumer` 共享 `Database`,`another_tenant` 与它们隔离。 隔离是按类型设置的,不会影响其他服务: ```cpp auto context = root.isolate("tenant-a"); // Database 使用 tenant-a 域 // Logger 等其他类型仍使用原来的域 ``` 从隔离 Context 创建的子插件会继承域映射。 ## 类型事件 定义普通事件类型: ```cpp struct UserCreated { int user_id; }; ``` 监听事件: ```cpp auto listener = root.plugin([](cordis::Context ctx) { ctx.on([](const UserCreated& event) { std::cout << event.user_id << '\n'; }); }); ``` 发送事件: ```cpp root.emit(UserCreated{42}); ``` 监听器会自动绑定到注册它的 Fiber: ```cpp listener.dispose(); root.emit(UserCreated{43}); // 已卸载的 listener 不会收到事件 ``` 事件回调由 Runtime 调度线程串行执行。回调中可以继续调用 `plugin()`、`provide()`、`effect()`、`on()` 或 `emit()`;调度线程内的嵌套调用会直接执行,不会自锁。 事件对象只在 `emit()` 调用期间有效,监听器若需延后使用,应自行复制数据。 ## 同步与异步卸载 同步卸载: ```cpp auto report = fiber.dispose(); auto shutdown_report = runtime.shutdown(); ``` 异步卸载: ```cpp auto disposing = fiber.dispose_async(); // 调用线程可以继续处理其他工作 const auto report = disposing.get(); const auto shutdown_report = runtime.shutdown_async().get(); ``` `dispose()` 和 `shutdown()` 可以重复调用。资源只会清理一次。 `dispose_async()` 与 `shutdown_async()` 返回标准 `std::future`。当前没有提供 coroutine `Task` 或 `co_await std::future` 适配器。 ## 跨线程调用 `Context`、`FiberHandle` 和 `Runtime` 可以从不同线程调用。运行时会把状态操作投递到单一调度线程,保证: - Registry 不被并发修改 - 同一个 Fiber 的加载和卸载不会并行 - 多线程同时调用 `dispose()` 时只清理一次 - 插件和事件回调在同一个调度线程中执行 用户回调不应长时间阻塞,否则会阻塞同一 Runtime 中的其他生命周期操作。后台任务应由 effect 创建,并在 disposer 中停止和等待: ```cpp #include ctx.effect([] { auto worker = std::make_shared([](std::stop_token stop) { while (!stop.stop_requested()) { // 后台工作 } }); return [worker] { worker->request_stop(); worker->join(); }; }); ``` ## 完整示例 ```cpp #include #include #include struct Database { int version; }; struct DatabaseReady { int version; }; int main() { cordis::Runtime runtime; auto root = runtime.root(); auto provider_context = root.isolate("main"); auto consumer_context = root.isolate("main"); auto consumer = consumer_context.plugin( {cordis::service_key()}, [](cordis::Context ctx) { auto database = ctx.require(); ctx.on([](const DatabaseReady& event) { std::cout << "database version: " << event.version << '\n'; }); ctx.effect([database] { std::cout << "consumer started\n"; return [] { std::cout << "consumer stopped\n"; }; }); }); auto provider = provider_context.plugin([](cordis::Context ctx) { ctx.provide(std::make_shared(Database{1})); }); root.emit(DatabaseReady{1}); provider.dispose(); // consumer 自动卸载并回到 pending provider = provider_context.plugin([](cordis::Context ctx) { ctx.provide(std::make_shared(Database{2})); }); // consumer 自动使用新 Provider 重载 const auto report = runtime.shutdown(); return report.success() ? 0 : 1; } ``` ## 当前边界 当前实现有意保持最小范围: - 不包含 coroutine executor 或 `Task` - 不包含事件优先级和异步事件传播 - 不包含跨 Runtime 的服务桥接 - 不包含分布式或持久化 Registry 需要这些能力时,应优先接入项目已有的 executor、I/O loop 或消息系统,而不是在核心运行时中重复实现。