Skip to content

Commit 62617d2

Browse files
Extract shared IOController scheduler wiring to reduce duplication.
Move bump reactions, shutdown control, and poll resubmit logic into IOController_Common.ipp so POSIX and Windows paths share one implementation and satisfy SonarCloud duplicated-lines quality gate. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent c762447 commit 62617d2

5 files changed

Lines changed: 99 additions & 45 deletions

File tree

src/extension/IOController.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,5 @@
2525
#else
2626
#include "IOController_Posix.ipp"
2727
#endif // _WIN32
28+
29+
#include "IOController_Common.ipp"

src/extension/IOController.hpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "../util/platform.hpp"
2929

3030
#include <atomic>
31+
#include <functional>
3132

3233
namespace NUClear {
3334
namespace extension {
@@ -126,6 +127,25 @@ namespace extension {
126127
*/
127128
void bump() const;
128129

130+
/// Inline bump reactions that wake the poll task from the emitting thread.
131+
void register_inline_bump_reactions();
132+
133+
/// HIGH-priority shutdown handler on the IO pool.
134+
void register_shutdown_control();
135+
136+
/**
137+
* Registers the self-resubmitting poll task.
138+
*
139+
* @param wait_and_process platform-specific blocking wait and event dispatch
140+
*/
141+
void register_poll_loop(std::function<void()> wait_and_process);
142+
143+
/// Returns false when the poll loop should exit without blocking.
144+
bool prepare_poll_iteration();
145+
146+
/// Resubmits the poll reaction after one blocking iteration.
147+
void resubmit_poll_task();
148+
129149
public:
130150
explicit IOController(std::unique_ptr<NUClear::Environment> environment);
131151

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* MIT License
3+
*
4+
* Copyright (c) 2026 NUClear Contributors
5+
*
6+
* This file is part of the NUClear codebase.
7+
* See https://github.com/Fastcode/NUClear for further info.
8+
*
9+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
10+
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
11+
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
12+
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
13+
*
14+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
15+
* Software.
16+
*
17+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
18+
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19+
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21+
*/
22+
23+
#include "IOController.hpp"
24+
25+
#include "../threading/ReactionTask.hpp"
26+
27+
namespace NUClear {
28+
namespace extension {
29+
30+
void IOController::register_inline_bump_reactions() {
31+
on<Trigger<dsl::word::IOConfiguration>, Inline::ALWAYS>().then("Configure IO bump", [this] { bump(); });
32+
on<Trigger<dsl::word::IOFinished>, Inline::ALWAYS>().then("IO Finished bump", [this] { bump(); });
33+
on<Trigger<dsl::operation::Unbind<IO>>, Inline::ALWAYS>().then("Unbind IO bump", [this] { bump(); });
34+
on<Shutdown, Inline::ALWAYS>().then("Shutdown IO bump", [this] { bump(); });
35+
}
36+
37+
void IOController::register_shutdown_control() {
38+
on<Shutdown, Pool<IOPool>, Priority::HIGH, Inline::NEVER>().then("Shutdown IO Controller", [this] {
39+
running.store(false, std::memory_order_release);
40+
});
41+
}
42+
43+
bool IOController::prepare_poll_iteration() {
44+
if (!running.load(std::memory_order_acquire)) {
45+
return false;
46+
}
47+
48+
if (dirty.load(std::memory_order_acquire)) {
49+
rebuild_list();
50+
}
51+
52+
return true;
53+
}
54+
55+
void IOController::resubmit_poll_task() {
56+
powerplant.submit(threading::ReactionTask::get_current_task()->parent->get_task());
57+
}
58+
59+
void IOController::register_poll_loop(std::function<void()> wait_and_process) {
60+
on<Startup, Pool<IOPool>, Priority::NORMAL, Inline::NEVER>().then("IO Poll", [this, wait = std::move(wait_and_process)] {
61+
if (!prepare_poll_iteration()) {
62+
return;
63+
}
64+
65+
wait();
66+
resubmit_poll_task();
67+
});
68+
}
69+
70+
} // namespace extension
71+
} // namespace NUClear

src/extension/IOController_Posix.ipp

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@
2222

2323
#include "IOController.hpp"
2424

25-
#include "../threading/ReactionTask.hpp"
26-
2725
namespace NUClear {
2826
namespace extension {
2927

@@ -169,9 +167,6 @@ namespace extension {
169167
// Start by rebuilding the list
170168
rebuild_list();
171169

172-
// Control handlers run on the IO pool at HIGH priority with Inline::NEVER so they are
173-
// always queued to the IO pool rather than running on the emitting thread. Inline wake
174-
// reactions registered after them only write to the notify pipe so ::poll() unblocks.
175170
on<Trigger<dsl::word::IOConfiguration>, Pool<IOPool>, Priority::HIGH, Inline::NEVER>().then(
176171
"Configure IO Reaction",
177172
[this](const dsl::word::IOConfiguration& config) {
@@ -181,7 +176,6 @@ namespace extension {
181176
std::sort(tasks.begin(), tasks.end());
182177
dirty.store(true, std::memory_order_release);
183178
});
184-
on<Trigger<dsl::word::IOConfiguration>, Inline::ALWAYS>().then("Configure IO bump", [this] { bump(); });
185179

186180
on<Trigger<dsl::word::IOFinished>, Pool<IOPool>, Priority::HIGH, Inline::NEVER>().then("IO Finished", [this](const dsl::word::IOFinished& event) {
187181
auto task = std::find_if(tasks.begin(), tasks.end(), [&event](const Task& t) {
@@ -207,7 +201,6 @@ namespace extension {
207201
}
208202
}
209203
});
210-
on<Trigger<dsl::word::IOFinished>, Inline::ALWAYS>().then("IO Finished bump", [this] { bump(); });
211204

212205
on<Trigger<dsl::operation::Unbind<IO>>, Pool<IOPool>, Priority::HIGH, Inline::NEVER>().then(
213206
"Unbind IO Reaction",
@@ -222,22 +215,10 @@ namespace extension {
222215

223216
dirty.store(true, std::memory_order_release);
224217
});
225-
on<Trigger<dsl::operation::Unbind<IO>>, Inline::ALWAYS>().then("Unbind IO bump", [this] { bump(); });
226-
227-
on<Shutdown, Pool<IOPool>, Priority::HIGH, Inline::NEVER>().then("Shutdown IO Controller", [this] {
228-
running.store(false, std::memory_order_release);
229-
});
230-
on<Shutdown, Inline::ALWAYS>().then("Shutdown IO bump", [this] { bump(); });
231-
232-
on<Startup, Pool<IOPool>, Priority::NORMAL, Inline::NEVER>().then("IO Poll", [this] {
233-
if (!running.load(std::memory_order_acquire)) {
234-
return;
235-
}
236-
237-
if (dirty.load(std::memory_order_acquire)) {
238-
rebuild_list();
239-
}
240218

219+
register_shutdown_control();
220+
register_inline_bump_reactions();
221+
register_poll_loop([this] {
241222
if (::poll(watches.data(), nfds_t(watches.size()), -1) < 0) {
242223
throw std::system_error(network_errno,
243224
std::system_category(),
@@ -249,8 +230,6 @@ namespace extension {
249230
process_event(fd);
250231
}
251232
}
252-
253-
powerplant.submit(threading::ReactionTask::get_current_task()->parent->get_task());
254233
});
255234
}
256235

src/extension/IOController_Windows.ipp

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@
2222

2323
#include "IOController.hpp"
2424

25-
#include "../threading/ReactionTask.hpp"
26-
2725
namespace NUClear {
2826
namespace extension {
2927

@@ -163,7 +161,6 @@ namespace extension {
163161
tasks.insert(std::make_pair(event, Task{config.fd, config.events, config.reaction}));
164162
dirty.store(true, std::memory_order_release);
165163
});
166-
on<Trigger<dsl::word::IOConfiguration>, Inline::ALWAYS>().then("Configure IO bump", [this] { bump(); });
167164

168165
on<Trigger<dsl::word::IOFinished>, Pool<IOPool>, Priority::HIGH, Inline::NEVER>().then("IO Finished", [this](const dsl::word::IOFinished& event) {
169166
auto it = std::find_if(tasks.begin(), tasks.end(), [&event](const std::pair<WSAEVENT, Task>& t) {
@@ -182,7 +179,6 @@ namespace extension {
182179
}
183180
}
184181
});
185-
on<Trigger<dsl::word::IOFinished>, Inline::ALWAYS>().then("IO Finished bump", [this] { bump(); });
186182

187183
on<Trigger<dsl::operation::Unbind<IO>>, Pool<IOPool>, Priority::HIGH, Inline::NEVER>().then(
188184
"Unbind IO Reaction",
@@ -197,22 +193,10 @@ namespace extension {
197193

198194
dirty.store(true, std::memory_order_release);
199195
});
200-
on<Trigger<dsl::operation::Unbind<IO>>, Inline::ALWAYS>().then("Unbind IO bump", [this] { bump(); });
201-
202-
on<Shutdown, Pool<IOPool>, Priority::HIGH, Inline::NEVER>().then("Shutdown IO Controller", [this] {
203-
running.store(false, std::memory_order_release);
204-
});
205-
on<Shutdown, Inline::ALWAYS>().then("Shutdown IO bump", [this] { bump(); });
206-
207-
on<Startup, Pool<IOPool>, Priority::NORMAL, Inline::NEVER>().then("IO Poll", [this] {
208-
if (!running.load(std::memory_order_acquire)) {
209-
return;
210-
}
211-
212-
if (dirty.load(std::memory_order_acquire)) {
213-
rebuild_list();
214-
}
215196

197+
register_shutdown_control();
198+
register_inline_bump_reactions();
199+
register_poll_loop([this] {
216200
const DWORD event_index = WSAWaitForMultipleEvents(static_cast<DWORD>(watches.size()),
217201
watches.data(),
218202
false,
@@ -223,8 +207,6 @@ namespace extension {
223207
auto& event = watches[event_index - WSA_WAIT_EVENT_0];
224208
process_event(event);
225209
}
226-
227-
powerplant.submit(threading::ReactionTask::get_current_task()->parent->get_task());
228210
});
229211
}
230212

0 commit comments

Comments
 (0)