1  
//
1  
//
2  
// Copyright (c) 2026 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2026 Vinnie Falco (vinnie.falco@gmail.com)
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/cppalliance/corosio
7  
// Official repository: https://github.com/cppalliance/corosio
8  
//
8  
//
9  

9  

10  
#ifndef BOOST_COROSIO_TCP_SERVER_HPP
10  
#ifndef BOOST_COROSIO_TCP_SERVER_HPP
11  
#define BOOST_COROSIO_TCP_SERVER_HPP
11  
#define BOOST_COROSIO_TCP_SERVER_HPP
12  

12  

13  
#include <boost/corosio/detail/config.hpp>
13  
#include <boost/corosio/detail/config.hpp>
14  
#include <boost/corosio/detail/except.hpp>
14  
#include <boost/corosio/detail/except.hpp>
15  
#include <boost/corosio/tcp_acceptor.hpp>
15  
#include <boost/corosio/tcp_acceptor.hpp>
16  
#include <boost/corosio/tcp_socket.hpp>
16  
#include <boost/corosio/tcp_socket.hpp>
17  
#include <boost/corosio/io_context.hpp>
17  
#include <boost/corosio/io_context.hpp>
18  
#include <boost/corosio/endpoint.hpp>
18  
#include <boost/corosio/endpoint.hpp>
19  
#include <boost/capy/task.hpp>
19  
#include <boost/capy/task.hpp>
20  
#include <boost/capy/concept/execution_context.hpp>
20  
#include <boost/capy/concept/execution_context.hpp>
21  
#include <boost/capy/concept/io_awaitable.hpp>
21  
#include <boost/capy/concept/io_awaitable.hpp>
22  
#include <boost/capy/concept/executor.hpp>
22  
#include <boost/capy/concept/executor.hpp>
23  
#include <boost/capy/ex/any_executor.hpp>
23  
#include <boost/capy/ex/any_executor.hpp>
24  
#include <boost/capy/ex/frame_allocator.hpp>
24  
#include <boost/capy/ex/frame_allocator.hpp>
25  
#include <boost/capy/ex/io_env.hpp>
25  
#include <boost/capy/ex/io_env.hpp>
26  
#include <boost/capy/ex/run_async.hpp>
26  
#include <boost/capy/ex/run_async.hpp>
27  

27  

28  
#include <coroutine>
28  
#include <coroutine>
29  
#include <memory>
29  
#include <memory>
30  
#include <ranges>
30  
#include <ranges>
31  
#include <vector>
31  
#include <vector>
32  

32  

33  
namespace boost::corosio {
33  
namespace boost::corosio {
34  

34  

35  
#ifdef _MSC_VER
35  
#ifdef _MSC_VER
36  
#pragma warning(push)
36  
#pragma warning(push)
37  
#pragma warning(disable: 4251) // class needs to have dll-interface
37  
#pragma warning(disable: 4251) // class needs to have dll-interface
38  
#endif
38  
#endif
39  

39  

40  
/** TCP server with pooled workers.
40  
/** TCP server with pooled workers.
41  

41  

42  
    This class manages a pool of reusable worker objects that handle
42  
    This class manages a pool of reusable worker objects that handle
43  
    incoming connections. When a connection arrives, an idle worker
43  
    incoming connections. When a connection arrives, an idle worker
44  
    is dispatched to handle it. After the connection completes, the
44  
    is dispatched to handle it. After the connection completes, the
45  
    worker returns to the pool for reuse, avoiding allocation overhead
45  
    worker returns to the pool for reuse, avoiding allocation overhead
46  
    per connection.
46  
    per connection.
47  

47  

48  
    Workers are set via @ref set_workers as a forward range of
48  
    Workers are set via @ref set_workers as a forward range of
49  
    pointer-like objects (e.g., `unique_ptr<worker_base>`). The server
49  
    pointer-like objects (e.g., `unique_ptr<worker_base>`). The server
50  
    takes ownership of the container via type erasure.
50  
    takes ownership of the container via type erasure.
51  

51  

52  
    @par Thread Safety
52  
    @par Thread Safety
53  
    Distinct objects: Safe.
53  
    Distinct objects: Safe.
54  
    Shared objects: Unsafe.
54  
    Shared objects: Unsafe.
55  

55  

56  
    @par Lifecycle
56  
    @par Lifecycle
57  
    The server operates in three states:
57  
    The server operates in three states:
58  

58  

59  
    - **Stopped**: Initial state, or after @ref join completes.
59  
    - **Stopped**: Initial state, or after @ref join completes.
60  
    - **Running**: After @ref start, actively accepting connections.
60  
    - **Running**: After @ref start, actively accepting connections.
61  
    - **Stopping**: After @ref stop, draining active work.
61  
    - **Stopping**: After @ref stop, draining active work.
62  

62  

63  
    State transitions:
63  
    State transitions:
64  
    @code
64  
    @code
65  
    [Stopped] --start()--> [Running] --stop()--> [Stopping] --join()--> [Stopped]
65  
    [Stopped] --start()--> [Running] --stop()--> [Stopping] --join()--> [Stopped]
66  
    @endcode
66  
    @endcode
67  

67  

68  
    @par Running the Server
68  
    @par Running the Server
69  
    @code
69  
    @code
70  
    io_context ioc;
70  
    io_context ioc;
71  
    tcp_server srv(ioc, ioc.get_executor());
71  
    tcp_server srv(ioc, ioc.get_executor());
72  
    srv.set_workers(make_workers(ioc, 100));
72  
    srv.set_workers(make_workers(ioc, 100));
73  
    srv.bind(endpoint{address_v4::any(), 8080});
73  
    srv.bind(endpoint{address_v4::any(), 8080});
74  
    srv.start();
74  
    srv.start();
75  
    ioc.run();  // Blocks until all work completes
75  
    ioc.run();  // Blocks until all work completes
76  
    @endcode
76  
    @endcode
77  

77  

78  
    @par Graceful Shutdown
78  
    @par Graceful Shutdown
79  
    To shut down gracefully, call @ref stop then drain the io_context:
79  
    To shut down gracefully, call @ref stop then drain the io_context:
80  
    @code
80  
    @code
81  
    // From a signal handler or timer callback:
81  
    // From a signal handler or timer callback:
82  
    srv.stop();
82  
    srv.stop();
83  

83  

84  
    // ioc.run() returns after pending work drains.
84  
    // ioc.run() returns after pending work drains.
85  
    // Then from the thread that called ioc.run():
85  
    // Then from the thread that called ioc.run():
86  
    srv.join();  // Wait for accept loops to finish
86  
    srv.join();  // Wait for accept loops to finish
87  
    @endcode
87  
    @endcode
88  

88  

89  
    @par Restart After Stop
89  
    @par Restart After Stop
90  
    The server can be restarted after a complete shutdown cycle.
90  
    The server can be restarted after a complete shutdown cycle.
91  
    You must drain the io_context and call @ref join before restarting:
91  
    You must drain the io_context and call @ref join before restarting:
92  
    @code
92  
    @code
93  
    srv.start();
93  
    srv.start();
94  
    ioc.run_for( 10s );   // Run for a while
94  
    ioc.run_for( 10s );   // Run for a while
95  
    srv.stop();           // Signal shutdown
95  
    srv.stop();           // Signal shutdown
96  
    ioc.run();            // REQUIRED: drain pending completions
96  
    ioc.run();            // REQUIRED: drain pending completions
97  
    srv.join();           // REQUIRED: wait for accept loops
97  
    srv.join();           // REQUIRED: wait for accept loops
98  

98  

99  
    // Now safe to restart
99  
    // Now safe to restart
100  
    srv.start();
100  
    srv.start();
101  
    ioc.run();
101  
    ioc.run();
102  
    @endcode
102  
    @endcode
103  

103  

104  
    @par WARNING: What NOT to Do
104  
    @par WARNING: What NOT to Do
105  
    - Do NOT call @ref join from inside a worker coroutine (deadlock).
105  
    - Do NOT call @ref join from inside a worker coroutine (deadlock).
106  
    - Do NOT call @ref join from a thread running `ioc.run()` (deadlock).
106  
    - Do NOT call @ref join from a thread running `ioc.run()` (deadlock).
107  
    - Do NOT call @ref start without completing @ref join after @ref stop.
107  
    - Do NOT call @ref start without completing @ref join after @ref stop.
108  
    - Do NOT call `ioc.stop()` for graceful shutdown; use @ref stop instead.
108  
    - Do NOT call `ioc.stop()` for graceful shutdown; use @ref stop instead.
109  

109  

110  
    @par Example
110  
    @par Example
111  
    @code
111  
    @code
112  
    class my_worker : public tcp_server::worker_base
112  
    class my_worker : public tcp_server::worker_base
113  
    {
113  
    {
114  
        corosio::tcp_socket sock_;
114  
        corosio::tcp_socket sock_;
115  
        capy::any_executor ex_;
115  
        capy::any_executor ex_;
116  
    public:
116  
    public:
117  
        my_worker(io_context& ctx)
117  
        my_worker(io_context& ctx)
118  
            : sock_(ctx)
118  
            : sock_(ctx)
119  
            , ex_(ctx.get_executor())
119  
            , ex_(ctx.get_executor())
120  
        {
120  
        {
121  
        }
121  
        }
122  

122  

123  
        corosio::tcp_socket& socket() override { return sock_; }
123  
        corosio::tcp_socket& socket() override { return sock_; }
124  

124  

125  
        void run(launcher launch) override
125  
        void run(launcher launch) override
126  
        {
126  
        {
127  
            launch(ex_, [](corosio::tcp_socket* sock) -> capy::task<>
127  
            launch(ex_, [](corosio::tcp_socket* sock) -> capy::task<>
128  
            {
128  
            {
129  
                // handle connection using sock
129  
                // handle connection using sock
130  
                co_return;
130  
                co_return;
131  
            }(&sock_));
131  
            }(&sock_));
132  
        }
132  
        }
133  
    };
133  
    };
134  

134  

135  
    auto make_workers(io_context& ctx, int n)
135  
    auto make_workers(io_context& ctx, int n)
136  
    {
136  
    {
137  
        std::vector<std::unique_ptr<tcp_server::worker_base>> v;
137  
        std::vector<std::unique_ptr<tcp_server::worker_base>> v;
138  
        v.reserve(n);
138  
        v.reserve(n);
139  
        for(int i = 0; i < n; ++i)
139  
        for(int i = 0; i < n; ++i)
140  
            v.push_back(std::make_unique<my_worker>(ctx));
140  
            v.push_back(std::make_unique<my_worker>(ctx));
141  
        return v;
141  
        return v;
142  
    }
142  
    }
143  

143  

144  
    io_context ioc;
144  
    io_context ioc;
145  
    tcp_server srv(ioc, ioc.get_executor());
145  
    tcp_server srv(ioc, ioc.get_executor());
146  
    srv.set_workers(make_workers(ioc, 100));
146  
    srv.set_workers(make_workers(ioc, 100));
147  
    @endcode
147  
    @endcode
148  

148  

149  
    @see worker_base, set_workers, launcher
149  
    @see worker_base, set_workers, launcher
150  
*/
150  
*/
151  
class BOOST_COROSIO_DECL
151  
class BOOST_COROSIO_DECL
152  
    tcp_server
152  
    tcp_server
153  
{
153  
{
154  
public:
154  
public:
155  
    class worker_base;  ///< Abstract base for connection handlers.
155  
    class worker_base;  ///< Abstract base for connection handlers.
156  
    class launcher;     ///< Move-only handle to launch worker coroutines.
156  
    class launcher;     ///< Move-only handle to launch worker coroutines.
157  

157  

158  
private:
158  
private:
159  
    struct waiter
159  
    struct waiter
160  
    {
160  
    {
161  
        waiter* next;
161  
        waiter* next;
162  
        std::coroutine_handle<> h;
162  
        std::coroutine_handle<> h;
163  
        worker_base* w;
163  
        worker_base* w;
164  
    };
164  
    };
165  

165  

166  
    struct impl;
166  
    struct impl;
167  

167  

168  
    static impl* make_impl(capy::execution_context& ctx);
168  
    static impl* make_impl(capy::execution_context& ctx);
169  

169  

170  
    impl* impl_;
170  
    impl* impl_;
171  
    capy::any_executor ex_;
171  
    capy::any_executor ex_;
172  
    waiter* waiters_ = nullptr;
172  
    waiter* waiters_ = nullptr;
173  
    worker_base* idle_head_ = nullptr;    // Forward list: available workers
173  
    worker_base* idle_head_ = nullptr;    // Forward list: available workers
174  
    worker_base* active_head_ = nullptr;  // Doubly linked: workers handling connections
174  
    worker_base* active_head_ = nullptr;  // Doubly linked: workers handling connections
175  
    worker_base* active_tail_ = nullptr;  // Tail for O(1) push_back
175  
    worker_base* active_tail_ = nullptr;  // Tail for O(1) push_back
176  
    std::size_t active_accepts_ = 0;      // Number of active do_accept coroutines
176  
    std::size_t active_accepts_ = 0;      // Number of active do_accept coroutines
177  
    std::shared_ptr<void> storage_;       // Owns the worker container (type-erased)
177  
    std::shared_ptr<void> storage_;       // Owns the worker container (type-erased)
178  
    bool running_ = false;
178  
    bool running_ = false;
179  

179  

180  
    // Idle list (forward/singly linked) - push front, pop front
180  
    // Idle list (forward/singly linked) - push front, pop front
181  
    void idle_push(worker_base* w) noexcept
181  
    void idle_push(worker_base* w) noexcept
182  
    {
182  
    {
183  
        w->next_ = idle_head_;
183  
        w->next_ = idle_head_;
184  
        idle_head_ = w;
184  
        idle_head_ = w;
185  
    }
185  
    }
186  

186  

187  
    worker_base* idle_pop() noexcept
187  
    worker_base* idle_pop() noexcept
188  
    {
188  
    {
189  
        auto* w = idle_head_;
189  
        auto* w = idle_head_;
190  
        if(w) idle_head_ = w->next_;
190  
        if(w) idle_head_ = w->next_;
191  
        return w;
191  
        return w;
192  
    }
192  
    }
193  

193  

194  
    bool idle_empty() const noexcept { return idle_head_ == nullptr; }
194  
    bool idle_empty() const noexcept { return idle_head_ == nullptr; }
195  

195  

196  
    // Active list (doubly linked) - push back, remove anywhere
196  
    // Active list (doubly linked) - push back, remove anywhere
197  
    void active_push(worker_base* w) noexcept
197  
    void active_push(worker_base* w) noexcept
198  
    {
198  
    {
199  
        w->next_ = nullptr;
199  
        w->next_ = nullptr;
200  
        w->prev_ = active_tail_;
200  
        w->prev_ = active_tail_;
201  
        if(active_tail_)
201  
        if(active_tail_)
202  
            active_tail_->next_ = w;
202  
            active_tail_->next_ = w;
203  
        else
203  
        else
204  
            active_head_ = w;
204  
            active_head_ = w;
205  
        active_tail_ = w;
205  
        active_tail_ = w;
206  
    }
206  
    }
207  

207  

208  
    void active_remove(worker_base* w) noexcept
208  
    void active_remove(worker_base* w) noexcept
209  
    {
209  
    {
210  
        // Skip if not in active list (e.g., after failed accept)
210  
        // Skip if not in active list (e.g., after failed accept)
211  
        if(w != active_head_ && w->prev_ == nullptr)
211  
        if(w != active_head_ && w->prev_ == nullptr)
212  
            return;
212  
            return;
213  
        if(w->prev_)
213  
        if(w->prev_)
214  
            w->prev_->next_ = w->next_;
214  
            w->prev_->next_ = w->next_;
215  
        else
215  
        else
216  
            active_head_ = w->next_;
216  
            active_head_ = w->next_;
217  
        if(w->next_)
217  
        if(w->next_)
218  
            w->next_->prev_ = w->prev_;
218  
            w->next_->prev_ = w->prev_;
219  
        else
219  
        else
220  
            active_tail_ = w->prev_;
220  
            active_tail_ = w->prev_;
221  
        w->prev_ = nullptr;  // Mark as not in active list
221  
        w->prev_ = nullptr;  // Mark as not in active list
222  
    }
222  
    }
223  

223  

224  
    template<capy::Executor Ex>
224  
    template<capy::Executor Ex>
225  
    struct launch_wrapper
225  
    struct launch_wrapper
226  
    {
226  
    {
227  
        struct promise_type
227  
        struct promise_type
228  
        {
228  
        {
229  
            Ex ex;  // Executor stored directly in frame (outlives child tasks)
229  
            Ex ex;  // Executor stored directly in frame (outlives child tasks)
230  
            capy::io_env env_;
230  
            capy::io_env env_;
231  

231  

232  
            // For regular coroutines: first arg is executor, second is stop token
232  
            // For regular coroutines: first arg is executor, second is stop token
233  
            template<class E, class S, class... Args>
233  
            template<class E, class S, class... Args>
234  
                requires capy::Executor<std::decay_t<E>>
234  
                requires capy::Executor<std::decay_t<E>>
235  
            promise_type(E e, S s, Args&&...)
235  
            promise_type(E e, S s, Args&&...)
236  
                : ex(std::move(e))
236  
                : ex(std::move(e))
237  
                , env_{capy::executor_ref(ex), std::move(s),
237  
                , env_{capy::executor_ref(ex), std::move(s),
238  
                       capy::current_frame_allocator()}
238  
                       capy::current_frame_allocator()}
239  
            {
239  
            {
240  
            }
240  
            }
241  

241  

242  
            // For lambda coroutines: first arg is closure, second is executor, third is stop token
242  
            // For lambda coroutines: first arg is closure, second is executor, third is stop token
243  
            template<class Closure, class E, class S, class... Args>
243  
            template<class Closure, class E, class S, class... Args>
244  
                requires (!capy::Executor<std::decay_t<Closure>> && 
244  
                requires (!capy::Executor<std::decay_t<Closure>> && 
245  
                          capy::Executor<std::decay_t<E>>)
245  
                          capy::Executor<std::decay_t<E>>)
246  
            promise_type(Closure&&, E e, S s, Args&&...)
246  
            promise_type(Closure&&, E e, S s, Args&&...)
247  
                : ex(std::move(e))
247  
                : ex(std::move(e))
248  
                , env_{capy::executor_ref(ex), std::move(s),
248  
                , env_{capy::executor_ref(ex), std::move(s),
249  
                       capy::current_frame_allocator()}
249  
                       capy::current_frame_allocator()}
250  
            {
250  
            {
251  
            }
251  
            }
252  

252  

253  
            launch_wrapper get_return_object() noexcept {
253  
            launch_wrapper get_return_object() noexcept {
254  
                return {std::coroutine_handle<promise_type>::from_promise(*this)};
254  
                return {std::coroutine_handle<promise_type>::from_promise(*this)};
255  
            }
255  
            }
256  
            std::suspend_always initial_suspend() noexcept { return {}; }
256  
            std::suspend_always initial_suspend() noexcept { return {}; }
257  
            std::suspend_never final_suspend() noexcept { return {}; }
257  
            std::suspend_never final_suspend() noexcept { return {}; }
258  
            void return_void() noexcept {}
258  
            void return_void() noexcept {}
259  
            void unhandled_exception() { std::terminate(); }
259  
            void unhandled_exception() { std::terminate(); }
260  

260  

261  
            // Inject io_env for IoAwaitable
261  
            // Inject io_env for IoAwaitable
262  
            template<capy::IoAwaitable Awaitable>
262  
            template<capy::IoAwaitable Awaitable>
263  
            auto await_transform(Awaitable&& a)
263  
            auto await_transform(Awaitable&& a)
264  
            {
264  
            {
265  
                using AwaitableT = std::decay_t<Awaitable>;
265  
                using AwaitableT = std::decay_t<Awaitable>;
266  
                struct adapter
266  
                struct adapter
267  
                {
267  
                {
268  
                    AwaitableT aw;
268  
                    AwaitableT aw;
269  
                    capy::io_env const* env;
269  
                    capy::io_env const* env;
270  

270  

271  
                    bool await_ready() { return aw.await_ready(); }
271  
                    bool await_ready() { return aw.await_ready(); }
272  
                    decltype(auto) await_resume() { return aw.await_resume(); }
272  
                    decltype(auto) await_resume() { return aw.await_resume(); }
273  

273  

274  
                    auto await_suspend(std::coroutine_handle<promise_type> h)
274  
                    auto await_suspend(std::coroutine_handle<promise_type> h)
275  
                    {
275  
                    {
276  
                        return aw.await_suspend(h, env);
276  
                        return aw.await_suspend(h, env);
277  
                    }
277  
                    }
278  
                };
278  
                };
279  
                return adapter{std::forward<Awaitable>(a), &env_};
279  
                return adapter{std::forward<Awaitable>(a), &env_};
280  
            }
280  
            }
281  
        };
281  
        };
282  

282  

283  
        std::coroutine_handle<promise_type> h;
283  
        std::coroutine_handle<promise_type> h;
284  

284  

285  
        launch_wrapper(std::coroutine_handle<promise_type> handle) noexcept
285  
        launch_wrapper(std::coroutine_handle<promise_type> handle) noexcept
286  
            : h(handle)
286  
            : h(handle)
287  
        {
287  
        {
288  
        }
288  
        }
289  

289  

290  
        ~launch_wrapper()
290  
        ~launch_wrapper()
291  
        {
291  
        {
292  
            if(h)
292  
            if(h)
293  
                h.destroy();
293  
                h.destroy();
294  
        }
294  
        }
295  

295  

296  
        launch_wrapper(launch_wrapper&& o) noexcept
296  
        launch_wrapper(launch_wrapper&& o) noexcept
297  
            : h(std::exchange(o.h, nullptr))
297  
            : h(std::exchange(o.h, nullptr))
298  
        {
298  
        {
299  
        }
299  
        }
300  

300  

301  
        launch_wrapper(launch_wrapper const&) = delete;
301  
        launch_wrapper(launch_wrapper const&) = delete;
302  
        launch_wrapper& operator=(launch_wrapper const&) = delete;
302  
        launch_wrapper& operator=(launch_wrapper const&) = delete;
303  
        launch_wrapper& operator=(launch_wrapper&&) = delete;
303  
        launch_wrapper& operator=(launch_wrapper&&) = delete;
304  
    };
304  
    };
305  

305  

306  
    // Named functor to avoid incomplete lambda type in coroutine promise
306  
    // Named functor to avoid incomplete lambda type in coroutine promise
307  
    template<class Executor>
307  
    template<class Executor>
308  
    struct launch_coro
308  
    struct launch_coro
309  
    {
309  
    {
310  
        launch_wrapper<Executor> operator()(
310  
        launch_wrapper<Executor> operator()(
311  
            Executor,
311  
            Executor,
312  
            std::stop_token,
312  
            std::stop_token,
313  
            tcp_server* self,
313  
            tcp_server* self,
314  
            capy::task<void> t,
314  
            capy::task<void> t,
315  
            worker_base* wp)
315  
            worker_base* wp)
316  
        {
316  
        {
317  
            // Executor and stop token stored in promise via constructor
317  
            // Executor and stop token stored in promise via constructor
318  
            co_await std::move(t);
318  
            co_await std::move(t);
319  
            co_await self->push(*wp); // worker goes back to idle list
319  
            co_await self->push(*wp); // worker goes back to idle list
320  
        }
320  
        }
321  
    };
321  
    };
322  

322  

323  
    class push_awaitable
323  
    class push_awaitable
324  
    {
324  
    {
325  
        tcp_server& self_;
325  
        tcp_server& self_;
326  
        worker_base& w_;
326  
        worker_base& w_;
327  

327  

328  
    public:
328  
    public:
329  
        push_awaitable(
329  
        push_awaitable(
330  
            tcp_server& self,
330  
            tcp_server& self,
331  
            worker_base& w) noexcept
331  
            worker_base& w) noexcept
332  
            : self_(self)
332  
            : self_(self)
333  
            , w_(w)
333  
            , w_(w)
334  
        {
334  
        {
335  
        }
335  
        }
336  

336  

337  
        bool await_ready() const noexcept
337  
        bool await_ready() const noexcept
338  
        {
338  
        {
339  
            return false;
339  
            return false;
340  
        }
340  
        }
341  

341  

342  
        std::coroutine_handle<>
342  
        std::coroutine_handle<>
343  
        await_suspend(
343  
        await_suspend(
344  
            std::coroutine_handle<> h,
344  
            std::coroutine_handle<> h,
345  
            capy::io_env const*) noexcept
345  
            capy::io_env const*) noexcept
346  
        {
346  
        {
347  
            // Symmetric transfer to server's executor
347  
            // Symmetric transfer to server's executor
348  
            return self_.ex_.dispatch(h);
348  
            return self_.ex_.dispatch(h);
349  
        }
349  
        }
350  

350  

351  
        void await_resume() noexcept
351  
        void await_resume() noexcept
352  
        {
352  
        {
353  
            // Running on server executor - safe to modify lists
353  
            // Running on server executor - safe to modify lists
354  
            // Remove from active (if present), then wake waiter or add to idle
354  
            // Remove from active (if present), then wake waiter or add to idle
355  
            self_.active_remove(&w_);
355  
            self_.active_remove(&w_);
356  
            if(self_.waiters_)
356  
            if(self_.waiters_)
357  
            {
357  
            {
358  
                auto* wait = self_.waiters_;
358  
                auto* wait = self_.waiters_;
359  
                self_.waiters_ = wait->next;
359  
                self_.waiters_ = wait->next;
360  
                wait->w = &w_;
360  
                wait->w = &w_;
361  
                self_.ex_.post(wait->h);
361  
                self_.ex_.post(wait->h);
362  
            }
362  
            }
363  
            else
363  
            else
364  
            {
364  
            {
365  
                self_.idle_push(&w_);
365  
                self_.idle_push(&w_);
366  
            }
366  
            }
367  
        }
367  
        }
368  
    };
368  
    };
369  

369  

370  
    class pop_awaitable
370  
    class pop_awaitable
371  
    {
371  
    {
372  
        tcp_server& self_;
372  
        tcp_server& self_;
373  
        waiter wait_;
373  
        waiter wait_;
374  

374  

375  
    public:
375  
    public:
376  
        pop_awaitable(tcp_server& self) noexcept
376  
        pop_awaitable(tcp_server& self) noexcept
377  
            : self_(self)
377  
            : self_(self)
378  
            , wait_{}
378  
            , wait_{}
379  
        {
379  
        {
380  
        }
380  
        }
381  

381  

382  
        bool await_ready() const noexcept
382  
        bool await_ready() const noexcept
383  
        {
383  
        {
384  
            return !self_.idle_empty();
384  
            return !self_.idle_empty();
385  
        }
385  
        }
386  

386  

387  
        bool
387  
        bool
388  
        await_suspend(
388  
        await_suspend(
389  
            std::coroutine_handle<> h,
389  
            std::coroutine_handle<> h,
390  
            capy::io_env const*) noexcept
390  
            capy::io_env const*) noexcept
391  
        {
391  
        {
392  
            // Running on server executor (do_accept runs there)
392  
            // Running on server executor (do_accept runs there)
393  
            wait_.h = h;
393  
            wait_.h = h;
394  
            wait_.w = nullptr;
394  
            wait_.w = nullptr;
395  
            wait_.next = self_.waiters_;
395  
            wait_.next = self_.waiters_;
396  
            self_.waiters_ = &wait_;
396  
            self_.waiters_ = &wait_;
397  
            return true;
397  
            return true;
398  
        }
398  
        }
399  

399  

400  
        worker_base& await_resume() noexcept
400  
        worker_base& await_resume() noexcept
401  
        {
401  
        {
402  
            // Running on server executor
402  
            // Running on server executor
403  
            if(wait_.w)
403  
            if(wait_.w)
404  
                return *wait_.w;  // Woken by push_awaitable
404  
                return *wait_.w;  // Woken by push_awaitable
405  
            return *self_.idle_pop();
405  
            return *self_.idle_pop();
406  
        }
406  
        }
407  
    };
407  
    };
408  

408  

409  
    push_awaitable push(worker_base& w)
409  
    push_awaitable push(worker_base& w)
410  
    {
410  
    {
411  
        return push_awaitable{*this, w};
411  
        return push_awaitable{*this, w};
412  
    }
412  
    }
413  

413  

414  
    // Synchronous version for destructor/guard paths
414  
    // Synchronous version for destructor/guard paths
415  
    // Must be called from server executor context
415  
    // Must be called from server executor context
416  
    void push_sync(worker_base& w) noexcept
416  
    void push_sync(worker_base& w) noexcept
417  
    {
417  
    {
418  
        active_remove(&w);
418  
        active_remove(&w);
419  
        if(waiters_)
419  
        if(waiters_)
420  
        {
420  
        {
421  
            auto* wait = waiters_;
421  
            auto* wait = waiters_;
422  
            waiters_ = wait->next;
422  
            waiters_ = wait->next;
423  
            wait->w = &w;
423  
            wait->w = &w;
424  
            ex_.post(wait->h);
424  
            ex_.post(wait->h);
425  
        }
425  
        }
426  
        else
426  
        else
427  
        {
427  
        {
428  
            idle_push(&w);
428  
            idle_push(&w);
429  
        }
429  
        }
430  
    }
430  
    }
431  

431  

432  
    pop_awaitable pop()
432  
    pop_awaitable pop()
433  
    {
433  
    {
434  
        return pop_awaitable{*this};
434  
        return pop_awaitable{*this};
435  
    }
435  
    }
436  

436  

437  
    capy::task<void> do_accept(tcp_acceptor& acc);
437  
    capy::task<void> do_accept(tcp_acceptor& acc);
438  

438  

439  
public:
439  
public:
440  
    /** Abstract base class for connection handlers.
440  
    /** Abstract base class for connection handlers.
441  

441  

442  
        Derive from this class to implement custom connection handling.
442  
        Derive from this class to implement custom connection handling.
443  
        Each worker owns a socket and is reused across multiple
443  
        Each worker owns a socket and is reused across multiple
444  
        connections to avoid per-connection allocation.
444  
        connections to avoid per-connection allocation.
445  

445  

446  
        @see tcp_server, launcher
446  
        @see tcp_server, launcher
447  
    */
447  
    */
448  
    class BOOST_COROSIO_DECL
448  
    class BOOST_COROSIO_DECL
449  
        worker_base
449  
        worker_base
450  
    {
450  
    {
451  
        // Ordered largest to smallest for optimal packing
451  
        // Ordered largest to smallest for optimal packing
452  
        std::stop_source stop_;        // ~16 bytes
452  
        std::stop_source stop_;        // ~16 bytes
453  
        worker_base* next_ = nullptr;  // 8 bytes - used by idle and active lists
453  
        worker_base* next_ = nullptr;  // 8 bytes - used by idle and active lists
454  
        worker_base* prev_ = nullptr;  // 8 bytes - used only by active list
454  
        worker_base* prev_ = nullptr;  // 8 bytes - used only by active list
455  

455  

456  
        friend class tcp_server;
456  
        friend class tcp_server;
457  

457  

458  
    public:
458  
    public:
459  
        /// Destroy the worker.
459  
        /// Destroy the worker.
460  
        virtual ~worker_base() = default;
460  
        virtual ~worker_base() = default;
461  

461  

462  
        /** Handle an accepted connection.
462  
        /** Handle an accepted connection.
463  

463  

464  
            Called when this worker is dispatched to handle a new
464  
            Called when this worker is dispatched to handle a new
465  
            connection. The implementation must invoke the launcher
465  
            connection. The implementation must invoke the launcher
466  
            exactly once to start the handling coroutine.
466  
            exactly once to start the handling coroutine.
467  

467  

468  
            @param launch Handle to launch the connection coroutine.
468  
            @param launch Handle to launch the connection coroutine.
469  
        */
469  
        */
470  
        virtual void run(launcher launch) = 0;
470  
        virtual void run(launcher launch) = 0;
471  

471  

472  
        /// Return the socket used for connections.
472  
        /// Return the socket used for connections.
473  
        virtual corosio::tcp_socket& socket() = 0;
473  
        virtual corosio::tcp_socket& socket() = 0;
474  
    };
474  
    };
475  

475  

476  
    /** Move-only handle to launch a worker coroutine.
476  
    /** Move-only handle to launch a worker coroutine.
477  

477  

478  
        Passed to @ref worker_base::run to start the connection-handling
478  
        Passed to @ref worker_base::run to start the connection-handling
479  
        coroutine. The launcher ensures the worker returns to the idle
479  
        coroutine. The launcher ensures the worker returns to the idle
480  
        pool when the coroutine completes or if launching fails.
480  
        pool when the coroutine completes or if launching fails.
481  

481  

482  
        The launcher must be invoked exactly once via `operator()`.
482  
        The launcher must be invoked exactly once via `operator()`.
483  
        If destroyed without invoking, the worker is returned to the
483  
        If destroyed without invoking, the worker is returned to the
484  
        idle pool automatically.
484  
        idle pool automatically.
485  

485  

486  
        @see worker_base::run
486  
        @see worker_base::run
487  
    */
487  
    */
488  
    class BOOST_COROSIO_DECL
488  
    class BOOST_COROSIO_DECL
489  
        launcher
489  
        launcher
490  
    {
490  
    {
491  
        tcp_server* srv_;
491  
        tcp_server* srv_;
492  
        worker_base* w_;
492  
        worker_base* w_;
493  

493  

494  
        friend class tcp_server;
494  
        friend class tcp_server;
495  

495  

496  
        launcher(tcp_server& srv, worker_base& w) noexcept
496  
        launcher(tcp_server& srv, worker_base& w) noexcept
497  
            : srv_(&srv)
497  
            : srv_(&srv)
498  
            , w_(&w)
498  
            , w_(&w)
499  
        {
499  
        {
500  
        }
500  
        }
501  

501  

502  
    public:
502  
    public:
503  
        /// Return the worker to the pool if not launched.
503  
        /// Return the worker to the pool if not launched.
504  
        ~launcher()
504  
        ~launcher()
505  
        {
505  
        {
506  
            if(w_)
506  
            if(w_)
507  
                srv_->push_sync(*w_);
507  
                srv_->push_sync(*w_);
508  
        }
508  
        }
509  

509  

510  
        launcher(launcher&& o) noexcept
510  
        launcher(launcher&& o) noexcept
511  
            : srv_(o.srv_)
511  
            : srv_(o.srv_)
512  
            , w_(std::exchange(o.w_, nullptr))
512  
            , w_(std::exchange(o.w_, nullptr))
513  
        {
513  
        {
514  
        }
514  
        }
515  
        launcher(launcher const&) = delete;
515  
        launcher(launcher const&) = delete;
516  
        launcher& operator=(launcher const&) = delete;
516  
        launcher& operator=(launcher const&) = delete;
517  
        launcher& operator=(launcher&&) = delete;
517  
        launcher& operator=(launcher&&) = delete;
518  

518  

519  
        /** Launch the connection-handling coroutine.
519  
        /** Launch the connection-handling coroutine.
520  

520  

521  
            Starts the given coroutine on the specified executor. When
521  
            Starts the given coroutine on the specified executor. When
522  
            the coroutine completes, the worker is automatically returned
522  
            the coroutine completes, the worker is automatically returned
523  
            to the idle pool.
523  
            to the idle pool.
524  

524  

525  
            @param ex The executor to run the coroutine on.
525  
            @param ex The executor to run the coroutine on.
526  
            @param task The coroutine to execute.
526  
            @param task The coroutine to execute.
527  

527  

528  
            @throws std::logic_error If this launcher was already invoked.
528  
            @throws std::logic_error If this launcher was already invoked.
529  
        */
529  
        */
530  
        template<class Executor>
530  
        template<class Executor>
531  
        void operator()(Executor const& ex, capy::task<void> task)
531  
        void operator()(Executor const& ex, capy::task<void> task)
532  
        {
532  
        {
533  
            if(! w_)
533  
            if(! w_)
534  
                detail::throw_logic_error(); // launcher already invoked
534  
                detail::throw_logic_error(); // launcher already invoked
535  

535  

536  
            auto* w = std::exchange(w_, nullptr);
536  
            auto* w = std::exchange(w_, nullptr);
537  

537  

538  
            // Worker is being dispatched - add to active list
538  
            // Worker is being dispatched - add to active list
539  
            srv_->active_push(w);
539  
            srv_->active_push(w);
540  

540  

541  
            // Return worker to pool if coroutine setup throws
541  
            // Return worker to pool if coroutine setup throws
542  
            struct guard_t {
542  
            struct guard_t {
543  
                tcp_server* srv;
543  
                tcp_server* srv;
544  
                worker_base* w;
544  
                worker_base* w;
545  
                ~guard_t() { if(w) srv->push_sync(*w); }
545  
                ~guard_t() { if(w) srv->push_sync(*w); }
546  
            } guard{srv_, w};
546  
            } guard{srv_, w};
547  

547  

548  
            // Reset worker's stop source for this connection
548  
            // Reset worker's stop source for this connection
549  
            w->stop_ = {};
549  
            w->stop_ = {};
550  
            auto st = w->stop_.get_token();
550  
            auto st = w->stop_.get_token();
551  

551  

552  
            auto wrapper = launch_coro<Executor>{}(
552  
            auto wrapper = launch_coro<Executor>{}(
553  
                ex, st, srv_, std::move(task), w);
553  
                ex, st, srv_, std::move(task), w);
554  

554  

555  
            // Executor and stop token stored in promise via constructor
555  
            // Executor and stop token stored in promise via constructor
556  
            ex.post(std::exchange(wrapper.h, nullptr)); // Release before post
556  
            ex.post(std::exchange(wrapper.h, nullptr)); // Release before post
557  
            guard.w = nullptr; // Success - dismiss guard
557  
            guard.w = nullptr; // Success - dismiss guard
558  
        }
558  
        }
559  
    };
559  
    };
560  

560  

561  
    /** Construct a TCP server.
561  
    /** Construct a TCP server.
562  

562  

563  
        @tparam Ctx Execution context type satisfying ExecutionContext.
563  
        @tparam Ctx Execution context type satisfying ExecutionContext.
564  
        @tparam Ex Executor type satisfying Executor.
564  
        @tparam Ex Executor type satisfying Executor.
565  

565  

566  
        @param ctx The execution context for socket operations.
566  
        @param ctx The execution context for socket operations.
567  
        @param ex The executor for dispatching coroutines.
567  
        @param ex The executor for dispatching coroutines.
568  

568  

569  
        @par Example
569  
        @par Example
570  
        @code
570  
        @code
571  
        tcp_server srv(ctx, ctx.get_executor());
571  
        tcp_server srv(ctx, ctx.get_executor());
572  
        srv.set_workers(make_workers(ctx, 100));
572  
        srv.set_workers(make_workers(ctx, 100));
573  
        srv.bind(endpoint{...});
573  
        srv.bind(endpoint{...});
574  
        srv.start();
574  
        srv.start();
575  
        @endcode
575  
        @endcode
576  
    */
576  
    */
577  
    template<
577  
    template<
578  
        capy::ExecutionContext Ctx,
578  
        capy::ExecutionContext Ctx,
579  
        capy::Executor Ex>
579  
        capy::Executor Ex>
580  
    tcp_server(Ctx& ctx, Ex ex)
580  
    tcp_server(Ctx& ctx, Ex ex)
581  
        : impl_(make_impl(ctx))
581  
        : impl_(make_impl(ctx))
582  
        , ex_(std::move(ex))
582  
        , ex_(std::move(ex))
583  
    {
583  
    {
584  
    }
584  
    }
585  

585  

586  
public:
586  
public:
587  
    ~tcp_server();
587  
    ~tcp_server();
588  
    tcp_server(tcp_server const&) = delete;
588  
    tcp_server(tcp_server const&) = delete;
589  
    tcp_server& operator=(tcp_server const&) = delete;
589  
    tcp_server& operator=(tcp_server const&) = delete;
590  
    tcp_server(tcp_server&& o) noexcept;
590  
    tcp_server(tcp_server&& o) noexcept;
591  
    tcp_server& operator=(tcp_server&& o) noexcept;
591  
    tcp_server& operator=(tcp_server&& o) noexcept;
592  

592  

593  
    /** Bind to a local endpoint.
593  
    /** Bind to a local endpoint.
594  

594  

595  
        Creates an acceptor listening on the specified endpoint.
595  
        Creates an acceptor listening on the specified endpoint.
596  
        Multiple endpoints can be bound by calling this method
596  
        Multiple endpoints can be bound by calling this method
597  
        multiple times before @ref start.
597  
        multiple times before @ref start.
598  

598  

599  
        @param ep The local endpoint to bind to.
599  
        @param ep The local endpoint to bind to.
600  

600  

601  
        @return The error code if binding fails.
601  
        @return The error code if binding fails.
602  
    */
602  
    */
603  
    std::error_code
603  
    std::error_code
604  
    bind(endpoint ep);
604  
    bind(endpoint ep);
605  

605  

606  
    /** Set the worker pool.
606  
    /** Set the worker pool.
607  

607  

608  
        Replaces any existing workers with the given range. Any
608  
        Replaces any existing workers with the given range. Any
609  
        previous workers are released and the idle/active lists
609  
        previous workers are released and the idle/active lists
610  
        are cleared before populating with new workers.
610  
        are cleared before populating with new workers.
611  

611  

612  
        @tparam Range Forward range of pointer-like objects to worker_base.
612  
        @tparam Range Forward range of pointer-like objects to worker_base.
613  

613  

614  
        @param workers Range of workers to manage. Each element must
614  
        @param workers Range of workers to manage. Each element must
615  
            support `std::to_address()` yielding `worker_base*`.
615  
            support `std::to_address()` yielding `worker_base*`.
616  

616  

617  
        @par Example
617  
        @par Example
618  
        @code
618  
        @code
619  
        std::vector<std::unique_ptr<my_worker>> workers;
619  
        std::vector<std::unique_ptr<my_worker>> workers;
620  
        for(int i = 0; i < 100; ++i)
620  
        for(int i = 0; i < 100; ++i)
621  
            workers.push_back(std::make_unique<my_worker>(ctx));
621  
            workers.push_back(std::make_unique<my_worker>(ctx));
622  
        srv.set_workers(std::move(workers));
622  
        srv.set_workers(std::move(workers));
623  
        @endcode
623  
        @endcode
624  
    */
624  
    */
625  
    template<std::ranges::forward_range Range>
625  
    template<std::ranges::forward_range Range>
626  
        requires std::convertible_to<
626  
        requires std::convertible_to<
627  
            decltype(std::to_address(
627  
            decltype(std::to_address(
628  
                std::declval<std::ranges::range_value_t<Range>&>())),
628  
                std::declval<std::ranges::range_value_t<Range>&>())),
629  
            worker_base*>
629  
            worker_base*>
630  
    void
630  
    void
631  
    set_workers(Range&& workers)
631  
    set_workers(Range&& workers)
632  
    {
632  
    {
633  
        // Clear existing state
633  
        // Clear existing state
634  
        storage_.reset();
634  
        storage_.reset();
635  
        idle_head_ = nullptr;
635  
        idle_head_ = nullptr;
636  
        active_head_ = nullptr;
636  
        active_head_ = nullptr;
637  
        active_tail_ = nullptr;
637  
        active_tail_ = nullptr;
638  

638  

639  
        // Take ownership and populate idle list
639  
        // Take ownership and populate idle list
640  
        using StorageType = std::decay_t<Range>;
640  
        using StorageType = std::decay_t<Range>;
641  
        auto* p = new StorageType(std::forward<Range>(workers));
641  
        auto* p = new StorageType(std::forward<Range>(workers));
642  
        storage_ = std::shared_ptr<void>(p, [](void* ptr) {
642  
        storage_ = std::shared_ptr<void>(p, [](void* ptr) {
643  
            delete static_cast<StorageType*>(ptr);
643  
            delete static_cast<StorageType*>(ptr);
644  
        });
644  
        });
645  
        for(auto&& elem : *static_cast<StorageType*>(p))
645  
        for(auto&& elem : *static_cast<StorageType*>(p))
646  
            idle_push(std::to_address(elem));
646  
            idle_push(std::to_address(elem));
647  
    }
647  
    }
648  

648  

649  
    /** Start accepting connections.
649  
    /** Start accepting connections.
650  

650  

651  
        Launches accept loops for all bound endpoints. Incoming
651  
        Launches accept loops for all bound endpoints. Incoming
652  
        connections are dispatched to idle workers from the pool.
652  
        connections are dispatched to idle workers from the pool.
653  
        
653  
        
654  
        Calling `start()` on an already-running server has no effect.
654  
        Calling `start()` on an already-running server has no effect.
655  

655  

656  
        @par Preconditions
656  
        @par Preconditions
657  
        - At least one endpoint bound via @ref bind.
657  
        - At least one endpoint bound via @ref bind.
658  
        - Workers provided to the constructor.
658  
        - Workers provided to the constructor.
659  
        - If restarting, @ref join must have completed first.
659  
        - If restarting, @ref join must have completed first.
660  

660  

661  
        @par Effects
661  
        @par Effects
662  
        Creates one accept coroutine per bound endpoint. Each coroutine
662  
        Creates one accept coroutine per bound endpoint. Each coroutine
663  
        runs on the server's executor, waiting for connections and
663  
        runs on the server's executor, waiting for connections and
664  
        dispatching them to idle workers.
664  
        dispatching them to idle workers.
665  

665  

666  
        @par Restart Sequence
666  
        @par Restart Sequence
667  
        To restart after stopping, complete the full shutdown cycle:
667  
        To restart after stopping, complete the full shutdown cycle:
668  
        @code
668  
        @code
669  
        srv.start();
669  
        srv.start();
670  
        ioc.run_for( 1s );
670  
        ioc.run_for( 1s );
671  
        srv.stop();       // 1. Signal shutdown
671  
        srv.stop();       // 1. Signal shutdown
672  
        ioc.run();        // 2. Drain remaining completions
672  
        ioc.run();        // 2. Drain remaining completions
673  
        srv.join();       // 3. Wait for accept loops
673  
        srv.join();       // 3. Wait for accept loops
674  

674  

675  
        // Now safe to restart
675  
        // Now safe to restart
676  
        srv.start();
676  
        srv.start();
677  
        ioc.run();
677  
        ioc.run();
678  
        @endcode
678  
        @endcode
679  

679  

680  
        @par Thread Safety
680  
        @par Thread Safety
681  
        Not thread safe.
681  
        Not thread safe.
682  
        
682  
        
683  
        @throws std::logic_error If a previous session has not been
683  
        @throws std::logic_error If a previous session has not been
684  
            joined (accept loops still active).
684  
            joined (accept loops still active).
685  
    */
685  
    */
686  
    void start();
686  
    void start();
687  

687  

688  
    /** Stop accepting connections.
688  
    /** Stop accepting connections.
689  

689  

690  
        Signals all listening ports to stop accepting new connections
690  
        Signals all listening ports to stop accepting new connections
691  
        and requests cancellation of active workers via their stop tokens.
691  
        and requests cancellation of active workers via their stop tokens.
692  
        
692  
        
693  
        This function returns immediately; it does not wait for workers
693  
        This function returns immediately; it does not wait for workers
694  
        to finish. Pending I/O operations complete asynchronously.
694  
        to finish. Pending I/O operations complete asynchronously.
695  

695  

696  
        Calling `stop()` on a non-running server has no effect.
696  
        Calling `stop()` on a non-running server has no effect.
697  

697  

698  
        @par Effects
698  
        @par Effects
699  
        - Closes all acceptors (pending accepts complete with error).
699  
        - Closes all acceptors (pending accepts complete with error).
700  
        - Requests stop on each active worker's stop token.
700  
        - Requests stop on each active worker's stop token.
701  
        - Workers observing their stop token should exit promptly.
701  
        - Workers observing their stop token should exit promptly.
702  

702  

703  
        @par Postconditions
703  
        @par Postconditions
704  
        No new connections will be accepted. Active workers continue
704  
        No new connections will be accepted. Active workers continue
705  
        until they observe their stop token or complete naturally.
705  
        until they observe their stop token or complete naturally.
706  

706  

707  
        @par What Happens Next
707  
        @par What Happens Next
708  
        After calling `stop()`:
708  
        After calling `stop()`:
709  
        1. Let `ioc.run()` return (drains pending completions).
709  
        1. Let `ioc.run()` return (drains pending completions).
710  
        2. Call @ref join to wait for accept loops to finish.
710  
        2. Call @ref join to wait for accept loops to finish.
711  
        3. Only then is it safe to restart or destroy the server.
711  
        3. Only then is it safe to restart or destroy the server.
712  

712  

713  
        @par Thread Safety
713  
        @par Thread Safety
714  
        Not thread safe.
714  
        Not thread safe.
715  

715  

716  
        @see join, start
716  
        @see join, start
717  
    */
717  
    */
718  
    void stop();
718  
    void stop();
719  

719  

720  
    /** Block until all accept loops complete.
720  
    /** Block until all accept loops complete.
721  

721  

722  
        Blocks the calling thread until all accept coroutines launched
722  
        Blocks the calling thread until all accept coroutines launched
723  
        by @ref start have finished executing. This synchronizes the
723  
        by @ref start have finished executing. This synchronizes the
724  
        shutdown sequence, ensuring the server is fully stopped before
724  
        shutdown sequence, ensuring the server is fully stopped before
725  
        restarting or destroying it.
725  
        restarting or destroying it.
726  

726  

727  
        @par Preconditions
727  
        @par Preconditions
728  
        @ref stop has been called and `ioc.run()` has returned.
728  
        @ref stop has been called and `ioc.run()` has returned.
729  

729  

730  
        @par Postconditions
730  
        @par Postconditions
731  
        All accept loops have completed. The server is in the stopped
731  
        All accept loops have completed. The server is in the stopped
732  
        state and may be restarted via @ref start.
732  
        state and may be restarted via @ref start.
733  

733  

734  
        @par Example (Correct Usage)
734  
        @par Example (Correct Usage)
735  
        @code
735  
        @code
736  
        // main thread
736  
        // main thread
737  
        srv.start();
737  
        srv.start();
738  
        ioc.run();      // Blocks until work completes
738  
        ioc.run();      // Blocks until work completes
739  
        srv.join();     // Safe: called after ioc.run() returns
739  
        srv.join();     // Safe: called after ioc.run() returns
740  
        @endcode
740  
        @endcode
741  

741  

742  
        @par WARNING: Deadlock Scenarios
742  
        @par WARNING: Deadlock Scenarios
743  
        Calling `join()` from the wrong context causes deadlock:
743  
        Calling `join()` from the wrong context causes deadlock:
744  

744  

745  
        @code
745  
        @code
746  
        // WRONG: calling join() from inside a worker coroutine
746  
        // WRONG: calling join() from inside a worker coroutine
747  
        void run( launcher launch ) override
747  
        void run( launcher launch ) override
748  
        {
748  
        {
749  
            launch( ex, [this]() -> capy::task<>
749  
            launch( ex, [this]() -> capy::task<>
750  
            {
750  
            {
751  
                srv_.join();  // DEADLOCK: blocks the executor
751  
                srv_.join();  // DEADLOCK: blocks the executor
752  
                co_return;
752  
                co_return;
753  
            }());
753  
            }());
754  
        }
754  
        }
755  

755  

756  
        // WRONG: calling join() while ioc.run() is still active
756  
        // WRONG: calling join() while ioc.run() is still active
757  
        std::thread t( [&]{ ioc.run(); } );
757  
        std::thread t( [&]{ ioc.run(); } );
758  
        srv.stop();
758  
        srv.stop();
759  
        srv.join();  // DEADLOCK: ioc.run() still running in thread t
759  
        srv.join();  // DEADLOCK: ioc.run() still running in thread t
760  
        @endcode
760  
        @endcode
761  

761  

762  
        @par Thread Safety
762  
        @par Thread Safety
763  
        May be called from any thread, but will deadlock if called
763  
        May be called from any thread, but will deadlock if called
764  
        from within the io_context event loop or from a worker coroutine.
764  
        from within the io_context event loop or from a worker coroutine.
765  

765  

766  
        @see stop, start
766  
        @see stop, start
767  
    */
767  
    */
768  
    void join();
768  
    void join();
769  

769  

770  
private:
770  
private:
771  
    capy::task<> do_stop();
771  
    capy::task<> do_stop();
772  
};
772  
};
773  

773  

774  
#ifdef _MSC_VER
774  
#ifdef _MSC_VER
775  
#pragma warning(pop)
775  
#pragma warning(pop)
776  
#endif
776  
#endif
777  

777  

778  
} // namespace boost::corosio
778  
} // namespace boost::corosio
779  

779  

780  
#endif
780  
#endif