// Copyright 2016 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 "test/cpp/end2end/connection_delay_injector.h" #include #include "absl/memory/memory.h" #include "absl/utility/utility.h" #include "src/core/lib/gprpp/sync.h" // defined in tcp_client.cc extern grpc_tcp_client_vtable* grpc_tcp_client_impl; namespace grpc { namespace testing { // // ConnectionAttemptInjector // namespace { grpc_tcp_client_vtable* g_original_vtable = nullptr; grpc_core::Mutex* g_mu = nullptr; ConnectionAttemptInjector* g_injector ABSL_GUARDED_BY(*g_mu) = nullptr; void TcpConnectWithDelay(grpc_closure* closure, grpc_endpoint** ep, grpc_pollset_set* interested_parties, const grpc_channel_args* channel_args, const grpc_resolved_address* addr, grpc_core::Timestamp deadline) { grpc_core::MutexLock lock(g_mu); if (g_injector == nullptr) { g_original_vtable->connect(closure, ep, interested_parties, channel_args, addr, deadline); return; } g_injector->HandleConnection(closure, ep, interested_parties, channel_args, addr, deadline); } grpc_tcp_client_vtable kDelayedConnectVTable = {TcpConnectWithDelay}; } // namespace void ConnectionAttemptInjector::Init() { g_mu = new grpc_core::Mutex(); g_original_vtable = grpc_tcp_client_impl; grpc_tcp_client_impl = &kDelayedConnectVTable; } ConnectionAttemptInjector::~ConnectionAttemptInjector() { grpc_core::MutexLock lock(g_mu); g_injector = nullptr; } void ConnectionAttemptInjector::Start() { // Fail if ConnectionAttemptInjector::Init() was not called after // grpc_init() to inject the vtable. GPR_ASSERT(grpc_tcp_client_impl == &kDelayedConnectVTable); grpc_core::MutexLock lock(g_mu); GPR_ASSERT(g_injector == nullptr); g_injector = this; } void ConnectionAttemptInjector::AttemptConnection( grpc_closure* closure, grpc_endpoint** ep, grpc_pollset_set* interested_parties, const grpc_channel_args* channel_args, const grpc_resolved_address* addr, grpc_core::Timestamp deadline) { g_original_vtable->connect(closure, ep, interested_parties, channel_args, addr, deadline); } // // ConnectionAttemptInjector::InjectedDelay // ConnectionAttemptInjector::InjectedDelay::InjectedDelay( grpc_core::Duration duration, grpc_closure* closure, grpc_endpoint** ep, grpc_pollset_set* interested_parties, const grpc_channel_args* channel_args, const grpc_resolved_address* addr, grpc_core::Timestamp deadline) : attempt_(closure, ep, interested_parties, channel_args, addr, deadline) { GRPC_CLOSURE_INIT(&timer_callback_, TimerCallback, this, nullptr); grpc_core::Timestamp now = grpc_core::ExecCtx::Get()->Now(); duration = std::min(duration, deadline - now); grpc_timer_init(&timer_, now + duration, &timer_callback_); } void ConnectionAttemptInjector::InjectedDelay::TimerCallback( void* arg, grpc_error_handle /*error*/) { auto* self = static_cast(arg); self->BeforeResumingAction(); self->attempt_.Resume(); delete self; } // // ConnectionDelayInjector // void ConnectionDelayInjector::HandleConnection( grpc_closure* closure, grpc_endpoint** ep, grpc_pollset_set* interested_parties, const grpc_channel_args* channel_args, const grpc_resolved_address* addr, grpc_core::Timestamp deadline) { new InjectedDelay(duration_, closure, ep, interested_parties, channel_args, addr, deadline); } } // namespace testing } // namespace grpc