async: add coroutine task support
Add sdbusplus::async::task<...> template which works with the
std::executors proposal (P2300) and test-cases.
Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
Change-Id: Ie63791daeab90ae1cc3862bb30878531b1775fa7
diff --git a/include/sdbusplus/async/execution.hpp b/include/sdbusplus/async/execution.hpp
new file mode 100644
index 0000000..6120627
--- /dev/null
+++ b/include/sdbusplus/async/execution.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+// The upstream code has some warnings under GCC, so turn them off
+// as needed.
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wnon-template-friend"
+#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
+#include <sdbusplus/async/stdexec/coroutine.hpp>
+#include <sdbusplus/async/stdexec/execution.hpp>
+#pragma GCC diagnostic pop
+
+// Add std::execution as sdbusplus::async::execution so that we can simplify
+// reference to any parts of it we use internally.
+namespace sdbusplus::async
+{
+namespace execution = std::execution;
+}
diff --git a/include/sdbusplus/async/task.hpp b/include/sdbusplus/async/task.hpp
new file mode 100644
index 0000000..e2618e4
--- /dev/null
+++ b/include/sdbusplus/async/task.hpp
@@ -0,0 +1,9 @@
+#pragma once
+
+#include <sdbusplus/async/stdexec/task.hpp>
+
+namespace sdbusplus::async
+{
+template <typename T = void>
+using task = exec::task<T>;
+}
diff --git a/test/async/task.cpp b/test/async/task.cpp
new file mode 100644
index 0000000..34d9e65
--- /dev/null
+++ b/test/async/task.cpp
@@ -0,0 +1,78 @@
+#include <sdbusplus/async/execution.hpp>
+#include <sdbusplus/async/task.hpp>
+
+#include <gtest/gtest.h>
+
+using namespace sdbusplus::async;
+
+TEST(Task, CoAwaitVoid)
+{
+ bool value = false;
+ auto t = [&]() -> task<> {
+ value = true;
+ co_return;
+ };
+
+ // Check to ensure the co-routine hasn't started executing yet.
+ EXPECT_FALSE(value);
+
+ // Run it and confirm the value is updated.
+ std::this_thread::sync_wait(t());
+ EXPECT_TRUE(value);
+}
+
+TEST(Task, CoAwaitInt)
+{
+ struct _
+ {
+ static auto one() -> task<int>
+ {
+ co_return 42;
+ }
+ static auto two(bool& executed) -> task<>
+ {
+ auto r = co_await one();
+ EXPECT_EQ(r, 42);
+ executed = true;
+ co_return;
+ }
+ };
+
+ // Add boolean to confirm that co-routine actually executed by the
+ // end.
+ bool executed = false;
+ std::this_thread::sync_wait(_::two(executed));
+ EXPECT_TRUE(executed);
+}
+
+TEST(Task, CoAwaitThrow)
+{
+ struct _
+ {
+ static auto one() -> task<>
+ {
+ throw std::logic_error("Failed");
+ co_return;
+ }
+
+ static auto two(bool& caught) -> task<>
+ {
+ try
+ {
+ co_await (one());
+ }
+ catch (const std::logic_error&)
+ {
+ caught = true;
+ }
+ }
+ };
+
+ // Ensure throws surface up.
+ EXPECT_THROW(std::this_thread::sync_wait(_::one()), std::logic_error);
+
+ // Ensure throws can be caught inside a co-routine.
+ bool caught = false;
+ std::this_thread::sync_wait(_::two(caught));
+ EXPECT_TRUE(caught);
+}
diff --git a/test/meson.build b/test/meson.build
index 4fa39d2..4d3114c 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -20,13 +20,14 @@
endif
tests = [
+ 'async/task',
'bus/list_names',
'bus/match',
'exception/sdbus_error',
'message/append',
'message/call',
- 'message/read',
'message/native_types',
+ 'message/read',
'message/types',
'timer',
'unpack_properties',