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
2 changes: 1 addition & 1 deletion doc/modules/ROOT/pages/4.coroutines/4h.allocators.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ Run the same task eight times through it, one run at a time:
include::example$snippets/4h_allocators.cpp[tag=recycling_observe,indent=0]
----

The first run finds an empty pool and takes its coroutine frames from the heap. Each `pool.join()` returns those frames to the resource, so the next cpp:run_async[] call reuses them. Once the pool is warm, the upstream count stops climbing while the task keeps running -- the seven warm runs allocate nothing new. cpp:recycling_memory_resource[] does the same thing with size-class freelists and a lock-free thread-local cache.
The first run finds an empty pool and takes its coroutine frames from the heap. Each `pool.join()` returns after the task has finished and its frames are back in the resource, so the next cpp:run_async[] call reuses them. Once the pool is warm, the upstream count stops climbing while the task keeps running -- the seven warm runs allocate nothing new. cpp:recycling_memory_resource[] does the same thing with size-class freelists and a lock-free thread-local cache.

== HALO Optimization

Expand Down
85 changes: 64 additions & 21 deletions include/boost/capy/ex/thread_pool.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,37 @@ class BOOST_CAPY_DECL

/** Destroy the thread pool.

Signals all worker threads to stop, waits for them to
finish, and destroys any pending work items.
Stops the pool, joins its worker threads, then abandons any
work that has not started:

@li Calls @ref stop. Each worker thread finishes the item it
is running and exits; queued work is not run.
@li Joins the worker threads. Unlike @ref join, this does not
wait for outstanding work. If @ref join was already called,
the threads are already joined and this step does nothing.
@li Shuts down the pool's services, abandons every coroutine
still queued on the pool or its strands, then destroys the
services.
Each abandoned chain is unwound, without delivering a
result, on the executor of the coroutine that started it.
That happens during this destructor when the executor is
this pool, and later when it belongs to another execution
context.
See @ref chain_owner.

To let queued work finish first, call @ref join before the
pool is destroyed.

@pre No thread outside this pool may post or dispatch work to it
(or to a strand built on it) concurrently with, or after,
destruction. Doing so is undefined behavior. Submit such work
through @ref run_async or @ref run and call @ref join before
the pool is destroyed, so it has completed first. A thread
owned by one of this pool's services may post or dispatch
until that service's `shutdown()` returns.
through @ref run_async and call @ref join, without calling
@ref stop, before the pool is destroyed, so it has completed
first. A thread owned by one of this pool's services may post
or dispatch until that service's `shutdown()` returns.

@pre Must not be called from a thread in this pool. A worker
thread cannot join itself, so this calls `std::terminate`.
*/
~thread_pool();

Expand Down Expand Up @@ -103,8 +124,8 @@ class BOOST_CAPY_DECL

/** Wait for all outstanding work to complete.

Releases the internal work guard, then blocks the calling
thread until all outstanding work tracked by
Blocks the calling thread until all outstanding work
tracked by
@ref executor_type::on_work_started and
@ref executor_type::on_work_finished completes. After all
work finishes, joins the worker threads.
Expand All @@ -115,15 +136,16 @@ class BOOST_CAPY_DECL
exit; `join()` still waits for all threads to be joined
before returning.

This function is idempotent. The first call performs the
join; subsequent calls return immediately.
Only the first call performs the join. A later call returns
immediately, even while the first call is still blocking on
another thread.

@pre Must not be called from a thread in this pool (undefined
behavior).

@par Postconditions
All worker threads have been joined. The pool cannot be
reused.
If this call performed the join, all worker threads have
been joined. The pool cannot be reused.

@par Thread Safety
May be called from any thread not in this pool.
Expand Down Expand Up @@ -158,7 +180,7 @@ class BOOST_CAPY_DECL
get_executor() const noexcept;
};

/** An executor that submits work to a thread_pool.
/** Runs continuations on the worker threads of a thread_pool.

Executors are lightweight handles that can be copied and stored.
All copies refer to the same underlying thread pool.
Expand Down Expand Up @@ -228,28 +250,49 @@ class thread_pool::executor_type

If the calling thread is a worker of this pool, returns
`c.h` for symmetric transfer so the caller can resume the
continuation inline. Otherwise, posts the continuation to
the pool for execution on a worker thread and returns
`std::noop_coroutine()`.
continuation inline. Otherwise, posts the continuation as
@ref post does and returns `std::noop_coroutine()`. On that
path, the continuation is not run if the pool stops first,
and a call that races with @ref thread_pool::stop can go
either way. See @ref post.

@param c The continuation to execute. On the post path,
must remain at a stable address until dequeued
and resumed.
must remain at a stable address until resumed or
abandoned.

@return `c.h` when the calling thread is a pool worker;
`std::noop_coroutine()` otherwise.

@throws std::system_error If this call posts and must start
the pool's worker threads, and a thread cannot be started.
`c` is not queued.
*/
BOOST_CAPY_DECL
std::coroutine_handle<>
dispatch(continuation& c) const;

/** Post a continuation to the thread pool.

The continuation is resumed on one of the pool's
worker threads. The continuation must remain at a stable
address until it is dequeued and resumed.
The continuation is resumed on one of the pool's worker
threads, unless the pool stops first. The pool stops when
@ref thread_pool::stop is called, or when
@ref thread_pool::join has finished waiting for outstanding
work. A continuation still queued when the pool stops, or
posted after it stops, is not run. It stays queued until
the pool is destroyed, which abandons it.

A call that races with @ref thread_pool::stop on another
thread can therefore go either way, and nothing in the call
tells the caller which.

The continuation must remain at a stable address until it
is resumed or abandoned.

@param c The continuation to execute.

@throws std::system_error If this call must start the pool's
worker threads, and a thread cannot be started. `c` is not
queued.
*/
BOOST_CAPY_DECL
void
Expand Down
3 changes: 2 additions & 1 deletion test/doc/programs/quick_start_hello.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//
// Copyright (c) 2026 Steve Gerbino
// Copyright (c) 2026 Michael Vandeberg
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Expand Down Expand Up @@ -38,7 +39,7 @@ int main()
capy::run_async(pool.get_executor())(greet());

// join() waits for outstanding work to complete; the pool
// destructor only stops the pool and discards pending work
// destructor does not wait, and abandons any work still queued
pool.join();
}
// end::full[]
2 changes: 1 addition & 1 deletion test/doc/reference/thread_pool.record.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ void run_on_pool()
auto ex = pool.get_executor();
capy::run_async(ex)(some_task()); // start work; tracked so join() waits for it
pool.join(); // wait for outstanding work to complete
// pool destructor stops the pool, discarding any pending work
// pool destructor stops the pool and abandons any work still queued
}
// end::example[]
} // namespace ex_1
Expand Down
Loading