1717
1818> [ !IMPORTANT]
1919> CMP provides a lazy, single-consumer ` Task<T> ` / ` Task<void> ` and a caller-thread ` RunLoop `
20- > with explicit and monotonic timed scheduling. Cancellation, asynchronous I/O, and detached
21- > execution are not implemented.
20+ > with explicit and monotonic timed scheduling. Timed waits support cooperative cancellation with
21+ > ` std::stop_token ` ; asynchronous I/O and detached execution are not implemented.
2222
2323CMP is being built as a modern coroutine runtime and library on standard stackless C++
24- coroutines. Its explicit ` co_await ` model now covers scheduling and monotonic timers and can grow,
25- in small verified steps, toward asynchronous I/O, cancellation, and safe handling of blocking
26- work.
24+ coroutines. Its explicit ` co_await ` model now covers scheduling, monotonic timers, and cancellable
25+ timed waits and can grow, in small verified steps, toward asynchronous I/O and safe handling of
26+ blocking work.
2727
2828## Why CMP?
2929
@@ -48,7 +48,7 @@ promise that:
4848- a task is automatically equivalent to a Go goroutine;
4949- an arbitrary blocking call becomes non-blocking;
5050- coroutine switching is safe directly inside a signal handler;
51- - M: N scheduling, work stealing, cancellation, or async I/O already exist.
51+ - M: N scheduling, work stealing, general cancellation propagation , or async I/O already exist.
5252
5353Those capabilities must be designed and verified individually. The expected direction is
5454explicit async I/O awaiters, a dedicated blocking pool, and cooperative safe points.
@@ -72,10 +72,10 @@ cd examples/basic
7272mcpp run
7373```
7474
75- The example waits for a short RunLoop timer, prints ` Coroutine result: 42 ` from inside a
76- ` Task<void> ` coroutine, and exits successfully. It proves that an independent mcpp package can
77- resolve the path dependency, import ` mcpplibs.cmp ` , compose Tasks, and use timed scheduling through
78- the public RunLoop.
75+ The example prints ` Coroutine result: 42 ` after a short RunLoop timer, then prints
76+ ` Coroutine cancelled ` from a pre-cancelled timed wait. Both messages come from ` Task<void> `
77+ coroutines. It proves that an independent mcpp package can import ` mcpplibs.cmp ` , compose Tasks,
78+ and use timed scheduling and cancellation through the public RunLoop.
7979
8080## Current API
8181
@@ -85,6 +85,7 @@ import mcpplibs.cmp;
8585
8686using mcpplibs::cmp::Task;
8787using mcpplibs::cmp::RunLoop;
88+ using mcpplibs::cmp::OperationCancelled;
8889
8990using namespace std ::chrono_literals;
9091
@@ -99,9 +100,22 @@ Task<void> print_answer(RunLoop::Scheduler scheduler) {
99100 co_return;
100101}
101102
103+ Task<void > print_cancellation(RunLoop::Scheduler scheduler, std::stop_token token) {
104+ try {
105+ co_await scheduler.schedule_after(1s, token);
106+ } catch (const OperationCancelled&) {
107+ std::println("Coroutine cancelled");
108+ }
109+ co_return;
110+ }
111+
102112int main() {
103113 RunLoop loop {};
104114 loop.run(print_answer(loop.get_scheduler()));
115+
116+ std::stop_source source {};
117+ source.request_stop();
118+ loop.run(print_cancellation(loop.get_scheduler(), source.get_token()));
105119}
106120```
107121
@@ -123,6 +137,11 @@ never resumes it inline. Scheduler handles are copyable, but remain tied to thei
123137RunLoop. Sequential `run()` calls are supported; nested or concurrent calls are rejected. A
124138moved-from Task must not be awaited.
125139
140+ The timed overloads accepting `std::stop_token` also always suspend. If cancellation wins before
141+ the deadline, awaiting throws `OperationCancelled`; a late stop request cannot replace an already
142+ queued deadline completion. The stop callback only wakes the RunLoop, so user coroutine code is
143+ still resumed by the thread driving `run()`. Cancellation currently performs an O(n) timer lookup.
144+
126145RunLoop is not a background thread and does not make blocking code asynchronous. A Task that
127146suspends without arranging a future resume can leave `run()` waiting indefinitely. CMP does not
128147provide automatic thread affinity: after an external awaiter resumes on another thread, explicitly
@@ -171,7 +190,8 @@ Runtime work is split into independently reviewable phases:
1711901 . package identity and importable-module bootstrap — implemented;
1721912 . coroutine task and lifetime semantics — initial ` Task ` implemented;
1731923 . a root runner and minimal single-thread scheduler — initially implemented;
174- 4 . monotonic Timer v1 — implemented; cancellation and structured wake-up paths remain;
193+ 4 . monotonic Timer v1 and cancellable timed waits — implemented; structured task ownership and
194+ wake-up paths remain;
1751955 . multi-worker scheduling and work stealing;
1761966 . asynchronous I/O integration and a blocking pool.
177197
0 commit comments