mirror of https://github.com/grpc/grpc.git
parent
aac5af4cde
commit
9b07a81b1a
7 changed files with 0 additions and 499 deletions
@ -1,181 +0,0 @@ |
|||||||
// 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 <grpc/support/log.h> |
|
||||||
|
|
||||||
#include "src/core/lib/gprpp/arena.h" |
|
||||||
#include "src/core/lib/promise/poll.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 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> { |
|
||||||
public: |
|
||||||
// Call the callable. Since it's empty it can't access any member variables,
|
|
||||||
// and as such we can choose any address for the object.
|
|
||||||
Poll<T> PollOnce() override { return (*static_cast<Callable*>(nullptr))(); } |
|
||||||
// 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() { |
|
||||||
static SharedImpl impl; |
|
||||||
return &impl; |
|
||||||
} |
|
||||||
|
|
||||||
private: |
|
||||||
SharedImpl() = default; |
|
||||||
~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(Arena* arena, Callable&& callable) { |
|
||||||
return 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(Arena*, Callable&&) { |
|
||||||
return SharedImpl<T, Callable>::Get(); |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
// Wrap ChooseImplForCallable with a friend approachable syntax.
|
|
||||||
template <typename T, typename Callable> |
|
||||||
ImplInterface<T>* MakeImplForCallable(Arena* arena, Callable&& callable) { |
|
||||||
return ChooseImplForCallable<T, Callable>::Make( |
|
||||||
arena, 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> |
|
||||||
ArenaPromise(Arena* arena, Callable&& callable) |
|
||||||
: impl_(arena_promise_detail::MakeImplForCallable<T>( |
|
||||||
arena, 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_ = 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(); } |
|
||||||
|
|
||||||
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 */ |
|
@ -1,45 +0,0 @@ |
|||||||
// 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.
|
|
||||||
|
|
||||||
#include "src/core/lib/promise/arena_promise.h" |
|
||||||
|
|
||||||
#include <memory> |
|
||||||
|
|
||||||
#include <gtest/gtest.h> |
|
||||||
|
|
||||||
namespace grpc_core { |
|
||||||
|
|
||||||
TEST(ArenaPromiseTest, AllocatedWorks) { |
|
||||||
auto arena = MakeScopedArena(1024); |
|
||||||
int x = 42; |
|
||||||
ArenaPromise<int> p(arena.get(), [x] { return Poll<int>(x); }); |
|
||||||
EXPECT_EQ(p(), Poll<int>(42)); |
|
||||||
p = ArenaPromise<int>(arena.get(), [] { return Poll<int>(43); }); |
|
||||||
EXPECT_EQ(p(), Poll<int>(43)); |
|
||||||
} |
|
||||||
|
|
||||||
TEST(ArenaPromiseTest, DestructionWorks) { |
|
||||||
auto arena = MakeScopedArena(1024); |
|
||||||
auto x = std::make_shared<int>(42); |
|
||||||
auto p = ArenaPromise<int>(arena.get(), [x] { return Poll<int>(*x); }); |
|
||||||
ArenaPromise<int> q(std::move(p)); |
|
||||||
EXPECT_EQ(q(), Poll<int>(42)); |
|
||||||
} |
|
||||||
|
|
||||||
} // namespace grpc_core
|
|
||||||
|
|
||||||
int main(int argc, char** argv) { |
|
||||||
::testing::InitGoogleTest(&argc, argv); |
|
||||||
return RUN_ALL_TESTS(); |
|
||||||
} |
|
Loading…
Reference in new issue