utility/sdbus: Add bus processing workaround
Provide a wrapper for others to use that works around a memory leak that
we have with processing dbus matches in an event loop.
Change-Id: I944e9c2547844b507216e334bd013f26b8a547da
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/.gitignore b/.gitignore
index 327ea97..860c423 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,5 @@
/build*/
-/subprojects/*/
+/subprojects/*
+!/subprojects/googletest.wrap
+!/subprojects/sdbusplus.wrap
+!/subprojects/stdplus.wrap
diff --git a/src/meson.build b/src/meson.build
index a9e0620..e736633 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -70,4 +70,5 @@
install_headers(
'sdeventplus/utility/timer.hpp',
+ 'sdeventplus/utility/sdbus.hpp',
subdir: 'sdeventplus/utility')
diff --git a/src/sdeventplus/utility/sdbus.hpp b/src/sdeventplus/utility/sdbus.hpp
new file mode 100644
index 0000000..bd4c5ce
--- /dev/null
+++ b/src/sdeventplus/utility/sdbus.hpp
@@ -0,0 +1,43 @@
+#include <sdbusplus/bus.hpp>
+#include <sdeventplus/event.hpp>
+#include <sdeventplus/exception.hpp>
+#include <stdexcept>
+
+namespace sdeventplus::utility
+{
+
+/**
+ * @brief Run and event loop with a given bus, bug workaround
+ * @detail There is a known issue with systemd not correctly dispatching
+ * all of the events for a given bus. We need a workaround to
+ * prevent memory leaks.
+ * https://github.com/systemd/systemd/issues/22046
+ */
+inline int loopWithBus(Event& event, sdbusplus::bus_t& bus)
+{
+ bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
+ try
+ {
+ do
+ {
+ if (bus.is_open())
+ {
+ // Process all outstanding bus events before running the loop.
+ // This prevents the sd-bus handling logic from leaking memory.
+ while (bus.process_discard() > 0)
+ ;
+ }
+ } while (event.run(std::nullopt) > 0);
+ }
+ catch (const SdEventError& e)
+ {
+ if (e.code().value() == ESTALE)
+ {
+ return event.get_exit_code();
+ }
+ throw;
+ }
+ throw std::runtime_error("Unknown sd-event terminaton");
+}
+
+} // namespace sdeventplus::utility
diff --git a/subprojects/fmt.wrap b/subprojects/fmt.wrap
deleted file mode 100644
index 6847ae5..0000000
--- a/subprojects/fmt.wrap
+++ /dev/null
@@ -1,3 +0,0 @@
-[wrap-git]
-url = https://github.com/fmtlib/fmt
-revision = HEAD
diff --git a/subprojects/sdbusplus.wrap b/subprojects/sdbusplus.wrap
new file mode 100644
index 0000000..edd9a31
--- /dev/null
+++ b/subprojects/sdbusplus.wrap
@@ -0,0 +1,7 @@
+[wrap-git]
+url = https://github.com/openbmc/sdbusplus.git
+revision = HEAD
+
+[provide]
+sdbusplus = sdbusplus_dep
+program_names = sdbus++, sdbus++-gen-meson
diff --git a/test/meson.build b/test/meson.build
index 29704ca..b2cdbc1 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -31,11 +31,20 @@
'source/io',
'source/signal',
'source/time',
+ 'utility/sdbus',
'utility/timer',
]
foreach t : tests
- test(t, executable(t.underscorify(), t + '.cpp',
- implicit_include_directories: false,
- dependencies: [sdeventplus_dep, gtest, gmock]))
+ test(t,
+ executable(
+ t.underscorify(),
+ t + '.cpp',
+ implicit_include_directories: false,
+ dependencies: [
+ dependency('sdbusplus'),
+ sdeventplus_dep,
+ gtest,
+ gmock,
+ ]))
endforeach
diff --git a/test/utility/sdbus.cpp b/test/utility/sdbus.cpp
new file mode 100644
index 0000000..dda33c3
--- /dev/null
+++ b/test/utility/sdbus.cpp
@@ -0,0 +1,43 @@
+#include <gtest/gtest.h>
+#include <sdeventplus/source/event.hpp>
+#include <sdeventplus/utility/sdbus.hpp>
+
+namespace sdeventplus::utility
+{
+
+struct LoopWithBus : testing::Test
+{
+ Event event = Event::get_new();
+ sdbusplus::bus_t bus = sdbusplus::bus::new_bus();
+};
+
+TEST_F(LoopWithBus, ImmediateExit)
+{
+ event.exit(0);
+ EXPECT_EQ(0, loopWithBus(event, bus));
+}
+
+TEST_F(LoopWithBus, DelayedExit)
+{
+ source::Defer(event, [](source::EventBase& b) {
+ b.get_event().exit(1);
+ }).set_floating(true);
+ EXPECT_EQ(1, loopWithBus(event, bus));
+}
+
+TEST_F(LoopWithBus, ExitSources)
+{
+ int d1 = 0, d2 = 0;
+ source::Exit(event, [&](source::EventBase&) { d1 = 1; }).set_floating(true);
+ source::Defer(event, [&](source::EventBase& b) {
+ source::Exit(event, [&](source::EventBase&) {
+ d2 = 2;
+ }).set_floating(true);
+ b.get_event().exit(3);
+ }).set_floating(true);
+ EXPECT_EQ(3, loopWithBus(event, bus));
+ EXPECT_EQ(1, d1);
+ EXPECT_EQ(2, d2);
+}
+
+} // namespace sdeventplus::utility