Loading include/ftl/details/future.h 0 → 100644 +98 −0 Original line number Diff line number Diff line /* * Copyright 2022 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 namespace android::ftl { template <typename, template <typename> class> class Future; namespace details { template <typename T> struct future_result { using type = T; }; template <typename T> struct future_result<std::future<T>> { using type = T; }; template <typename T> struct future_result<std::shared_future<T>> { using type = T; }; template <typename T, template <typename> class FutureImpl> struct future_result<Future<T, FutureImpl>> { using type = T; }; template <typename T> using future_result_t = typename future_result<T>::type; struct ValueTag {}; template <typename, typename T, template <typename> class> class BaseFuture; template <typename Self, typename T> class BaseFuture<Self, T, std::future> { using Impl = std::future<T>; public: Future<T, std::shared_future> share() { if (T* value = std::get_if<T>(&self())) { return {ValueTag{}, std::move(*value)}; } return std::get<Impl>(self()).share(); } protected: T get() { if (T* value = std::get_if<T>(&self())) { return std::move(*value); } return std::get<Impl>(self()).get(); } private: auto& self() { return static_cast<Self&>(*this).future_; } }; template <typename Self, typename T> class BaseFuture<Self, T, std::shared_future> { using Impl = std::shared_future<T>; protected: const T& get() const { if (const T* value = std::get_if<T>(&self())) { return *value; } return std::get<Impl>(self()).get(); } private: const auto& self() const { return static_cast<const Self&>(*this).future_; } }; } // namespace details } // namespace android::ftl include/ftl/future.h +85 −61 Original line number Diff line number Diff line Loading @@ -19,91 +19,115 @@ #include <future> #include <type_traits> #include <utility> #include <variant> #include <ftl/details/future.h> namespace android::ftl { // Creates a future that defers a function call until its result is queried. // Thin wrapper around FutureImpl<T> (concretely std::future<T> or std::shared_future<T>) with // extensions for pure values (created via ftl::yield) and continuations. // // auto future = ftl::defer([](int x) { return x + 1; }, 99); // assert(future.get() == 100); // See also SharedFuture<T> shorthand below. // template <typename F, typename... Args> inline auto defer(F&& f, Args&&... args) { return std::async(std::launch::deferred, std::forward<F>(f), std::forward<Args>(args)...); } template <typename T, template <typename> class FutureImpl = std::future> class Future final : public details::BaseFuture<Future<T, FutureImpl>, T, FutureImpl> { using Base = details::BaseFuture<Future, T, FutureImpl>; // Creates a future that wraps a value. // // auto future = ftl::yield(42); // assert(future.get() == 42); // // auto ptr = std::make_unique<char>('!'); // auto future = ftl::yield(std::move(ptr)); // assert(*future.get() == '!'); // template <typename T> inline std::future<T> yield(T&& v) { return defer([](T&& v) { return std::forward<T>(v); }, std::forward<T>(v)); } friend Base; // For BaseFuture<...>::self. friend details::BaseFuture<Future<T>, T, std::future>; // For BaseFuture<...>::share. namespace details { public: // Constructs an invalid future. Future() : future_(std::in_place_type<FutureImpl<T>>) {} template <typename T> struct future_result { using type = T; }; // Constructs a future from its standard counterpart, implicitly. Future(FutureImpl<T>&& f) : future_(std::move(f)) {} template <typename T> struct future_result<std::future<T>> { using type = T; }; bool valid() const { return std::holds_alternative<T>(future_) || std::get<FutureImpl<T>>(future_).valid(); } template <typename T> using future_result_t = typename future_result<T>::type; // Forwarding functions. Base::share is only defined when FutureImpl is std::future, whereas the // following are defined for either FutureImpl: using Base::get; // Attaches a continuation to a future. The continuation is a function that maps T to either R or // std::future<R>. In the former case, the chain wraps the result in a future as if by ftl::yield. // Attaches a continuation to the future. The continuation is a function that maps T to either R // or ftl::Future<R>. In the former case, the chain wraps the result in a future as if by // ftl::yield. // // auto future = ftl::yield(123); // std::future<char> futures[] = {ftl::yield('a'), ftl::yield('b')}; // ftl::Future<char> futures[] = {ftl::yield('a'), ftl::yield('b')}; // // std::future<char> chain = // ftl::chain(std::move(future)) // auto chain = // ftl::Future(std::move(future)) // .then([](int x) { return static_cast<std::size_t>(x % 2); }) // .then([&futures](std::size_t i) { return std::move(futures[i]); }); // // assert(chain.get() == 'b'); // template <typename T> struct Chain { // Implicit conversion. 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<future_result_t<R>> { auto then(F&& op) && -> Future<details::future_result_t<R>> { return defer( [](auto&& f, F&& op) { R r = op(f.get()); if constexpr (std::is_same_v<R, future_result_t<R>>) { if constexpr (std::is_same_v<R, details::future_result_t<R>>) { return r; } else { return r.get(); } }, std::move(future), std::forward<F>(op)); std::move(*this), std::forward<F>(op)); } std::future<T> future; }; private: template <typename V> friend Future<V> yield(V&&); template <typename V, typename... Args> friend Future<V> yield(Args&&...); } // namespace details template <typename... Args> Future(details::ValueTag, Args&&... args) : future_(std::in_place_type<T>, std::forward<Args>(args)...) {} std::variant<T, FutureImpl<T>> future_; }; template <typename T> inline auto chain(std::future<T>&& f) -> details::Chain<T> { return std::move(f); using SharedFuture = Future<T, std::shared_future>; // Deduction guide for implicit conversion. template <typename T, template <typename> class FutureImpl> Future(FutureImpl<T>&&) -> Future<T, FutureImpl>; // Creates a future that wraps a value. // // auto future = ftl::yield(42); // assert(future.get() == 42); // // auto ptr = std::make_unique<char>('!'); // auto future = ftl::yield(std::move(ptr)); // assert(*future.get() == '!'); // template <typename V> inline Future<V> yield(V&& value) { return {details::ValueTag{}, std::move(value)}; } template <typename V, typename... Args> inline Future<V> yield(Args&&... args) { return {details::ValueTag{}, std::forward<Args>(args)...}; } // Creates a future that defers a function call until its result is queried. // // auto future = ftl::defer([](int x) { return x + 1; }, 99); // assert(future.get() == 100); // template <typename F, typename... Args> inline auto defer(F&& f, Args&&... args) { return Future(std::async(std::launch::deferred, std::forward<F>(f), std::forward<Args>(args)...)); } } // namespace android::ftl libs/ftl/future_test.cpp +5 −5 Original line number Diff line number Diff line Loading @@ -42,9 +42,9 @@ TEST(Future, Example) { } { auto future = ftl::yield(123); std::future<char> futures[] = {ftl::yield('a'), ftl::yield('b')}; ftl::Future<char> futures[] = {ftl::yield('a'), ftl::yield('b')}; std::future<char> chain = ftl::chain(std::move(future)) ftl::Future<char> chain = ftl::Future(std::move(future)) .then([](int x) { return static_cast<size_t>(x % 2); }) .then([&futures](size_t i) { return std::move(futures[i]); }); Loading @@ -71,7 +71,7 @@ TEST(Future, Chain) { return ByteVector{str.begin(), str.end()}; }); std::packaged_task<std::future<ByteVector>(ByteVector)> decrement_bytes( std::packaged_task<ftl::Future<ByteVector>(ByteVector)> decrement_bytes( [](ByteVector bytes) { return ftl::defer(decrement, std::move(bytes)); }); auto fetch = fetch_string.get_future(); Loading @@ -81,7 +81,7 @@ TEST(Future, Chain) { EXPECT_EQ( "hello, world", ftl::chain(std::move(fetch)) ftl::Future(std::move(fetch)) .then([](const char* str) { return std::string(str); }) .then([&](std::string str) { auto append = append_string.get_future(); Loading @@ -93,7 +93,7 @@ TEST(Future, Chain) { decrement_thread = std::thread(std::move(decrement_bytes), std::move(bytes)); return decrement; }) .then([](std::future<ByteVector> bytes) { return bytes; }) .then([](ftl::Future<ByteVector> bytes) { return bytes; }) .then([](const ByteVector& bytes) { return std::string(bytes.begin(), bytes.end()); }) .get()); Loading services/surfaceflinger/BufferQueueLayer.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -48,7 +48,7 @@ BufferQueueLayer::~BufferQueueLayer() { // Interface implementation for Layer // ----------------------------------------------------------------------- void BufferQueueLayer::onLayerDisplayed(std::shared_future<FenceResult> futureFenceResult) { void BufferQueueLayer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult) { const sp<Fence> releaseFence = futureFenceResult.get().value_or(Fence::NO_FENCE); mConsumer->setReleaseFence(releaseFence); Loading services/surfaceflinger/BufferQueueLayer.h +1 −1 Original line number Diff line number Diff line Loading @@ -42,7 +42,7 @@ public: // Implements Layer. const char* getType() const override { return "BufferQueueLayer"; } void onLayerDisplayed(std::shared_future<FenceResult>) override; void onLayerDisplayed(ftl::SharedFuture<FenceResult>) override; // If a buffer was replaced this frame, release the former buffer void releasePendingBuffer(nsecs_t dequeueReadyTime) override; Loading Loading
include/ftl/details/future.h 0 → 100644 +98 −0 Original line number Diff line number Diff line /* * Copyright 2022 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 namespace android::ftl { template <typename, template <typename> class> class Future; namespace details { template <typename T> struct future_result { using type = T; }; template <typename T> struct future_result<std::future<T>> { using type = T; }; template <typename T> struct future_result<std::shared_future<T>> { using type = T; }; template <typename T, template <typename> class FutureImpl> struct future_result<Future<T, FutureImpl>> { using type = T; }; template <typename T> using future_result_t = typename future_result<T>::type; struct ValueTag {}; template <typename, typename T, template <typename> class> class BaseFuture; template <typename Self, typename T> class BaseFuture<Self, T, std::future> { using Impl = std::future<T>; public: Future<T, std::shared_future> share() { if (T* value = std::get_if<T>(&self())) { return {ValueTag{}, std::move(*value)}; } return std::get<Impl>(self()).share(); } protected: T get() { if (T* value = std::get_if<T>(&self())) { return std::move(*value); } return std::get<Impl>(self()).get(); } private: auto& self() { return static_cast<Self&>(*this).future_; } }; template <typename Self, typename T> class BaseFuture<Self, T, std::shared_future> { using Impl = std::shared_future<T>; protected: const T& get() const { if (const T* value = std::get_if<T>(&self())) { return *value; } return std::get<Impl>(self()).get(); } private: const auto& self() const { return static_cast<const Self&>(*this).future_; } }; } // namespace details } // namespace android::ftl
include/ftl/future.h +85 −61 Original line number Diff line number Diff line Loading @@ -19,91 +19,115 @@ #include <future> #include <type_traits> #include <utility> #include <variant> #include <ftl/details/future.h> namespace android::ftl { // Creates a future that defers a function call until its result is queried. // Thin wrapper around FutureImpl<T> (concretely std::future<T> or std::shared_future<T>) with // extensions for pure values (created via ftl::yield) and continuations. // // auto future = ftl::defer([](int x) { return x + 1; }, 99); // assert(future.get() == 100); // See also SharedFuture<T> shorthand below. // template <typename F, typename... Args> inline auto defer(F&& f, Args&&... args) { return std::async(std::launch::deferred, std::forward<F>(f), std::forward<Args>(args)...); } template <typename T, template <typename> class FutureImpl = std::future> class Future final : public details::BaseFuture<Future<T, FutureImpl>, T, FutureImpl> { using Base = details::BaseFuture<Future, T, FutureImpl>; // Creates a future that wraps a value. // // auto future = ftl::yield(42); // assert(future.get() == 42); // // auto ptr = std::make_unique<char>('!'); // auto future = ftl::yield(std::move(ptr)); // assert(*future.get() == '!'); // template <typename T> inline std::future<T> yield(T&& v) { return defer([](T&& v) { return std::forward<T>(v); }, std::forward<T>(v)); } friend Base; // For BaseFuture<...>::self. friend details::BaseFuture<Future<T>, T, std::future>; // For BaseFuture<...>::share. namespace details { public: // Constructs an invalid future. Future() : future_(std::in_place_type<FutureImpl<T>>) {} template <typename T> struct future_result { using type = T; }; // Constructs a future from its standard counterpart, implicitly. Future(FutureImpl<T>&& f) : future_(std::move(f)) {} template <typename T> struct future_result<std::future<T>> { using type = T; }; bool valid() const { return std::holds_alternative<T>(future_) || std::get<FutureImpl<T>>(future_).valid(); } template <typename T> using future_result_t = typename future_result<T>::type; // Forwarding functions. Base::share is only defined when FutureImpl is std::future, whereas the // following are defined for either FutureImpl: using Base::get; // Attaches a continuation to a future. The continuation is a function that maps T to either R or // std::future<R>. In the former case, the chain wraps the result in a future as if by ftl::yield. // Attaches a continuation to the future. The continuation is a function that maps T to either R // or ftl::Future<R>. In the former case, the chain wraps the result in a future as if by // ftl::yield. // // auto future = ftl::yield(123); // std::future<char> futures[] = {ftl::yield('a'), ftl::yield('b')}; // ftl::Future<char> futures[] = {ftl::yield('a'), ftl::yield('b')}; // // std::future<char> chain = // ftl::chain(std::move(future)) // auto chain = // ftl::Future(std::move(future)) // .then([](int x) { return static_cast<std::size_t>(x % 2); }) // .then([&futures](std::size_t i) { return std::move(futures[i]); }); // // assert(chain.get() == 'b'); // template <typename T> struct Chain { // Implicit conversion. 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<future_result_t<R>> { auto then(F&& op) && -> Future<details::future_result_t<R>> { return defer( [](auto&& f, F&& op) { R r = op(f.get()); if constexpr (std::is_same_v<R, future_result_t<R>>) { if constexpr (std::is_same_v<R, details::future_result_t<R>>) { return r; } else { return r.get(); } }, std::move(future), std::forward<F>(op)); std::move(*this), std::forward<F>(op)); } std::future<T> future; }; private: template <typename V> friend Future<V> yield(V&&); template <typename V, typename... Args> friend Future<V> yield(Args&&...); } // namespace details template <typename... Args> Future(details::ValueTag, Args&&... args) : future_(std::in_place_type<T>, std::forward<Args>(args)...) {} std::variant<T, FutureImpl<T>> future_; }; template <typename T> inline auto chain(std::future<T>&& f) -> details::Chain<T> { return std::move(f); using SharedFuture = Future<T, std::shared_future>; // Deduction guide for implicit conversion. template <typename T, template <typename> class FutureImpl> Future(FutureImpl<T>&&) -> Future<T, FutureImpl>; // Creates a future that wraps a value. // // auto future = ftl::yield(42); // assert(future.get() == 42); // // auto ptr = std::make_unique<char>('!'); // auto future = ftl::yield(std::move(ptr)); // assert(*future.get() == '!'); // template <typename V> inline Future<V> yield(V&& value) { return {details::ValueTag{}, std::move(value)}; } template <typename V, typename... Args> inline Future<V> yield(Args&&... args) { return {details::ValueTag{}, std::forward<Args>(args)...}; } // Creates a future that defers a function call until its result is queried. // // auto future = ftl::defer([](int x) { return x + 1; }, 99); // assert(future.get() == 100); // template <typename F, typename... Args> inline auto defer(F&& f, Args&&... args) { return Future(std::async(std::launch::deferred, std::forward<F>(f), std::forward<Args>(args)...)); } } // namespace android::ftl
libs/ftl/future_test.cpp +5 −5 Original line number Diff line number Diff line Loading @@ -42,9 +42,9 @@ TEST(Future, Example) { } { auto future = ftl::yield(123); std::future<char> futures[] = {ftl::yield('a'), ftl::yield('b')}; ftl::Future<char> futures[] = {ftl::yield('a'), ftl::yield('b')}; std::future<char> chain = ftl::chain(std::move(future)) ftl::Future<char> chain = ftl::Future(std::move(future)) .then([](int x) { return static_cast<size_t>(x % 2); }) .then([&futures](size_t i) { return std::move(futures[i]); }); Loading @@ -71,7 +71,7 @@ TEST(Future, Chain) { return ByteVector{str.begin(), str.end()}; }); std::packaged_task<std::future<ByteVector>(ByteVector)> decrement_bytes( std::packaged_task<ftl::Future<ByteVector>(ByteVector)> decrement_bytes( [](ByteVector bytes) { return ftl::defer(decrement, std::move(bytes)); }); auto fetch = fetch_string.get_future(); Loading @@ -81,7 +81,7 @@ TEST(Future, Chain) { EXPECT_EQ( "hello, world", ftl::chain(std::move(fetch)) ftl::Future(std::move(fetch)) .then([](const char* str) { return std::string(str); }) .then([&](std::string str) { auto append = append_string.get_future(); Loading @@ -93,7 +93,7 @@ TEST(Future, Chain) { decrement_thread = std::thread(std::move(decrement_bytes), std::move(bytes)); return decrement; }) .then([](std::future<ByteVector> bytes) { return bytes; }) .then([](ftl::Future<ByteVector> bytes) { return bytes; }) .then([](const ByteVector& bytes) { return std::string(bytes.begin(), bytes.end()); }) .get()); Loading
services/surfaceflinger/BufferQueueLayer.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -48,7 +48,7 @@ BufferQueueLayer::~BufferQueueLayer() { // Interface implementation for Layer // ----------------------------------------------------------------------- void BufferQueueLayer::onLayerDisplayed(std::shared_future<FenceResult> futureFenceResult) { void BufferQueueLayer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult) { const sp<Fence> releaseFence = futureFenceResult.get().value_or(Fence::NO_FENCE); mConsumer->setReleaseFence(releaseFence); Loading
services/surfaceflinger/BufferQueueLayer.h +1 −1 Original line number Diff line number Diff line Loading @@ -42,7 +42,7 @@ public: // Implements Layer. const char* getType() const override { return "BufferQueueLayer"; } void onLayerDisplayed(std::shared_future<FenceResult>) override; void onLayerDisplayed(ftl::SharedFuture<FenceResult>) override; // If a buffer was replaced this frame, release the former buffer void releasePendingBuffer(nsecs_t dequeueReadyTime) override; Loading