|  | @@ -100,6 +100,7 @@ template <class Response>
 | 
	
		
			
				|  |  |  class ClientReadReactor;
 | 
	
		
			
				|  |  |  template <class Request>
 | 
	
		
			
				|  |  |  class ClientWriteReactor;
 | 
	
		
			
				|  |  | +class ClientUnaryReactor;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  // NOTE: The streaming objects are not actually implemented in the public API.
 | 
	
		
			
				|  |  |  //       These interfaces are provided for mocking only. Typical applications
 | 
	
	
		
			
				|  | @@ -157,6 +158,15 @@ class ClientCallbackWriter {
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +class ClientCallbackUnary {
 | 
	
		
			
				|  |  | + public:
 | 
	
		
			
				|  |  | +  virtual ~ClientCallbackUnary() {}
 | 
	
		
			
				|  |  | +  virtual void StartCall() = 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + protected:
 | 
	
		
			
				|  |  | +  void BindReactor(ClientUnaryReactor* reactor);
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  // The following classes are the reactor interfaces that are to be implemented
 | 
	
		
			
				|  |  |  // by the user. They are passed in to the library as an argument to a call on a
 | 
	
		
			
				|  |  |  // stub (either a codegen-ed call or a generic call). The streaming RPC is
 | 
	
	
		
			
				|  | @@ -346,6 +356,36 @@ class ClientWriteReactor {
 | 
	
		
			
				|  |  |    ClientCallbackWriter<Request>* writer_;
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +/// \a ClientUnaryReactor is a reactor-style interface for a unary RPC.
 | 
	
		
			
				|  |  | +/// This is _not_ a common way of invoking a unary RPC. In practice, this
 | 
	
		
			
				|  |  | +/// option should be used only if the unary RPC wants to receive initial
 | 
	
		
			
				|  |  | +/// metadata without waiting for the response to complete. Most deployments of
 | 
	
		
			
				|  |  | +/// RPC systems do not use this option, but it is needed for generality.
 | 
	
		
			
				|  |  | +/// All public methods behave as in ClientBidiReactor.
 | 
	
		
			
				|  |  | +/// StartCall is included for consistency with the other reactor flavors: even
 | 
	
		
			
				|  |  | +/// though there are no StartRead or StartWrite operations to queue before the
 | 
	
		
			
				|  |  | +/// call (that is part of the unary call itself) and there is no reactor object
 | 
	
		
			
				|  |  | +/// being created as a result of this call, we keep a consistent 2-phase
 | 
	
		
			
				|  |  | +/// initiation API among all the reactor flavors.
 | 
	
		
			
				|  |  | +class ClientUnaryReactor {
 | 
	
		
			
				|  |  | + public:
 | 
	
		
			
				|  |  | +  virtual ~ClientUnaryReactor() {}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void StartCall() { call_->StartCall(); }
 | 
	
		
			
				|  |  | +  virtual void OnDone(const Status& s) {}
 | 
	
		
			
				|  |  | +  virtual void OnReadInitialMetadataDone(bool ok) {}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + private:
 | 
	
		
			
				|  |  | +  friend class ClientCallbackUnary;
 | 
	
		
			
				|  |  | +  void BindCall(ClientCallbackUnary* call) { call_ = call; }
 | 
	
		
			
				|  |  | +  ClientCallbackUnary* call_;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// Define function out-of-line from class to avoid forward declaration issue
 | 
	
		
			
				|  |  | +inline void ClientCallbackUnary::BindReactor(ClientUnaryReactor* reactor) {
 | 
	
		
			
				|  |  | +  reactor->BindCall(this);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  }  // namespace experimental
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  namespace internal {
 | 
	
	
		
			
				|  | @@ -512,9 +552,9 @@ class ClientCallbackReaderWriterImpl
 | 
	
		
			
				|  |  |      this->BindReactor(reactor);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  ClientContext* context_;
 | 
	
		
			
				|  |  | +  ClientContext* const context_;
 | 
	
		
			
				|  |  |    Call call_;
 | 
	
		
			
				|  |  | -  ::grpc::experimental::ClientBidiReactor<Request, Response>* reactor_;
 | 
	
		
			
				|  |  | +  ::grpc::experimental::ClientBidiReactor<Request, Response>* const reactor_;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    CallOpSet<CallOpSendInitialMetadata, CallOpRecvInitialMetadata> start_ops_;
 | 
	
		
			
				|  |  |    CallbackWithSuccessTag start_tag_;
 | 
	
	
		
			
				|  | @@ -651,9 +691,9 @@ class ClientCallbackReaderImpl
 | 
	
		
			
				|  |  |      start_ops_.ClientSendClose();
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  ClientContext* context_;
 | 
	
		
			
				|  |  | +  ClientContext* const context_;
 | 
	
		
			
				|  |  |    Call call_;
 | 
	
		
			
				|  |  | -  ::grpc::experimental::ClientReadReactor<Response>* reactor_;
 | 
	
		
			
				|  |  | +  ::grpc::experimental::ClientReadReactor<Response>* const reactor_;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage, CallOpClientSendClose,
 | 
	
		
			
				|  |  |              CallOpRecvInitialMetadata>
 | 
	
	
		
			
				|  | @@ -824,9 +864,9 @@ class ClientCallbackWriterImpl
 | 
	
		
			
				|  |  |      finish_ops_.AllowNoMessage();
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  ClientContext* context_;
 | 
	
		
			
				|  |  | +  ClientContext* const context_;
 | 
	
		
			
				|  |  |    Call call_;
 | 
	
		
			
				|  |  | -  ::grpc::experimental::ClientWriteReactor<Request>* reactor_;
 | 
	
		
			
				|  |  | +  ::grpc::experimental::ClientWriteReactor<Request>* const reactor_;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    CallOpSet<CallOpSendInitialMetadata, CallOpRecvInitialMetadata> start_ops_;
 | 
	
		
			
				|  |  |    CallbackWithSuccessTag start_tag_;
 | 
	
	
		
			
				|  | @@ -867,6 +907,109 @@ class ClientCallbackWriterFactory {
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +class ClientCallbackUnaryImpl final
 | 
	
		
			
				|  |  | +    : public ::grpc::experimental::ClientCallbackUnary {
 | 
	
		
			
				|  |  | + public:
 | 
	
		
			
				|  |  | +  // always allocated against a call arena, no memory free required
 | 
	
		
			
				|  |  | +  static void operator delete(void* ptr, std::size_t size) {
 | 
	
		
			
				|  |  | +    assert(size == sizeof(ClientCallbackUnaryImpl));
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // This operator should never be called as the memory should be freed as part
 | 
	
		
			
				|  |  | +  // of the arena destruction. It only exists to provide a matching operator
 | 
	
		
			
				|  |  | +  // delete to the operator new so that some compilers will not complain (see
 | 
	
		
			
				|  |  | +  // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this
 | 
	
		
			
				|  |  | +  // there are no tests catching the compiler warning.
 | 
	
		
			
				|  |  | +  static void operator delete(void*, void*) { assert(0); }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void StartCall() override {
 | 
	
		
			
				|  |  | +    // This call initiates two batches, each with a callback
 | 
	
		
			
				|  |  | +    // 1. Send initial metadata + write + writes done + recv initial metadata
 | 
	
		
			
				|  |  | +    // 2. Read message, recv trailing metadata
 | 
	
		
			
				|  |  | +    started_ = true;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    start_tag_.Set(call_.call(),
 | 
	
		
			
				|  |  | +                   [this](bool ok) {
 | 
	
		
			
				|  |  | +                     reactor_->OnReadInitialMetadataDone(ok);
 | 
	
		
			
				|  |  | +                     MaybeFinish();
 | 
	
		
			
				|  |  | +                   },
 | 
	
		
			
				|  |  | +                   &start_ops_);
 | 
	
		
			
				|  |  | +    start_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
 | 
	
		
			
				|  |  | +                                   context_->initial_metadata_flags());
 | 
	
		
			
				|  |  | +    start_ops_.RecvInitialMetadata(context_);
 | 
	
		
			
				|  |  | +    start_ops_.set_core_cq_tag(&start_tag_);
 | 
	
		
			
				|  |  | +    call_.PerformOps(&start_ops_);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    finish_tag_.Set(call_.call(), [this](bool ok) { MaybeFinish(); },
 | 
	
		
			
				|  |  | +                    &finish_ops_);
 | 
	
		
			
				|  |  | +    finish_ops_.ClientRecvStatus(context_, &finish_status_);
 | 
	
		
			
				|  |  | +    finish_ops_.set_core_cq_tag(&finish_tag_);
 | 
	
		
			
				|  |  | +    call_.PerformOps(&finish_ops_);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void MaybeFinish() {
 | 
	
		
			
				|  |  | +    if (--callbacks_outstanding_ == 0) {
 | 
	
		
			
				|  |  | +      Status s = std::move(finish_status_);
 | 
	
		
			
				|  |  | +      auto* reactor = reactor_;
 | 
	
		
			
				|  |  | +      auto* call = call_.call();
 | 
	
		
			
				|  |  | +      this->~ClientCallbackUnaryImpl();
 | 
	
		
			
				|  |  | +      g_core_codegen_interface->grpc_call_unref(call);
 | 
	
		
			
				|  |  | +      reactor->OnDone(s);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + private:
 | 
	
		
			
				|  |  | +  friend class ClientCallbackUnaryFactory;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  template <class Request, class Response>
 | 
	
		
			
				|  |  | +  ClientCallbackUnaryImpl(Call call, ClientContext* context, Request* request,
 | 
	
		
			
				|  |  | +                          Response* response,
 | 
	
		
			
				|  |  | +                          ::grpc::experimental::ClientUnaryReactor* reactor)
 | 
	
		
			
				|  |  | +      : context_(context), call_(call), reactor_(reactor) {
 | 
	
		
			
				|  |  | +    this->BindReactor(reactor);
 | 
	
		
			
				|  |  | +    // TODO(vjpai): don't assert
 | 
	
		
			
				|  |  | +    GPR_CODEGEN_ASSERT(start_ops_.SendMessagePtr(request).ok());
 | 
	
		
			
				|  |  | +    start_ops_.ClientSendClose();
 | 
	
		
			
				|  |  | +    finish_ops_.RecvMessage(response);
 | 
	
		
			
				|  |  | +    finish_ops_.AllowNoMessage();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  ClientContext* const context_;
 | 
	
		
			
				|  |  | +  Call call_;
 | 
	
		
			
				|  |  | +  ::grpc::experimental::ClientUnaryReactor* const reactor_;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage, CallOpClientSendClose,
 | 
	
		
			
				|  |  | +            CallOpRecvInitialMetadata>
 | 
	
		
			
				|  |  | +      start_ops_;
 | 
	
		
			
				|  |  | +  CallbackWithSuccessTag start_tag_;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  CallOpSet<CallOpGenericRecvMessage, CallOpClientRecvStatus> finish_ops_;
 | 
	
		
			
				|  |  | +  CallbackWithSuccessTag finish_tag_;
 | 
	
		
			
				|  |  | +  Status finish_status_;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // This call will have 2 callbacks: start and finish
 | 
	
		
			
				|  |  | +  std::atomic_int callbacks_outstanding_{2};
 | 
	
		
			
				|  |  | +  bool started_{false};
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +class ClientCallbackUnaryFactory {
 | 
	
		
			
				|  |  | + public:
 | 
	
		
			
				|  |  | +  template <class Request, class Response>
 | 
	
		
			
				|  |  | +  static void Create(ChannelInterface* channel,
 | 
	
		
			
				|  |  | +                     const ::grpc::internal::RpcMethod& method,
 | 
	
		
			
				|  |  | +                     ClientContext* context, const Request* request,
 | 
	
		
			
				|  |  | +                     Response* response,
 | 
	
		
			
				|  |  | +                     ::grpc::experimental::ClientUnaryReactor* reactor) {
 | 
	
		
			
				|  |  | +    Call call = channel->CreateCall(method, context, channel->CallbackCQ());
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    g_core_codegen_interface->grpc_call_ref(call.call());
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    new (g_core_codegen_interface->grpc_call_arena_alloc(
 | 
	
		
			
				|  |  | +        call.call(), sizeof(ClientCallbackUnaryImpl)))
 | 
	
		
			
				|  |  | +        ClientCallbackUnaryImpl(call, context, request, response, reactor);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  }  // namespace internal
 | 
	
		
			
				|  |  |  }  // namespace grpc
 | 
	
		
			
				|  |  |  
 |