Skip to content

Repository files navigation

nexec

https://github.com/wkcs/nexec

nexec 是一个 C++20 异步执行框架,基于 sender/receiver 模型,并提供与 C++20 协程的桥接。概念上对齐 P2300 (std::execution) 的一部分设计,覆盖常用适配器与调度能力,适合在生产或学习场景中组合异步任务。

接入方式

1. CMake find_package(安装后 / vcpkg)

find_package(nexec CONFIG REQUIRED)
target_link_libraries(your_target PRIVATE nexec::nexec)

源码安装示例:

cmake -S . -B build -DNEXEC_BUILD_TESTS=OFF -DNEXEC_BUILD_EXAMPLES=OFF
cmake --build build
cmake --install build --prefix /path/to/prefix
# 消费方: -DCMAKE_PREFIX_PATH=/path/to/prefix

2. FetchContent / add_subdirectory

include(FetchContent)
FetchContent_Declare(
  nexec
  GIT_REPOSITORY https://github.com/wkcs/nexec.git
  GIT_TAG        v1.0.0   # 或某个 commit
)
FetchContent_MakeAvailable(nexec)

target_link_libraries(your_target PRIVATE nexec::nexec)

或:

add_subdirectory(third_party/nexec)
target_link_libraries(your_target PRIVATE nexec::nexec)

作为子项目引入时,默认不编译 examples / tests。

3. vcpkg(仓库内 overlay port)

本仓库提供 overlay port,可在未合入官方 registry 前本地安装:

# 在任意使用 vcpkg 的工程中
vcpkg install nexec --overlay-ports=/path/to/nexec/ports

# 或在工程根目录放置 / 引用本仓库的 vcpkg-configuration.json:
# { "overlay-ports": ["/path/to/nexec/ports"] }

然后:

find_package(nexec CONFIG REQUIRED)
target_link_libraries(your_target PRIVATE nexec::nexec)

可选 feature:nexec[uring](强制 Linux io_uring 后端,依赖 liburing)。

头文件用法:

#include <nexec/execution.hpp>

核心概念

┌────────────┐     Connect     ┌──────────────────┐     Start     ┌──────────────┐
│   Sender   │ ─────────────→ │  OperationState  │ ────────────→ │   Receiver   │
│ (描述任务) │                │ (执行中的任务)    │               │ (接收结果)   │
└────────────┘                └──────────────────┘               └──────────────┘
                                                                   ↓ SetValue
                                                                   ↓ SetError
                                                                   ↓ SetStopped
  • Sender(发送器):描述一个异步操作,但不立即执行
  • Receiver(接收器):接收异步操作的完成信号(值/错误/停止)
  • OperationState(操作状态):Connect(sender, receiver) 的结果,调用 Start() 后开始执行

生命周期与停止契约

  • 调用 Start(op) 后,调用方必须让 OperationState、receiver 和其捕获的引用一直存活到收到一次完成信号。SyncWait 会在等待期间持有 operation state;手写 Connect/Start 时由调用方负责。
  • 线程池 scheduler 是非拥有句柄。持有它的线程池必须在所有使用该 scheduler 的 sender、operation state 和后台任务完成后才析构。
  • SyncWait(sender) 的语义为:成功时返回有值的 std::optional;sender 以 stopped 完成时返回空 std::optional;错误会重新抛出。
  • AsyncScope 析构不会 join 任务。若后续要销毁任务所依赖的 scheduler 或外部资源,请先 SyncWait(scope.OnEmpty()),或在协程中 co_await scope.OnEmpty()Spawn 未处理的 set_errorstd::terminate()
  • AsyncMutex 的等待队列当前不支持取消:等待 LockLockScoped 的协程会一直等待到获得锁。
  • StartDetached 不会延长用户捕获对象或 scheduler 的寿命;后台任务所引用的资源必须自行保持有效。未处理的 set_errorstd::terminate()(与 P2300 一致);需吞错时请先 LetError
  • 线程池 Enqueue 在池已停止时返回失败;库内所有完成/唤醒路径必须兜底(同步执行或同步 resume),禁止静默丢弃回调。
  • split 多播要求完成值可拷贝;Task/多数 operation state 在 Start 后不可移动。

文档

详细的 Wiki 文档位于 docs/wiki/,包含核心概念、全部组件用法、线程池、协程、同步原语、I/O 子系统、构建配置、C++20 Modules 与测试覆盖率等主题。设计文档见 docs/design_document.md

项目结构

nexec/
├── CMakeLists.txt                      # CMake 构建与安装导出
├── LICENSE                             # MIT
├── ports/nexec/                        # vcpkg overlay port
├── include/nexec/
│   ├── core.hpp                        # 核心类型和概念定义
│   ├── just.hpp                        # Just/JustError/JustStopped 发送器工厂
│   ├── then.hpp                        # Then 发送器适配器
│   ├── when_all.hpp                    # WhenAll 并行组合器
│   ├── sync_wait.hpp                   # SyncWait 同步等待
│   ├── thread_pool.hpp                 # 线程池调度器
│   ├── async_scope.hpp                 # 后台任务作用域及 OnEmpty 屏障
│   ├── mutex.hpp / semaphore.hpp       # 异步互斥锁与信号量
│   ├── coroutine.hpp                   # C++20 协程集成
│   └── execution.hpp                   # 主头文件(包含所有组件)
├── modules/
│   └── nexec.cppm                      # C++20 named module(import nexec)
└── examples/
    ├── basic.cpp
    ├── pipeline.cpp
    ├── thread_pool.cpp
    ├── when_all.cpp
    ├── coroutine.cpp
    ├── adapters_demo.cpp
    ├── production_demo.cpp
    └── module_basic.cpp                # 需 NEXEC_ENABLE_MODULES

编译和运行

# 需要支持 C++20 与协程的编译器(Clang 14+, GCC 12+, MSVC 2022+)
cmake -S . -B build
cmake --build build

# 运行示例(顶层构建默认开启 NEXEC_BUILD_EXAMPLES)
./build/example_basic
./build/example_pipeline
./build/example_thread_pool
./build/example_when_all
./build/example_coroutine
./build/example_adapters_demo
./build/example_production_demo

常用选项:

选项 默认(顶层) 说明
NEXEC_BUILD_EXAMPLES ON 编译 examples
NEXEC_BUILD_TESTS ON 编译单元测试
NEXEC_IO_BACKEND auto auto / thread_pool / uring
NEXEC_ENABLE_MODULES OFF C++20 modules(需 CMake ≥ 3.28 + Ninja)

运行测试

cmake -B build -DNEXEC_BUILD_TESTS=ON
cmake --build build --target nexec_tests
ctest --test-dir build --output-on-failure

测试框架为 GoogleTest(CMake FetchContent 拉取 v1.14.0)。可通过 -DNEXEC_BUILD_TESTS=OFF 跳过。 默认且受支持的测试路径是头文件模式(NEXEC_ENABLE_MODULES=OFF)。 在 GCC 16/libstdc++ 16 下,同时启用 NEXEC_ENABLE_MODULES=ON 与 GoogleTest 的 test_module_import 当前会因标准库模块与头文件声明冲突而构建失败;这不是 nexec 头文件模式的限制。需要测试 modules + GoogleTest 时请优先使用 Clang。

覆盖率

需要本机安装 gcovr。对 include/nexec 行覆盖率执行 100% 门禁;未启用 io_uring 时排除 uring_backend.hpp

cmake -B build-cov -DNEXEC_BUILD_TESTS=ON -DNEXEC_ENABLE_COVERAGE=ON \
  -DCMAKE_BUILD_TYPE=Debug -DNEXEC_IO_BACKEND=thread_pool
cmake --build build-cov --target coverage

报告目录:build-cov/coverage/(HTML)。生成脚本使用 gcovr --merge-lines,用于合并头文件-only 模板行的计数,避免重复统计导致门槛误判。

C++ Modules(可选)

默认仍使用头文件:#include <nexec/execution.hpp>

若需要 import nexec;,需 CMake ≥ 3.28Ninja 生成器-G Ninja),并建议使用 Clang 16+-DCMAKE_CXX_COMPILER=clang++)。开启方式:

cmake -S . -B build-modules -G Ninja \
  -DNEXEC_ENABLE_MODULES=ON -DCMAKE_CXX_COMPILER=clang++
cmake --build build-modules
./build-modules/example_module_basic
ctest --test-dir build-modules -R test_module_import --output-on-failure

要求:Clang 16+(首版以 Clang 验证;不保证 MSVC)。GCC 14+ 可用于常规 头文件构建,但 GCC 16/libstdc++ 16 的 modules + GoogleTest 组合存在上文所述 冲突,当前不属于受支持的测试路径。

import nexec;

auto result = nexec::SyncWait(nexec::Just(42));

同一翻译单元内请只选一种引入方式:import nexec; #include <nexec/execution.hpp>,不要混用。

对外 cmake --install / vcpkg 打包以头文件 INTERFACE 库为准;modules 模式不作为安装主路径。

快速上手

1. Just + SyncWait

#include <nexec/execution.hpp>

auto sender = nexec::Just(42);
auto result = nexec::SyncWait(std::move(sender));
auto [value] = result.value();  // value == 42

2. Just + Then 管道变换

auto sender = nexec::Just(21)
            | nexec::Then([](int x) { return x * 2; });

auto result = nexec::SyncWait(std::move(sender));
auto [value] = result.value();  // value == 42

3. C++20 协程 (co_await)

#include <nexec/execution.hpp>

nexec::Task<int> GetValueAsync() {
  int val = co_await (
      nexec::Just(20)
      | nexec::Then([](int x) { return x * 2; })
  );
  co_return val + 2;
}

4. 线程池与协程:跨线程切换

nexec::Task<void> WorkFlow(nexec::ThreadPoolScheduler scheduler) {
  co_await scheduler.Schedule();
  std::cout << "当前执行线程: " << std::this_thread::get_id() << std::endl;
}

协程模块设计细节 (coroutine.hpp)

  • SenderAwaiter<Sender>:通过实现 await_readyawait_suspendawait_resume,将底层协程句柄包装为 Receiver 送入发送器,使得发送器支持 co_await
  • 强制复制消除技术:由于底层异步操作状态 OperationState 不可移动,SenderAwaiter 内部使用 OpStateType* 并在堆上通过 new 进行原位构造,通过 C++17 的 Guaranteed Copy Elision 完美避开了移动构造被删除的限制。
  • Task<T>:实现为一个标准的 C++20 协程承诺类(Promise Concept),在最终挂起阶段 (final_suspend) 自主判定:若作为 Sender 连接,则通知 downstream 接收器;若作为普通协程,则顺延恢复父协程帧,保证其双重身份。

变更履历

版本变更见 CHANGELOG.md

许可证

本项目采用 MIT License,Copyright (c) 2026 Nick Huhuqihan@live.coms。

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages