Call finalization for promises (#29008)
* Call finalization for promises * comment * split out and test * dont use promise_detail:: directly * fix * Automated change: Fix sanity tests * fix Co-authored-by: ctiller <ctiller@users.noreply.github.com>pull/29016/head
parent
a04739095e
commit
047642c5c4
19 changed files with 300 additions and 10 deletions
@ -0,0 +1,86 @@ |
||||
// Copyright 2022 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_CHANNEL_CALL_FINALIZATION_H |
||||
#define GRPC_CORE_LIB_CHANNEL_CALL_FINALIZATION_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include "src/core/lib/channel/channel_stack.h" |
||||
#include "src/core/lib/promise/context.h" |
||||
#include "src/core/lib/resource_quota/arena.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
// Call finalization context.
|
||||
// Sometimes a filter needs to perform some operation after the last byte of
|
||||
// data is flushed to the wire. This context is used to perform that
|
||||
// finalization.
|
||||
// Filters can register a finalizer by calling Add().
|
||||
// The finalizer will be called before the call is destroyed but after
|
||||
// the top level promise is completed.
|
||||
class CallFinalization { |
||||
public: |
||||
// Add a step to the finalization context.
|
||||
// Takes a callable with a signature compatible with:
|
||||
// (const grpc_call_final_info&) -> void.
|
||||
// Finalizers are run in the reverse order they are added.
|
||||
template <typename F> |
||||
void Add(F&& t) { |
||||
first_ = |
||||
GetContext<Arena>()->New<FuncFinalizer<F>>(std::forward<F>(t), first_); |
||||
} |
||||
|
||||
void Run(const grpc_call_final_info* final_info) { |
||||
if (Finalizer* f = absl::exchange(first_, nullptr)) f->Run(final_info); |
||||
} |
||||
|
||||
private: |
||||
// Base class for finalizer implementations.
|
||||
class Finalizer { |
||||
public: |
||||
// Run the finalizer and call the destructor of this Finalizer.
|
||||
virtual void Run(const grpc_call_final_info* final_info) = 0; |
||||
|
||||
protected: |
||||
~Finalizer() {} |
||||
}; |
||||
// Specialization for callable objects.
|
||||
template <typename F> |
||||
class FuncFinalizer final : public Finalizer { |
||||
public: |
||||
FuncFinalizer(F&& f, Finalizer* next) |
||||
: next_(next), f_(std::forward<F>(f)) {} |
||||
|
||||
void Run(const grpc_call_final_info* final_info) override { |
||||
f_(final_info); |
||||
Finalizer* next = next_; |
||||
this->~FuncFinalizer(); |
||||
if (next != nullptr) next->Run(final_info); |
||||
} |
||||
|
||||
private: |
||||
Finalizer* next_; |
||||
F f_; |
||||
}; |
||||
// The first finalizer in the chain.
|
||||
Finalizer* first_ = nullptr; |
||||
}; |
||||
|
||||
template <> |
||||
struct ContextType<CallFinalization> {}; |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_CORE_LIB_CHANNEL_CALL_FINALIZATION_H
|
@ -0,0 +1,50 @@ |
||||
// 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/channel/call_finalization.h" |
||||
|
||||
#include <gtest/gtest.h> |
||||
|
||||
#include "src/core/lib/resource_quota/resource_quota.h" |
||||
#include "test/core/promise/test_context.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
static auto* g_memory_allocator = new MemoryAllocator( |
||||
ResourceQuota::Default()->memory_quota()->CreateMemoryAllocator("test")); |
||||
|
||||
TEST(CallFinalizationTest, Works) { |
||||
auto arena = MakeScopedArena(1024, g_memory_allocator); |
||||
std::string evidence; |
||||
TestContext<Arena> context(arena.get()); |
||||
CallFinalization finalization; |
||||
auto p = std::make_shared<int>(42); |
||||
finalization.Add([&evidence, p](const grpc_call_final_info* final_info) { |
||||
evidence += absl::StrCat("FIRST", final_info->error_string, *p, "\n"); |
||||
}); |
||||
finalization.Add([&evidence, p](const grpc_call_final_info* final_info) { |
||||
evidence += absl::StrCat("SECOND", final_info->error_string, *p, "\n"); |
||||
}); |
||||
grpc_call_final_info final_info{}; |
||||
final_info.error_string = "123"; |
||||
finalization.Run(&final_info); |
||||
EXPECT_EQ(evidence, "SECOND12342\nFIRST12342\n"); |
||||
} |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
int main(int argc, char** argv) { |
||||
::testing::InitGoogleTest(&argc, argv); |
||||
return RUN_ALL_TESTS(); |
||||
} |
@ -0,0 +1,27 @@ |
||||
// Copyright 2022 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 TEST_PROMISE_H |
||||
#define TEST_PROMISE_H |
||||
|
||||
#include "src/core/lib/promise/context.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
template <class T> |
||||
using TestContext = promise_detail::Context<T>; |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif |
Loading…
Reference in new issue