summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHÃ¥vard Pettersen <3535158+havardpe@users.noreply.github.com>2022-10-11 16:21:23 +0200
committerGitHub <noreply@github.com>2022-10-11 16:21:23 +0200
commit0d6d4159ba109f78fd9fea0c6b5e8d940ffc7e90 (patch)
treecea5970bbdc0bca6a52b988081881c39ecbe513c
parentd06fa108aa31cbffb644c1af72fe3074938a0c1d (diff)
parent2743ff3bc794ccdd276800a0f98050fbe8f346d3 (diff)
Merge pull request #24339 from vespa-engine/havardpe/experiment-with-coroutines
experiment with coroutines
-rw-r--r--vespalib/CMakeLists.txt9
-rw-r--r--vespalib/src/tests/coro/detached/CMakeLists.txt9
-rw-r--r--vespalib/src/tests/coro/detached/detached_test.cpp19
-rw-r--r--vespalib/src/tests/coro/lazy/CMakeLists.txt9
-rw-r--r--vespalib/src/tests/coro/lazy/lazy_test.cpp44
-rw-r--r--vespalib/src/vespa/vespalib/coro/CMakeLists.txt5
-rw-r--r--vespalib/src/vespa/vespalib/coro/detached.h20
-rw-r--r--vespalib/src/vespa/vespalib/coro/lazy.h72
-rw-r--r--vespalib/src/vespa/vespalib/coro/sync_wait.h34
9 files changed, 218 insertions, 3 deletions
diff --git a/vespalib/CMakeLists.txt b/vespalib/CMakeLists.txt
index 8edd7fb2c5d..6ecac23d5fa 100644
--- a/vespalib/CMakeLists.txt
+++ b/vespalib/CMakeLists.txt
@@ -42,6 +42,8 @@ vespa_define_module(
src/tests/component
src/tests/compress
src/tests/compression
+ src/tests/coro/detached
+ src/tests/coro/lazy
src/tests/cpu_usage
src/tests/crc
src/tests/crypto
@@ -202,9 +204,13 @@ vespa_define_module(
src/tests/fastlib/text
LIBS
+ src/vespa/fastlib/io
+ src/vespa/fastlib/text
+ src/vespa/fastlib/text/apps
src/vespa/vespalib
src/vespa/vespalib/btree
src/vespa/vespalib/component
+ src/vespa/vespalib/coro
src/vespa/vespalib/crypto
src/vespa/vespalib/data
src/vespa/vespalib/data/slime
@@ -231,7 +237,4 @@ vespa_define_module(
src/vespa/vespalib/time
src/vespa/vespalib/trace
src/vespa/vespalib/util
- src/vespa/fastlib/io
- src/vespa/fastlib/text
- src/vespa/fastlib/text/apps
)
diff --git a/vespalib/src/tests/coro/detached/CMakeLists.txt b/vespalib/src/tests/coro/detached/CMakeLists.txt
new file mode 100644
index 00000000000..237b8615fec
--- /dev/null
+++ b/vespalib/src/tests/coro/detached/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vespalib_detached_test_app TEST
+ SOURCES
+ detached_test.cpp
+ DEPENDS
+ vespalib
+ GTest::GTest
+)
+vespa_add_test(NAME vespalib_detached_test_app COMMAND vespalib_detached_test_app)
diff --git a/vespalib/src/tests/coro/detached/detached_test.cpp b/vespalib/src/tests/coro/detached/detached_test.cpp
new file mode 100644
index 00000000000..f23d16cc75c
--- /dev/null
+++ b/vespalib/src/tests/coro/detached/detached_test.cpp
@@ -0,0 +1,19 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/vespalib/coro/detached.h>
+#include <vespa/vespalib/gtest/gtest.h>
+
+using vespalib::coro::Detached;
+
+Detached set_result(int &res, int value) {
+ res = value;
+ co_return;
+}
+
+TEST(DetachedTest, call_detached_coroutine) {
+ int result = 0;
+ set_result(result, 42);
+ EXPECT_EQ(result, 42);
+}
+
+GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/vespalib/src/tests/coro/lazy/CMakeLists.txt b/vespalib/src/tests/coro/lazy/CMakeLists.txt
new file mode 100644
index 00000000000..daa11eb3576
--- /dev/null
+++ b/vespalib/src/tests/coro/lazy/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vespalib_lazy_test_app TEST
+ SOURCES
+ lazy_test.cpp
+ DEPENDS
+ vespalib
+ GTest::GTest
+)
+vespa_add_test(NAME vespalib_lazy_test_app COMMAND vespalib_lazy_test_app)
diff --git a/vespalib/src/tests/coro/lazy/lazy_test.cpp b/vespalib/src/tests/coro/lazy/lazy_test.cpp
new file mode 100644
index 00000000000..a715e473aaf
--- /dev/null
+++ b/vespalib/src/tests/coro/lazy/lazy_test.cpp
@@ -0,0 +1,44 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/vespalib/coro/lazy.h>
+#include <vespa/vespalib/coro/sync_wait.h>
+#include <vespa/vespalib/gtest/gtest.h>
+
+using vespalib::coro::Lazy;
+using vespalib::coro::sync_wait;
+
+Lazy<int> make_lazy(int value) {
+ co_return value;
+}
+
+Lazy<int> async_add_values(int a, int b) {
+ auto lazy_a = make_lazy(a);
+ auto lazy_b = make_lazy(b);
+ co_return (co_await lazy_a + co_await lazy_b);
+}
+
+Lazy<int> async_sum(Lazy<int> a, Lazy<int> b) {
+ co_return (co_await a + co_await b);
+}
+
+TEST(LazyTest, simple_lazy_value) {
+ auto lazy = make_lazy(42);
+ auto result = sync_wait(lazy);
+ EXPECT_EQ(result, 42);
+}
+
+TEST(LazyTest, async_sum_of_async_values) {
+ auto lazy = async_add_values(10, 20);
+ auto result = sync_wait(lazy);
+ EXPECT_EQ(result, 30);
+}
+
+TEST(LazyTest, async_sum_of_external_async_values) {
+ auto a = make_lazy(100);
+ auto b = make_lazy(200);
+ auto lazy = async_sum(std::move(a), std::move(b));
+ auto result = sync_wait(lazy);
+ EXPECT_EQ(result, 300);
+}
+
+GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/vespalib/src/vespa/vespalib/coro/CMakeLists.txt b/vespalib/src/vespa/vespalib/coro/CMakeLists.txt
new file mode 100644
index 00000000000..d190c2e8ddc
--- /dev/null
+++ b/vespalib/src/vespa/vespalib/coro/CMakeLists.txt
@@ -0,0 +1,5 @@
+# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_library(vespalib_vespalib_coro OBJECT
+ SOURCES
+ DEPENDS
+)
diff --git a/vespalib/src/vespa/vespalib/coro/detached.h b/vespalib/src/vespa/vespalib/coro/detached.h
new file mode 100644
index 00000000000..6d051e53121
--- /dev/null
+++ b/vespalib/src/vespa/vespalib/coro/detached.h
@@ -0,0 +1,20 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include <coroutine>
+#include <exception>
+
+namespace vespalib::coro {
+
+struct Detached {
+ struct promise_type {
+ Detached get_return_object() { return {}; }
+ static std::suspend_never initial_suspend() noexcept { return {}; }
+ static std::suspend_never final_suspend() noexcept { return {}; }
+ static void unhandled_exception() { std::terminate(); }
+ void return_void() noexcept {};
+ };
+};
+
+}
diff --git a/vespalib/src/vespa/vespalib/coro/lazy.h b/vespalib/src/vespa/vespalib/coro/lazy.h
new file mode 100644
index 00000000000..b007f565c93
--- /dev/null
+++ b/vespalib/src/vespa/vespalib/coro/lazy.h
@@ -0,0 +1,72 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include <concepts>
+#include <coroutine>
+#include <optional>
+
+namespace vespalib::coro {
+
+template <std::movable T>
+class [[nodiscard]] Lazy {
+public:
+ struct promise_type {
+ Lazy<T> get_return_object() { return Lazy(Handle::from_promise(*this)); }
+ static std::suspend_always initial_suspend() noexcept { return {}; }
+ static auto final_suspend() noexcept {
+ struct awaiter {
+ bool await_ready() const noexcept { return false; }
+ std::coroutine_handle<> await_suspend(Handle handle) const noexcept {
+ auto waiter = handle.promise().waiter;
+ return waiter ? waiter : std::noop_coroutine();
+ }
+ void await_resume() const noexcept {}
+ };
+ return awaiter();
+ }
+ void return_value(T ret_value) noexcept {
+ value = std::move(ret_value);
+ }
+ static void unhandled_exception() { std::terminate(); }
+ std::optional<T> value;
+ std::coroutine_handle<> waiter;
+ promise_type(promise_type &&) = delete;
+ promise_type(const promise_type &) = delete;
+ promise_type() : value(std::nullopt), waiter(nullptr) {}
+ };
+ using Handle = std::coroutine_handle<promise_type>;
+
+private:
+ Handle _handle;
+
+public:
+ Lazy(const Lazy &) = delete;
+ Lazy &operator=(const Lazy &) = delete;
+ explicit Lazy(Handle handle_in) noexcept : _handle(handle_in) {}
+ Lazy(Lazy &&rhs) noexcept : _handle(std::exchange(rhs._handle, nullptr)) {}
+ auto operator co_await() {
+ struct awaiter {
+ Handle handle;
+ bool await_ready() const noexcept {
+ return handle.done();
+ }
+ Handle await_suspend(std::coroutine_handle<> waiter) const noexcept {
+ handle.promise().waiter = waiter;
+ return handle;
+ }
+ T &await_resume() const noexcept {
+ return *handle.promise().value;
+ }
+ awaiter(Handle handle_in) : handle(handle_in) {}
+ };
+ return awaiter(_handle);
+ }
+ ~Lazy() {
+ if (_handle) {
+ _handle.destroy();
+ }
+ }
+};
+
+}
diff --git a/vespalib/src/vespa/vespalib/coro/sync_wait.h b/vespalib/src/vespa/vespalib/coro/sync_wait.h
new file mode 100644
index 00000000000..e6a8fdc43f6
--- /dev/null
+++ b/vespalib/src/vespa/vespalib/coro/sync_wait.h
@@ -0,0 +1,34 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include "detached.h"
+#include "lazy.h"
+#include <coroutine>
+#include <vespa/vespalib/util/gate.h>
+
+namespace vespalib::coro {
+
+template <typename T, typename S>
+Detached signal_when_done(Lazy<T> &value, S &sink) {
+ sink(co_await value);
+}
+
+template <typename T>
+T &sync_wait(Lazy<T> &value) {
+ struct MySink {
+ Gate gate;
+ T *result;
+ void operator()(T &result_in) {
+ result = &result_in;
+ gate.countDown();
+ }
+ MySink() : gate(), result(nullptr) {}
+ };
+ MySink sink;
+ signal_when_done(value, sink);
+ sink.gate.await();
+ return *sink.result;
+}
+
+}