Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
208 changes: 143 additions & 65 deletions .agent/HANDOFF.md

Large diffs are not rendered by default.

42 changes: 28 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
> CMP provides a lazy, single-consumer `Task<T>` / `Task<void>`, structured variadic and vector
> `when_all()`, an eager structured `TaskGroup`, one-shot and reusable events, an RAII `AsyncMutex`,
> a caller-thread `RunLoop` with explicit and monotonic timed scheduling, and a fixed-size CPU
> `ThreadPool`. Phase 6B adds an explicit `IoContext` and move-only `TcpStream` for native async
> numeric-address TCP clients. `run_blocking()` executes an owned synchronous callable on a
> dedicated pool instance
> `ThreadPool`. Phase 6B/6C add an explicit `IoContext`, move-only `TcpStream`, and move-only
> `TcpListener` for native async numeric-address TCP clients and servers. `run_blocking()` executes
> an owned synchronous callable on a dedicated pool instance
> and delivers its outcome through an explicit return Scheduler. Ready scheduling on either
> executor, timed waits, reusable-event waits, TaskGroup children, and queued blocking offloads can
> use explicit cooperative cancellation with `std::stop_token`; TCP operations use the same token
Expand All @@ -32,8 +32,8 @@
CMP is being built as a modern coroutine runtime and library on standard stackless C++
coroutines. Its explicit `co_await` model now covers fixed and incremental structured concurrency,
one-time event notification, caller-thread and multi-worker scheduling, monotonic timers, and
cancellable waits, structured isolation of blocking work, and one portable native async TCP client
slice.
cancellable waits, structured isolation of blocking work, and portable native async TCP client and
listener slices.

## Why CMP?

Expand Down Expand Up @@ -61,8 +61,8 @@ promise that:
- task migration is implicit, work stealing is already enabled, or every I/O family is async.

Those capabilities must be designed and verified individually. CMP now uses an explicitly
dedicated `ThreadPool` instance with `run_blocking()` for synchronous work. Phase 6B provides native
async TCP clients; DNS, listening sockets, TLS, file I/O, and cooperative safe points remain
dedicated `ThreadPool` instance with `run_blocking()` for synchronous work. Phase 6B/6C provide
native async TCP clients and listeners; DNS, TLS, file I/O, and cooperative safe points remain
separate work.

## Quick Start
Expand Down Expand Up @@ -103,6 +103,7 @@ using mcpplibs::cmp::OneShotEvent;
using mcpplibs::cmp::TaskGroup;
using mcpplibs::cmp::ThreadPool;
using mcpplibs::cmp::IoContext;
using mcpplibs::cmp::TcpListener;
using mcpplibs::cmp::TcpStream;
using mcpplibs::cmp::run_blocking;
using mcpplibs::cmp::when_all;
Expand Down Expand Up @@ -343,6 +344,16 @@ Task<std::size_t> exchange(
co_await stream.write_all(caller, request, token);
co_return co_await stream.read_some(caller, reply, token);
}

Task<TcpStream> accept_one(
IoContext& io,
RunLoop::Scheduler caller,
std::uint16_t port,
std::stop_token token = {}) {
auto listener = co_await TcpListener::bind(
io, caller, "127.0.0.1", port, token);
co_return co_await listener.accept(caller, token);
}
```

Share one immovable `IoContext` across streams; it owns one private I/O driver. `TcpStream` is
Expand All @@ -351,9 +362,12 @@ Read/write spans borrow their storage until the returned Task completes. Every s
attempts the explicit return-Scheduler hop. One read and one write may coexist; another operation
in the same direction throws `std::logic_error`. Pre-cancellation leaves an open stream usable,
while cancellation after `write_all()` starts closes it. `close()` is thread-safe, idempotent, and
causes accepted operations to complete once with `OperationCancelled` when close wins. Phase 6A
isolates arbitrary synchronous calls on ThreadPool workers; Phase 6B is native async TCP, not a
generic async-I/O layer.
causes accepted operations to complete once with `OperationCancelled` when close wins.
`TcpListener::bind()` performs numeric bind-and-listen, accepts port zero, and exposes the selected
port through `local_port()`. One accept may be pending; pre/active accept cancellation leaves the
listener usable, while close cancels the pending accept. Accepted streams are independent from the
listener. Phase 6A isolates arbitrary synchronous calls on ThreadPool workers; Phase 6B/6C are
native async TCP, not a generic async-I/O layer.

RunLoop is not a background thread and does not make blocking code asynchronous. A Task that
suspends without arranging a future resume can leave `run()` waiting indefinitely. CMP does not
Expand All @@ -372,7 +386,7 @@ await the desired Scheduler to return to its RunLoop.
├── src/run_loop.cppm # RunLoop and Scheduler partition
├── src/thread_pool.cppm # fixed-size CPU worker scheduler
├── src/blocking.cppm # structured blocking-call offload
├── src/tcp.cppm # native async TCP client and I/O context
├── src/tcp.cppm # native async TCP client/server and I/O context
├── src/when_all.cppm # structured concurrent Task join
├── src/task_group.cppm # eager mutable structured Task scope
├── src/one_shot_event.cppm # allocation-free one-time notification
Expand Down Expand Up @@ -416,7 +430,7 @@ global mcpp installation.

CMP does not track `mcpp.lock`; `.gitignore` enforces that repository policy. Runtime dependencies
belong in `[dependencies]`; gtest is declared explicitly under `[dev-dependencies.compat]`.
The current local suite contains 140 tests across ten binaries. The POSIX-only Release pressure
The current local suite contains 161 tests across ten binaries. The POSIX-only Release pressure
consumer and its recorded success/failure data are documented in the
[v1 readiness benchmark](docs/benchmarks/2026-08-29-cmp-v1-readiness.md).
Multi-worker correctness and performance data are documented in the
Expand All @@ -433,8 +447,8 @@ Runtime work is split into independently reviewable phases:
OneShotEvent, AsyncManualResetEvent, and AsyncMutex — implemented and pressure-tested;
5. fixed-size multi-worker scheduling — implemented and benchmarked; work stealing remains gated
by profiling evidence;
6. structured blocking offload and native async numeric-address TCP client — implemented and
verified by Linux, macOS, and Windows CI in PR #10.
6. structured blocking offload and native async numeric-address TCP client/listener — implemented;
the client was verified by PR #10 and the listener by PR #11 on Linux, macOS, and Windows.

The remaining order is directional, not a promise that a listed feature is already implemented.

Expand Down
35 changes: 25 additions & 10 deletions README.zh.hant.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,16 @@
> CMP 已提供延遲啟動、單一消費者的 `Task<T>` / `Task<void>`、支援變參和 vector 的結構化
> `when_all()`、eager 結構化 `TaskGroup`、一次性與可複用事件、RAII `AsyncMutex`,以及在呼叫
> 執行緒運行、支援明確排程和單調時鐘定時排程的 `RunLoop`,以及固定大小的 CPU
> `ThreadPool`。Phase 6B 新增明確的 `IoContext` 和 move-only `TcpStream`,用於原生非同步的
> 數值位址 TCP client。`run_blocking()` 可在專用的 pool 實例上執行同步 callable,並透過
> `ThreadPool`。Phase 6B/6C 新增明確的 `IoContext`、move-only `TcpStream` 和 move-only
> `TcpListener`,用於原生非同步的數值位址 TCP client 與 server。`run_blocking()` 可在專用的
> pool 實例上執行同步 callable,並透過
> 明確的返回 Scheduler 交付結果。兩種執行器的就緒排程、定時等待、可複用事件等待、TaskGroup
> 子任務和排隊中的阻塞 offload 可明確使用基於 `std::stop_token` 的協作式取消;TCP 操作採用
> 相同 token 模型。detached 執行和其他原生 I/O 類型尚未實作。

CMP 計畫以標準無堆疊 C++ 協程建構現代協程執行期與函式庫。明確的 `co_await` 模型現已
涵蓋固定與增量結構化並行、一次性事件通知、呼叫執行緒與多 worker 排程、單調時鐘計時器、
可取消等待和阻塞工作的結構化隔離,以及一個可攜式原生非同步 TCP client 切片。
可取消等待和阻塞工作的結構化隔離,以及可攜式原生非同步 TCP client 與 listener 切片。

## 為什麼叫 CMP?

Expand All @@ -55,7 +56,7 @@ C++ 標準協程是語言機制,不是完整執行期。因此 CMP 不會宣
- task 會隱式遷移、work stealing 已啟用,或所有 I/O 類型都已非同步化。

這些能力必須分別設計和驗證。CMP 目前透過明確的專用 `ThreadPool` 實例和
`run_blocking()` 隔離同步工作。Phase 6B 已提供原生非同步 TCP client;DNS、監聽 socket
`run_blocking()` 隔離同步工作。Phase 6B/6C 已提供原生非同步 TCP client 與 listener;DNS、
TLS、檔案 I/O 和協作式安全點仍需分別設計。

## 快速開始
Expand Down Expand Up @@ -97,6 +98,7 @@ using mcpplibs::cmp::OneShotEvent;
using mcpplibs::cmp::TaskGroup;
using mcpplibs::cmp::ThreadPool;
using mcpplibs::cmp::IoContext;
using mcpplibs::cmp::TcpListener;
using mcpplibs::cmp::TcpStream;
using mcpplibs::cmp::run_blocking;
using mcpplibs::cmp::when_all;
Expand Down Expand Up @@ -324,15 +326,28 @@ Task<std::size_t> exchange(
co_await stream.write_all(caller, request, token);
co_return co_await stream.read_some(caller, reply, token);
}

Task<TcpStream> accept_one(
IoContext& io,
RunLoop::Scheduler caller,
std::uint16_t port,
std::stop_token token = {}) {
auto listener = co_await TcpListener::bind(
io, caller, "127.0.0.1", port, token);
co_return co_await listener.accept(caller, token);
}
```

一個不可移動的 `IoContext` 可供多個 stream 共用,並擁有一個私有 I/O driver。`TcpStream` 只能
移動;connect 只接受數值 IPv4/IPv6 文字,不會隱藏阻塞 DNS。read/write span 的底層儲存必須
保持到返回的 Task 完成。每種成功或錯誤都會嘗試明確返回 Scheduler。一個 read 和一個 write
可以並存;同方向第二個操作拋出 `std::logic_error`。預取消不會關閉仍可用的 stream,而已發起
的 `write_all()` 被取消後會關閉 stream。`close()` 執行緒安全、冪等;當 close 獲勝時,已接納
操作恰好一次以 `OperationCancelled` 完成。Phase 6A 在執行緒池上隔離任意同步呼叫;Phase 6B
只提供原生非同步 TCP,不是通用非同步 I/O 層。
操作恰好一次以 `OperationCancelled` 完成。`TcpListener::bind()` 完成數值位址的
bind-and-listen,允許連接埠零,並由 `local_port()` 返回實際連接埠。同一 listener 只允許一個
pending accept;預取消或主動取消 accept 後 listener 仍可使用,close 會取消 pending accept。
已接受 stream 與 listener 生命週期獨立。Phase 6A 在執行緒池上隔離任意同步呼叫;Phase 6B/6C
提供原生非同步 TCP,不是通用非同步 I/O 層。

RunLoop 不是背景執行緒,也不會把阻塞程式碼自動變成非同步程式碼。如果 Task 暫停後沒有
安排未來的恢復動作,`run()` 可能一直等待。CMP 不提供隱式執行緒親和:外部 awaiter 在其他
Expand All @@ -350,7 +365,7 @@ RunLoop 不是背景執行緒,也不會把阻塞程式碼自動變成非同步
├── src/run_loop.cppm # RunLoop 與 Scheduler 分割區
├── src/thread_pool.cppm # 固定大小的 CPU worker 排程器
├── src/blocking.cppm # 結構化阻塞呼叫 offload
├── src/tcp.cppm # 原生非同步 TCP client 與 I/O context
├── src/tcp.cppm # 原生非同步 TCP client/server 與 I/O context
├── src/when_all.cppm # 結構化並行 Task 匯合
├── src/task_group.cppm # eager 可變結構化 Task 作用域
├── src/one_shot_event.cppm # 無分配一次性通知
Expand Down Expand Up @@ -392,7 +407,7 @@ CI 在 Linux、macOS 和 Windows 上執行等價的建構、測試與獨立範

CMP 目前不追蹤 `mcpp.lock`,`.gitignore` 明確執行這項儲存庫約定。執行期相依放在
`[dependencies]`,gtest 明確宣告在 `[dev-dependencies.compat]` 中。
目前本機套件包含 10 個測試二進位檔、140 項測試。僅用於 POSIX 的 Release 壓測 consumer 及其
目前本機套件包含 10 個測試二進位檔、161 項測試。僅用於 POSIX 的 Release 壓測 consumer 及其
成功/失敗資料記錄在 [v1 可開發性壓測](docs/benchmarks/2026-08-29-cmp-v1-readiness.md)。
多 worker 正確性和效能資料記錄在[執行緒池壓測](docs/benchmarks/2026-08-29-cmp-thread-pool.md)。

Expand All @@ -406,8 +421,8 @@ CMP 目前不追蹤 `mcpp.lock`,`.gitignore` 明確執行這項儲存庫約定
4. 單調時鐘 Timer v1、可取消就緒/定時等待、變參/vector 匯合、靜止點 TaskGroup、
OneShotEvent、AsyncManualResetEvent 和 AsyncMutex——已實作並完成壓力驗證;
5. 固定大小的多 worker 排程——已實作並完成壓測;work stealing 仍需 profiling 證據;
6. 結構化阻塞 offload 與數值位址原生非同步 TCP client——已實作,並由 PR #10 的 Linux、
macOS 和 Windows CI 驗證。
6. 結構化阻塞 offload 與數值位址原生非同步 TCP client/listener——均已實作;client 由
PR #10、listener 由 PR #11 在 Linux、macOS 和 Windows 上完成 CI 驗證。

剩餘順序只是方向,不代表列出的能力已經實作。

Expand Down
33 changes: 24 additions & 9 deletions README.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,16 @@
> CMP 已提供懒启动、单消费者的 `Task<T>` / `Task<void>`、支持变参和 vector 的结构化
> `when_all()`、eager 结构化 `TaskGroup`、一次性与可复用事件、RAII `AsyncMutex`,以及在调用
> 线程运行、支持显式调度和单调时钟定时调度的 `RunLoop`,以及固定大小的 CPU `ThreadPool`。
> Phase 6B 新增显式 `IoContext` 和 move-only `TcpStream`,用于原生异步的数值地址 TCP client。
> Phase 6B/6C 新增显式 `IoContext`、move-only `TcpStream` 和 move-only `TcpListener`,用于原生
> 异步的数值地址 TCP client 与 server。
> `run_blocking()` 可在专用的 pool 实例上执行同步 callable,并通过显式的返回 Scheduler
> 交付结果。两种执行器的就绪调度、定时等待、可复用事件等待、TaskGroup 子任务和排队中的
> 阻塞 offload 可显式使用基于 `std::stop_token` 的协作式取消;TCP 操作采用同一 token 模型。
> detached 执行和其他原生 I/O 类型尚未实现。

CMP 计划基于标准无栈 C++ 协程构建现代协程运行时和库。显式 `co_await` 模型现已覆盖固定与
增量结构化并发、一次性事件通知、调用线程与多 worker 调度、单调时钟定时器、可取消等待和
阻塞工作的结构化隔离,以及一个可移植的原生异步 TCP client 切片。
阻塞工作的结构化隔离,以及可移植的原生异步 TCP client 与 listener 切片。

## 为什么叫 CMP?

Expand All @@ -55,7 +56,7 @@ C++ 标准协程是语言机制,不是完整运行时。因此 CMP 不会宣
- task 会隐式迁移、work stealing 已启用,或所有 I/O 类型都已异步化。

这些能力必须分别设计和验证。CMP 目前通过显式的专用 `ThreadPool` 实例和
`run_blocking()` 隔离同步工作。Phase 6B 已提供原生异步 TCP client;DNS、监听 socket、TLS、
`run_blocking()` 隔离同步工作。Phase 6B/6C 已提供原生异步 TCP client 与 listener;DNS、TLS、
文件 I/O 和协作式安全点仍需分别设计。

## 快速开始
Expand Down Expand Up @@ -97,6 +98,7 @@ using mcpplibs::cmp::OneShotEvent;
using mcpplibs::cmp::TaskGroup;
using mcpplibs::cmp::ThreadPool;
using mcpplibs::cmp::IoContext;
using mcpplibs::cmp::TcpListener;
using mcpplibs::cmp::TcpStream;
using mcpplibs::cmp::run_blocking;
using mcpplibs::cmp::when_all;
Expand Down Expand Up @@ -324,15 +326,28 @@ Task<std::size_t> exchange(
co_await stream.write_all(caller, request, token);
co_return co_await stream.read_some(caller, reply, token);
}

Task<TcpStream> accept_one(
IoContext& io,
RunLoop::Scheduler caller,
std::uint16_t port,
std::stop_token token = {}) {
auto listener = co_await TcpListener::bind(
io, caller, "127.0.0.1", port, token);
co_return co_await listener.accept(caller, token);
}
```

一个不可移动的 `IoContext` 可供多个 stream 共享,并拥有一个私有 I/O driver。`TcpStream` 只能
移动;connect 只接受数值 IPv4/IPv6 文本,不会隐藏阻塞 DNS。read/write span 的底层存储必须
保持到返回的 Task 完成。每种成功或错误都会尝试显式返回 Scheduler。一个 read 和一个 write
可以并存;同方向第二个操作抛出 `std::logic_error`。预取消不会关闭仍可用的 stream,而已发起
的 `write_all()` 被取消后会关闭 stream。`close()` 线程安全、幂等;当 close 获胜时,已接纳
操作恰好一次以 `OperationCancelled` 完成。Phase 6A 在线程池上隔离任意同步调用;Phase 6B
只提供原生异步 TCP,不是通用异步 I/O 层。
操作恰好一次以 `OperationCancelled` 完成。`TcpListener::bind()` 完成数值地址的 bind-and-listen,
允许端口零,并由 `local_port()` 返回实际端口。同一 listener 只允许一个 pending accept;预取消
或主动取消 accept 后 listener 仍可使用,close 会取消 pending accept。已接受 stream 与 listener
生命周期独立。Phase 6A 在线程池上隔离任意同步调用;Phase 6B/6C 提供原生异步 TCP,不是通用
异步 I/O 层。

RunLoop 不是后台线程,也不会把阻塞代码自动变成异步代码。如果 Task 挂起后没有安排未来的
恢复动作,`run()` 可能一直等待。CMP 不提供隐式线程亲和:外部 awaiter 在其他线程恢复协程
Expand All @@ -350,7 +365,7 @@ RunLoop 不是后台线程,也不会把阻塞代码自动变成异步代码。
├── src/run_loop.cppm # RunLoop 与 Scheduler 分区
├── src/thread_pool.cppm # 固定大小的 CPU worker 调度器
├── src/blocking.cppm # 结构化阻塞调用 offload
├── src/tcp.cppm # 原生异步 TCP client 与 I/O context
├── src/tcp.cppm # 原生异步 TCP client/server 与 I/O context
├── src/when_all.cppm # 结构化并发 Task 汇合
├── src/task_group.cppm # eager 可变结构化 Task 作用域
├── src/one_shot_event.cppm # 无分配一次性通知
Expand Down Expand Up @@ -392,7 +407,7 @@ CI 在 Linux、macOS 和 Windows 上执行等价的构建、测试和独立示

CMP 当前不跟踪 `mcpp.lock`,`.gitignore` 明确执行这一仓库约定。运行时依赖放在
`[dependencies]`,gtest 明确声明在 `[dev-dependencies.compat]` 中。
当前本地套件包含 10 个测试二进制、140 项测试。仅用于 POSIX 的 Release 压测 consumer 及其
当前本地套件包含 10 个测试二进制、161 项测试。仅用于 POSIX 的 Release 压测 consumer 及其
成功/失败数据记录在 [v1 可开发性压测](docs/benchmarks/2026-08-29-cmp-v1-readiness.md)。
多 worker 正确性和性能数据记录在[线程池压测](docs/benchmarks/2026-08-29-cmp-thread-pool.md)。

Expand All @@ -406,8 +421,8 @@ CMP 当前不跟踪 `mcpp.lock`,`.gitignore` 明确执行这一仓库约定。
4. 单调时钟 Timer v1、可取消就绪/定时等待、变参/vector 汇合、静止点 TaskGroup、
OneShotEvent、AsyncManualResetEvent 和 AsyncMutex——已实现并完成压力验证;
5. 固定大小的多 worker 调度——已实现并完成压测;work stealing 仍需 profiling 证据;
6. 结构化阻塞 offload 与数值地址原生异步 TCP client——已实现,并由 PR #10 的 Linux、
macOS 和 Windows CI 验证。
6. 结构化阻塞 offload 与数值地址原生异步 TCP client/listener——均已实现;client 由
PR #10、listener 由 PR #11 在 Linux、macOS 和 Windows 上完成 CI 验证。

剩余顺序只是方向,不代表列出的能力已经实现。

Expand Down
Loading
Loading