diff options
| -rw-r--r-- | services/surfaceflinger/Promise.h | 80 | ||||
| -rw-r--r-- | services/surfaceflinger/tests/unittests/Android.bp | 1 | ||||
| -rw-r--r-- | services/surfaceflinger/tests/unittests/PromiseTest.cpp | 88 |
3 files changed, 169 insertions, 0 deletions
diff --git a/services/surfaceflinger/Promise.h b/services/surfaceflinger/Promise.h new file mode 100644 index 0000000000..a80d441a4b --- /dev/null +++ b/services/surfaceflinger/Promise.h @@ -0,0 +1,80 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * 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 <future> +#include <type_traits> +#include <utility> + +namespace android::promise { +namespace impl { + +template <typename T> +struct FutureResult { + using Type = T; +}; + +template <typename T> +struct FutureResult<std::future<T>> { + using Type = T; +}; + +} // namespace impl + +template <typename T> +using FutureResult = typename impl::FutureResult<T>::Type; + +template <typename... Args> +inline auto defer(Args... args) { + return std::async(std::launch::deferred, std::forward<Args>(args)...); +} + +template <typename T> +inline std::future<T> yield(T&& v) { + return defer([](T&& v) { return std::forward<T>(v); }, std::forward<T>(v)); +} + +template <typename T> +struct Chain { + Chain(std::future<T>&& f) : future(std::move(f)) {} + operator std::future<T>&&() && { return std::move(future); } + + T get() && { return future.get(); } + + template <typename F, typename R = std::invoke_result_t<F, T>> + auto then(F&& op) && -> Chain<FutureResult<R>> { + return defer( + [](auto&& f, F&& op) { + R r = op(f.get()); + if constexpr (std::is_same_v<R, FutureResult<R>>) { + return r; + } else { + return r.get(); + } + }, + std::move(future), std::forward<F>(op)); + } + + std::future<T> future; +}; + +template <typename T> +inline Chain<T> chain(std::future<T>&& f) { + return std::move(f); +} + +} // namespace android::promise diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index 2f2fb20e21..7574ff1773 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -48,6 +48,7 @@ cc_test { "LayerHistoryTestV2.cpp", "LayerMetadataTest.cpp", "PhaseOffsetsTest.cpp", + "PromiseTest.cpp", "SchedulerTest.cpp", "SchedulerUtilsTest.cpp", "SetFrameRateTest.cpp", diff --git a/services/surfaceflinger/tests/unittests/PromiseTest.cpp b/services/surfaceflinger/tests/unittests/PromiseTest.cpp new file mode 100644 index 0000000000..e4dc1fedb1 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/PromiseTest.cpp @@ -0,0 +1,88 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * 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. + */ + +#include <algorithm> +#include <future> +#include <string> +#include <thread> +#include <vector> + +#include <gtest/gtest.h> + +#include "Promise.h" + +namespace android { +namespace { + +using Bytes = std::vector<uint8_t>; + +Bytes decrement(Bytes bytes) { + std::transform(bytes.begin(), bytes.end(), bytes.begin(), [](auto b) { return b - 1; }); + return bytes; +} + +} // namespace + +TEST(PromiseTest, yield) { + EXPECT_EQ(42, promise::yield(42).get()); + + auto ptr = std::make_unique<char>('!'); + auto future = promise::yield(std::move(ptr)); + EXPECT_EQ('!', *future.get()); +} + +TEST(PromiseTest, chain) { + std::packaged_task<const char*()> fetchString([] { return "ifmmp-"; }); + + std::packaged_task<Bytes(std::string)> appendString([](std::string str) { + str += "!xpsme"; + return Bytes{str.begin(), str.end()}; + }); + + std::packaged_task<std::future<Bytes>(Bytes)> decrementBytes( + [](Bytes bytes) { return promise::defer(decrement, std::move(bytes)); }); + + auto fetch = fetchString.get_future(); + std::thread fetchThread(std::move(fetchString)); + + std::thread appendThread, decrementThread; + + EXPECT_EQ("hello, world", + promise::chain(std::move(fetch)) + .then([](const char* str) { return std::string(str); }) + .then([&](std::string str) { + auto append = appendString.get_future(); + appendThread = std::thread(std::move(appendString), std::move(str)); + return append; + }) + .then([&](Bytes bytes) { + auto decrement = decrementBytes.get_future(); + decrementThread = std::thread(std::move(decrementBytes), + std::move(bytes)); + return decrement; + }) + .then([](std::future<Bytes> bytes) { return bytes; }) + .then([](const Bytes& bytes) { + return std::string(bytes.begin(), bytes.end()); + }) + .get()); + + fetchThread.join(); + appendThread.join(); + decrementThread.join(); +} + +} // namespace android |