| 
					
				 | 
			
			
				@@ -140,7 +140,7 @@ class ClientRpcContextUnaryImpl : public ClientRpcContext { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 class AsyncClient : public Client { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  public: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   explicit AsyncClient(const ClientConfig& config, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		       std::function<void(CompletionQueue*, TestService::Stub*, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		       std::function<ClientRpcContext*(CompletionQueue*, TestService::Stub*, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 					  const SimpleRequest&)> setup_ctx) : 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       Client(config) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     for (int i = 0; i < config.async_client_threads(); i++) { 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -158,18 +158,22 @@ class AsyncClient : public Client { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     if (!closed_loop_) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       for (auto channel = channels_.begin(); channel != channels_.end(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	   channel++) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	channel_rpc_count_lock.emplace_back(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	channel_rpc_lock_.emplace_back(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	rpcs_outstanding_.push_back(0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-      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(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	  setup_ctx(cq, channel->get_stub(), request_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    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(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	ClientRpcContext *ctx = setup_ctx(cq, channel->get_stub(), request_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	if (closed_loop_) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	  // only relevant for closed_loop unary, but harmless for 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	  // closed_loop streaming 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	  ctx->Start(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -222,12 +226,13 @@ class AsyncClient : public Client { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      issue_allowed_[thread_idx] = true; // may be ok now even if it hadn't been 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-   if (issue_allowed && grpc_time_source::now() >= next_issue_[thread_idx]) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+   if (issue_allowed_[thread_idx] && 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+       grpc_time_source::now() >= next_issue_[thread_idx]) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      // Attempt to issue                                                                                                                  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      bool issued = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				      for (int num_attempts = 0; num_attempts < channel_count_ && !issued; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	  num_attempts++, next_channel_[thread_idx] = (next_channel_[thread_idx]+1)%channel_count_) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-       std::lock_guard g(channel_rpc_count_lock_[next_channel_[thread_idx]]); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+       std::lock_guard g(channel_rpc_lock_[next_channel_[thread_idx]]); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				        if (rpcs_outstanding[next_channel_[thread_idx]] < max_outstanding_per_channel_) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	 // do the work to issue 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	 rpcs_outstanding[next_channel_[thread_idx]]++; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -247,7 +252,7 @@ class AsyncClient : public Client { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   std::vector<bool> issue_allowed_; // may this thread attempt to issue 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   std::vector<grpc_time> next_issue_; // when should it issue? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  std::vector<std::mutex> channel_rpc_count_lock_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  std::vector<std::mutex> channel_rpc_lock_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   std::vector<int> rpcs_outstanding_; // per-channel vector 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   int max_outstanding_per_channel_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   int channel_count_; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -261,15 +266,15 @@ class AsyncUnaryClient GRPC_FINAL : public AsyncClient { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   ~AsyncUnaryClient() GRPC_OVERRIDE { EndThreads(); } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 private: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  static void SetupCtx(CompletionQueue* cq, TestService::Stub* stub, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  static ClientRpcContext *SetupCtx(CompletionQueue* cq, TestService::Stub* stub, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                        const SimpleRequest& req) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     auto check_done = [](grpc::Status s, SimpleResponse* response) {}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     auto start_req = [cq](TestService::Stub* stub, grpc::ClientContext* ctx, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                           const SimpleRequest& request) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       return stub->AsyncUnaryCall(ctx, request, cq); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    new ClientRpcContextUnaryImpl<SimpleRequest, SimpleResponse>( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        stub, req, start_req, check_done); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return new ClientRpcContextUnaryImpl<SimpleRequest, SimpleResponse>( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+       stub, req, start_req, check_done); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				    
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 }; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -350,7 +355,7 @@ class AsyncStreamingClient GRPC_FINAL : public AsyncClient { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   ~AsyncStreamingClient() GRPC_OVERRIDE { EndThreads(); } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 private: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-  static void SetupCtx(CompletionQueue* cq, TestService::Stub* stub, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  static ClientRpcContext *SetupCtx(CompletionQueue* cq, TestService::Stub* stub, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                        const SimpleRequest& req)  { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     auto check_done = [](grpc::Status s, SimpleResponse* response) {}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     auto start_req = [cq](TestService::Stub *stub, grpc::ClientContext *ctx, 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -358,7 +363,7 @@ private: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       auto stream = stub->AsyncStreamingCall(ctx, cq, tag); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       return stream; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    new ClientRpcContextStreamingImpl<SimpleRequest, SimpleResponse>( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return new ClientRpcContextStreamingImpl<SimpleRequest, SimpleResponse>( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         stub, req, start_req, check_done); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 }; 
			 |