Skip to content

Commit e2fc714

Browse files
committed
fix(async): harden shutdown for io_context services and worker threads
1 parent e21dc4b commit e2fc714

7 files changed

Lines changed: 542 additions & 293 deletions

File tree

include/vix/async/core/io_context.hpp

Lines changed: 72 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,16 @@
1616
#ifndef VIX_ASYNC_IO_CONTEXT_HPP
1717
#define VIX_ASYNC_IO_CONTEXT_HPP
1818

19+
#include <atomic>
1920
#include <coroutine>
2021
#include <memory>
22+
#include <mutex>
2123
#include <utility>
2224

2325
#include <vix/async/core/scheduler.hpp>
2426

2527
namespace vix::async::net::detail
2628
{
27-
/**
28-
* @brief Internal networking service (Asio-backed).
29-
*
30-
* This type is forward-declared to keep io_context lightweight and
31-
* avoid pulling networking implementation details into the public API.
32-
*/
3329
class asio_net_service;
3430
}
3531

@@ -40,69 +36,65 @@ namespace vix::async::core
4036
class signal_set;
4137

4238
/**
43-
* @brief Core runtime context for async operations.
39+
* @brief Central asynchronous execution context.
40+
*
41+
* io_context is the core coordination object of the async runtime.
4442
*
45-
* io_context owns a scheduler that drives coroutine continuations and
46-
* posted tasks. It also exposes lazily-created services used by higher
47-
* level facilities:
48-
* - CPU thread pool for compute-bound work
49-
* - timers for scheduling time-based events
50-
* - signals for signal handling
51-
* - net for networking (implementation detail service)
43+
* It provides:
44+
* - a scheduler for coroutine and task execution
45+
* - a CPU thread pool for blocking or heavy work
46+
* - a timer service for delayed execution
47+
* - a signal handling service
48+
* - a networking backend (asio-based)
5249
*
53-
* The context is non-copyable and typically lives for the duration of
54-
* the program or subsystem using it.
50+
* Services are lazily initialized and owned by the context.
51+
*
52+
* Lifecycle:
53+
* - Services are created on first use
54+
* - shutdown() stops the scheduler and destroys all services
55+
* - destruction is safe and idempotent
56+
*
57+
* Thread-safety:
58+
* - posting tasks is thread-safe
59+
* - shutdown is serialized and idempotent
5560
*/
5661
class io_context
5762
{
5863
public:
5964
/**
60-
* @brief Construct a new io_context.
61-
*
62-
* Initializes the underlying scheduler. Optional services are created
63-
* lazily on first access through their corresponding accessors.
65+
* @brief Construct an empty io_context.
6466
*/
6567
io_context();
6668

6769
/**
68-
* @brief Destroy the io_context.
70+
* @brief Destroy the io_context and release all resources.
6971
*
70-
* Destroys any lazily-initialized services and the scheduler.
72+
* Automatically calls shutdown() if not already done.
7173
*/
72-
~io_context();
74+
~io_context() noexcept;
7375

74-
/**
75-
* @brief io_context is non-copyable.
76-
*/
7776
io_context(const io_context &) = delete;
78-
79-
/**
80-
* @brief io_context is non-copyable.
81-
*/
8277
io_context &operator=(const io_context &) = delete;
8378

8479
/**
85-
* @brief Access the underlying scheduler.
80+
* @brief Access the internal scheduler.
8681
*
87-
* @return Reference to the scheduler.
82+
* @return Reference to scheduler.
8883
*/
89-
scheduler &get_scheduler() noexcept { return sched_; }
84+
[[nodiscard]] scheduler &get_scheduler() noexcept { return sched_; }
9085

9186
/**
92-
* @brief Access the underlying scheduler.
87+
* @brief Access the internal scheduler (const).
9388
*
94-
* @return Const reference to the scheduler.
89+
* @return Const reference to scheduler.
9590
*/
96-
const scheduler &get_scheduler() const noexcept { return sched_; }
91+
[[nodiscard]] const scheduler &get_scheduler() const noexcept { return sched_; }
9792

9893
/**
99-
* @brief Post a callable to be executed by the scheduler.
100-
*
101-
* The callable is forwarded into the scheduler queue and will be
102-
* executed when run() drives the scheduler.
94+
* @brief Post a callable task to the scheduler.
10395
*
10496
* @tparam Fn Callable type.
105-
* @param fn Callable to enqueue.
97+
* @param fn Task to execute.
10698
*/
10799
template <typename Fn>
108100
void post(Fn &&fn)
@@ -111,7 +103,7 @@ namespace vix::async::core
111103
}
112104

113105
/**
114-
* @brief Post a coroutine continuation to be resumed by the scheduler.
106+
* @brief Post a coroutine handle to the scheduler.
115107
*
116108
* @param h Coroutine handle to resume.
117109
*/
@@ -121,10 +113,9 @@ namespace vix::async::core
121113
}
122114

123115
/**
124-
* @brief Run the scheduler event loop.
116+
* @brief Run the scheduler loop.
125117
*
126-
* This call typically blocks and processes queued tasks until stop()
127-
* is called or the scheduler decides to return.
118+
* Blocks until stop() is called.
128119
*/
129120
void run()
130121
{
@@ -134,96 +125,91 @@ namespace vix::async::core
134125
/**
135126
* @brief Stop the scheduler.
136127
*
137-
* Signals the scheduler to stop processing and return from run().
128+
* Causes run() to exit.
138129
*/
139130
void stop() noexcept
140131
{
141132
sched_.stop();
142133
}
143134

144135
/**
145-
* @brief Check whether the scheduler is currently running.
136+
* @brief Check whether the scheduler is running.
146137
*
147138
* @return true if running, false otherwise.
148139
*/
149-
bool is_running() const noexcept
140+
[[nodiscard]] bool is_running() const noexcept
150141
{
151142
return sched_.is_running();
152143
}
153144

154145
/**
155-
* @name Lazy services
146+
* @brief Access the CPU thread pool.
156147
*
157-
* Services below are initialized on first access and are owned by
158-
* this io_context instance.
148+
* Lazily initialized.
159149
*
160-
* @{
150+
* @return Reference to thread_pool.
161151
*/
152+
[[nodiscard]] thread_pool &cpu_pool();
162153

163154
/**
164-
* @brief Access the CPU thread pool service.
155+
* @brief Access the timer service.
165156
*
166-
* Lazily constructs the pool on first call.
157+
* Lazily initialized.
167158
*
168-
* @return Reference to the thread_pool service.
159+
* @return Reference to timer.
169160
*/
170-
thread_pool &cpu_pool();
161+
[[nodiscard]] timer &timers();
171162

172163
/**
173-
* @brief Access the timers service.
164+
* @brief Access the signal handling service.
174165
*
175-
* Lazily constructs the timer service on first call.
166+
* Lazily initialized.
176167
*
177-
* @return Reference to the timer service.
168+
* @return Reference to signal_set.
178169
*/
179-
timer &timers();
170+
[[nodiscard]] signal_set &signals();
180171

181172
/**
182-
* @brief Access the signal handling service.
173+
* @brief Access the networking backend service.
183174
*
184-
* Lazily constructs the signal_set service on first call.
175+
* Lazily initialized.
185176
*
186-
* @return Reference to the signal_set service.
177+
* @return Reference to asio_net_service.
187178
*/
188-
signal_set &signals();
179+
[[nodiscard]] vix::async::net::detail::asio_net_service &net();
189180

190181
/**
191-
* @brief Access the networking service.
182+
* @brief Stop scheduler and destroy all services.
192183
*
193-
* Lazily constructs the internal Asio-backed networking service on
194-
* first call. The type is intentionally kept in a detail namespace.
184+
* This function:
185+
* - stops the scheduler
186+
* - destroys all lazily created services
195187
*
196-
* @return Reference to the asio_net_service.
188+
* It is safe to call multiple times.
197189
*/
198-
vix::async::net::detail::asio_net_service &net();
199-
200-
/** @} */
190+
void shutdown() noexcept;
201191

202192
private:
203-
/**
204-
* @brief Primary scheduler driving task execution and coroutine resumption.
205-
*/
193+
/** @brief Core scheduler. */
206194
scheduler sched_;
207195

208-
/**
209-
* @brief Lazily-created CPU pool.
210-
*/
196+
/** @brief CPU thread pool (lazy). */
211197
std::unique_ptr<thread_pool> cpu_pool_;
212198

213-
/**
214-
* @brief Lazily-created timers service.
215-
*/
199+
/** @brief Timer service (lazy). */
216200
std::unique_ptr<timer> timer_;
217201

218-
/**
219-
* @brief Lazily-created signals service.
220-
*/
202+
/** @brief Signal handling service (lazy). */
221203
std::unique_ptr<signal_set> signals_;
222204

223-
/**
224-
* @brief Lazily-created networking service.
225-
*/
205+
/** @brief Networking backend (lazy). */
226206
std::unique_ptr<vix::async::net::detail::asio_net_service> net_;
207+
208+
/** @brief Ensures shutdown runs once. */
209+
std::atomic<bool> shutdown_done_{false};
210+
211+
/** @brief Protects shutdown sequence. */
212+
std::mutex shutdown_mutex_;
227213
};
228214

229215
} // namespace vix::async::core

0 commit comments

Comments
 (0)