diff options
author | Håvard Pettersen <havardpe@yahooinc.com> | 2022-10-04 12:03:32 +0000 |
---|---|---|
committer | Håvard Pettersen <havardpe@yahooinc.com> | 2022-10-06 14:11:25 +0000 |
commit | 2743ff3bc794ccdd276800a0f98050fbe8f346d3 (patch) | |
tree | 5071d3528dcf05358c6fff1b4c0c3dd7d4711324 /vespalib | |
parent | 51e61ab17c38d01981a08527ad5a698beeb14411 (diff) |
experiment with coroutines
Diffstat (limited to 'vespalib')
-rw-r--r-- | vespalib/CMakeLists.txt | 9 | ||||
-rw-r--r-- | vespalib/src/tests/coro/detached/CMakeLists.txt | 9 | ||||
-rw-r--r-- | vespalib/src/tests/coro/detached/detached_test.cpp | 19 | ||||
-rw-r--r-- | vespalib/src/tests/coro/lazy/CMakeLists.txt | 9 | ||||
-rw-r--r-- | vespalib/src/tests/coro/lazy/lazy_test.cpp | 44 | ||||
-rw-r--r-- | vespalib/src/vespa/vespalib/coro/CMakeLists.txt | 5 | ||||
-rw-r--r-- | vespalib/src/vespa/vespalib/coro/detached.h | 20 | ||||
-rw-r--r-- | vespalib/src/vespa/vespalib/coro/lazy.h | 72 | ||||
-rw-r--r-- | vespalib/src/vespa/vespalib/coro/sync_wait.h | 34 |
9 files changed, 218 insertions, 3 deletions
diff --git a/vespalib/CMakeLists.txt b/vespalib/CMakeLists.txt index df1e1006828..c5c9552a09b 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 @@ -200,9 +202,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 @@ -229,7 +235,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; +} + +} |