|  | @@ -174,6 +174,7 @@ class AsyncClient : public ClientImpl<StubType, RequestType> {
 | 
	
		
			
				|  |  |      for (int i = 0; i < num_async_threads_; i++) {
 | 
	
		
			
				|  |  |        cli_cqs_.emplace_back(new CompletionQueue);
 | 
	
		
			
				|  |  |        next_issuers_.emplace_back(NextIssuer(i));
 | 
	
		
			
				|  |  | +      shutdown_state_.emplace_back(new PerThreadShutdownState());
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      using namespace std::placeholders;
 | 
	
	
		
			
				|  | @@ -189,7 +190,21 @@ class AsyncClient : public ClientImpl<StubType, RequestType> {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    virtual ~AsyncClient() {
 | 
	
		
			
				|  |  | -    FinalShutdownCQs();
 | 
	
		
			
				|  |  | +    for (auto ss = shutdown_state_.begin(); ss != shutdown_state_.end(); ++ss) {
 | 
	
		
			
				|  |  | +      std::lock_guard<std::mutex> lock((*ss)->mutex);
 | 
	
		
			
				|  |  | +      (*ss)->shutdown = true;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    for (auto cq = cli_cqs_.begin(); cq != cli_cqs_.end(); cq++) {
 | 
	
		
			
				|  |  | +      (*cq)->Shutdown();
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    this->EndThreads(); // Need "this->" for resolution
 | 
	
		
			
				|  |  | +    for (auto cq = cli_cqs_.begin(); cq != cli_cqs_.end(); cq++) {
 | 
	
		
			
				|  |  | +      void* got_tag;
 | 
	
		
			
				|  |  | +      bool ok;
 | 
	
		
			
				|  |  | +      while ((*cq)->Next(&got_tag, &ok)) {
 | 
	
		
			
				|  |  | +        delete ClientRpcContext::detag(got_tag);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    bool ThreadFunc(HistogramEntry* entry,
 | 
	
	
		
			
				|  | @@ -200,7 +215,12 @@ class AsyncClient : public ClientImpl<StubType, RequestType> {
 | 
	
		
			
				|  |  |      if (cli_cqs_[thread_idx]->Next(&got_tag, &ok)) {
 | 
	
		
			
				|  |  |        // Got a regular event, so process it
 | 
	
		
			
				|  |  |        ClientRpcContext* ctx = ClientRpcContext::detag(got_tag);
 | 
	
		
			
				|  |  | -      if (!ctx->RunNextState(ok, entry)) {
 | 
	
		
			
				|  |  | +      // Proceed while holding a lock to make sure that
 | 
	
		
			
				|  |  | +      // this thread isn't supposed to shut down
 | 
	
		
			
				|  |  | +      std::lock_guard<std::mutex> l(shutdown_state_[thread_idx]->mutex);
 | 
	
		
			
				|  |  | +      if (shutdown_state_[thread_idx]->shutdown) {
 | 
	
		
			
				|  |  | +        return true;
 | 
	
		
			
				|  |  | +      } else if (!ctx->RunNextState(ok, entry)) {
 | 
	
		
			
				|  |  |          // The RPC and callback are done, so clone the ctx
 | 
	
		
			
				|  |  |          // and kickstart the new one
 | 
	
		
			
				|  |  |          auto clone = ctx->StartNewClone();
 | 
	
	
		
			
				|  | @@ -217,22 +237,13 @@ class AsyncClient : public ClientImpl<StubType, RequestType> {
 | 
	
		
			
				|  |  |   protected:
 | 
	
		
			
				|  |  |    const int num_async_threads_;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  void ShutdownCQs() {
 | 
	
		
			
				|  |  | -    for (auto cq = cli_cqs_.begin(); cq != cli_cqs_.end(); cq++) {
 | 
	
		
			
				|  |  | -      (*cq)->Shutdown();
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  void FinalShutdownCQs() {
 | 
	
		
			
				|  |  | -    for (auto cq = cli_cqs_.begin(); cq != cli_cqs_.end(); cq++) {
 | 
	
		
			
				|  |  | -      void* got_tag;
 | 
	
		
			
				|  |  | -      bool ok;
 | 
	
		
			
				|  |  | -      while ((*cq)->Next(&got_tag, &ok)) {
 | 
	
		
			
				|  |  | -        delete ClientRpcContext::detag(got_tag);
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |   private:
 | 
	
		
			
				|  |  | +  struct PerThreadShutdownState {
 | 
	
		
			
				|  |  | +    mutable std::mutex mutex;
 | 
	
		
			
				|  |  | +    bool shutdown;
 | 
	
		
			
				|  |  | +    PerThreadShutdownState() : shutdown(false) {}
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    int NumThreads(const ClientConfig& config) {
 | 
	
		
			
				|  |  |      int num_threads = config.async_client_threads();
 | 
	
		
			
				|  |  |      if (num_threads <= 0) {  // Use dynamic sizing
 | 
	
	
		
			
				|  | @@ -241,9 +252,9 @@ class AsyncClient : public ClientImpl<StubType, RequestType> {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      return num_threads;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |    std::vector<std::unique_ptr<CompletionQueue>> cli_cqs_;
 | 
	
		
			
				|  |  |    std::vector<std::function<gpr_timespec()>> next_issuers_;
 | 
	
		
			
				|  |  | +  std::vector<std::unique_ptr<PerThreadShutdownState>> shutdown_state_;
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static std::unique_ptr<BenchmarkService::Stub> BenchmarkStubCreator(
 | 
	
	
		
			
				|  | @@ -259,10 +270,7 @@ class AsyncUnaryClient GRPC_FINAL
 | 
	
		
			
				|  |  |              config, SetupCtx, BenchmarkStubCreator) {
 | 
	
		
			
				|  |  |      StartThreads(num_async_threads_);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -  ~AsyncUnaryClient() GRPC_OVERRIDE {
 | 
	
		
			
				|  |  | -    ShutdownCQs();
 | 
	
		
			
				|  |  | -    EndThreads();
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +  ~AsyncUnaryClient() GRPC_OVERRIDE {}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |   private:
 | 
	
		
			
				|  |  |    static void CheckDone(grpc::Status s, SimpleResponse* response) {}
 | 
	
	
		
			
				|  | @@ -391,10 +399,7 @@ class AsyncStreamingClient GRPC_FINAL
 | 
	
		
			
				|  |  |      StartThreads(num_async_threads_);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  ~AsyncStreamingClient() GRPC_OVERRIDE {
 | 
	
		
			
				|  |  | -    ShutdownCQs();
 | 
	
		
			
				|  |  | -    EndThreads();
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +  ~AsyncStreamingClient() GRPC_OVERRIDE {}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |   private:
 | 
	
		
			
				|  |  |    static void CheckDone(grpc::Status s, SimpleResponse* response) {}
 | 
	
	
		
			
				|  | @@ -530,10 +535,7 @@ class GenericAsyncStreamingClient GRPC_FINAL
 | 
	
		
			
				|  |  |      StartThreads(num_async_threads_);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  ~GenericAsyncStreamingClient() GRPC_OVERRIDE {
 | 
	
		
			
				|  |  | -    ShutdownCQs();
 | 
	
		
			
				|  |  | -    EndThreads();
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +  ~GenericAsyncStreamingClient() GRPC_OVERRIDE {}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |   private:
 | 
	
		
			
				|  |  |    static void CheckDone(grpc::Status s, ByteBuffer* response) {}
 |