|  | @@ -350,10 +350,10 @@ class Server::SyncRequest final : public internal::CompletionQueueTag {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  class Server::CallbackRequest final : public internal::CompletionQueueTag {
 | 
	
		
			
				|  |  |   public:
 | 
	
		
			
				|  |  | -  CallbackRequest(Server* server, Server::MethodReqList* list,
 | 
	
		
			
				|  |  | +  CallbackRequest(Server* server, size_t method_idx,
 | 
	
		
			
				|  |  |                    internal::RpcServiceMethod* method, void* method_tag)
 | 
	
		
			
				|  |  |        : server_(server),
 | 
	
		
			
				|  |  | -        req_list_(list),
 | 
	
		
			
				|  |  | +        method_index_(method_idx),
 | 
	
		
			
				|  |  |          method_(method),
 | 
	
		
			
				|  |  |          method_tag_(method_tag),
 | 
	
		
			
				|  |  |          has_request_payload_(
 | 
	
	
		
			
				|  | @@ -428,46 +428,31 @@ class Server::CallbackRequest final : public internal::CompletionQueueTag {
 | 
	
		
			
				|  |  |        GPR_ASSERT(!req_->FinalizeResult(&ignored, &new_ok));
 | 
	
		
			
				|  |  |        GPR_ASSERT(ignored == req_);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      bool spawn_new = false;
 | 
	
		
			
				|  |  | -      {
 | 
	
		
			
				|  |  | -        std::unique_lock<std::mutex> l(req_->req_list_->reqs_mu);
 | 
	
		
			
				|  |  | -        req_->req_list_->reqs_list.erase(req_->req_list_iterator_);
 | 
	
		
			
				|  |  | -        req_->req_list_->reqs_list_sz--;
 | 
	
		
			
				|  |  | -        if (!ok) {
 | 
	
		
			
				|  |  | -          // The call has been shutdown.
 | 
	
		
			
				|  |  | -          // Delete its contents to free up the request.
 | 
	
		
			
				|  |  | -          // First release the lock in case the deletion of the request
 | 
	
		
			
				|  |  | -          // completes the full server shutdown and allows the destructor
 | 
	
		
			
				|  |  | -          // of the req_list to proceed.
 | 
	
		
			
				|  |  | -          l.unlock();
 | 
	
		
			
				|  |  | -          delete req_;
 | 
	
		
			
				|  |  | -          return;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        // If this was the last request in the list or it is below the soft
 | 
	
		
			
				|  |  | -        // minimum and there are spare requests available, set up a new one, but
 | 
	
		
			
				|  |  | -        // do it outside the lock since the Request could otherwise deadlock
 | 
	
		
			
				|  |  | -        if (req_->req_list_->reqs_list_sz == 0 ||
 | 
	
		
			
				|  |  | -            (req_->req_list_->reqs_list_sz <
 | 
	
		
			
				|  |  | -                 SOFT_MINIMUM_SPARE_CALLBACK_REQS_PER_METHOD &&
 | 
	
		
			
				|  |  | -             req_->server_->callback_reqs_outstanding_ <
 | 
	
		
			
				|  |  | -                 SOFT_MAXIMUM_CALLBACK_REQS_OUTSTANDING)) {
 | 
	
		
			
				|  |  | -          spawn_new = true;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | +      int count =
 | 
	
		
			
				|  |  | +          static_cast<int>(gpr_atm_no_barrier_fetch_add(
 | 
	
		
			
				|  |  | +              &req_->server_
 | 
	
		
			
				|  |  | +                   ->callback_unmatched_reqs_count_[req_->method_index_],
 | 
	
		
			
				|  |  | +              -1)) -
 | 
	
		
			
				|  |  | +          1;
 | 
	
		
			
				|  |  | +      if (!ok) {
 | 
	
		
			
				|  |  | +        // The call has been shutdown.
 | 
	
		
			
				|  |  | +        // Delete its contents to free up the request.
 | 
	
		
			
				|  |  | +        delete req_;
 | 
	
		
			
				|  |  | +        return;
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | -      if (spawn_new) {
 | 
	
		
			
				|  |  | -        auto* new_req = new CallbackRequest(req_->server_, req_->req_list_,
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      // If this was the last request in the list or it is below the soft
 | 
	
		
			
				|  |  | +      // minimum and there are spare requests available, set up a new one.
 | 
	
		
			
				|  |  | +      if (count == 0 || (count < SOFT_MINIMUM_SPARE_CALLBACK_REQS_PER_METHOD &&
 | 
	
		
			
				|  |  | +                         count < SOFT_MAXIMUM_CALLBACK_REQS_OUTSTANDING)) {
 | 
	
		
			
				|  |  | +        auto* new_req = new CallbackRequest(req_->server_, req_->method_index_,
 | 
	
		
			
				|  |  |                                              req_->method_, req_->method_tag_);
 | 
	
		
			
				|  |  |          if (!new_req->Request()) {
 | 
	
		
			
				|  |  | -          // The server must have just decided to shutdown. Erase
 | 
	
		
			
				|  |  | -          // from the list under lock but release the lock before
 | 
	
		
			
				|  |  | -          // deleting the new_req (in case that request was what
 | 
	
		
			
				|  |  | -          // would allow the destruction of the req_list)
 | 
	
		
			
				|  |  | -          {
 | 
	
		
			
				|  |  | -            std::lock_guard<std::mutex> l(new_req->req_list_->reqs_mu);
 | 
	
		
			
				|  |  | -            new_req->req_list_->reqs_list.erase(new_req->req_list_iterator_);
 | 
	
		
			
				|  |  | -            new_req->req_list_->reqs_list_sz--;
 | 
	
		
			
				|  |  | -          }
 | 
	
		
			
				|  |  | +          // The server must have just decided to shutdown.
 | 
	
		
			
				|  |  | +          gpr_atm_no_barrier_fetch_add(
 | 
	
		
			
				|  |  | +              &new_req->server_
 | 
	
		
			
				|  |  | +                   ->callback_unmatched_reqs_count_[new_req->method_index_],
 | 
	
		
			
				|  |  | +              -1);
 | 
	
		
			
				|  |  |            delete new_req;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |        }
 | 
	
	
		
			
				|  | @@ -557,20 +542,17 @@ class Server::CallbackRequest final : public internal::CompletionQueueTag {
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    void Setup() {
 | 
	
		
			
				|  |  | +    gpr_atm_no_barrier_fetch_add(
 | 
	
		
			
				|  |  | +        &server_->callback_unmatched_reqs_count_[method_index_], 1);
 | 
	
		
			
				|  |  |      grpc_metadata_array_init(&request_metadata_);
 | 
	
		
			
				|  |  |      ctx_.Setup(gpr_inf_future(GPR_CLOCK_REALTIME));
 | 
	
		
			
				|  |  |      request_payload_ = nullptr;
 | 
	
		
			
				|  |  |      request_ = nullptr;
 | 
	
		
			
				|  |  |      request_status_ = Status();
 | 
	
		
			
				|  |  | -    std::lock_guard<std::mutex> l(req_list_->reqs_mu);
 | 
	
		
			
				|  |  | -    req_list_->reqs_list.push_front(this);
 | 
	
		
			
				|  |  | -    req_list_->reqs_list_sz++;
 | 
	
		
			
				|  |  | -    req_list_iterator_ = req_list_->reqs_list.begin();
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    Server* const server_;
 | 
	
		
			
				|  |  | -  Server::MethodReqList* req_list_;
 | 
	
		
			
				|  |  | -  Server::MethodReqList::iterator req_list_iterator_;
 | 
	
		
			
				|  |  | +  size_t method_index_;
 | 
	
		
			
				|  |  |    internal::RpcServiceMethod* const method_;
 | 
	
		
			
				|  |  |    void* const method_tag_;
 | 
	
		
			
				|  |  |    const bool has_request_payload_;
 | 
	
	
		
			
				|  | @@ -791,12 +773,12 @@ Server::~Server() {
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    grpc_server_destroy(server_);
 | 
	
		
			
				|  |  | -  for (auto* method_list : callback_reqs_) {
 | 
	
		
			
				|  |  | -    // The entries of the method_list should have already been emptied
 | 
	
		
			
				|  |  | -    // during Shutdown as each request is failed by Shutdown. Check that
 | 
	
		
			
				|  |  | -    // this actually happened.
 | 
	
		
			
				|  |  | -    GPR_ASSERT(method_list->reqs_list.empty());
 | 
	
		
			
				|  |  | -    delete method_list;
 | 
	
		
			
				|  |  | +  for (auto& per_method_count : callback_unmatched_reqs_count_) {
 | 
	
		
			
				|  |  | +    // There should be no more unmatched callbacks for any method
 | 
	
		
			
				|  |  | +    // as each request is failed by Shutdown. Check that this actually
 | 
	
		
			
				|  |  | +    // happened
 | 
	
		
			
				|  |  | +    GPR_ASSERT(static_cast<int>(gpr_atm_no_barrier_load(&per_method_count)) ==
 | 
	
		
			
				|  |  | +               0);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -852,6 +834,7 @@ bool Server::RegisterService(const grpc::string* host, Service* service) {
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    const char* method_name = nullptr;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    for (auto it = service->methods_.begin(); it != service->methods_.end();
 | 
	
		
			
				|  |  |         ++it) {
 | 
	
		
			
				|  |  |      if (it->get() == nullptr) {  // Handled by generic service if any.
 | 
	
	
		
			
				|  | @@ -877,15 +860,15 @@ bool Server::RegisterService(const grpc::string* host, Service* service) {
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |      } else {
 | 
	
		
			
				|  |  |        // a callback method. Register at least some callback requests
 | 
	
		
			
				|  |  | -      callback_reqs_.push_back(new Server::MethodReqList);
 | 
	
		
			
				|  |  | -      auto* method_req_list = callback_reqs_.back();
 | 
	
		
			
				|  |  | +      callback_unmatched_reqs_count_.push_back(0);
 | 
	
		
			
				|  |  | +      auto method_index = callback_unmatched_reqs_count_.size() - 1;
 | 
	
		
			
				|  |  |        // TODO(vjpai): Register these dynamically based on need
 | 
	
		
			
				|  |  |        for (int i = 0; i < DEFAULT_CALLBACK_REQS_PER_METHOD; i++) {
 | 
	
		
			
				|  |  | -        new CallbackRequest(this, method_req_list, method,
 | 
	
		
			
				|  |  | -                            method_registration_tag);
 | 
	
		
			
				|  |  | +        callback_reqs_to_start_.push_back(new CallbackRequest(
 | 
	
		
			
				|  |  | +            this, method_index, method, method_registration_tag));
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | -      // Enqueue it so that it will be Request'ed later once
 | 
	
		
			
				|  |  | -      // all request matchers are created at core server startup
 | 
	
		
			
				|  |  | +      // Enqueue it so that it will be Request'ed later after all request
 | 
	
		
			
				|  |  | +      // matchers are created at core server startup
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      method_name = method->name();
 | 
	
	
		
			
				|  | @@ -974,11 +957,10 @@ void Server::Start(ServerCompletionQueue** cqs, size_t num_cqs) {
 | 
	
		
			
				|  |  |      (*it)->Start();
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  for (auto* cbmethods : callback_reqs_) {
 | 
	
		
			
				|  |  | -    for (auto* cbreq : cbmethods->reqs_list) {
 | 
	
		
			
				|  |  | -      GPR_ASSERT(cbreq->Request());
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +  for (auto* cbreq : callback_reqs_to_start_) {
 | 
	
		
			
				|  |  | +    GPR_ASSERT(cbreq->Request());
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | +  callback_reqs_to_start_.clear();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    if (default_health_check_service_impl != nullptr) {
 | 
	
		
			
				|  |  |      default_health_check_service_impl->StartServingThread();
 |