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