diff --git a/bench/bench.cpp b/bench/bench.cpp index 2a918246..e1311ce1 100644 --- a/bench/bench.cpp +++ b/bench/bench.cpp @@ -81,11 +81,6 @@ class test_executor return h; } - any_coro dispatch(any_coro h) const - { - return h; - } - // Post interface - resume inline for benchmarking void post(any_coro h) const { diff --git a/doc/modules/ROOT/pages/execution/executors.adoc b/doc/modules/ROOT/pages/execution/executors.adoc index ea3dfa05..22923552 100644 --- a/doc/modules/ROOT/pages/execution/executors.adoc +++ b/doc/modules/ROOT/pages/execution/executors.adoc @@ -89,7 +89,7 @@ concept executor = { ce.context() } -> /* reference to execution context */; { ce.on_work_started() } noexcept; { ce.on_work_finished() } noexcept; - { ce.dispatch(h) } -> std::convertible_to>; + { ce(h) } -> std::convertible_to>; { ce.post(h) }; { ce.defer(h) }; }; @@ -101,8 +101,8 @@ concept executor = |=== | Operation | Behavior -| `dispatch(h)` -| Run inline if safe, otherwise queue. Cheapest path. +| `operator()(h)` +| Run inline if safe, otherwise queue. Cheapest path. Also serves as dispatcher interface. | `post(h)` | Always queue, never inline. Guaranteed asynchrony. @@ -113,7 +113,7 @@ concept executor = **When to use each:** -* `dispatch` — Default choice. Allows the executor to optimize. +* `operator()` — Default choice. Allows the executor to optimize. * `post` — When you need guaranteed asynchrony (e.g., releasing a lock first). * `defer` — When posting your own continuation (enables thread-local queuing). @@ -171,7 +171,7 @@ Stoppable awaitables provide _both_ overloads of `await_suspend`. Executors have specific thread safety guarantees: * Copy constructor, comparison, `context()` — always thread-safe -* `dispatch`, `post`, `defer` — thread-safe for concurrent calls +* `operator()`, `post`, `defer` — thread-safe for concurrent calls * `on_work_started`, `on_work_finished` — thread-safe, must not throw == Executor Validity @@ -184,7 +184,7 @@ thread_pool pool; auto ex = pool.get_executor(); // When pool is destroyed, ex becomes invalid -// WARNING: Calling ex.dispatch() after pool destruction is undefined behavior +// WARNING: Calling ex() after pool destruction is undefined behavior ---- The copy constructor and `context()` remain valid until the context is diff --git a/doc/modules/ROOT/pages/execution/thread-pool.adoc b/doc/modules/ROOT/pages/execution/thread-pool.adoc index 6090dfd4..6cba5ae5 100644 --- a/doc/modules/ROOT/pages/execution/thread-pool.adoc +++ b/doc/modules/ROOT/pages/execution/thread-pool.adoc @@ -123,16 +123,16 @@ auto ex = pool.get_executor(); thread_pool& ctx = ex.context(); // Submit coroutines -ex.post(handle); // Queue for execution -ex.dispatch(handle); // Same as post (always queues) -ex.defer(handle); // Same as post +ex.post(handle); // Queue for execution +ex(handle); // Same as post (always queues) +ex.defer(handle); // Same as post // Work tracking ex.on_work_started(); ex.on_work_finished(); ---- -=== dispatch vs post vs defer +=== operator() vs post vs defer For `thread_pool`, all three operations behave identically: they queue the work for execution on a pool thread. The distinction matters for other @@ -145,7 +145,7 @@ execution contexts: | `post(h)` | Always queue, never execute inline -| `dispatch(h)` +| `operator()(h)` | Execute inline if safe, otherwise queue | `defer(h)` @@ -153,7 +153,7 @@ execution contexts: |=== Since callers are never "inside" the thread pool's execution context, -`dispatch` always queues. +`operator()` always queues. == Work Tracking @@ -216,7 +216,7 @@ Services are shut down and destroyed when the pool is destroyed. | `get_executor()` | Safe -| `executor.post/dispatch/defer` +| `executor.post/operator()/defer` | Safe (concurrent calls allowed) | `executor.context()` diff --git a/include/boost/capy/concept/executor.hpp b/include/boost/capy/concept/executor.hpp index 5c30c5ce..8d58e710 100644 --- a/include/boost/capy/concept/executor.hpp +++ b/include/boost/capy/concept/executor.hpp @@ -37,10 +37,10 @@ namespace capy { completed. Precondition: a preceding call to `on_work_started()` on an equal executor. - @li `dispatch(h)` - Execute a coroutine, potentially immediately + @li `operator()(h)` - Execute a coroutine, potentially immediately if the executor determines it is safe to do so. The executor may block forward progress of the caller until execution - completes. + completes. This also serves as the dispatcher interface. @li `post(h)` - Queue a coroutine for later execution. The executor shall not block forward progress of the caller @@ -96,7 +96,7 @@ concept executor = { ce.on_work_finished() } noexcept; // Work submission - { ce.dispatch(h) } -> std::convertible_to>; + { ce(h) } -> std::convertible_to>; { ce.post(h) }; { ce.defer(h) }; }; diff --git a/include/boost/capy/ex/strand.hpp b/include/boost/capy/ex/strand.hpp index 36a456a2..1e04895b 100644 --- a/include/boost/capy/ex/strand.hpp +++ b/include/boost/capy/ex/strand.hpp @@ -47,7 +47,7 @@ namespace capy { This class satisfies the `executor` concept, providing: - `context()` - Returns the underlying execution context - `on_work_started()` / `on_work_finished()` - Work tracking - - `dispatch(h)` - May run immediately if strand is idle + - `operator()(h)` - May run immediately if strand is idle - `post(h)` - Always queues for later execution - `defer(h)` - Same as post (continuation hint) @@ -196,30 +196,6 @@ class strand return impl_ == other.impl_; } - /** Dispatch a coroutine through the strand. - - If the calling thread is already executing within this strand, - the coroutine is resumed immediately via symmetric transfer, - bypassing the queue. This provides optimal performance but - means the coroutine may execute before previously queued work. - - Otherwise, the coroutine is queued and will execute in FIFO - order relative to other queued coroutines. - - @par Ordering - Callers requiring strict FIFO ordering should use post() - instead, which always queues the coroutine. - - @param h The coroutine handle to dispatch. - @return A coroutine handle for symmetric transfer. - */ - // TODO: measure before deciding to split strand_impl for inlining fast-path check - any_coro - dispatch(any_coro h) const - { - return detail::strand_service::dispatch(*impl_, any_dispatcher(post_), h); - } - /** Post a coroutine to the strand. The coroutine is always queued for execution, never resumed @@ -254,16 +230,26 @@ class strand /** Dispatch a coroutine through the strand. - This operator provides a dispatcher-style interface for - use with symmetric transfer. Equivalent to `dispatch()`. + If the calling thread is already executing within this strand, + the coroutine is resumed immediately via symmetric transfer, + bypassing the queue. This provides optimal performance but + means the coroutine may execute before previously queued work. + + Otherwise, the coroutine is queued and will execute in FIFO + order relative to other queued coroutines. + + @par Ordering + Callers requiring strict FIFO ordering should use post() + instead, which always queues the coroutine. @param h The coroutine handle to dispatch. @return A coroutine handle for symmetric transfer. */ + // TODO: measure before deciding to split strand_impl for inlining fast-path check any_coro operator()(any_coro h) const { - return dispatch(h); + return detail::strand_service::dispatch(*impl_, any_dispatcher(post_), h); } }; diff --git a/include/boost/capy/ex/thread_pool.hpp b/include/boost/capy/ex/thread_pool.hpp index a31f1a2d..c34fbe6c 100644 --- a/include/boost/capy/ex/thread_pool.hpp +++ b/include/boost/capy/ex/thread_pool.hpp @@ -127,14 +127,14 @@ class thread_pool::executor_type Posts the coroutine to the thread pool and returns immediately. The caller should suspend after calling - this function. + this function. Also serves as the dispatcher interface. @param h The coroutine handle to execute. @return A noop coroutine handle to resume. */ any_coro - dispatch(any_coro h) const + operator()(any_coro h) const { post(h); return std::noop_coroutine(); diff --git a/test/unit/ex/executor_work_guard.cpp b/test/unit/ex/executor_work_guard.cpp index ac1588eb..bae24b5d 100644 --- a/test/unit/ex/executor_work_guard.cpp +++ b/test/unit/ex/executor_work_guard.cpp @@ -61,7 +61,7 @@ struct guard_test_executor } std::coroutine_handle<> - dispatch(std::coroutine_handle<> h) const + operator()(std::coroutine_handle<> h) const { return h; } diff --git a/test/unit/ex/strand.cpp b/test/unit/ex/strand.cpp index 05a3fbab..57647422 100644 --- a/test/unit/ex/strand.cpp +++ b/test/unit/ex/strand.cpp @@ -354,7 +354,7 @@ struct strand_test std::atomic counter{0}; auto coro = make_counter_coro(counter); - s.dispatch(coro.handle()); + s(coro.handle()); coro.release(); // Wait for work to complete @@ -550,7 +550,7 @@ struct strand_test std::atomic counter{0}; auto coro = make_counter_coro(counter); - s.dispatch(coro.handle()); + s(coro.handle()); coro.release(); BOOST_TEST(wait_for([&]{ return counter.load() >= 1; })); diff --git a/test/unit/ex/thread_pool.cpp b/test/unit/ex/thread_pool.cpp index 9d3c895f..3e3d5f86 100644 --- a/test/unit/ex/thread_pool.cpp +++ b/test/unit/ex/thread_pool.cpp @@ -150,8 +150,8 @@ struct thread_pool_test thread_pool pool(1); auto ex = pool.get_executor(); - // dispatch returns noop_coroutine (always posts for thread_pool) - auto result = ex.dispatch(std::noop_coroutine()); + // operator() returns noop_coroutine (always posts for thread_pool) + auto result = ex(std::noop_coroutine()); BOOST_TEST(result == std::noop_coroutine()); } diff --git a/test/unit/executor.cpp b/test/unit/executor.cpp index bb998da8..1a2bc966 100644 --- a/test/unit/executor.cpp +++ b/test/unit/executor.cpp @@ -64,7 +64,7 @@ struct test_executor // Work submission std::coroutine_handle<> - dispatch(std::coroutine_handle<> h) const + operator()(std::coroutine_handle<> h) const { return h; }