1  
//
1  
//
2  
// Copyright (c) 2026 Vinnie Falco (vinnie dot falco at gmail dot com)
2  
// Copyright (c) 2026 Vinnie Falco (vinnie dot falco at gmail dot 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/run_async.hpp>
24  
#include <boost/capy/ex/run_async.hpp>
25  

25  

26  
#include <coroutine>
26  
#include <coroutine>
27  
#include <memory>
27  
#include <memory>
28  
#include <ranges>
28  
#include <ranges>
29  
#include <vector>
29  
#include <vector>
30  

30  

31  
namespace boost::corosio {
31  
namespace boost::corosio {
32  

32  

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

37  

38  
/** TCP server with pooled workers.
38  
/** TCP server with pooled workers.
39  

39  

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

45  

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

49  

50  
    @par Thread Safety
50  
    @par Thread Safety
51  
    Distinct objects: Safe.
51  
    Distinct objects: Safe.
52  
    Shared objects: Unsafe.
52  
    Shared objects: Unsafe.
53  

53  

54  
    @par Lifecycle
54  
    @par Lifecycle
55  
    The server operates in three states:
55  
    The server operates in three states:
56  

56  

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

60  

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

65  

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

75  

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

81  

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

86  

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

96  

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

101  

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

107  

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

120  

121  
        corosio::tcp_socket& socket() override { return sock_; }
121  
        corosio::tcp_socket& socket() override { return sock_; }
122  

122  

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

132  

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

141  

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

146  

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

155  

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

163  

164  
    struct impl;
164  
    struct impl;
165  

165  

166  
    static impl* make_impl(capy::execution_context& ctx);
166  
    static impl* make_impl(capy::execution_context& ctx);
167  

167  

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

177  

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

184  

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

191  

192  
    bool idle_empty() const noexcept { return idle_head_ == nullptr; }
192  
    bool idle_empty() const noexcept { return idle_head_ == nullptr; }
193  

193  

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

205  

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

221  

222  
    template<capy::Executor Ex>
222  
    template<capy::Executor Ex>
223  
    struct launch_wrapper
223  
    struct launch_wrapper
224  
    {
224  
    {
225  
        struct promise_type
225  
        struct promise_type
226  
        {
226  
        {
227  
            Ex ex;  // Stored directly in frame, no allocation
227  
            Ex ex;  // Stored directly in frame, no allocation
228  
            std::stop_token st;
228  
            std::stop_token st;
229  

229  

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

238  

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

248  

249  
            launch_wrapper get_return_object() noexcept {
249  
            launch_wrapper get_return_object() noexcept {
250  
                return {std::coroutine_handle<promise_type>::from_promise(*this)};
250  
                return {std::coroutine_handle<promise_type>::from_promise(*this)};
251  
            }
251  
            }
252  
            std::suspend_always initial_suspend() noexcept { return {}; }
252  
            std::suspend_always initial_suspend() noexcept { return {}; }
253  
            std::suspend_never final_suspend() noexcept { return {}; }
253  
            std::suspend_never final_suspend() noexcept { return {}; }
254  
            void return_void() noexcept {}
254  
            void return_void() noexcept {}
255  
            void unhandled_exception() { std::terminate(); }
255  
            void unhandled_exception() { std::terminate(); }
256  

256  

257  
            // Pass through simple awaitables, inject executor/stop_token for IoAwaitable
257  
            // Pass through simple awaitables, inject executor/stop_token for IoAwaitable
258  
            template<class Awaitable>
258  
            template<class Awaitable>
259  
            auto await_transform(Awaitable&& a)
259  
            auto await_transform(Awaitable&& a)
260  
            {
260  
            {
261  
                using AwaitableT = std::decay_t<Awaitable>;
261  
                using AwaitableT = std::decay_t<Awaitable>;
262  
                // Simple awaitable: has await_suspend(coroutine_handle<>) but not IoAwaitable
262  
                // Simple awaitable: has await_suspend(coroutine_handle<>) but not IoAwaitable
263  
                if constexpr (
263  
                if constexpr (
264  
                    requires { a.await_suspend(std::declval<std::coroutine_handle<>>()); } &&
264  
                    requires { a.await_suspend(std::declval<std::coroutine_handle<>>()); } &&
265  
                    !capy::IoAwaitable<AwaitableT>)
265  
                    !capy::IoAwaitable<AwaitableT>)
266  
                {
266  
                {
267  
                    return std::forward<Awaitable>(a);
267  
                    return std::forward<Awaitable>(a);
268  
                }
268  
                }
269  
                else
269  
                else
270  
                {
270  
                {
271  
                    struct adapter
271  
                    struct adapter
272  
                    {
272  
                    {
273  
                        AwaitableT aw;
273  
                        AwaitableT aw;
274  
                        Ex* ex_ptr;
274  
                        Ex* ex_ptr;
275  
                        std::stop_token* st_ptr;
275  
                        std::stop_token* st_ptr;
276  

276  

277  
                        bool await_ready() { return aw.await_ready(); }
277  
                        bool await_ready() { return aw.await_ready(); }
278  
                        decltype(auto) await_resume() { return aw.await_resume(); }
278  
                        decltype(auto) await_resume() { return aw.await_resume(); }
279  

279  

280  
                        auto await_suspend(std::coroutine_handle<promise_type> h)
280  
                        auto await_suspend(std::coroutine_handle<promise_type> h)
281  
                        {
281  
                        {
282  
                            static_assert(capy::IoAwaitable<AwaitableT>);
282  
                            static_assert(capy::IoAwaitable<AwaitableT>);
283  
                            return aw.await_suspend(h, *ex_ptr, *st_ptr);
283  
                            return aw.await_suspend(h, *ex_ptr, *st_ptr);
284  
                        }
284  
                        }
285  
                    };
285  
                    };
286  
                    return adapter{std::forward<Awaitable>(a), &ex, &st};
286  
                    return adapter{std::forward<Awaitable>(a), &ex, &st};
287  
                }
287  
                }
288  
            }
288  
            }
289  
        };
289  
        };
290  

290  

291  
        std::coroutine_handle<promise_type> h;
291  
        std::coroutine_handle<promise_type> h;
292  

292  

293  
        launch_wrapper(std::coroutine_handle<promise_type> handle) noexcept
293  
        launch_wrapper(std::coroutine_handle<promise_type> handle) noexcept
294  
            : h(handle)
294  
            : h(handle)
295  
        {
295  
        {
296  
        }
296  
        }
297  

297  

298  
        ~launch_wrapper()
298  
        ~launch_wrapper()
299  
        {
299  
        {
300  
            if(h)
300  
            if(h)
301  
                h.destroy();
301  
                h.destroy();
302  
        }
302  
        }
303  

303  

304  
        launch_wrapper(launch_wrapper&& o) noexcept
304  
        launch_wrapper(launch_wrapper&& o) noexcept
305  
            : h(std::exchange(o.h, nullptr))
305  
            : h(std::exchange(o.h, nullptr))
306  
        {
306  
        {
307  
        }
307  
        }
308  

308  

309  
        launch_wrapper(launch_wrapper const&) = delete;
309  
        launch_wrapper(launch_wrapper const&) = delete;
310  
        launch_wrapper& operator=(launch_wrapper const&) = delete;
310  
        launch_wrapper& operator=(launch_wrapper const&) = delete;
311  
        launch_wrapper& operator=(launch_wrapper&&) = delete;
311  
        launch_wrapper& operator=(launch_wrapper&&) = delete;
312  
    };
312  
    };
313  

313  

314  
    // Named functor to avoid incomplete lambda type in coroutine promise
314  
    // Named functor to avoid incomplete lambda type in coroutine promise
315  
    template<class Executor>
315  
    template<class Executor>
316  
    struct launch_coro
316  
    struct launch_coro
317  
    {
317  
    {
318  
        launch_wrapper<Executor> operator()(
318  
        launch_wrapper<Executor> operator()(
319  
            Executor,
319  
            Executor,
320  
            std::stop_token,
320  
            std::stop_token,
321  
            tcp_server* self,
321  
            tcp_server* self,
322  
            capy::task<void> t,
322  
            capy::task<void> t,
323  
            worker_base* wp)
323  
            worker_base* wp)
324  
        {
324  
        {
325  
            // Executor and stop token stored in promise via constructor
325  
            // Executor and stop token stored in promise via constructor
326  
            co_await std::move(t);
326  
            co_await std::move(t);
327  
            co_await self->push(*wp);
327  
            co_await self->push(*wp);
328  
        }
328  
        }
329  
    };
329  
    };
330  

330  

331  
    class push_awaitable
331  
    class push_awaitable
332  
    {
332  
    {
333  
        tcp_server& self_;
333  
        tcp_server& self_;
334  
        worker_base& w_;
334  
        worker_base& w_;
335  

335  

336  
    public:
336  
    public:
337  
        push_awaitable(
337  
        push_awaitable(
338  
            tcp_server& self,
338  
            tcp_server& self,
339  
            worker_base& w) noexcept
339  
            worker_base& w) noexcept
340  
            : self_(self)
340  
            : self_(self)
341  
            , w_(w)
341  
            , w_(w)
342  
        {
342  
        {
343  
        }
343  
        }
344  

344  

345  
        bool await_ready() const noexcept
345  
        bool await_ready() const noexcept
346  
        {
346  
        {
347  
            return false;
347  
            return false;
348  
        }
348  
        }
349  

349  

350  
        template<class Ex>
350  
        template<class Ex>
351  
        std::coroutine_handle<>
351  
        std::coroutine_handle<>
352  
        await_suspend(
352  
        await_suspend(
353  
            std::coroutine_handle<> h,
353  
            std::coroutine_handle<> h,
354  
            Ex const&, std::stop_token) noexcept
354  
            Ex const&, std::stop_token) noexcept
355  
        {
355  
        {
356  
            // Dispatch to server's executor before touching shared state
356  
            // Dispatch to server's executor before touching shared state
357  
            self_.ex_.dispatch(h);
357  
            self_.ex_.dispatch(h);
358  
            return std::noop_coroutine();
358  
            return std::noop_coroutine();
359  
        }
359  
        }
360  

360  

361  
        void await_resume() noexcept
361  
        void await_resume() noexcept
362  
        {
362  
        {
363  
            // Running on server executor - safe to modify lists
363  
            // Running on server executor - safe to modify lists
364  
            // Remove from active (if present), then wake waiter or add to idle
364  
            // Remove from active (if present), then wake waiter or add to idle
365  
            self_.active_remove(&w_);
365  
            self_.active_remove(&w_);
366  
            if(self_.waiters_)
366  
            if(self_.waiters_)
367  
            {
367  
            {
368  
                auto* wait = self_.waiters_;
368  
                auto* wait = self_.waiters_;
369  
                self_.waiters_ = wait->next;
369  
                self_.waiters_ = wait->next;
370  
                wait->w = &w_;
370  
                wait->w = &w_;
371  
                self_.ex_.post(wait->h);
371  
                self_.ex_.post(wait->h);
372  
            }
372  
            }
373  
            else
373  
            else
374  
            {
374  
            {
375  
                self_.idle_push(&w_);
375  
                self_.idle_push(&w_);
376  
            }
376  
            }
377  
        }
377  
        }
378  
    };
378  
    };
379  

379  

380  
    class pop_awaitable
380  
    class pop_awaitable
381  
    {
381  
    {
382  
        tcp_server& self_;
382  
        tcp_server& self_;
383  
        waiter wait_;
383  
        waiter wait_;
384  

384  

385  
    public:
385  
    public:
386  
        pop_awaitable(tcp_server& self) noexcept
386  
        pop_awaitable(tcp_server& self) noexcept
387  
            : self_(self)
387  
            : self_(self)
388  
            , wait_{}
388  
            , wait_{}
389  
        {
389  
        {
390  
        }
390  
        }
391  

391  

392  
        bool await_ready() const noexcept
392  
        bool await_ready() const noexcept
393  
        {
393  
        {
394  
            return !self_.idle_empty();
394  
            return !self_.idle_empty();
395  
        }
395  
        }
396  

396  

397  
        template<class Ex>
397  
        template<class Ex>
398  
        bool
398  
        bool
399  
        await_suspend(
399  
        await_suspend(
400  
            std::coroutine_handle<> h,
400  
            std::coroutine_handle<> h,
401  
            Ex const&, std::stop_token) noexcept
401  
            Ex const&, std::stop_token) noexcept
402  
        {
402  
        {
403  
            // Running on server executor (do_accept runs there)
403  
            // Running on server executor (do_accept runs there)
404  
            wait_.h = h;
404  
            wait_.h = h;
405  
            wait_.w = nullptr;
405  
            wait_.w = nullptr;
406  
            wait_.next = self_.waiters_;
406  
            wait_.next = self_.waiters_;
407  
            self_.waiters_ = &wait_;
407  
            self_.waiters_ = &wait_;
408  
            return true;
408  
            return true;
409  
        }
409  
        }
410  

410  

411  
        worker_base& await_resume() noexcept
411  
        worker_base& await_resume() noexcept
412  
        {
412  
        {
413  
            // Running on server executor
413  
            // Running on server executor
414  
            if(wait_.w)
414  
            if(wait_.w)
415  
                return *wait_.w;  // Woken by push_awaitable
415  
                return *wait_.w;  // Woken by push_awaitable
416  
            return *self_.idle_pop();
416  
            return *self_.idle_pop();
417  
        }
417  
        }
418  
    };
418  
    };
419  

419  

420  
    push_awaitable push(worker_base& w)
420  
    push_awaitable push(worker_base& w)
421  
    {
421  
    {
422  
        return push_awaitable{*this, w};
422  
        return push_awaitable{*this, w};
423  
    }
423  
    }
424  

424  

425  
    // Synchronous version for destructor/guard paths
425  
    // Synchronous version for destructor/guard paths
426  
    // Must be called from server executor context
426  
    // Must be called from server executor context
427  
    void push_sync(worker_base& w) noexcept
427  
    void push_sync(worker_base& w) noexcept
428  
    {
428  
    {
429  
        active_remove(&w);
429  
        active_remove(&w);
430  
        if(waiters_)
430  
        if(waiters_)
431  
        {
431  
        {
432  
            auto* wait = waiters_;
432  
            auto* wait = waiters_;
433  
            waiters_ = wait->next;
433  
            waiters_ = wait->next;
434  
            wait->w = &w;
434  
            wait->w = &w;
435  
            ex_.post(wait->h);
435  
            ex_.post(wait->h);
436  
        }
436  
        }
437  
        else
437  
        else
438  
        {
438  
        {
439  
            idle_push(&w);
439  
            idle_push(&w);
440  
        }
440  
        }
441  
    }
441  
    }
442  

442  

443  
    pop_awaitable pop()
443  
    pop_awaitable pop()
444  
    {
444  
    {
445  
        return pop_awaitable{*this};
445  
        return pop_awaitable{*this};
446  
    }
446  
    }
447  

447  

448  
    capy::task<void> do_accept(tcp_acceptor& acc);
448  
    capy::task<void> do_accept(tcp_acceptor& acc);
449  

449  

450  
public:
450  
public:
451  
    /** Abstract base class for connection handlers.
451  
    /** Abstract base class for connection handlers.
452  

452  

453  
        Derive from this class to implement custom connection handling.
453  
        Derive from this class to implement custom connection handling.
454  
        Each worker owns a socket and is reused across multiple
454  
        Each worker owns a socket and is reused across multiple
455  
        connections to avoid per-connection allocation.
455  
        connections to avoid per-connection allocation.
456  

456  

457  
        @see tcp_server, launcher
457  
        @see tcp_server, launcher
458  
    */
458  
    */
459  
    class BOOST_COROSIO_DECL
459  
    class BOOST_COROSIO_DECL
460  
        worker_base
460  
        worker_base
461  
    {
461  
    {
462  
        // Ordered largest to smallest for optimal packing
462  
        // Ordered largest to smallest for optimal packing
463  
        std::stop_source stop_;        // ~16 bytes
463  
        std::stop_source stop_;        // ~16 bytes
464  
        worker_base* next_ = nullptr;  // 8 bytes - used by idle and active lists
464  
        worker_base* next_ = nullptr;  // 8 bytes - used by idle and active lists
465  
        worker_base* prev_ = nullptr;  // 8 bytes - used only by active list
465  
        worker_base* prev_ = nullptr;  // 8 bytes - used only by active list
466  

466  

467  
        friend class tcp_server;
467  
        friend class tcp_server;
468  

468  

469  
    public:
469  
    public:
470  
        /// Destroy the worker.
470  
        /// Destroy the worker.
471  
        virtual ~worker_base() = default;
471  
        virtual ~worker_base() = default;
472  

472  

473  
        /** Handle an accepted connection.
473  
        /** Handle an accepted connection.
474  

474  

475  
            Called when this worker is dispatched to handle a new
475  
            Called when this worker is dispatched to handle a new
476  
            connection. The implementation must invoke the launcher
476  
            connection. The implementation must invoke the launcher
477  
            exactly once to start the handling coroutine.
477  
            exactly once to start the handling coroutine.
478  

478  

479  
            @param launch Handle to launch the connection coroutine.
479  
            @param launch Handle to launch the connection coroutine.
480  
        */
480  
        */
481  
        virtual void run(launcher launch) = 0;
481  
        virtual void run(launcher launch) = 0;
482  

482  

483  
        /// Return the socket used for connections.
483  
        /// Return the socket used for connections.
484  
        virtual corosio::tcp_socket& socket() = 0;
484  
        virtual corosio::tcp_socket& socket() = 0;
485  
    };
485  
    };
486  

486  

487  
    /** Move-only handle to launch a worker coroutine.
487  
    /** Move-only handle to launch a worker coroutine.
488  

488  

489  
        Passed to @ref worker_base::run to start the connection-handling
489  
        Passed to @ref worker_base::run to start the connection-handling
490  
        coroutine. The launcher ensures the worker returns to the idle
490  
        coroutine. The launcher ensures the worker returns to the idle
491  
        pool when the coroutine completes or if launching fails.
491  
        pool when the coroutine completes or if launching fails.
492  

492  

493  
        The launcher must be invoked exactly once via `operator()`.
493  
        The launcher must be invoked exactly once via `operator()`.
494  
        If destroyed without invoking, the worker is returned to the
494  
        If destroyed without invoking, the worker is returned to the
495  
        idle pool automatically.
495  
        idle pool automatically.
496  

496  

497  
        @see worker_base::run
497  
        @see worker_base::run
498  
    */
498  
    */
499  
    class BOOST_COROSIO_DECL
499  
    class BOOST_COROSIO_DECL
500  
        launcher
500  
        launcher
501  
    {
501  
    {
502  
        tcp_server* srv_;
502  
        tcp_server* srv_;
503  
        worker_base* w_;
503  
        worker_base* w_;
504  

504  

505  
        friend class tcp_server;
505  
        friend class tcp_server;
506  

506  

507  
        launcher(tcp_server& srv, worker_base& w) noexcept
507  
        launcher(tcp_server& srv, worker_base& w) noexcept
508  
            : srv_(&srv)
508  
            : srv_(&srv)
509  
            , w_(&w)
509  
            , w_(&w)
510  
        {
510  
        {
511  
        }
511  
        }
512  

512  

513  
    public:
513  
    public:
514  
        /// Return the worker to the pool if not launched.
514  
        /// Return the worker to the pool if not launched.
515  
        ~launcher()
515  
        ~launcher()
516  
        {
516  
        {
517  
            if(w_)
517  
            if(w_)
518  
                srv_->push_sync(*w_);
518  
                srv_->push_sync(*w_);
519  
        }
519  
        }
520  

520  

521  
        launcher(launcher&& o) noexcept
521  
        launcher(launcher&& o) noexcept
522  
            : srv_(o.srv_)
522  
            : srv_(o.srv_)
523  
            , w_(std::exchange(o.w_, nullptr))
523  
            , w_(std::exchange(o.w_, nullptr))
524  
        {
524  
        {
525  
        }
525  
        }
526  
        launcher(launcher const&) = delete;
526  
        launcher(launcher const&) = delete;
527  
        launcher& operator=(launcher const&) = delete;
527  
        launcher& operator=(launcher const&) = delete;
528  
        launcher& operator=(launcher&&) = delete;
528  
        launcher& operator=(launcher&&) = delete;
529  

529  

530  
        /** Launch the connection-handling coroutine.
530  
        /** Launch the connection-handling coroutine.
531  

531  

532  
            Starts the given coroutine on the specified executor. When
532  
            Starts the given coroutine on the specified executor. When
533  
            the coroutine completes, the worker is automatically returned
533  
            the coroutine completes, the worker is automatically returned
534  
            to the idle pool.
534  
            to the idle pool.
535  

535  

536  
            @param ex The executor to run the coroutine on.
536  
            @param ex The executor to run the coroutine on.
537  
            @param task The coroutine to execute.
537  
            @param task The coroutine to execute.
538  

538  

539  
            @throws std::logic_error If this launcher was already invoked.
539  
            @throws std::logic_error If this launcher was already invoked.
540  
        */
540  
        */
541  
        template<class Executor>
541  
        template<class Executor>
542  
        void operator()(Executor const& ex, capy::task<void> task)
542  
        void operator()(Executor const& ex, capy::task<void> task)
543  
        {
543  
        {
544  
            if(! w_)
544  
            if(! w_)
545  
                detail::throw_logic_error(); // launcher already invoked
545  
                detail::throw_logic_error(); // launcher already invoked
546  

546  

547  
            auto* w = std::exchange(w_, nullptr);
547  
            auto* w = std::exchange(w_, nullptr);
548  

548  

549  
            // Worker is being dispatched - add to active list
549  
            // Worker is being dispatched - add to active list
550  
            srv_->active_push(w);
550  
            srv_->active_push(w);
551  

551  

552  
            // Return worker to pool if coroutine setup throws
552  
            // Return worker to pool if coroutine setup throws
553  
            struct guard_t {
553  
            struct guard_t {
554  
                tcp_server* srv;
554  
                tcp_server* srv;
555  
                worker_base* w;
555  
                worker_base* w;
556  
                ~guard_t() { if(w) srv->push_sync(*w); }
556  
                ~guard_t() { if(w) srv->push_sync(*w); }
557  
            } guard{srv_, w};
557  
            } guard{srv_, w};
558  

558  

559  
            // Reset worker's stop source for this connection
559  
            // Reset worker's stop source for this connection
560  
            w->stop_ = {};
560  
            w->stop_ = {};
561  
            auto st = w->stop_.get_token();
561  
            auto st = w->stop_.get_token();
562  

562  

563  
            auto wrapper = launch_coro<Executor>{}(
563  
            auto wrapper = launch_coro<Executor>{}(
564  
                ex, st, srv_, std::move(task), w);
564  
                ex, st, srv_, std::move(task), w);
565  

565  

566  
            // Executor and stop token stored in promise via constructor
566  
            // Executor and stop token stored in promise via constructor
567  
            ex.post(std::exchange(wrapper.h, nullptr)); // Release before post
567  
            ex.post(std::exchange(wrapper.h, nullptr)); // Release before post
568  
            guard.w = nullptr; // Success - dismiss guard
568  
            guard.w = nullptr; // Success - dismiss guard
569  
        }
569  
        }
570  
    };
570  
    };
571  

571  

572  
    /** Construct a TCP server.
572  
    /** Construct a TCP server.
573  

573  

574  
        @tparam Ctx Execution context type satisfying ExecutionContext.
574  
        @tparam Ctx Execution context type satisfying ExecutionContext.
575  
        @tparam Ex Executor type satisfying Executor.
575  
        @tparam Ex Executor type satisfying Executor.
576  

576  

577  
        @param ctx The execution context for socket operations.
577  
        @param ctx The execution context for socket operations.
578  
        @param ex The executor for dispatching coroutines.
578  
        @param ex The executor for dispatching coroutines.
579  

579  

580  
        @par Example
580  
        @par Example
581  
        @code
581  
        @code
582  
        tcp_server srv(ctx, ctx.get_executor());
582  
        tcp_server srv(ctx, ctx.get_executor());
583  
        srv.set_workers(make_workers(ctx, 100));
583  
        srv.set_workers(make_workers(ctx, 100));
584  
        srv.bind(endpoint{...});
584  
        srv.bind(endpoint{...});
585  
        srv.start();
585  
        srv.start();
586  
        @endcode
586  
        @endcode
587  
    */
587  
    */
588  
    template<
588  
    template<
589  
        capy::ExecutionContext Ctx,
589  
        capy::ExecutionContext Ctx,
590  
        capy::Executor Ex>
590  
        capy::Executor Ex>
591  
    tcp_server(Ctx& ctx, Ex ex)
591  
    tcp_server(Ctx& ctx, Ex ex)
592  
        : impl_(make_impl(ctx))
592  
        : impl_(make_impl(ctx))
593  
        , ex_(std::move(ex))
593  
        , ex_(std::move(ex))
594  
    {
594  
    {
595  
    }
595  
    }
596  

596  

597  
public:
597  
public:
598  
    ~tcp_server();
598  
    ~tcp_server();
599  
    tcp_server(tcp_server const&) = delete;
599  
    tcp_server(tcp_server const&) = delete;
600  
    tcp_server& operator=(tcp_server const&) = delete;
600  
    tcp_server& operator=(tcp_server const&) = delete;
601  
    tcp_server(tcp_server&& o) noexcept;
601  
    tcp_server(tcp_server&& o) noexcept;
602  
    tcp_server& operator=(tcp_server&& o) noexcept;
602  
    tcp_server& operator=(tcp_server&& o) noexcept;
603  

603  

604  
    /** Bind to a local endpoint.
604  
    /** Bind to a local endpoint.
605  

605  

606  
        Creates an acceptor listening on the specified endpoint.
606  
        Creates an acceptor listening on the specified endpoint.
607  
        Multiple endpoints can be bound by calling this method
607  
        Multiple endpoints can be bound by calling this method
608  
        multiple times before @ref start.
608  
        multiple times before @ref start.
609  

609  

610  
        @param ep The local endpoint to bind to.
610  
        @param ep The local endpoint to bind to.
611  

611  

612  
        @return The error code if binding fails.
612  
        @return The error code if binding fails.
613  
    */
613  
    */
614  
    std::error_code
614  
    std::error_code
615  
    bind(endpoint ep);
615  
    bind(endpoint ep);
616  

616  

617  
    /** Set the worker pool.
617  
    /** Set the worker pool.
618  

618  

619  
        Replaces any existing workers with the given range. Any
619  
        Replaces any existing workers with the given range. Any
620  
        previous workers are released and the idle/active lists
620  
        previous workers are released and the idle/active lists
621  
        are cleared before populating with new workers.
621  
        are cleared before populating with new workers.
622  

622  

623  
        @tparam Range Forward range of pointer-like objects to worker_base.
623  
        @tparam Range Forward range of pointer-like objects to worker_base.
624  

624  

625  
        @param workers Range of workers to manage. Each element must
625  
        @param workers Range of workers to manage. Each element must
626  
            support `std::to_address()` yielding `worker_base*`.
626  
            support `std::to_address()` yielding `worker_base*`.
627  

627  

628  
        @par Example
628  
        @par Example
629  
        @code
629  
        @code
630  
        std::vector<std::unique_ptr<my_worker>> workers;
630  
        std::vector<std::unique_ptr<my_worker>> workers;
631  
        for(int i = 0; i < 100; ++i)
631  
        for(int i = 0; i < 100; ++i)
632  
            workers.push_back(std::make_unique<my_worker>(ctx));
632  
            workers.push_back(std::make_unique<my_worker>(ctx));
633  
        srv.set_workers(std::move(workers));
633  
        srv.set_workers(std::move(workers));
634  
        @endcode
634  
        @endcode
635  
    */
635  
    */
636  
    template<std::ranges::forward_range Range>
636  
    template<std::ranges::forward_range Range>
637  
        requires std::convertible_to<
637  
        requires std::convertible_to<
638  
            decltype(std::to_address(
638  
            decltype(std::to_address(
639  
                std::declval<std::ranges::range_value_t<Range>&>())),
639  
                std::declval<std::ranges::range_value_t<Range>&>())),
640  
            worker_base*>
640  
            worker_base*>
641  
    void
641  
    void
642  
    set_workers(Range&& workers)
642  
    set_workers(Range&& workers)
643  
    {
643  
    {
644  
        // Clear existing state
644  
        // Clear existing state
645  
        storage_.reset();
645  
        storage_.reset();
646  
        idle_head_ = nullptr;
646  
        idle_head_ = nullptr;
647  
        active_head_ = nullptr;
647  
        active_head_ = nullptr;
648  
        active_tail_ = nullptr;
648  
        active_tail_ = nullptr;
649  

649  

650  
        // Take ownership and populate idle list
650  
        // Take ownership and populate idle list
651  
        using StorageType = std::decay_t<Range>;
651  
        using StorageType = std::decay_t<Range>;
652  
        auto* p = new StorageType(std::forward<Range>(workers));
652  
        auto* p = new StorageType(std::forward<Range>(workers));
653  
        storage_ = std::shared_ptr<void>(p, [](void* ptr) {
653  
        storage_ = std::shared_ptr<void>(p, [](void* ptr) {
654  
            delete static_cast<StorageType*>(ptr);
654  
            delete static_cast<StorageType*>(ptr);
655  
        });
655  
        });
656  
        for(auto&& elem : *static_cast<StorageType*>(p))
656  
        for(auto&& elem : *static_cast<StorageType*>(p))
657  
            idle_push(std::to_address(elem));
657  
            idle_push(std::to_address(elem));
658  
    }
658  
    }
659  

659  

660  
    /** Start accepting connections.
660  
    /** Start accepting connections.
661  

661  

662  
        Launches accept loops for all bound endpoints. Incoming
662  
        Launches accept loops for all bound endpoints. Incoming
663  
        connections are dispatched to idle workers from the pool.
663  
        connections are dispatched to idle workers from the pool.
664  
        
664  
        
665  
        Calling `start()` on an already-running server has no effect.
665  
        Calling `start()` on an already-running server has no effect.
666  

666  

667  
        @par Preconditions
667  
        @par Preconditions
668  
        - At least one endpoint bound via @ref bind.
668  
        - At least one endpoint bound via @ref bind.
669  
        - Workers provided to the constructor.
669  
        - Workers provided to the constructor.
670  
        - If restarting, @ref join must have completed first.
670  
        - If restarting, @ref join must have completed first.
671  

671  

672  
        @par Effects
672  
        @par Effects
673  
        Creates one accept coroutine per bound endpoint. Each coroutine
673  
        Creates one accept coroutine per bound endpoint. Each coroutine
674  
        runs on the server's executor, waiting for connections and
674  
        runs on the server's executor, waiting for connections and
675  
        dispatching them to idle workers.
675  
        dispatching them to idle workers.
676  

676  

677  
        @par Restart Sequence
677  
        @par Restart Sequence
678  
        To restart after stopping, complete the full shutdown cycle:
678  
        To restart after stopping, complete the full shutdown cycle:
679  
        @code
679  
        @code
680  
        srv.start();
680  
        srv.start();
681  
        ioc.run_for( 1s );
681  
        ioc.run_for( 1s );
682  
        srv.stop();       // 1. Signal shutdown
682  
        srv.stop();       // 1. Signal shutdown
683  
        ioc.run();        // 2. Drain remaining completions
683  
        ioc.run();        // 2. Drain remaining completions
684  
        srv.join();       // 3. Wait for accept loops
684  
        srv.join();       // 3. Wait for accept loops
685  

685  

686  
        // Now safe to restart
686  
        // Now safe to restart
687  
        srv.start();
687  
        srv.start();
688  
        ioc.run();
688  
        ioc.run();
689  
        @endcode
689  
        @endcode
690  

690  

691  
        @par Thread Safety
691  
        @par Thread Safety
692  
        Not thread safe.
692  
        Not thread safe.
693  
        
693  
        
694  
        @throws std::logic_error If a previous session has not been
694  
        @throws std::logic_error If a previous session has not been
695  
            joined (accept loops still active).
695  
            joined (accept loops still active).
696  
    */
696  
    */
697  
    void start();
697  
    void start();
698  

698  

699  
    /** Stop accepting connections.
699  
    /** Stop accepting connections.
700  

700  

701  
        Signals all listening ports to stop accepting new connections
701  
        Signals all listening ports to stop accepting new connections
702  
        and requests cancellation of active workers via their stop tokens.
702  
        and requests cancellation of active workers via their stop tokens.
703  
        
703  
        
704  
        This function returns immediately; it does not wait for workers
704  
        This function returns immediately; it does not wait for workers
705  
        to finish. Pending I/O operations complete asynchronously.
705  
        to finish. Pending I/O operations complete asynchronously.
706  

706  

707  
        Calling `stop()` on a non-running server has no effect.
707  
        Calling `stop()` on a non-running server has no effect.
708  

708  

709  
        @par Effects
709  
        @par Effects
710  
        - Closes all acceptors (pending accepts complete with error).
710  
        - Closes all acceptors (pending accepts complete with error).
711  
        - Requests stop on each active worker's stop token.
711  
        - Requests stop on each active worker's stop token.
712  
        - Workers observing their stop token should exit promptly.
712  
        - Workers observing their stop token should exit promptly.
713  

713  

714  
        @par Postconditions
714  
        @par Postconditions
715  
        No new connections will be accepted. Active workers continue
715  
        No new connections will be accepted. Active workers continue
716  
        until they observe their stop token or complete naturally.
716  
        until they observe their stop token or complete naturally.
717  

717  

718  
        @par What Happens Next
718  
        @par What Happens Next
719  
        After calling `stop()`:
719  
        After calling `stop()`:
720  
        1. Let `ioc.run()` return (drains pending completions).
720  
        1. Let `ioc.run()` return (drains pending completions).
721  
        2. Call @ref join to wait for accept loops to finish.
721  
        2. Call @ref join to wait for accept loops to finish.
722  
        3. Only then is it safe to restart or destroy the server.
722  
        3. Only then is it safe to restart or destroy the server.
723  

723  

724  
        @par Thread Safety
724  
        @par Thread Safety
725  
        Not thread safe.
725  
        Not thread safe.
726  

726  

727  
        @see join, start
727  
        @see join, start
728  
    */
728  
    */
729  
    void stop();
729  
    void stop();
730  

730  

731  
    /** Block until all accept loops complete.
731  
    /** Block until all accept loops complete.
732  

732  

733  
        Blocks the calling thread until all accept coroutines launched
733  
        Blocks the calling thread until all accept coroutines launched
734  
        by @ref start have finished executing. This synchronizes the
734  
        by @ref start have finished executing. This synchronizes the
735  
        shutdown sequence, ensuring the server is fully stopped before
735  
        shutdown sequence, ensuring the server is fully stopped before
736  
        restarting or destroying it.
736  
        restarting or destroying it.
737  

737  

738  
        @par Preconditions
738  
        @par Preconditions
739  
        @ref stop has been called and `ioc.run()` has returned.
739  
        @ref stop has been called and `ioc.run()` has returned.
740  

740  

741  
        @par Postconditions
741  
        @par Postconditions
742  
        All accept loops have completed. The server is in the stopped
742  
        All accept loops have completed. The server is in the stopped
743  
        state and may be restarted via @ref start.
743  
        state and may be restarted via @ref start.
744  

744  

745  
        @par Example (Correct Usage)
745  
        @par Example (Correct Usage)
746  
        @code
746  
        @code
747  
        // main thread
747  
        // main thread
748  
        srv.start();
748  
        srv.start();
749  
        ioc.run();      // Blocks until work completes
749  
        ioc.run();      // Blocks until work completes
750  
        srv.join();     // Safe: called after ioc.run() returns
750  
        srv.join();     // Safe: called after ioc.run() returns
751  
        @endcode
751  
        @endcode
752  

752  

753  
        @par WARNING: Deadlock Scenarios
753  
        @par WARNING: Deadlock Scenarios
754  
        Calling `join()` from the wrong context causes deadlock:
754  
        Calling `join()` from the wrong context causes deadlock:
755  

755  

756  
        @code
756  
        @code
757  
        // WRONG: calling join() from inside a worker coroutine
757  
        // WRONG: calling join() from inside a worker coroutine
758  
        void run( launcher launch ) override
758  
        void run( launcher launch ) override
759  
        {
759  
        {
760  
            launch( ex, [this]() -> capy::task<>
760  
            launch( ex, [this]() -> capy::task<>
761  
            {
761  
            {
762  
                srv_.join();  // DEADLOCK: blocks the executor
762  
                srv_.join();  // DEADLOCK: blocks the executor
763  
                co_return;
763  
                co_return;
764  
            }());
764  
            }());
765  
        }
765  
        }
766  

766  

767  
        // WRONG: calling join() while ioc.run() is still active
767  
        // WRONG: calling join() while ioc.run() is still active
768  
        std::thread t( [&]{ ioc.run(); } );
768  
        std::thread t( [&]{ ioc.run(); } );
769  
        srv.stop();
769  
        srv.stop();
770  
        srv.join();  // DEADLOCK: ioc.run() still running in thread t
770  
        srv.join();  // DEADLOCK: ioc.run() still running in thread t
771  
        @endcode
771  
        @endcode
772  

772  

773  
        @par Thread Safety
773  
        @par Thread Safety
774  
        May be called from any thread, but will deadlock if called
774  
        May be called from any thread, but will deadlock if called
775  
        from within the io_context event loop or from a worker coroutine.
775  
        from within the io_context event loop or from a worker coroutine.
776  

776  

777  
        @see stop, start
777  
        @see stop, start
778  
    */
778  
    */
779  
    void join();
779  
    void join();
780  

780  

781  
private:
781  
private:
782  
    capy::task<> do_stop();
782  
    capy::task<> do_stop();
783  
};
783  
};
784  

784  

785  
#ifdef _MSC_VER
785  
#ifdef _MSC_VER
786  
#pragma warning(pop)
786  
#pragma warning(pop)
787  
#endif
787  
#endif
788  

788  

789  
} // namespace boost::corosio
789  
} // namespace boost::corosio
790  

790  

791  
#endif
791  
#endif