src/corosio/src/tcp_server.cpp

95.6% Lines (86/90) 100.0% List of functions (17/17) 56.8% Branches (83/146)
tcp_server.cpp
f(x) Functions (17)
Line Branch TLA Hits Source Code
1 //
2 // Copyright (c) 2026 Vinnie Falco ([email protected])
3 // Copyright (c) 2026 Steve Gerbino
4 //
5 // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 //
8 // Official repository: https://github.com/cppalliance/corosio
9 //
10
11 #include <boost/corosio/tcp_server.hpp>
12 #include <boost/corosio/detail/except.hpp>
13 #include <condition_variable>
14 #include <mutex>
15 #include <utility>
16
17 namespace boost::corosio {
18
19 118x tcp_server::worker_base::worker_base() = default;
20 118x tcp_server::worker_base::~worker_base() = default;
21
22 struct tcp_server::impl
23 {
24 std::mutex join_mutex;
25 std::condition_variable join_cv;
26 capy::execution_context& ctx;
27 std::vector<tcp_acceptor> ports;
28 std::stop_source stop;
29
30
1/2
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
68x explicit impl(capy::execution_context& c) noexcept : ctx(c) {}
31 };
32
33 tcp_server::impl*
34 34x tcp_server::make_impl(capy::execution_context& ctx)
35 {
36 34x return new impl(ctx);
37 }
38
39 36x tcp_server::~tcp_server()
40 {
41
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 32 times.
36x delete impl_;
42 36x }
43
44 2x tcp_server::tcp_server(tcp_server&& o) noexcept
45 2x : impl_(std::exchange(o.impl_, nullptr))
46 2x , ex_(o.ex_)
47 2x , waiters_(std::exchange(o.waiters_, nullptr))
48 2x , idle_head_(std::exchange(o.idle_head_, nullptr))
49 2x , active_head_(std::exchange(o.active_head_, nullptr))
50 2x , active_tail_(std::exchange(o.active_tail_, nullptr))
51 2x , active_accepts_(std::exchange(o.active_accepts_, 0))
52 2x , storage_(std::move(o.storage_))
53 2x , running_(std::exchange(o.running_, false))
54 {
55 2x }
56
57 tcp_server&
58 2x tcp_server::operator=(tcp_server&& o) noexcept
59 {
60
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2x delete impl_;
61 2x impl_ = std::exchange(o.impl_, nullptr);
62 2x ex_ = o.ex_;
63 2x waiters_ = std::exchange(o.waiters_, nullptr);
64 2x idle_head_ = std::exchange(o.idle_head_, nullptr);
65 2x active_head_ = std::exchange(o.active_head_, nullptr);
66 2x active_tail_ = std::exchange(o.active_tail_, nullptr);
67 2x active_accepts_ = std::exchange(o.active_accepts_, 0);
68 2x storage_ = std::move(o.storage_);
69 2x running_ = std::exchange(o.running_, false);
70 2x return *this;
71 }
72
73 // Accept loop: wait for idle worker, accept connection, dispatch
74 capy::task<void>
75
11/26
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 24 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 24 times.
✓ Branch 8 taken 24 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 24 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✓ Branch 16 taken 24 times.
✓ Branch 17 taken 24 times.
✓ Branch 18 taken 24 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 124 times.
✓ Branch 21 taken 24 times.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
368x tcp_server::do_accept(tcp_acceptor& acc)
76 24x {
77 // Analyzer can't trace value through coroutine await_transform
78 // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
79
3/8
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 24 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 24 times.
48x auto env = co_await capy::this_coro::environment;
80
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 24 times.
66x while (!env->stop_token.stop_requested())
81 {
82 // Wait for an idle worker before blocking on accept
83
9/14
✗ Branch 0 not taken.
✓ Branch 1 taken 42 times.
✓ Branch 2 taken 42 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 36 times.
✓ Branch 6 taken 6 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 6 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 42 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 84 times.
✓ Branch 13 taken 126 times.
48x auto& w = co_await pop();
84
12/16
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 84 times.
✓ Branch 2 taken 126 times.
✓ Branch 3 taken 84 times.
✓ Branch 4 taken 126 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 42 times.
✓ Branch 7 taken 84 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 42 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 42 times.
✓ Branch 13 taken 42 times.
✓ Branch 14 taken 40 times.
✓ Branch 15 taken 82 times.
168x auto [ec] = co_await acc.accept(w.socket());
85
2/2
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 22 times.
82x if (ec)
86 {
87
8/14
✓ Branch 0 taken 60 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 60 times.
✓ Branch 4 taken 20 times.
✓ Branch 5 taken 40 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 20 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 20 times.
✓ Branch 11 taken 20 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 20 times.
80x co_await push(w);
88 20x continue;
89 }
90
1/2
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
22x w.run(launcher{*this, w});
91 166x }
92 440x }
93
94 std::error_code
95 34x tcp_server::bind(endpoint ep)
96 {
97 try
98 {
99
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 2 times.
34x impl_->ports.emplace_back(impl_->ctx, ep);
100 32x return {};
101 2x }
102 catch (std::system_error const& e)
103 {
104 2x return e.code();
105 2x }
106 36x }
107
108 endpoint
109 24x tcp_server::local_endpoint(std::size_t index) const noexcept
110 {
111
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 22 times.
24x if (index >= impl_->ports.size())
112 2x return endpoint{};
113 22x return impl_->ports[index].local_endpoint();
114 24x }
115
116 void
117 28x tcp_server::start()
118 {
119 // Idempotent - only start if not already running
120
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 26 times.
28x if (running_)
121 2x return;
122
123 // Previous session must be fully stopped before restart
124
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 2 times.
26x if (active_accepts_ != 0)
125 2x detail::throw_logic_error(
126 "tcp_server::start: previous session not joined");
127
128 24x running_ = true;
129
130 24x impl_->stop = {}; // Fresh stop source
131 24x auto st = impl_->stop.get_token();
132
133 24x active_accepts_ = impl_->ports.size();
134
135 // Launch with completion handler that decrements counter
136
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 24 times.
48x for (auto& t : impl_->ports)
137
2/4
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 24 times.
72x capy::run_async(ex_, st, [this]() {
138 24x std::lock_guard lock(impl_->join_mutex);
139
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24x if (--active_accepts_ == 0)
140 24x impl_->join_cv.notify_all();
141
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
48x })(do_accept(t));
142 26x }
143
144 void
145 28x tcp_server::stop()
146 {
147 // Idempotent - only stop if running
148
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 24 times.
28x if (!running_)
149 4x return;
150 24x running_ = false;
151
152 // Stop accept loops
153 24x impl_->stop.request_stop();
154
155 // Launch cancellation coroutine on server executor
156
3/6
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 24 times.
✗ Branch 5 not taken.
24x capy::run_async(ex_, std::stop_token{})(do_stop());
157 28x }
158
159 void
160 16x tcp_server::join()
161 {
162 16x std::unique_lock lock(impl_->join_mutex);
163
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
32x impl_->join_cv.wait(lock, [this] { return active_accepts_ == 0; });
164 16x }
165
166 capy::task<>
167
9/24
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 24 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 24 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 24 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 24 times.
✓ Branch 15 taken 24 times.
✓ Branch 16 taken 24 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 19 taken 24 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
120x tcp_server::do_stop()
168 24x {
169 // Running on server executor - safe to iterate active list
170 // Just cancel, don't modify list - workers return themselves when done
171
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24x for (auto* w = active_head_; w; w = w->next_)
172 w->stop_.request_stop();
173
1/2
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
24x co_return;
174 }
175
176 } // namespace boost::corosio
177