|
|
|
@ -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
|
|
|
|
|
|
|
|
|
|