sdbus::asio: service sd_event loop upon request
If requested, add in an asynchronous sd_event servicing mechanism so
sd_events can be used in conjunction with boost::asio events. Code
throughout the openbmc repositories use sd_events, especially for
timers. In some cases, we may want to add boost::asio event handling for
superior asynchronous eventing, but not want to rewrite all the existing
sd_event code. This gives us the best of both worlds, with a
low-overhead mechanism to handle the sd_events.
Change-Id: I3f8e2aafa3f733439e1494253698d17c2f3a2321
Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com>
diff --git a/Makefile.am b/Makefile.am
index ab07920..ec552c3 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -8,6 +8,7 @@
sdbusplus/asio/connection.hpp \
sdbusplus/asio/object_server.hpp \
sdbusplus/asio/detail/async_send_handler.hpp \
+ sdbusplus/asio/sd_event.hpp \
sdbusplus/bus.hpp \
sdbusplus/bus/match.hpp \
sdbusplus/sdbus.hpp \
diff --git a/example/asio-example.cpp b/example/asio-example.cpp
index 354c7b0..cced85d 100644
--- a/example/asio-example.cpp
+++ b/example/asio-example.cpp
@@ -4,8 +4,10 @@
#include <iostream>
#include <sdbusplus/asio/connection.hpp>
#include <sdbusplus/asio/object_server.hpp>
+#include <sdbusplus/asio/sd_event.hpp>
#include <sdbusplus/bus.hpp>
#include <sdbusplus/server.hpp>
+#include <sdbusplus/timer.hpp>
int foo(int test)
{
@@ -130,6 +132,15 @@
iface->initialize();
iface->set_property("int", 45);
+
+ // sd_events work too using the default event loop
+ phosphor::Timer t1([]() { std::cerr << "*** tock ***\n"; });
+ t1.start(std::chrono::microseconds(1000000));
+ phosphor::Timer t2([]() { std::cerr << "*** tick ***\n"; });
+ t2.start(std::chrono::microseconds(500000), true);
+ // add the sd_event wrapper to the io object
+ sdbusplus::asio::sd_event_wrapper sdEvents(io);
+
io.run();
return 0;
diff --git a/sdbusplus/asio/sd_event.hpp b/sdbusplus/asio/sd_event.hpp
new file mode 100644
index 0000000..b4a2ae3
--- /dev/null
+++ b/sdbusplus/asio/sd_event.hpp
@@ -0,0 +1,125 @@
+/*
+// Copyright (c) 2018 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+#pragma once
+
+#include <systemd/sd-event.h>
+
+#include <boost/asio.hpp>
+
+namespace sdbusplus
+{
+
+namespace asio
+{
+/* A simple class to integrate the sd_event_loop into the boost::asio io_service
+ * in case a boost::asio user needs sd_events
+ */
+class sd_event_wrapper
+{
+ public:
+ sd_event_wrapper(boost::asio::io_service& io) :
+ evt(nullptr), descriptor(io), io(io)
+ {
+ sd_event_default(&evt);
+ if (evt)
+ {
+ descriptor.assign(sd_event_get_fd(evt));
+ async_run();
+ }
+ }
+ sd_event_wrapper(sd_event* evt, boost::asio::io_service& io) :
+ evt(evt), descriptor(io), io(io)
+ {
+ if (evt)
+ {
+ sd_event_ref(evt);
+ descriptor.assign(sd_event_get_fd(evt));
+ async_run();
+ }
+ }
+ ~sd_event_wrapper()
+ {
+ sd_event_unref(evt);
+ }
+ // process one event step in the queue
+ // return true if the queue is still alive
+ void run()
+ {
+ int ret;
+ int state = sd_event_get_state(evt);
+ switch (state)
+ {
+ case SD_EVENT_INITIAL:
+ ret = sd_event_prepare(evt);
+ if (ret > 0)
+ {
+ async_run();
+ }
+ else if (ret == 0)
+ {
+ async_wait();
+ }
+ break;
+ case SD_EVENT_ARMED:
+ ret = sd_event_wait(evt, 0);
+ if (ret >= 0)
+ {
+ async_run();
+ }
+ break;
+ case SD_EVENT_PENDING:
+ ret = sd_event_dispatch(evt);
+ if (ret > 0)
+ {
+ async_run();
+ }
+ break;
+ case SD_EVENT_FINISHED:
+ break;
+ default:
+ // throw something?
+ // doing nothing will break out of the async loop
+ break;
+ }
+ }
+ sd_event* get() const
+ {
+ return evt;
+ }
+
+ private:
+ void async_run()
+ {
+ io.post([this]() { run(); });
+ }
+ void async_wait()
+ {
+ descriptor.async_wait(boost::asio::posix::stream_descriptor::wait_read,
+ [this](const boost::system::error_code& error) {
+ if (!error)
+ {
+ run();
+ }
+ });
+ }
+ sd_event* evt;
+ boost::asio::posix::stream_descriptor descriptor;
+ boost::asio::io_service& io;
+};
+
+} // namespace asio
+
+} // namespace sdbusplus