// 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_SRC_CORE_LIB_PROMISE_ARENA_PROMISE_H #define GRPC_SRC_CORE_LIB_PROMISE_ARENA_PROMISE_H #include #include #include #include #include #include "absl/meta/type_traits.h" #include #include "src/core/lib/gprpp/construct_destruct.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 { struct ArgType { alignas(std::max_align_t) char buffer[sizeof(void*)]; }; template T*& ArgAsPtr(ArgType* arg) { static_assert(sizeof(ArgType) >= sizeof(T**), "Must have ArgType of at least one pointer size"); return *reinterpret_cast(arg); } template struct Vtable { // Poll the promise, once. Poll (*poll_once)(ArgType* arg); // 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. void (*destroy)(ArgType* arg); }; template struct VtableAndArg { const Vtable* vtable; ArgType arg; }; // Implementation of Vtable 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 struct Null { static const Vtable vtable; static Poll PollOnce(ArgType*) { abort(); GPR_UNREACHABLE_CODE(return Pending{}); } static void Destroy(ArgType*) {} }; template const Vtable Null::vtable = {PollOnce, Destroy}; // Implementation of ImplInterface for a callable object. template struct AllocatedCallable { static const Vtable vtable; static Poll PollOnce(ArgType* arg) { return poll_cast((*ArgAsPtr(arg))()); } static void Destroy(ArgType* arg) { Destruct(ArgAsPtr(arg)); } }; template const Vtable AllocatedCallable::vtable = {PollOnce, Destroy}; // Implementation of ImplInterface for a small callable object (one that fits // within the ArgType arg) template struct Inlined { static const Vtable vtable; static Poll PollOnce(ArgType* arg) { return poll_cast((*reinterpret_cast(arg))()); } static void Destroy(ArgType* arg) { Destruct(reinterpret_cast(arg)); } }; template const Vtable Inlined::vtable = {PollOnce, Destroy}; // 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 struct SharedCallable { static const Vtable vtable; static Poll PollOnce(ArgType* arg) { return (*reinterpret_cast(arg))(); } }; template const Vtable SharedCallable::vtable = {PollOnce, Null::Destroy}; // Redirector type: given a callable type, expose a Make() function that creates // the appropriate underlying implementation. template struct ChooseImplForCallable; template struct ChooseImplForCallable< T, Callable, absl::enable_if_t::value && (sizeof(Callable) > sizeof(ArgType))>> { static void Make(Callable&& callable, VtableAndArg* out) { out->vtable = &AllocatedCallable::vtable; ArgAsPtr(&out->arg) = GetContext()->template New( std::forward(callable)); } }; template struct ChooseImplForCallable< T, Callable, absl::enable_if_t::value && (sizeof(Callable) <= sizeof(ArgType))>> { static void Make(Callable&& callable, VtableAndArg* out) { out->vtable = &Inlined::vtable; Construct(reinterpret_cast(&out->arg), std::forward(callable)); } }; template struct ChooseImplForCallable< T, Callable, absl::enable_if_t::value>> { static void Make(Callable&&, VtableAndArg* out) { out->vtable = &SharedCallable::vtable; } }; // Wrap ChooseImplForCallable with a friend approachable syntax. template void MakeImplForCallable(Callable&& callable, VtableAndArg* out) { ChooseImplForCallable::Make(std::forward(callable), out); } } // namespace arena_promise_detail // A promise for which the state memory is allocated from an arena. template class ArenaPromise { public: // Construct an empty, uncallable, invalid ArenaPromise. ArenaPromise() = default; // Construct an ArenaPromise that will call the given callable when polled. template ::value>> // NOLINTNEXTLINE(google-explicit-constructor) ArenaPromise(Callable&& callable) { arena_promise_detail::MakeImplForCallable(std::forward(callable), &vtable_and_arg_); } // ArenaPromise is not copyable. ArenaPromise(const ArenaPromise&) = delete; ArenaPromise& operator=(const ArenaPromise&) = delete; // ArenaPromise is movable. ArenaPromise(ArenaPromise&& other) noexcept : vtable_and_arg_(other.vtable_and_arg_) { other.vtable_and_arg_.vtable = &arena_promise_detail::Null::vtable; } ArenaPromise& operator=(ArenaPromise&& other) noexcept { vtable_and_arg_.vtable->destroy(&vtable_and_arg_.arg); vtable_and_arg_ = other.vtable_and_arg_; other.vtable_and_arg_.vtable = &arena_promise_detail::Null::vtable; return *this; } // Destruction => call Destroy on the underlying impl object. ~ArenaPromise() { vtable_and_arg_.vtable->destroy(&vtable_and_arg_.arg); } // Expose the promise interface: a call operator that returns Poll. Poll operator()() { return vtable_and_arg_.vtable->poll_once(&vtable_and_arg_.arg); } bool has_value() const { return vtable_and_arg_.vtable != &arena_promise_detail::Null::vtable; } private: // Underlying impl object. arena_promise_detail::VtableAndArg vtable_and_arg_ = { &arena_promise_detail::Null::vtable, {}}; }; } // namespace grpc_core #endif // GRPC_SRC_CORE_LIB_PROMISE_ARENA_PROMISE_H