|  | @@ -147,9 +147,9 @@ class Server::UnimplementedAsyncResponse final
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  class Server::SyncRequest final : public internal::CompletionQueueTag {
 | 
	
		
			
				|  |  |   public:
 | 
	
		
			
				|  |  | -  SyncRequest(internal::RpcServiceMethod* method, void* tag)
 | 
	
		
			
				|  |  | +  SyncRequest(internal::RpcServiceMethod* method, void* method_tag)
 | 
	
		
			
				|  |  |        : method_(method),
 | 
	
		
			
				|  |  | -        tag_(tag),
 | 
	
		
			
				|  |  | +        method_tag_(method_tag),
 | 
	
		
			
				|  |  |          in_flight_(false),
 | 
	
		
			
				|  |  |          has_request_payload_(
 | 
	
		
			
				|  |  |              method->method_type() == internal::RpcMethod::NORMAL_RPC ||
 | 
	
	
		
			
				|  | @@ -176,10 +176,10 @@ class Server::SyncRequest final : public internal::CompletionQueueTag {
 | 
	
		
			
				|  |  |    void Request(grpc_server* server, grpc_completion_queue* notify_cq) {
 | 
	
		
			
				|  |  |      GPR_ASSERT(cq_ && !in_flight_);
 | 
	
		
			
				|  |  |      in_flight_ = true;
 | 
	
		
			
				|  |  | -    if (tag_) {
 | 
	
		
			
				|  |  | +    if (method_tag_) {
 | 
	
		
			
				|  |  |        if (GRPC_CALL_OK !=
 | 
	
		
			
				|  |  |            grpc_server_request_registered_call(
 | 
	
		
			
				|  |  | -              server, tag_, &call_, &deadline_, &request_metadata_,
 | 
	
		
			
				|  |  | +              server, method_tag_, &call_, &deadline_, &request_metadata_,
 | 
	
		
			
				|  |  |                has_request_payload_ ? &request_payload_ : nullptr, cq_,
 | 
	
		
			
				|  |  |                notify_cq, this)) {
 | 
	
		
			
				|  |  |          TeardownRequest();
 | 
	
	
		
			
				|  | @@ -211,6 +211,9 @@ class Server::SyncRequest final : public internal::CompletionQueueTag {
 | 
	
		
			
				|  |  |      return true;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  // The CallData class represents a call that is "active" as opposed
 | 
	
		
			
				|  |  | +  // to just being requested. It wraps and takes ownership of the cq from
 | 
	
		
			
				|  |  | +  // the call request
 | 
	
		
			
				|  |  |    class CallData final {
 | 
	
		
			
				|  |  |     public:
 | 
	
		
			
				|  |  |      explicit CallData(Server* server, SyncRequest* mrd)
 | 
	
	
		
			
				|  | @@ -281,7 +284,7 @@ class Server::SyncRequest final : public internal::CompletionQueueTag {
 | 
	
		
			
				|  |  |          auto* handler = resources_ ? method_->handler()
 | 
	
		
			
				|  |  |                                     : server_->resource_exhausted_handler_.get();
 | 
	
		
			
				|  |  |          handler->RunHandler(internal::MethodHandler::HandlerParameter(
 | 
	
		
			
				|  |  | -            &call_, &ctx_, request_, request_status_));
 | 
	
		
			
				|  |  | +            &call_, &ctx_, request_, request_status_, nullptr));
 | 
	
		
			
				|  |  |          request_ = nullptr;
 | 
	
		
			
				|  |  |          global_callbacks_->PostSynchronousRequest(&ctx_);
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -314,7 +317,7 @@ class Server::SyncRequest final : public internal::CompletionQueueTag {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |   private:
 | 
	
		
			
				|  |  |    internal::RpcServiceMethod* const method_;
 | 
	
		
			
				|  |  | -  void* const tag_;
 | 
	
		
			
				|  |  | +  void* const method_tag_;
 | 
	
		
			
				|  |  |    bool in_flight_;
 | 
	
		
			
				|  |  |    const bool has_request_payload_;
 | 
	
		
			
				|  |  |    grpc_call* call_;
 | 
	
	
		
			
				|  | @@ -325,6 +328,176 @@ class Server::SyncRequest final : public internal::CompletionQueueTag {
 | 
	
		
			
				|  |  |    grpc_completion_queue* cq_;
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +class Server::CallbackRequest final : public internal::CompletionQueueTag {
 | 
	
		
			
				|  |  | + public:
 | 
	
		
			
				|  |  | +  CallbackRequest(Server* server, internal::RpcServiceMethod* method,
 | 
	
		
			
				|  |  | +                  void* method_tag)
 | 
	
		
			
				|  |  | +      : server_(server),
 | 
	
		
			
				|  |  | +        method_(method),
 | 
	
		
			
				|  |  | +        method_tag_(method_tag),
 | 
	
		
			
				|  |  | +        has_request_payload_(
 | 
	
		
			
				|  |  | +            method->method_type() == internal::RpcMethod::NORMAL_RPC ||
 | 
	
		
			
				|  |  | +            method->method_type() == internal::RpcMethod::SERVER_STREAMING),
 | 
	
		
			
				|  |  | +        cq_(server->CallbackCQ()),
 | 
	
		
			
				|  |  | +        tag_(this) {
 | 
	
		
			
				|  |  | +    Setup();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  ~CallbackRequest() { Clear(); }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void Request() {
 | 
	
		
			
				|  |  | +    if (method_tag_) {
 | 
	
		
			
				|  |  | +      if (GRPC_CALL_OK !=
 | 
	
		
			
				|  |  | +          grpc_server_request_registered_call(
 | 
	
		
			
				|  |  | +              server_->c_server(), method_tag_, &call_, &deadline_,
 | 
	
		
			
				|  |  | +              &request_metadata_,
 | 
	
		
			
				|  |  | +              has_request_payload_ ? &request_payload_ : nullptr, cq_->cq(),
 | 
	
		
			
				|  |  | +              cq_->cq(), static_cast<void*>(&tag_))) {
 | 
	
		
			
				|  |  | +        return;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +      if (!call_details_) {
 | 
	
		
			
				|  |  | +        call_details_ = new grpc_call_details;
 | 
	
		
			
				|  |  | +        grpc_call_details_init(call_details_);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      if (grpc_server_request_call(server_->c_server(), &call_, call_details_,
 | 
	
		
			
				|  |  | +                                   &request_metadata_, cq_->cq(), cq_->cq(),
 | 
	
		
			
				|  |  | +                                   static_cast<void*>(&tag_)) != GRPC_CALL_OK) {
 | 
	
		
			
				|  |  | +        return;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  bool FinalizeResult(void** tag, bool* status) override { return false; }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + private:
 | 
	
		
			
				|  |  | +  class CallbackCallTag : public grpc_experimental_completion_queue_functor {
 | 
	
		
			
				|  |  | +   public:
 | 
	
		
			
				|  |  | +    CallbackCallTag(Server::CallbackRequest* req) : req_(req) {
 | 
	
		
			
				|  |  | +      functor_run = &CallbackCallTag::StaticRun;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // force_run can not be performed on a tag if operations using this tag
 | 
	
		
			
				|  |  | +    // have been sent to PerformOpsOnCall. It is intended for error conditions
 | 
	
		
			
				|  |  | +    // that are detected before the operations are internally processed.
 | 
	
		
			
				|  |  | +    void force_run(bool ok) { Run(ok); }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +   private:
 | 
	
		
			
				|  |  | +    Server::CallbackRequest* req_;
 | 
	
		
			
				|  |  | +    internal::Call* call_;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    static void StaticRun(grpc_experimental_completion_queue_functor* cb,
 | 
	
		
			
				|  |  | +                          int ok) {
 | 
	
		
			
				|  |  | +      static_cast<CallbackCallTag*>(cb)->Run(static_cast<bool>(ok));
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    void Run(bool ok) {
 | 
	
		
			
				|  |  | +      void* ignored = req_;
 | 
	
		
			
				|  |  | +      bool new_ok = ok;
 | 
	
		
			
				|  |  | +      GPR_ASSERT(!req_->FinalizeResult(&ignored, &new_ok));
 | 
	
		
			
				|  |  | +      GPR_ASSERT(ignored == req_);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      if (!ok) {
 | 
	
		
			
				|  |  | +        // The call has been shutdown
 | 
	
		
			
				|  |  | +        req_->Clear();
 | 
	
		
			
				|  |  | +        return;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      // Bind the call, deadline, and metadata from what we got
 | 
	
		
			
				|  |  | +      req_->ctx_.set_call(req_->call_);
 | 
	
		
			
				|  |  | +      req_->ctx_.cq_ = req_->cq_;
 | 
	
		
			
				|  |  | +      req_->ctx_.BindDeadlineAndMetadata(req_->deadline_,
 | 
	
		
			
				|  |  | +                                         &req_->request_metadata_);
 | 
	
		
			
				|  |  | +      req_->request_metadata_.count = 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      // Create a C++ Call to control the underlying core call
 | 
	
		
			
				|  |  | +      call_ = new (grpc_call_arena_alloc(req_->call_, sizeof(internal::Call)))
 | 
	
		
			
				|  |  | +          internal::Call(
 | 
	
		
			
				|  |  | +              req_->call_, req_->server_, req_->cq_,
 | 
	
		
			
				|  |  | +              req_->server_->max_receive_message_size(),
 | 
	
		
			
				|  |  | +              req_->ctx_.set_server_rpc_info(
 | 
	
		
			
				|  |  | +                  req_->method_->name(), req_->server_->interceptor_creators_));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      req_->interceptor_methods_.SetCall(call_);
 | 
	
		
			
				|  |  | +      req_->interceptor_methods_.SetReverse();
 | 
	
		
			
				|  |  | +      // Set interception point for RECV INITIAL METADATA
 | 
	
		
			
				|  |  | +      req_->interceptor_methods_.AddInterceptionHookPoint(
 | 
	
		
			
				|  |  | +          experimental::InterceptionHookPoints::POST_RECV_INITIAL_METADATA);
 | 
	
		
			
				|  |  | +      req_->interceptor_methods_.SetRecvInitialMetadata(
 | 
	
		
			
				|  |  | +          &req_->ctx_.client_metadata_);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      if (req_->has_request_payload_) {
 | 
	
		
			
				|  |  | +        // Set interception point for RECV MESSAGE
 | 
	
		
			
				|  |  | +        req_->request_ = req_->method_->handler()->Deserialize(
 | 
	
		
			
				|  |  | +            req_->call_, req_->request_payload_, &req_->request_status_);
 | 
	
		
			
				|  |  | +        req_->request_payload_ = nullptr;
 | 
	
		
			
				|  |  | +        req_->interceptor_methods_.AddInterceptionHookPoint(
 | 
	
		
			
				|  |  | +            experimental::InterceptionHookPoints::POST_RECV_MESSAGE);
 | 
	
		
			
				|  |  | +        req_->interceptor_methods_.SetRecvMessage(req_->request_);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      if (req_->interceptor_methods_.RunInterceptors(
 | 
	
		
			
				|  |  | +              [this] { ContinueRunAfterInterception(); })) {
 | 
	
		
			
				|  |  | +        ContinueRunAfterInterception();
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        // There were interceptors to be run, so ContinueRunAfterInterception
 | 
	
		
			
				|  |  | +        // will be run when interceptors are done.
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    void ContinueRunAfterInterception() {
 | 
	
		
			
				|  |  | +      // req_->ctx_.BeginCompletionOp(call_);
 | 
	
		
			
				|  |  | +      req_->method_->handler()->RunHandler(
 | 
	
		
			
				|  |  | +          internal::MethodHandler::HandlerParameter(
 | 
	
		
			
				|  |  | +              call_, &req_->ctx_, req_->request_, req_->request_status_,
 | 
	
		
			
				|  |  | +              [this] {
 | 
	
		
			
				|  |  | +                req_->Reset();
 | 
	
		
			
				|  |  | +                req_->Request();
 | 
	
		
			
				|  |  | +              }));
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void Reset() {
 | 
	
		
			
				|  |  | +    Clear();
 | 
	
		
			
				|  |  | +    Setup();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void Clear() {
 | 
	
		
			
				|  |  | +    if (call_details_) {
 | 
	
		
			
				|  |  | +      delete call_details_;
 | 
	
		
			
				|  |  | +      call_details_ = nullptr;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    grpc_metadata_array_destroy(&request_metadata_);
 | 
	
		
			
				|  |  | +    if (has_request_payload_ && request_payload_) {
 | 
	
		
			
				|  |  | +      grpc_byte_buffer_destroy(request_payload_);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    ctx_.Clear();
 | 
	
		
			
				|  |  | +    interceptor_methods_.ClearState();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void Setup() {
 | 
	
		
			
				|  |  | +    grpc_metadata_array_init(&request_metadata_);
 | 
	
		
			
				|  |  | +    ctx_.Setup(gpr_inf_future(GPR_CLOCK_REALTIME));
 | 
	
		
			
				|  |  | +    request_payload_ = nullptr;
 | 
	
		
			
				|  |  | +    request_ = nullptr;
 | 
	
		
			
				|  |  | +    request_status_ = Status();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  Server* const server_;
 | 
	
		
			
				|  |  | +  internal::RpcServiceMethod* const method_;
 | 
	
		
			
				|  |  | +  void* const method_tag_;
 | 
	
		
			
				|  |  | +  const bool has_request_payload_;
 | 
	
		
			
				|  |  | +  grpc_byte_buffer* request_payload_;
 | 
	
		
			
				|  |  | +  void* request_;
 | 
	
		
			
				|  |  | +  Status request_status_;
 | 
	
		
			
				|  |  | +  grpc_call_details* call_details_ = nullptr;
 | 
	
		
			
				|  |  | +  grpc_call* call_;
 | 
	
		
			
				|  |  | +  gpr_timespec deadline_;
 | 
	
		
			
				|  |  | +  grpc_metadata_array request_metadata_;
 | 
	
		
			
				|  |  | +  CompletionQueue* cq_;
 | 
	
		
			
				|  |  | +  CallbackCallTag tag_;
 | 
	
		
			
				|  |  | +  ServerContext ctx_;
 | 
	
		
			
				|  |  | +  internal::InterceptorBatchMethodsImpl interceptor_methods_;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  // Implementation of ThreadManager. Each instance of SyncRequestThreadManager
 | 
	
		
			
				|  |  |  // manages a pool of threads that poll for incoming Sync RPCs and call the
 | 
	
		
			
				|  |  |  // appropriate RPC handlers
 | 
	
	
		
			
				|  | @@ -504,6 +677,9 @@ Server::Server(
 | 
	
		
			
				|  |  |  Server::~Server() {
 | 
	
		
			
				|  |  |    {
 | 
	
		
			
				|  |  |      std::unique_lock<std::mutex> lock(mu_);
 | 
	
		
			
				|  |  | +    if (callback_cq_ != nullptr) {
 | 
	
		
			
				|  |  | +      callback_cq_->Shutdown();
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |      if (started_ && !shutdown_) {
 | 
	
		
			
				|  |  |        lock.unlock();
 | 
	
		
			
				|  |  |        Shutdown();
 | 
	
	
		
			
				|  | @@ -576,21 +752,28 @@ bool Server::RegisterService(const grpc::string* host, Service* service) {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      internal::RpcServiceMethod* method = it->get();
 | 
	
		
			
				|  |  | -    void* tag = grpc_server_register_method(
 | 
	
		
			
				|  |  | +    void* method_registration_tag = grpc_server_register_method(
 | 
	
		
			
				|  |  |          server_, method->name(), host ? host->c_str() : nullptr,
 | 
	
		
			
				|  |  |          PayloadHandlingForMethod(method), 0);
 | 
	
		
			
				|  |  | -    if (tag == nullptr) {
 | 
	
		
			
				|  |  | +    if (method_registration_tag == nullptr) {
 | 
	
		
			
				|  |  |        gpr_log(GPR_DEBUG, "Attempt to register %s multiple times",
 | 
	
		
			
				|  |  |                method->name());
 | 
	
		
			
				|  |  |        return false;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    if (method->handler() == nullptr) {  // Async method
 | 
	
		
			
				|  |  | -      method->set_server_tag(tag);
 | 
	
		
			
				|  |  | -    } else {
 | 
	
		
			
				|  |  | +    if (method->handler() == nullptr) {  // Async method without handler
 | 
	
		
			
				|  |  | +      method->set_server_tag(method_registration_tag);
 | 
	
		
			
				|  |  | +    } else if (method->api_type() ==
 | 
	
		
			
				|  |  | +               internal::RpcServiceMethod::ApiType::SYNC) {
 | 
	
		
			
				|  |  |        for (auto it = sync_req_mgrs_.begin(); it != sync_req_mgrs_.end(); it++) {
 | 
	
		
			
				|  |  | -        (*it)->AddSyncMethod(method, tag);
 | 
	
		
			
				|  |  | +        (*it)->AddSyncMethod(method, method_registration_tag);
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +      // a callback method
 | 
	
		
			
				|  |  | +      auto* req = new CallbackRequest(this, method, method_registration_tag);
 | 
	
		
			
				|  |  | +      callback_reqs_.emplace_back(req);
 | 
	
		
			
				|  |  | +      // Enqueue it so that it will be Request'ed later once
 | 
	
		
			
				|  |  | +      // all request matchers are created at core server startup
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      method_name = method->name();
 | 
	
	
		
			
				|  | @@ -641,7 +824,8 @@ void Server::Start(ServerCompletionQueue** cqs, size_t num_cqs) {
 | 
	
		
			
				|  |  |      // performance.  This ensures that we don't introduce thread hops
 | 
	
		
			
				|  |  |      // for application requests that wind up on this CQ, which is polled
 | 
	
		
			
				|  |  |      // in its own thread.
 | 
	
		
			
				|  |  | -    health_check_cq = new ServerCompletionQueue(GRPC_CQ_NON_POLLING);
 | 
	
		
			
				|  |  | +    health_check_cq =
 | 
	
		
			
				|  |  | +        new ServerCompletionQueue(GRPC_CQ_NEXT, GRPC_CQ_NON_POLLING, nullptr);
 | 
	
		
			
				|  |  |      grpc_server_register_completion_queue(server_, health_check_cq->cq(),
 | 
	
		
			
				|  |  |                                            nullptr);
 | 
	
		
			
				|  |  |      default_health_check_service_impl =
 | 
	
	
		
			
				|  | @@ -678,6 +862,10 @@ void Server::Start(ServerCompletionQueue** cqs, size_t num_cqs) {
 | 
	
		
			
				|  |  |      (*it)->Start();
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  for (auto& cbreq : callback_reqs_) {
 | 
	
		
			
				|  |  | +    cbreq->Request();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    if (default_health_check_service_impl != nullptr) {
 | 
	
		
			
				|  |  |      default_health_check_service_impl->StartServingThread();
 | 
	
		
			
				|  |  |    }
 | 
	
	
		
			
				|  | @@ -910,4 +1098,41 @@ Server::UnimplementedAsyncResponse::UnimplementedAsyncResponse(
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  ServerInitializer* Server::initializer() { return server_initializer_.get(); }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +namespace {
 | 
	
		
			
				|  |  | +class ShutdownCallback : public grpc_experimental_completion_queue_functor {
 | 
	
		
			
				|  |  | + public:
 | 
	
		
			
				|  |  | +  ShutdownCallback() { functor_run = &ShutdownCallback::Run; }
 | 
	
		
			
				|  |  | +  // TakeCQ takes ownership of the cq into the shutdown callback
 | 
	
		
			
				|  |  | +  // so that the shutdown callback will be responsible for destroying it
 | 
	
		
			
				|  |  | +  void TakeCQ(CompletionQueue* cq) { cq_ = cq; }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // The Run function will get invoked by the completion queue library
 | 
	
		
			
				|  |  | +  // when the shutdown is actually complete
 | 
	
		
			
				|  |  | +  static void Run(grpc_experimental_completion_queue_functor* cb, int) {
 | 
	
		
			
				|  |  | +    auto* callback = static_cast<ShutdownCallback*>(cb);
 | 
	
		
			
				|  |  | +    delete callback->cq_;
 | 
	
		
			
				|  |  | +    grpc_core::Delete(callback);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + private:
 | 
	
		
			
				|  |  | +  CompletionQueue* cq_ = nullptr;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +}  // namespace
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +CompletionQueue* Server::CallbackCQ() {
 | 
	
		
			
				|  |  | +  // TODO(vjpai): Consider using a single global CQ for the default CQ
 | 
	
		
			
				|  |  | +  // if there is no explicit per-server CQ registered
 | 
	
		
			
				|  |  | +  std::lock_guard<std::mutex> l(mu_);
 | 
	
		
			
				|  |  | +  if (callback_cq_ == nullptr) {
 | 
	
		
			
				|  |  | +    auto* shutdown_callback = grpc_core::New<ShutdownCallback>();
 | 
	
		
			
				|  |  | +    callback_cq_ = new CompletionQueue(grpc_completion_queue_attributes{
 | 
	
		
			
				|  |  | +        GRPC_CQ_CURRENT_VERSION, GRPC_CQ_CALLBACK, GRPC_CQ_DEFAULT_POLLING,
 | 
	
		
			
				|  |  | +        shutdown_callback});
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // Transfer ownership of the new cq to its own shutdown callback
 | 
	
		
			
				|  |  | +    shutdown_callback->TakeCQ(callback_cq_);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  return callback_cq_;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  }  // namespace grpc
 |