| // Copyright 2021 gRPC authors. |
| // |
| // 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. |
| |
| #ifndef GRPC_CORE_LIB_PROMISE_ARENA_PROMISE_H |
| #define GRPC_CORE_LIB_PROMISE_ARENA_PROMISE_H |
| |
| #include <grpc/support/port_platform.h> |
| |
| #include <stdlib.h> |
| |
| #include <type_traits> |
| #include <utility> |
| |
| #include "absl/meta/type_traits.h" |
| |
| #include "src/core/lib/promise/context.h" |
| #include "src/core/lib/promise/poll.h" |
| #include "src/core/lib/resource_quota/arena.h" |
| |
| namespace grpc_core { |
| |
| namespace arena_promise_detail { |
| |
| // Type erased promise stored in the arena. |
| template <typename T> |
| class ImplInterface { |
| public: |
| // Poll the promise, once. |
| virtual Poll<T> PollOnce() = 0; |
| // Destroy the underlying callable object if there is one. |
| // Since we don't delete (the arena owns the memory) but we may need to call a |
| // destructor, we expose this for when the ArenaPromise object is destroyed. |
| virtual void Destroy() = 0; |
| |
| protected: |
| ~ImplInterface() = default; |
| }; |
| |
| // Implementation of ImplInterface for an empty object. |
| // Used when an empty ArenaPromise is created, or when the ArenaPromise is moved |
| // from. Since in either case these objects should not be polled, we simply |
| // crash if it is. |
| template <typename T> |
| class NullImpl final : public ImplInterface<T> { |
| public: |
| Poll<T> PollOnce() override { |
| abort(); |
| GPR_UNREACHABLE_CODE(return Pending{}); |
| } |
| void Destroy() override {} |
| |
| static ImplInterface<T>* Get() { |
| static NullImpl<T> instance; |
| return &instance; |
| } |
| |
| private: |
| ~NullImpl() = default; |
| }; |
| |
| // Implementation of ImplInterface for a callable object. |
| template <typename T, typename Callable> |
| class CallableImpl final : public ImplInterface<T> { |
| public: |
| explicit CallableImpl(Callable&& callable) : callable_(std::move(callable)) {} |
| // Forward polls to the callable object. |
| Poll<T> PollOnce() override { return poll_cast<T>(callable_()); } |
| // Destroy destructs the callable object. |
| void Destroy() override { this->~CallableImpl(); } |
| |
| private: |
| // Should only be called by Destroy(). |
| ~CallableImpl() = default; |
| |
| Callable callable_; |
| }; |
| |
| // If a callable object is empty we can substitute any instance of that callable |
| // for the one we call (for how could we tell the difference)? |
| // Since this corresponds to a lambda with no fields, and we expect these to be |
| // reasonably common, we can elide the arena allocation entirely and simply poll |
| // a global shared instance. |
| // (this comes up often when the promise only accesses context data from the |
| // containing activity). |
| template <typename T, typename Callable> |
| class SharedImpl final : public ImplInterface<T>, private Callable { |
| public: |
| // Call the callable, or at least an exact duplicate of it - if you have no |
| // members, all your instances look the same. |
| Poll<T> PollOnce() override { return Callable::operator()(); } |
| // Nothing to destroy. |
| void Destroy() override {} |
| // Return a pointer to the shared instance - these are singletons, and are |
| // needed just to get the vtable in place. |
| static SharedImpl* Get(Callable&& callable) { |
| static_assert(sizeof(SharedImpl) == sizeof(void*), |
| "SharedImpl should be pointer sized"); |
| static SharedImpl impl(std::forward<Callable>(callable)); |
| return &impl; |
| } |
| |
| private: |
| explicit SharedImpl(Callable&& callable) |
| : Callable(std::forward<Callable>(callable)) {} |
| ~SharedImpl() = default; |
| }; |
| |
| // Redirector type: given a callable type, expose a Make() function that creates |
| // the appropriate underlying implementation. |
| template <typename T, typename Callable, typename Ignored = void> |
| struct ChooseImplForCallable; |
| |
| template <typename T, typename Callable> |
| struct ChooseImplForCallable< |
| T, Callable, absl::enable_if_t<!std::is_empty<Callable>::value>> { |
| static ImplInterface<T>* Make(Callable&& callable) { |
| return GetContext<Arena>()->template New<CallableImpl<T, Callable>>( |
| std::forward<Callable>(callable)); |
| } |
| }; |
| |
| template <typename T, typename Callable> |
| struct ChooseImplForCallable< |
| T, Callable, absl::enable_if_t<std::is_empty<Callable>::value>> { |
| static ImplInterface<T>* Make(Callable&& callable) { |
| return SharedImpl<T, Callable>::Get(std::forward<Callable>(callable)); |
| } |
| }; |
| |
| // Wrap ChooseImplForCallable with a friend approachable syntax. |
| template <typename T, typename Callable> |
| ImplInterface<T>* MakeImplForCallable(Callable&& callable) { |
| return ChooseImplForCallable<T, Callable>::Make( |
| std::forward<Callable>(callable)); |
| } |
| |
| } // namespace arena_promise_detail |
| |
| // A promise for which the state memory is allocated from an arena. |
| template <typename T> |
| class ArenaPromise { |
| public: |
| // Construct an empty, uncallable, invalid ArenaPromise. |
| ArenaPromise() = default; |
| |
| // Construct an ArenaPromise that will call the given callable when polled. |
| template <typename Callable, |
| typename Ignored = |
| absl::enable_if_t<!std::is_same<Callable, ArenaPromise>::value>> |
| // NOLINTNEXTLINE(google-explicit-constructor) |
| ArenaPromise(Callable&& callable) |
| : impl_(arena_promise_detail::MakeImplForCallable<T>( |
| std::forward<Callable>(callable))) {} |
| |
| // ArenaPromise is not copyable. |
| ArenaPromise(const ArenaPromise&) = delete; |
| ArenaPromise& operator=(const ArenaPromise&) = delete; |
| // ArenaPromise is movable. |
| ArenaPromise(ArenaPromise&& other) noexcept : impl_(other.impl_) { |
| other.impl_ = arena_promise_detail::NullImpl<T>::Get(); |
| } |
| ArenaPromise& operator=(ArenaPromise&& other) noexcept { |
| impl_->Destroy(); |
| impl_ = other.impl_; |
| other.impl_ = arena_promise_detail::NullImpl<T>::Get(); |
| return *this; |
| } |
| |
| // Destruction => call Destroy on the underlying impl object. |
| ~ArenaPromise() { impl_->Destroy(); } |
| |
| // Expose the promise interface: a call operator that returns Poll<T>. |
| Poll<T> operator()() { return impl_->PollOnce(); } |
| |
| bool has_value() const { |
| return impl_ != arena_promise_detail::NullImpl<T>::Get(); |
| } |
| |
| private: |
| // Underlying impl object. |
| arena_promise_detail::ImplInterface<T>* impl_ = |
| arena_promise_detail::NullImpl<T>::Get(); |
| }; |
| |
| } // namespace grpc_core |
| |
| #endif /* GRPC_CORE_LIB_PROMISE_ARENA_PROMISE_H */ |