Add single-threaded EventLoop that multiplexes D-Bus and fd sources - #21
Merged
Conversation
Foundation for retiring the per-client worker-thread + mutex pattern in the BlueZ clients. EventLoop drives an sdbus::IConnection via its poll data (getEventLoopPollData / processPendingEvent) together with a set of EventSource objects, so D-Bus callbacks and device/fd I/O run on one thread and state shared between them needs no locking. - EventSource: fd() + events() + dispatch(revents), invoked on the loop thread. - EventLoop::run(bus) polls the bus fd/eventFd plus every registered source; stop() is async-signal-safe and thread-safe via an eventfd; add()/remove() defer to the next iteration so they are safe to call from within a dispatch(). - Opt-in self-test (-DSDBUS_CPP_EXAMPLES_BUILD_TESTS=ON) drives the system bus with two fd sources on one thread and asserts token dispatch, async-reply delivery, and a clean stop, with a timerfd deadline so it cannot hang. No client uses it yet; converting the BlueZ clients is a follow-up. Signed-off-by: Joel Winarske <joel.winarske@linux.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First step of the event-loop source contract that was scoped earlier. This PR adds the machinery only — no client is converted yet.
What
EventSource—fd()+events()+dispatch(revents), invoked on the loop thread.EventLoop— drives ansdbus::IConnectionvia its external-loop poll data (getEventLoopPollData()/processPendingEvent()) alongside a set ofEventSources, all on the thread that callsrun(). This is the alternative toenterEventLoopAsync(): because D-Bus callbacks and device/fd I/O run on one thread, state shared between them needs no locking.stop(code)is async-signal-safe and thread-safe (writes an eventfd to breakpoll()), so it can be driven from a signal handler or another thread.add()/remove()defer to the next iteration, so they are safe to call from within adispatch()(controller plug/unplug does exactly this). Removed sources must outlive the iteration — documented on the method.Why
It's the foundation for retiring the per-client worker-thread +
std::mutexpattern in the BlueZ clients — the same fragmentation that PR #18's races came from. sdbus-c++ supports exactly this viagetEventLoopPollData()/processPendingEvent(), so the D-Bus connection,UdevMonitor, and eachInputReadercan eventually share one loop.Validation
Opt-in self-test (
-DSDBUS_CPP_EXAMPLES_BUILD_TESTS=ON, wired into CTest) drives the live system bus with two fd sources on a single thread and asserts:ListNamesreply is delivered byrun()— proving the loop drivesprocessPendingEvent(125 names, ok),stop()unblocksrun()and returns 0,with a timerfd as a hard deadline so it can never hang. Passes repeatably;
ctest100%. Verified clean under clang-format--Werrorand clang-tidy-19.Notes
compile_commands.json); it needs a reachable system bus and is intended for local/CTest runs. It skips gracefully with success if no bus is present.NOLINT(performance-unnecessary-value-param)in the test marks an API-mandated by-value parameter: sdbus deduces reply-handler argument types viafunction_traitsand requiresstd::optional<sdbus::Error>by value (a const-ref signature fails to compile), matching every existing async handler in the tree.Next
Step 2 — convert
UdevMonitorandInputReaderintoEventSources and move the three BlueZ clients ontoEventLoop, deleting their threads and mutexes. That step first needs the design decision flagged in the scoping: whether synchronous D-Bus calls inside handlers stay synchronous (simplest, but a slow call stalls input reading) or move to async / a dedicated HID read thread.