/* * * Copyright 2015, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "test/cpp/util/create_test_channel.h" #include "test/cpp/qps/qpstest.pb.h" #include "test/cpp/qps/timer.h" #include "test/cpp/qps/client.h" namespace grpc { namespace testing { class ClientRpcContext { public: ClientRpcContext() {} virtual ~ClientRpcContext() {} // next state, return false if done. Collect stats when appropriate virtual bool RunNextState(bool, Histogram* hist) = 0; virtual void StartNewClone() = 0; static void* tag(ClientRpcContext* c) { return reinterpret_cast(c); } static ClientRpcContext* detag(void* t) { return reinterpret_cast(t); } }; template class ClientRpcContextUnaryImpl : public ClientRpcContext { public: ClientRpcContextUnaryImpl( TestService::Stub* stub, const RequestType& req, std::function< std::unique_ptr>( TestService::Stub*, grpc::ClientContext*, const RequestType&, void*)> start_req, std::function on_done) : context_(), stub_(stub), req_(req), response_(), next_state_(&ClientRpcContextUnaryImpl::ReqSent), callback_(on_done), start_req_(start_req), start_(Timer::Now()), response_reader_( start_req(stub_, &context_, req_, ClientRpcContext::tag(this))) {} ~ClientRpcContextUnaryImpl() GRPC_OVERRIDE {} bool RunNextState(bool ok, Histogram* hist) GRPC_OVERRIDE { bool ret = (this->*next_state_)(ok); if (!ret) { hist->Add((Timer::Now() - start_) * 1e9); } return ret; } void StartNewClone() GRPC_OVERRIDE { new ClientRpcContextUnaryImpl(stub_, req_, start_req_, callback_); } private: bool ReqSent(bool) { next_state_ = &ClientRpcContextUnaryImpl::RespDone; response_reader_->Finish(&response_, &status_, ClientRpcContext::tag(this)); return true; } bool RespDone(bool) { next_state_ = &ClientRpcContextUnaryImpl::DoCallBack; return false; } bool DoCallBack(bool) { callback_(status_, &response_); return false; } grpc::ClientContext context_; TestService::Stub* stub_; RequestType req_; ResponseType response_; bool (ClientRpcContextUnaryImpl::*next_state_)(bool); std::function callback_; std::function>( TestService::Stub*, grpc::ClientContext*, const RequestType&, void*)> start_req_; grpc::Status status_; double start_; std::unique_ptr> response_reader_; }; class AsyncUnaryClient GRPC_FINAL : public Client { public: explicit AsyncUnaryClient(const ClientConfig& config) : Client(config) { for (int i = 0; i < config.async_client_threads(); i++) { cli_cqs_.emplace_back(new CompletionQueue); } auto check_done = [](grpc::Status s, SimpleResponse* response) {}; int t = 0; for (int i = 0; i < config.outstanding_rpcs_per_channel(); i++) { for (auto channel = channels_.begin(); channel != channels_.end(); channel++) { auto* cq = cli_cqs_[t].get(); t = (t + 1) % cli_cqs_.size(); auto start_req = [cq](TestService::Stub* stub, grpc::ClientContext* ctx, const SimpleRequest& request, void* tag) { return stub->AsyncUnaryCall(ctx, request, cq, tag); }; TestService::Stub* stub = channel->get_stub(); const SimpleRequest& request = request_; new ClientRpcContextUnaryImpl( stub, request, start_req, check_done); } } StartThreads(config.async_client_threads()); } ~AsyncUnaryClient() GRPC_OVERRIDE { EndThreads(); for (auto cq = cli_cqs_.begin(); cq != cli_cqs_.end(); cq++) { (*cq)->Shutdown(); void* got_tag; bool ok; while ((*cq)->Next(&got_tag, &ok)) { delete ClientRpcContext::detag(got_tag); } } } bool ThreadFunc(Histogram* histogram, size_t thread_idx) GRPC_OVERRIDE { void* got_tag; bool ok; switch (cli_cqs_[thread_idx]->AsyncNext(&got_tag, &ok, std::chrono::system_clock::now() + std::chrono::seconds(11))) { case CompletionQueue::SHUTDOWN: return false; case CompletionQueue::TIMEOUT: return true; case CompletionQueue::GOT_EVENT: break; } ClientRpcContext* ctx = ClientRpcContext::detag(got_tag); if (ctx->RunNextState(ok, histogram) == false) { // call the callback and then delete it ctx->RunNextState(ok, histogram); ctx->StartNewClone(); delete ctx; } return true; } std::vector> cli_cqs_; }; template class ClientRpcContextStreamingImpl : public ClientRpcContext { public: ClientRpcContextStreamingImpl( TestService::Stub *stub, const RequestType &req, std::function< std::unique_ptr>( TestService::Stub *, grpc::ClientContext *, void *)> start_req, std::function on_done) : context_(), stub_(stub), req_(req), response_(), next_state_(&ClientRpcContextStreamingImpl::ReqSent), callback_(on_done), start_req_(start_req), start_(Timer::Now()), stream_(start_req_(stub_, &context_, ClientRpcContext::tag(this))) {} ~ClientRpcContextStreamingImpl() GRPC_OVERRIDE {} bool RunNextState(bool ok, Histogram *hist) GRPC_OVERRIDE { return (this->*next_state_)(ok, hist); } void StartNewClone() GRPC_OVERRIDE { new ClientRpcContextStreamingImpl(stub_, req_, start_req_, callback_); } private: bool ReqSent(bool ok, Histogram *) { return StartWrite(ok); } bool StartWrite(bool ok) { if (!ok) { return(false); } start_ = Timer::Now(); next_state_ = &ClientRpcContextStreamingImpl::WriteDone; stream_->Write(req_, ClientRpcContext::tag(this)); return true; } bool WriteDone(bool ok, Histogram *) { if (!ok) { return(false); } next_state_ = &ClientRpcContextStreamingImpl::ReadDone; stream_->Read(&response_, ClientRpcContext::tag(this)); return true; } bool ReadDone(bool ok, Histogram *hist) { hist->Add((Timer::Now() - start_) * 1e9); return StartWrite(ok); } grpc::ClientContext context_; TestService::Stub *stub_; RequestType req_; ResponseType response_; bool (ClientRpcContextStreamingImpl::*next_state_)(bool, Histogram *); std::function callback_; std::function>( TestService::Stub *, grpc::ClientContext *, void *)> start_req_; grpc::Status status_; double start_; std::unique_ptr> stream_; }; class AsyncStreamingClient GRPC_FINAL : public Client { public: explicit AsyncStreamingClient(const ClientConfig &config) : Client(config) { for (int i = 0; i < config.async_client_threads(); i++) { cli_cqs_.emplace_back(new CompletionQueue); } auto check_done = [](grpc::Status s, SimpleResponse* response) {}; int t = 0; for (int i = 0; i < config.outstanding_rpcs_per_channel(); i++) { for (auto channel = channels_.begin(); channel != channels_.end(); channel++) { auto* cq = cli_cqs_[t].get(); t = (t + 1) % cli_cqs_.size(); auto start_req = [cq](TestService::Stub *stub, grpc::ClientContext *ctx, void *tag) { auto stream = stub->AsyncStreamingCall(ctx, cq, tag); return stream; }; TestService::Stub *stub = channel->get_stub(); const SimpleRequest &request = request_; new ClientRpcContextStreamingImpl( stub, request, start_req, check_done); } } StartThreads(config.async_client_threads()); } ~AsyncStreamingClient() GRPC_OVERRIDE { EndThreads(); for (auto cq = cli_cqs_.begin(); cq != cli_cqs_.end(); cq++) { (*cq)->Shutdown(); void *got_tag; bool ok; while ((*cq)->Next(&got_tag, &ok)) { delete ClientRpcContext::detag(got_tag); } } } bool ThreadFunc(Histogram *histogram, size_t thread_idx) GRPC_OVERRIDE { void *got_tag; bool ok; switch (cli_cqs_[thread_idx]->AsyncNext(&got_tag, &ok, std::chrono::system_clock::now() + std::chrono::seconds(1))) { case CompletionQueue::SHUTDOWN: return false; case CompletionQueue::TIMEOUT: return true; case CompletionQueue::GOT_EVENT: break; } ClientRpcContext *ctx = ClientRpcContext::detag(got_tag); if (ctx->RunNextState(ok, histogram) == false) { // call the callback and then delete it ctx->RunNextState(ok, histogram); ctx->StartNewClone(); delete ctx; } return true; } std::vector> cli_cqs_; }; std::unique_ptr CreateAsyncUnaryClient(const ClientConfig& args) { return std::unique_ptr(new AsyncUnaryClient(args)); } std::unique_ptr CreateAsyncStreamingClient(const ClientConfig& args) { return std::unique_ptr(new AsyncStreamingClient(args)); } } // namespace testing } // namespace grpc