Add coroutine support for sdbusplus::asio method calls
Using a coroutine to asynchronously execute method calls gives the best
of both worlds:
1) better readability because the code reads like synchronous code
2) better throughput because it is actually asynchronous
When passed in a boost::asio::yield_context, the sdbusplus::asio dbus
connection members async_send and async_method_call will execute
asynchronously using coroutines.
This also adds an example of how this works in the
example/asio-example.cpp file.
Change-Id: Ifb71b2c757ecbfd16b3be95bdefc45a701ca0d51
Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com>
diff --git a/example/asio-example.cpp b/example/asio-example.cpp
index cced85d..9819eba 100644
--- a/example/asio-example.cpp
+++ b/example/asio-example.cpp
@@ -1,4 +1,5 @@
#include <boost/asio.hpp>
+#include <boost/asio/spawn.hpp>
#include <chrono>
#include <ctime>
#include <iostream>
@@ -9,6 +10,7 @@
#include <sdbusplus/server.hpp>
#include <sdbusplus/timer.hpp>
+using variant = sdbusplus::message::variant<int, std::string>;
int foo(int test)
{
return ++test;
@@ -24,6 +26,68 @@
return 42;
}
+void do_start_async_method_call_one(
+ std::shared_ptr<sdbusplus::asio::connection> conn,
+ boost::asio::yield_context yield)
+{
+ boost::system::error_code ec;
+ variant testValue;
+ conn->yield_method_call<>(yield[ec], "xyz.openbmc_project.asio-test",
+ "/xyz/openbmc_project/test",
+ "org.freedesktop.DBus.Properties", "Set",
+ "xyz.openbmc_project.test", "int", variant(24));
+ testValue = conn->yield_method_call<variant>(
+ yield[ec], "xyz.openbmc_project.asio-test", "/xyz/openbmc_project/test",
+ "org.freedesktop.DBus.Properties", "Get", "xyz.openbmc_project.test",
+ "int");
+ if (!ec && testValue.get<int>() == 24)
+ {
+ std::cout << "async call to Properties.Get serialized via yield OK!\n";
+ }
+ else
+ {
+ std::cout << "ec = " << ec << ": " << testValue.get<int>() << "\n";
+ }
+ conn->yield_method_call<void>(
+ yield[ec], "xyz.openbmc_project.asio-test", "/xyz/openbmc_project/test",
+ "org.freedesktop.DBus.Properties", "Set", "xyz.openbmc_project.test",
+ "int", variant(42));
+ testValue = conn->yield_method_call<variant>(
+ yield[ec], "xyz.openbmc_project.asio-test", "/xyz/openbmc_project/test",
+ "org.freedesktop.DBus.Properties", "Get", "xyz.openbmc_project.test",
+ "int");
+ if (!ec && testValue.get<int>() == 42)
+ {
+ std::cout << "async call to Properties.Get serialized via yield OK!\n";
+ }
+ else
+ {
+ std::cout << "ec = " << ec << ": " << testValue.get<int>() << "\n";
+ }
+}
+
+void do_start_async_method_call_two(
+ std::shared_ptr<sdbusplus::asio::connection> conn,
+ boost::asio::yield_context yield)
+{
+ boost::system::error_code ec;
+ int32_t testCount;
+ std::string testValue;
+ std::tie(testCount, testValue) =
+ conn->yield_method_call<int32_t, std::string>(
+ yield[ec], "xyz.openbmc_project.asio-test",
+ "/xyz/openbmc_project/test", "xyz.openbmc_project.test",
+ "TestMethod", int32_t(42));
+ if (!ec && testCount == 42 && testValue == "success: 42")
+ {
+ std::cout << "async call to TestMethod serialized via yield OK!\n";
+ }
+ else
+ {
+ std::cout << "ec = " << ec << ": " << testValue << "\n";
+ }
+}
+
int main()
{
using GetSubTreeType = std::vector<std::pair<
@@ -121,7 +185,8 @@
// test method creation
iface->register_method("TestMethod", [](const int32_t& callCount) {
- return "success: " + std::to_string(callCount);
+ return std::make_tuple(callCount,
+ "success: " + std::to_string(callCount));
});
iface->register_method("TestFunction", foo);
@@ -141,6 +206,14 @@
// add the sd_event wrapper to the io object
sdbusplus::asio::sd_event_wrapper sdEvents(io);
+ // set up a client to make an async call to the server
+ // using coroutines (userspace cooperative multitasking)
+ boost::asio::spawn(io, [conn](boost::asio::yield_context yield) {
+ do_start_async_method_call_one(conn, yield);
+ });
+ boost::asio::spawn(io, [conn](boost::asio::yield_context yield) {
+ do_start_async_method_call_two(conn, yield);
+ });
io.run();
return 0;