1 //
2 //
3 // Copyright 2018 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18
19 #ifndef GRPCPP_SUPPORT_CLIENT_CALLBACK_H
20 #define GRPCPP_SUPPORT_CLIENT_CALLBACK_H
21
22 #include <grpc/grpc.h>
23 #include <grpc/impl/call.h>
24 #include <grpcpp/impl/call.h>
25 #include <grpcpp/impl/call_op_set.h>
26 #include <grpcpp/impl/sync.h>
27 #include <grpcpp/support/callback_common.h>
28 #include <grpcpp/support/config.h>
29 #include <grpcpp/support/status.h>
30
31 #include <atomic>
32 #include <functional>
33
34 #include "absl/log/absl_check.h"
35
36 namespace grpc {
37 class Channel;
38 class ClientContext;
39
40 namespace internal {
41 class RpcMethod;
42
43 /// Perform a callback-based unary call. May optionally specify the base
44 /// class of the Request and Response so that the internal calls and structures
45 /// below this may be based on those base classes and thus achieve code reuse
46 /// across different RPCs (e.g., for protobuf, MessageLite would be a base
47 /// class).
48 /// TODO(vjpai): Combine as much as possible with the blocking unary call code
49 template <class InputMessage, class OutputMessage,
50 class BaseInputMessage = InputMessage,
51 class BaseOutputMessage = OutputMessage>
CallbackUnaryCall(grpc::ChannelInterface * channel,const grpc::internal::RpcMethod & method,grpc::ClientContext * context,const InputMessage * request,OutputMessage * result,std::function<void (grpc::Status)> on_completion)52 void CallbackUnaryCall(grpc::ChannelInterface* channel,
53 const grpc::internal::RpcMethod& method,
54 grpc::ClientContext* context,
55 const InputMessage* request, OutputMessage* result,
56 std::function<void(grpc::Status)> on_completion) {
57 static_assert(std::is_base_of<BaseInputMessage, InputMessage>::value,
58 "Invalid input message specification");
59 static_assert(std::is_base_of<BaseOutputMessage, OutputMessage>::value,
60 "Invalid output message specification");
61 CallbackUnaryCallImpl<BaseInputMessage, BaseOutputMessage> x(
62 channel, method, context, request, result, on_completion);
63 }
64
65 template <class InputMessage, class OutputMessage>
66 class CallbackUnaryCallImpl {
67 public:
CallbackUnaryCallImpl(grpc::ChannelInterface * channel,const grpc::internal::RpcMethod & method,grpc::ClientContext * context,const InputMessage * request,OutputMessage * result,std::function<void (grpc::Status)> on_completion)68 CallbackUnaryCallImpl(grpc::ChannelInterface* channel,
69 const grpc::internal::RpcMethod& method,
70 grpc::ClientContext* context,
71 const InputMessage* request, OutputMessage* result,
72 std::function<void(grpc::Status)> on_completion) {
73 grpc::CompletionQueue* cq = channel->CallbackCQ();
74 ABSL_CHECK_NE(cq, nullptr);
75 grpc::internal::Call call(channel->CreateCall(method, context, cq));
76
77 using FullCallOpSet = grpc::internal::CallOpSet<
78 grpc::internal::CallOpSendInitialMetadata,
79 grpc::internal::CallOpSendMessage,
80 grpc::internal::CallOpRecvInitialMetadata,
81 grpc::internal::CallOpRecvMessage<OutputMessage>,
82 grpc::internal::CallOpClientSendClose,
83 grpc::internal::CallOpClientRecvStatus>;
84
85 struct OpSetAndTag {
86 FullCallOpSet opset;
87 grpc::internal::CallbackWithStatusTag tag;
88 };
89 const size_t alloc_sz = sizeof(OpSetAndTag);
90 auto* const alloced =
91 static_cast<OpSetAndTag*>(grpc_call_arena_alloc(call.call(), alloc_sz));
92 auto* ops = new (&alloced->opset) FullCallOpSet;
93 auto* tag = new (&alloced->tag)
94 grpc::internal::CallbackWithStatusTag(call.call(), on_completion, ops);
95
96 // TODO(vjpai): Unify code with sync API as much as possible
97 grpc::Status s = ops->SendMessagePtr(request);
98 if (!s.ok()) {
99 tag->force_run(s);
100 return;
101 }
102 ops->SendInitialMetadata(&context->send_initial_metadata_,
103 context->initial_metadata_flags());
104 ops->RecvInitialMetadata(context);
105 ops->RecvMessage(result);
106 ops->AllowNoMessage();
107 ops->ClientSendClose();
108 ops->ClientRecvStatus(context, tag->status_ptr());
109 ops->set_core_cq_tag(tag);
110 call.PerformOps(ops);
111 }
112 };
113
114 // Base class for public API classes.
115 class ClientReactor {
116 public:
117 virtual ~ClientReactor() = default;
118
119 /// Called by the library when all operations associated with this RPC have
120 /// completed and all Holds have been removed. OnDone provides the RPC status
121 /// outcome for both successful and failed RPCs. If it is never called on an
122 /// RPC, it indicates an application-level problem (like failure to remove a
123 /// hold).
124 ///
125 /// \param[in] s The status outcome of this RPC
126 virtual void OnDone(const grpc::Status& /*s*/) = 0;
127
128 /// InternalTrailersOnly is not part of the API and is not meant to be
129 /// overridden. It is virtual to allow successful builds for certain bazel
130 /// build users that only want to depend on gRPC codegen headers and not the
131 /// full library (although this is not a generally-supported option). Although
132 /// the virtual call is slower than a direct call, this function is
133 /// heavyweight and the cost of the virtual call is not much in comparison.
134 /// This function may be removed or devirtualized in the future.
135 virtual bool InternalTrailersOnly(const grpc_call* call) const;
136 };
137
138 } // namespace internal
139
140 // Forward declarations
141 template <class Request, class Response>
142 class ClientBidiReactor;
143 template <class Response>
144 class ClientReadReactor;
145 template <class Request>
146 class ClientWriteReactor;
147 class ClientUnaryReactor;
148
149 // NOTE: The streaming objects are not actually implemented in the public API.
150 // These interfaces are provided for mocking only. Typical applications
151 // will interact exclusively with the reactors that they define.
152 template <class Request, class Response>
153 class ClientCallbackReaderWriter {
154 public:
~ClientCallbackReaderWriter()155 virtual ~ClientCallbackReaderWriter() {}
156 virtual void StartCall() = 0;
157 virtual void Write(const Request* req, grpc::WriteOptions options) = 0;
158 virtual void WritesDone() = 0;
159 virtual void Read(Response* resp) = 0;
160 virtual void AddHold(int holds) = 0;
161 virtual void RemoveHold() = 0;
162
163 protected:
BindReactor(ClientBidiReactor<Request,Response> * reactor)164 void BindReactor(ClientBidiReactor<Request, Response>* reactor) {
165 reactor->BindStream(this);
166 }
167 };
168
169 template <class Response>
170 class ClientCallbackReader {
171 public:
~ClientCallbackReader()172 virtual ~ClientCallbackReader() {}
173 virtual void StartCall() = 0;
174 virtual void Read(Response* resp) = 0;
175 virtual void AddHold(int holds) = 0;
176 virtual void RemoveHold() = 0;
177
178 protected:
BindReactor(ClientReadReactor<Response> * reactor)179 void BindReactor(ClientReadReactor<Response>* reactor) {
180 reactor->BindReader(this);
181 }
182 };
183
184 template <class Request>
185 class ClientCallbackWriter {
186 public:
~ClientCallbackWriter()187 virtual ~ClientCallbackWriter() {}
188 virtual void StartCall() = 0;
Write(const Request * req)189 void Write(const Request* req) { Write(req, grpc::WriteOptions()); }
190 virtual void Write(const Request* req, grpc::WriteOptions options) = 0;
WriteLast(const Request * req,grpc::WriteOptions options)191 void WriteLast(const Request* req, grpc::WriteOptions options) {
192 Write(req, options.set_last_message());
193 }
194 virtual void WritesDone() = 0;
195
196 virtual void AddHold(int holds) = 0;
197 virtual void RemoveHold() = 0;
198
199 protected:
BindReactor(ClientWriteReactor<Request> * reactor)200 void BindReactor(ClientWriteReactor<Request>* reactor) {
201 reactor->BindWriter(this);
202 }
203 };
204
205 class ClientCallbackUnary {
206 public:
~ClientCallbackUnary()207 virtual ~ClientCallbackUnary() {}
208 virtual void StartCall() = 0;
209
210 protected:
211 void BindReactor(ClientUnaryReactor* reactor);
212 };
213
214 // The following classes are the reactor interfaces that are to be implemented
215 // by the user. They are passed in to the library as an argument to a call on a
216 // stub (either a codegen-ed call or a generic call). The streaming RPC is
217 // activated by calling StartCall, possibly after initiating StartRead,
218 // StartWrite, or AddHold operations on the streaming object. Note that none of
219 // the classes are pure; all reactions have a default empty reaction so that the
220 // user class only needs to override those reactions that it cares about.
221 // The reactor must be passed to the stub invocation before any of the below
222 // operations can be called and its reactions will be invoked by the library in
223 // response to the completion of various operations. Reactions must not include
224 // blocking operations (such as blocking I/O, starting synchronous RPCs, or
225 // waiting on condition variables). Reactions may be invoked concurrently,
226 // except that OnDone is called after all others (assuming proper API usage).
227 // The reactor may not be deleted until OnDone is called.
228
229 /// \a ClientBidiReactor is the interface for a bidirectional streaming RPC.
230 template <class Request, class Response>
231 class ClientBidiReactor : public internal::ClientReactor {
232 public:
233 /// Activate the RPC and initiate any reads or writes that have been Start'ed
234 /// before this call. All streaming RPCs issued by the client MUST have
235 /// StartCall invoked on them (even if they are canceled) as this call is the
236 /// activation of their lifecycle.
StartCall()237 void StartCall() { stream_->StartCall(); }
238
239 /// Initiate a read operation (or post it for later initiation if StartCall
240 /// has not yet been invoked).
241 ///
242 /// \param[out] resp Where to eventually store the read message. Valid when
243 /// the library calls OnReadDone
StartRead(Response * resp)244 void StartRead(Response* resp) { stream_->Read(resp); }
245
246 /// Initiate a write operation (or post it for later initiation if StartCall
247 /// has not yet been invoked).
248 ///
249 /// \param[in] req The message to be written. The library does not take
250 /// ownership but the caller must ensure that the message is
251 /// not deleted or modified until OnWriteDone is called.
StartWrite(const Request * req)252 void StartWrite(const Request* req) { StartWrite(req, grpc::WriteOptions()); }
253
254 /// Initiate/post a write operation with specified options.
255 ///
256 /// \param[in] req The message to be written. The library does not take
257 /// ownership but the caller must ensure that the message is
258 /// not deleted or modified until OnWriteDone is called.
259 /// \param[in] options The WriteOptions to use for writing this message
StartWrite(const Request * req,grpc::WriteOptions options)260 void StartWrite(const Request* req, grpc::WriteOptions options) {
261 stream_->Write(req, options);
262 }
263
264 /// Initiate/post a write operation with specified options and an indication
265 /// that this is the last write (like StartWrite and StartWritesDone, merged).
266 /// Note that calling this means that no more calls to StartWrite,
267 /// StartWriteLast, or StartWritesDone are allowed.
268 ///
269 /// \param[in] req The message to be written. The library does not take
270 /// ownership but the caller must ensure that the message is
271 /// not deleted or modified until OnWriteDone is called.
272 /// \param[in] options The WriteOptions to use for writing this message
StartWriteLast(const Request * req,grpc::WriteOptions options)273 void StartWriteLast(const Request* req, grpc::WriteOptions options) {
274 StartWrite(req, options.set_last_message());
275 }
276
277 /// Indicate that the RPC will have no more write operations. This can only be
278 /// issued once for a given RPC. This is not required or allowed if
279 /// StartWriteLast is used since that already has the same implication.
280 /// Note that calling this means that no more calls to StartWrite,
281 /// StartWriteLast, or StartWritesDone are allowed.
StartWritesDone()282 void StartWritesDone() { stream_->WritesDone(); }
283
284 /// Holds are needed if (and only if) this stream has operations that take
285 /// place on it after StartCall but from outside one of the reactions
286 /// (OnReadDone, etc). This is _not_ a common use of the streaming API.
287 ///
288 /// Holds must be added before calling StartCall. If a stream still has a hold
289 /// in place, its resources will not be destroyed even if the status has
290 /// already come in from the wire and there are currently no active callbacks
291 /// outstanding. Similarly, the stream will not call OnDone if there are still
292 /// holds on it.
293 ///
294 /// For example, if a StartRead or StartWrite operation is going to be
295 /// initiated from elsewhere in the application, the application should call
296 /// AddHold or AddMultipleHolds before StartCall. If there is going to be,
297 /// for example, a read-flow and a write-flow taking place outside the
298 /// reactions, then call AddMultipleHolds(2) before StartCall. When the
299 /// application knows that it won't issue any more read operations (such as
300 /// when a read comes back as not ok), it should issue a RemoveHold(). It
301 /// should also call RemoveHold() again after it does StartWriteLast or
302 /// StartWritesDone that indicates that there will be no more write ops.
303 /// The number of RemoveHold calls must match the total number of AddHold
304 /// calls plus the number of holds added by AddMultipleHolds.
305 /// The argument to AddMultipleHolds must be positive.
AddHold()306 void AddHold() { AddMultipleHolds(1); }
AddMultipleHolds(int holds)307 void AddMultipleHolds(int holds) {
308 ABSL_DCHECK_GT(holds, 0);
309 stream_->AddHold(holds);
310 }
RemoveHold()311 void RemoveHold() { stream_->RemoveHold(); }
312
313 /// Notifies the application that all operations associated with this RPC
314 /// have completed and all Holds have been removed. OnDone provides the RPC
315 /// status outcome for both successful and failed RPCs and will be called in
316 /// all cases. If it is not called, it indicates an application-level problem
317 /// (like failure to remove a hold).
318 ///
319 /// OnDone is called exactly once, and not concurrently with any (other)
320 /// reaction. (Holds may be needed (see above) to prevent OnDone from being
321 /// called concurrently with calls to the reactor from outside of reactions.)
322 ///
323 /// \param[in] s The status outcome of this RPC
OnDone(const grpc::Status &)324 void OnDone(const grpc::Status& /*s*/) override {}
325
326 /// Notifies the application that a read of initial metadata from the
327 /// server is done. If the application chooses not to implement this method,
328 /// it can assume that the initial metadata has been read before the first
329 /// call of OnReadDone or OnDone.
330 ///
331 /// \param[in] ok Was the initial metadata read successfully? If false, no
332 /// new read/write operation will succeed.
OnReadInitialMetadataDone(bool)333 virtual void OnReadInitialMetadataDone(bool /*ok*/) {}
334
335 /// Notifies the application that a StartRead operation completed.
336 ///
337 /// \param[in] ok Was it successful? If false, no new read/write operation
338 /// will succeed.
OnReadDone(bool)339 virtual void OnReadDone(bool /*ok*/) {}
340
341 /// Notifies the application that a StartWrite or StartWriteLast operation
342 /// completed.
343 ///
344 /// \param[in] ok Was it successful? If false, no new read/write operation
345 /// will succeed.
OnWriteDone(bool)346 virtual void OnWriteDone(bool /*ok*/) {}
347
348 /// Notifies the application that a StartWritesDone operation completed. Note
349 /// that this is only used on explicit StartWritesDone operations and not for
350 /// those that are implicitly invoked as part of a StartWriteLast.
351 ///
352 /// \param[in] ok Was it successful? If false, the application will later see
353 /// the failure reflected as a bad status in OnDone and no
354 /// further Start* should be called.
OnWritesDoneDone(bool)355 virtual void OnWritesDoneDone(bool /*ok*/) {}
356
357 private:
358 friend class ClientCallbackReaderWriter<Request, Response>;
BindStream(ClientCallbackReaderWriter<Request,Response> * stream)359 void BindStream(ClientCallbackReaderWriter<Request, Response>* stream) {
360 stream_ = stream;
361 }
362 ClientCallbackReaderWriter<Request, Response>* stream_;
363 };
364
365 /// \a ClientReadReactor is the interface for a server-streaming RPC.
366 /// All public methods behave as in ClientBidiReactor.
367 template <class Response>
368 class ClientReadReactor : public internal::ClientReactor {
369 public:
StartCall()370 void StartCall() { reader_->StartCall(); }
StartRead(Response * resp)371 void StartRead(Response* resp) { reader_->Read(resp); }
372
AddHold()373 void AddHold() { AddMultipleHolds(1); }
AddMultipleHolds(int holds)374 void AddMultipleHolds(int holds) {
375 ABSL_DCHECK_GT(holds, 0);
376 reader_->AddHold(holds);
377 }
RemoveHold()378 void RemoveHold() { reader_->RemoveHold(); }
379
OnDone(const grpc::Status &)380 void OnDone(const grpc::Status& /*s*/) override {}
OnReadInitialMetadataDone(bool)381 virtual void OnReadInitialMetadataDone(bool /*ok*/) {}
OnReadDone(bool)382 virtual void OnReadDone(bool /*ok*/) {}
383
384 private:
385 friend class ClientCallbackReader<Response>;
BindReader(ClientCallbackReader<Response> * reader)386 void BindReader(ClientCallbackReader<Response>* reader) { reader_ = reader; }
387 ClientCallbackReader<Response>* reader_;
388 };
389
390 /// \a ClientWriteReactor is the interface for a client-streaming RPC.
391 /// All public methods behave as in ClientBidiReactor.
392 template <class Request>
393 class ClientWriteReactor : public internal::ClientReactor {
394 public:
StartCall()395 void StartCall() { writer_->StartCall(); }
StartWrite(const Request * req)396 void StartWrite(const Request* req) { StartWrite(req, grpc::WriteOptions()); }
StartWrite(const Request * req,grpc::WriteOptions options)397 void StartWrite(const Request* req, grpc::WriteOptions options) {
398 writer_->Write(req, options);
399 }
StartWriteLast(const Request * req,grpc::WriteOptions options)400 void StartWriteLast(const Request* req, grpc::WriteOptions options) {
401 StartWrite(req, options.set_last_message());
402 }
StartWritesDone()403 void StartWritesDone() { writer_->WritesDone(); }
404
AddHold()405 void AddHold() { AddMultipleHolds(1); }
AddMultipleHolds(int holds)406 void AddMultipleHolds(int holds) {
407 ABSL_DCHECK_GT(holds, 0);
408 writer_->AddHold(holds);
409 }
RemoveHold()410 void RemoveHold() { writer_->RemoveHold(); }
411
OnDone(const grpc::Status &)412 void OnDone(const grpc::Status& /*s*/) override {}
OnReadInitialMetadataDone(bool)413 virtual void OnReadInitialMetadataDone(bool /*ok*/) {}
OnWriteDone(bool)414 virtual void OnWriteDone(bool /*ok*/) {}
OnWritesDoneDone(bool)415 virtual void OnWritesDoneDone(bool /*ok*/) {}
416
417 private:
418 friend class ClientCallbackWriter<Request>;
BindWriter(ClientCallbackWriter<Request> * writer)419 void BindWriter(ClientCallbackWriter<Request>* writer) { writer_ = writer; }
420
421 ClientCallbackWriter<Request>* writer_;
422 };
423
424 /// \a ClientUnaryReactor is a reactor-style interface for a unary RPC.
425 /// This is _not_ a common way of invoking a unary RPC. In practice, this
426 /// option should be used only if the unary RPC wants to receive initial
427 /// metadata without waiting for the response to complete. Most deployments of
428 /// RPC systems do not use this option, but it is needed for generality.
429 /// All public methods behave as in ClientBidiReactor.
430 /// StartCall is included for consistency with the other reactor flavors: even
431 /// though there are no StartRead or StartWrite operations to queue before the
432 /// call (that is part of the unary call itself) and there is no reactor object
433 /// being created as a result of this call, we keep a consistent 2-phase
434 /// initiation API among all the reactor flavors.
435 class ClientUnaryReactor : public internal::ClientReactor {
436 public:
StartCall()437 void StartCall() { call_->StartCall(); }
OnDone(const grpc::Status &)438 void OnDone(const grpc::Status& /*s*/) override {}
OnReadInitialMetadataDone(bool)439 virtual void OnReadInitialMetadataDone(bool /*ok*/) {}
440
441 private:
442 friend class ClientCallbackUnary;
BindCall(ClientCallbackUnary * call)443 void BindCall(ClientCallbackUnary* call) { call_ = call; }
444 ClientCallbackUnary* call_;
445 };
446
447 // Define function out-of-line from class to avoid forward declaration issue
BindReactor(ClientUnaryReactor * reactor)448 inline void ClientCallbackUnary::BindReactor(ClientUnaryReactor* reactor) {
449 reactor->BindCall(this);
450 }
451
452 namespace internal {
453
454 // Forward declare factory classes for friendship
455 template <class Request, class Response>
456 class ClientCallbackReaderWriterFactory;
457 template <class Response>
458 class ClientCallbackReaderFactory;
459 template <class Request>
460 class ClientCallbackWriterFactory;
461
462 template <class Request, class Response>
463 class ClientCallbackReaderWriterImpl
464 : public ClientCallbackReaderWriter<Request, Response> {
465 public:
466 // always allocated against a call arena, no memory free required
delete(void *,std::size_t size)467 static void operator delete(void* /*ptr*/, std::size_t size) {
468 ABSL_CHECK_EQ(size, sizeof(ClientCallbackReaderWriterImpl));
469 }
470
471 // This operator should never be called as the memory should be freed as part
472 // of the arena destruction. It only exists to provide a matching operator
473 // delete to the operator new so that some compilers will not complain (see
474 // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this
475 // there are no tests catching the compiler warning.
delete(void *,void *)476 static void operator delete(void*, void*) { ABSL_CHECK(false); }
477
StartCall()478 void StartCall() ABSL_LOCKS_EXCLUDED(start_mu_) override {
479 // This call initiates two batches, plus any backlog, each with a callback
480 // 1. Send initial metadata (unless corked) + recv initial metadata
481 // 2. Any read backlog
482 // 3. Any write backlog
483 // 4. Recv trailing metadata (unless corked)
484 if (!start_corked_) {
485 start_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
486 context_->initial_metadata_flags());
487 }
488
489 call_.PerformOps(&start_ops_);
490
491 {
492 grpc::internal::MutexLock lock(&start_mu_);
493
494 if (backlog_.read_ops) {
495 call_.PerformOps(&read_ops_);
496 }
497 if (backlog_.write_ops) {
498 call_.PerformOps(&write_ops_);
499 }
500 if (backlog_.writes_done_ops) {
501 call_.PerformOps(&writes_done_ops_);
502 }
503 call_.PerformOps(&finish_ops_);
504 // The last thing in this critical section is to set started_ so that it
505 // can be used lock-free as well.
506 started_.store(true, std::memory_order_release);
507 }
508 // MaybeFinish outside the lock to make sure that destruction of this object
509 // doesn't take place while holding the lock (which would cause the lock to
510 // be released after destruction)
511 this->MaybeFinish(/*from_reaction=*/false);
512 }
513
Read(Response * msg)514 void Read(Response* msg) override {
515 read_ops_.RecvMessage(msg);
516 callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed);
517 if (GPR_UNLIKELY(!started_.load(std::memory_order_acquire))) {
518 grpc::internal::MutexLock lock(&start_mu_);
519 if (GPR_LIKELY(!started_.load(std::memory_order_relaxed))) {
520 backlog_.read_ops = true;
521 return;
522 }
523 }
524 call_.PerformOps(&read_ops_);
525 }
526
Write(const Request * msg,grpc::WriteOptions options)527 void Write(const Request* msg, grpc::WriteOptions options)
528 ABSL_LOCKS_EXCLUDED(start_mu_) override {
529 if (options.is_last_message()) {
530 options.set_buffer_hint();
531 write_ops_.ClientSendClose();
532 }
533 // TODO(vjpai): don't assert
534 ABSL_CHECK(write_ops_.SendMessagePtr(msg, options).ok());
535 callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed);
536 if (GPR_UNLIKELY(corked_write_needed_)) {
537 write_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
538 context_->initial_metadata_flags());
539 corked_write_needed_ = false;
540 }
541
542 if (GPR_UNLIKELY(!started_.load(std::memory_order_acquire))) {
543 grpc::internal::MutexLock lock(&start_mu_);
544 if (GPR_LIKELY(!started_.load(std::memory_order_relaxed))) {
545 backlog_.write_ops = true;
546 return;
547 }
548 }
549 call_.PerformOps(&write_ops_);
550 }
WritesDone()551 void WritesDone() ABSL_LOCKS_EXCLUDED(start_mu_) override {
552 writes_done_ops_.ClientSendClose();
553 writes_done_tag_.Set(
554 call_.call(),
555 [this](bool ok) {
556 reactor_->OnWritesDoneDone(ok);
557 MaybeFinish(/*from_reaction=*/true);
558 },
559 &writes_done_ops_, /*can_inline=*/false);
560 writes_done_ops_.set_core_cq_tag(&writes_done_tag_);
561 callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed);
562 if (GPR_UNLIKELY(corked_write_needed_)) {
563 writes_done_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
564 context_->initial_metadata_flags());
565 corked_write_needed_ = false;
566 }
567 if (GPR_UNLIKELY(!started_.load(std::memory_order_acquire))) {
568 grpc::internal::MutexLock lock(&start_mu_);
569 if (GPR_LIKELY(!started_.load(std::memory_order_relaxed))) {
570 backlog_.writes_done_ops = true;
571 return;
572 }
573 }
574 call_.PerformOps(&writes_done_ops_);
575 }
576
AddHold(int holds)577 void AddHold(int holds) override {
578 callbacks_outstanding_.fetch_add(holds, std::memory_order_relaxed);
579 }
RemoveHold()580 void RemoveHold() override { MaybeFinish(/*from_reaction=*/false); }
581
582 private:
583 friend class ClientCallbackReaderWriterFactory<Request, Response>;
584
ClientCallbackReaderWriterImpl(grpc::internal::Call call,grpc::ClientContext * context,ClientBidiReactor<Request,Response> * reactor)585 ClientCallbackReaderWriterImpl(grpc::internal::Call call,
586 grpc::ClientContext* context,
587 ClientBidiReactor<Request, Response>* reactor)
588 : context_(context),
589 call_(call),
590 reactor_(reactor),
591 start_corked_(context_->initial_metadata_corked_),
592 corked_write_needed_(start_corked_) {
593 this->BindReactor(reactor);
594
595 // Set up the unchanging parts of the start, read, and write tags and ops.
596 start_tag_.Set(
597 call_.call(),
598 [this](bool ok) {
599 reactor_->OnReadInitialMetadataDone(
600 ok && !reactor_->InternalTrailersOnly(call_.call()));
601 MaybeFinish(/*from_reaction=*/true);
602 },
603 &start_ops_, /*can_inline=*/false);
604 start_ops_.RecvInitialMetadata(context_);
605 start_ops_.set_core_cq_tag(&start_tag_);
606
607 write_tag_.Set(
608 call_.call(),
609 [this](bool ok) {
610 reactor_->OnWriteDone(ok);
611 MaybeFinish(/*from_reaction=*/true);
612 },
613 &write_ops_, /*can_inline=*/false);
614 write_ops_.set_core_cq_tag(&write_tag_);
615
616 read_tag_.Set(
617 call_.call(),
618 [this](bool ok) {
619 reactor_->OnReadDone(ok);
620 MaybeFinish(/*from_reaction=*/true);
621 },
622 &read_ops_, /*can_inline=*/false);
623 read_ops_.set_core_cq_tag(&read_tag_);
624
625 // Also set up the Finish tag and op set.
626 finish_tag_.Set(
627 call_.call(),
628 [this](bool /*ok*/) { MaybeFinish(/*from_reaction=*/true); },
629 &finish_ops_,
630 /*can_inline=*/false);
631 finish_ops_.ClientRecvStatus(context_, &finish_status_);
632 finish_ops_.set_core_cq_tag(&finish_tag_);
633 }
634
635 // MaybeFinish can be called from reactions or from user-initiated operations
636 // like StartCall or RemoveHold. If this is the last operation or hold on this
637 // object, it will invoke the OnDone reaction. If MaybeFinish was called from
638 // a reaction, it can call OnDone directly. If not, it would need to schedule
639 // OnDone onto an executor thread to avoid the possibility of deadlocking with
640 // any locks in the user code that invoked it.
MaybeFinish(bool from_reaction)641 void MaybeFinish(bool from_reaction) {
642 if (GPR_UNLIKELY(callbacks_outstanding_.fetch_sub(
643 1, std::memory_order_acq_rel) == 1)) {
644 grpc::Status s = std::move(finish_status_);
645 auto* reactor = reactor_;
646 auto* call = call_.call();
647 this->~ClientCallbackReaderWriterImpl();
648 if (GPR_LIKELY(from_reaction)) {
649 grpc_call_unref(call);
650 reactor->OnDone(s);
651 } else {
652 grpc_call_run_in_event_engine(
653 call, [reactor, s = std::move(s)]() { reactor->OnDone(s); });
654 grpc_call_unref(call);
655 }
656 }
657 }
658
659 grpc::ClientContext* const context_;
660 grpc::internal::Call call_;
661 ClientBidiReactor<Request, Response>* const reactor_;
662
663 grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata,
664 grpc::internal::CallOpRecvInitialMetadata>
665 start_ops_;
666 grpc::internal::CallbackWithSuccessTag start_tag_;
667 const bool start_corked_;
668 bool corked_write_needed_; // no lock needed since only accessed in
669 // Write/WritesDone which cannot be concurrent
670
671 grpc::internal::CallOpSet<grpc::internal::CallOpClientRecvStatus> finish_ops_;
672 grpc::internal::CallbackWithSuccessTag finish_tag_;
673 grpc::Status finish_status_;
674
675 grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata,
676 grpc::internal::CallOpSendMessage,
677 grpc::internal::CallOpClientSendClose>
678 write_ops_;
679 grpc::internal::CallbackWithSuccessTag write_tag_;
680
681 grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata,
682 grpc::internal::CallOpClientSendClose>
683 writes_done_ops_;
684 grpc::internal::CallbackWithSuccessTag writes_done_tag_;
685
686 grpc::internal::CallOpSet<grpc::internal::CallOpRecvMessage<Response>>
687 read_ops_;
688 grpc::internal::CallbackWithSuccessTag read_tag_;
689
690 struct StartCallBacklog {
691 bool write_ops = false;
692 bool writes_done_ops = false;
693 bool read_ops = false;
694 };
695 StartCallBacklog backlog_ ABSL_GUARDED_BY(start_mu_);
696
697 // Minimum of 3 callbacks to pre-register for start ops, StartCall, and finish
698 std::atomic<intptr_t> callbacks_outstanding_{3};
699 std::atomic_bool started_{false};
700 grpc::internal::Mutex start_mu_;
701 };
702
703 template <class Request, class Response>
704 class ClientCallbackReaderWriterFactory {
705 public:
Create(grpc::ChannelInterface * channel,const grpc::internal::RpcMethod & method,grpc::ClientContext * context,ClientBidiReactor<Request,Response> * reactor)706 static void Create(grpc::ChannelInterface* channel,
707 const grpc::internal::RpcMethod& method,
708 grpc::ClientContext* context,
709 ClientBidiReactor<Request, Response>* reactor) {
710 grpc::internal::Call call =
711 channel->CreateCall(method, context, channel->CallbackCQ());
712
713 grpc_call_ref(call.call());
714 new (grpc_call_arena_alloc(
715 call.call(), sizeof(ClientCallbackReaderWriterImpl<Request, Response>)))
716 ClientCallbackReaderWriterImpl<Request, Response>(call, context,
717 reactor);
718 }
719 };
720
721 template <class Response>
722 class ClientCallbackReaderImpl : public ClientCallbackReader<Response> {
723 public:
724 // always allocated against a call arena, no memory free required
delete(void *,std::size_t size)725 static void operator delete(void* /*ptr*/, std::size_t size) {
726 ABSL_CHECK_EQ(size, sizeof(ClientCallbackReaderImpl));
727 }
728
729 // This operator should never be called as the memory should be freed as part
730 // of the arena destruction. It only exists to provide a matching operator
731 // delete to the operator new so that some compilers will not complain (see
732 // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this
733 // there are no tests catching the compiler warning.
delete(void *,void *)734 static void operator delete(void*, void*) { ABSL_CHECK(false); }
735
StartCall()736 void StartCall() override {
737 // This call initiates two batches, plus any backlog, each with a callback
738 // 1. Send initial metadata (unless corked) + recv initial metadata
739 // 2. Any backlog
740 // 3. Recv trailing metadata
741
742 start_tag_.Set(
743 call_.call(),
744 [this](bool ok) {
745 reactor_->OnReadInitialMetadataDone(
746 ok && !reactor_->InternalTrailersOnly(call_.call()));
747 MaybeFinish(/*from_reaction=*/true);
748 },
749 &start_ops_, /*can_inline=*/false);
750 start_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
751 context_->initial_metadata_flags());
752 start_ops_.RecvInitialMetadata(context_);
753 start_ops_.set_core_cq_tag(&start_tag_);
754 call_.PerformOps(&start_ops_);
755
756 // Also set up the read tag so it doesn't have to be set up each time
757 read_tag_.Set(
758 call_.call(),
759 [this](bool ok) {
760 reactor_->OnReadDone(ok);
761 MaybeFinish(/*from_reaction=*/true);
762 },
763 &read_ops_, /*can_inline=*/false);
764 read_ops_.set_core_cq_tag(&read_tag_);
765
766 {
767 grpc::internal::MutexLock lock(&start_mu_);
768 if (backlog_.read_ops) {
769 call_.PerformOps(&read_ops_);
770 }
771 started_.store(true, std::memory_order_release);
772 }
773
774 finish_tag_.Set(
775 call_.call(),
776 [this](bool /*ok*/) { MaybeFinish(/*from_reaction=*/true); },
777 &finish_ops_, /*can_inline=*/false);
778 finish_ops_.ClientRecvStatus(context_, &finish_status_);
779 finish_ops_.set_core_cq_tag(&finish_tag_);
780 call_.PerformOps(&finish_ops_);
781 }
782
Read(Response * msg)783 void Read(Response* msg) override {
784 read_ops_.RecvMessage(msg);
785 callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed);
786 if (GPR_UNLIKELY(!started_.load(std::memory_order_acquire))) {
787 grpc::internal::MutexLock lock(&start_mu_);
788 if (GPR_LIKELY(!started_.load(std::memory_order_relaxed))) {
789 backlog_.read_ops = true;
790 return;
791 }
792 }
793 call_.PerformOps(&read_ops_);
794 }
795
AddHold(int holds)796 void AddHold(int holds) override {
797 callbacks_outstanding_.fetch_add(holds, std::memory_order_relaxed);
798 }
RemoveHold()799 void RemoveHold() override { MaybeFinish(/*from_reaction=*/false); }
800
801 private:
802 friend class ClientCallbackReaderFactory<Response>;
803
804 template <class Request>
ClientCallbackReaderImpl(grpc::internal::Call call,grpc::ClientContext * context,Request * request,ClientReadReactor<Response> * reactor)805 ClientCallbackReaderImpl(grpc::internal::Call call,
806 grpc::ClientContext* context, Request* request,
807 ClientReadReactor<Response>* reactor)
808 : context_(context), call_(call), reactor_(reactor) {
809 this->BindReactor(reactor);
810 // TODO(vjpai): don't assert
811 ABSL_CHECK(start_ops_.SendMessagePtr(request).ok());
812 start_ops_.ClientSendClose();
813 }
814
815 // MaybeFinish behaves as in ClientCallbackReaderWriterImpl.
MaybeFinish(bool from_reaction)816 void MaybeFinish(bool from_reaction) {
817 if (GPR_UNLIKELY(callbacks_outstanding_.fetch_sub(
818 1, std::memory_order_acq_rel) == 1)) {
819 grpc::Status s = std::move(finish_status_);
820 auto* reactor = reactor_;
821 auto* call = call_.call();
822 this->~ClientCallbackReaderImpl();
823 if (GPR_LIKELY(from_reaction)) {
824 grpc_call_unref(call);
825 reactor->OnDone(s);
826 } else {
827 grpc_call_run_in_event_engine(
828 call, [reactor, s = std::move(s)]() { reactor->OnDone(s); });
829 grpc_call_unref(call);
830 }
831 }
832 }
833
834 grpc::ClientContext* const context_;
835 grpc::internal::Call call_;
836 ClientReadReactor<Response>* const reactor_;
837
838 grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata,
839 grpc::internal::CallOpSendMessage,
840 grpc::internal::CallOpClientSendClose,
841 grpc::internal::CallOpRecvInitialMetadata>
842 start_ops_;
843 grpc::internal::CallbackWithSuccessTag start_tag_;
844
845 grpc::internal::CallOpSet<grpc::internal::CallOpClientRecvStatus> finish_ops_;
846 grpc::internal::CallbackWithSuccessTag finish_tag_;
847 grpc::Status finish_status_;
848
849 grpc::internal::CallOpSet<grpc::internal::CallOpRecvMessage<Response>>
850 read_ops_;
851 grpc::internal::CallbackWithSuccessTag read_tag_;
852
853 struct StartCallBacklog {
854 bool read_ops = false;
855 };
856 StartCallBacklog backlog_ ABSL_GUARDED_BY(start_mu_);
857
858 // Minimum of 2 callbacks to pre-register for start and finish
859 std::atomic<intptr_t> callbacks_outstanding_{2};
860 std::atomic_bool started_{false};
861 grpc::internal::Mutex start_mu_;
862 };
863
864 template <class Response>
865 class ClientCallbackReaderFactory {
866 public:
867 template <class Request>
Create(grpc::ChannelInterface * channel,const grpc::internal::RpcMethod & method,grpc::ClientContext * context,const Request * request,ClientReadReactor<Response> * reactor)868 static void Create(grpc::ChannelInterface* channel,
869 const grpc::internal::RpcMethod& method,
870 grpc::ClientContext* context, const Request* request,
871 ClientReadReactor<Response>* reactor) {
872 grpc::internal::Call call =
873 channel->CreateCall(method, context, channel->CallbackCQ());
874
875 grpc_call_ref(call.call());
876 new (grpc_call_arena_alloc(call.call(),
877 sizeof(ClientCallbackReaderImpl<Response>)))
878 ClientCallbackReaderImpl<Response>(call, context, request, reactor);
879 }
880 };
881
882 template <class Request>
883 class ClientCallbackWriterImpl : public ClientCallbackWriter<Request> {
884 public:
885 // always allocated against a call arena, no memory free required
delete(void *,std::size_t size)886 static void operator delete(void* /*ptr*/, std::size_t size) {
887 ABSL_CHECK_EQ(size, sizeof(ClientCallbackWriterImpl));
888 }
889
890 // This operator should never be called as the memory should be freed as part
891 // of the arena destruction. It only exists to provide a matching operator
892 // delete to the operator new so that some compilers will not complain (see
893 // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this
894 // there are no tests catching the compiler warning.
delete(void *,void *)895 static void operator delete(void*, void*) { ABSL_CHECK(false); }
896
StartCall()897 void StartCall() ABSL_LOCKS_EXCLUDED(start_mu_) override {
898 // This call initiates two batches, plus any backlog, each with a callback
899 // 1. Send initial metadata (unless corked) + recv initial metadata
900 // 2. Any backlog
901 // 3. Recv trailing metadata
902
903 if (!start_corked_) {
904 start_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
905 context_->initial_metadata_flags());
906 }
907 call_.PerformOps(&start_ops_);
908
909 {
910 grpc::internal::MutexLock lock(&start_mu_);
911
912 if (backlog_.write_ops) {
913 call_.PerformOps(&write_ops_);
914 }
915 if (backlog_.writes_done_ops) {
916 call_.PerformOps(&writes_done_ops_);
917 }
918 call_.PerformOps(&finish_ops_);
919 // The last thing in this critical section is to set started_ so that it
920 // can be used lock-free as well.
921 started_.store(true, std::memory_order_release);
922 }
923 // MaybeFinish outside the lock to make sure that destruction of this object
924 // doesn't take place while holding the lock (which would cause the lock to
925 // be released after destruction)
926 this->MaybeFinish(/*from_reaction=*/false);
927 }
928
Write(const Request * msg,grpc::WriteOptions options)929 void Write(const Request* msg, grpc::WriteOptions options)
930 ABSL_LOCKS_EXCLUDED(start_mu_) override {
931 if (GPR_UNLIKELY(options.is_last_message())) {
932 options.set_buffer_hint();
933 write_ops_.ClientSendClose();
934 }
935 // TODO(vjpai): don't assert
936 ABSL_CHECK(write_ops_.SendMessagePtr(msg, options).ok());
937 callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed);
938
939 if (GPR_UNLIKELY(corked_write_needed_)) {
940 write_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
941 context_->initial_metadata_flags());
942 corked_write_needed_ = false;
943 }
944
945 if (GPR_UNLIKELY(!started_.load(std::memory_order_acquire))) {
946 grpc::internal::MutexLock lock(&start_mu_);
947 if (GPR_LIKELY(!started_.load(std::memory_order_relaxed))) {
948 backlog_.write_ops = true;
949 return;
950 }
951 }
952 call_.PerformOps(&write_ops_);
953 }
954
WritesDone()955 void WritesDone() ABSL_LOCKS_EXCLUDED(start_mu_) override {
956 writes_done_ops_.ClientSendClose();
957 writes_done_tag_.Set(
958 call_.call(),
959 [this](bool ok) {
960 reactor_->OnWritesDoneDone(ok);
961 MaybeFinish(/*from_reaction=*/true);
962 },
963 &writes_done_ops_, /*can_inline=*/false);
964 writes_done_ops_.set_core_cq_tag(&writes_done_tag_);
965 callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed);
966
967 if (GPR_UNLIKELY(corked_write_needed_)) {
968 writes_done_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
969 context_->initial_metadata_flags());
970 corked_write_needed_ = false;
971 }
972
973 if (GPR_UNLIKELY(!started_.load(std::memory_order_acquire))) {
974 grpc::internal::MutexLock lock(&start_mu_);
975 if (GPR_LIKELY(!started_.load(std::memory_order_relaxed))) {
976 backlog_.writes_done_ops = true;
977 return;
978 }
979 }
980 call_.PerformOps(&writes_done_ops_);
981 }
982
AddHold(int holds)983 void AddHold(int holds) override {
984 callbacks_outstanding_.fetch_add(holds, std::memory_order_relaxed);
985 }
RemoveHold()986 void RemoveHold() override { MaybeFinish(/*from_reaction=*/false); }
987
988 private:
989 friend class ClientCallbackWriterFactory<Request>;
990
991 template <class Response>
ClientCallbackWriterImpl(grpc::internal::Call call,grpc::ClientContext * context,Response * response,ClientWriteReactor<Request> * reactor)992 ClientCallbackWriterImpl(grpc::internal::Call call,
993 grpc::ClientContext* context, Response* response,
994 ClientWriteReactor<Request>* reactor)
995 : context_(context),
996 call_(call),
997 reactor_(reactor),
998 start_corked_(context_->initial_metadata_corked_),
999 corked_write_needed_(start_corked_) {
1000 this->BindReactor(reactor);
1001
1002 // Set up the unchanging parts of the start and write tags and ops.
1003 start_tag_.Set(
1004 call_.call(),
1005 [this](bool ok) {
1006 reactor_->OnReadInitialMetadataDone(
1007 ok && !reactor_->InternalTrailersOnly(call_.call()));
1008 MaybeFinish(/*from_reaction=*/true);
1009 },
1010 &start_ops_, /*can_inline=*/false);
1011 start_ops_.RecvInitialMetadata(context_);
1012 start_ops_.set_core_cq_tag(&start_tag_);
1013
1014 write_tag_.Set(
1015 call_.call(),
1016 [this](bool ok) {
1017 reactor_->OnWriteDone(ok);
1018 MaybeFinish(/*from_reaction=*/true);
1019 },
1020 &write_ops_, /*can_inline=*/false);
1021 write_ops_.set_core_cq_tag(&write_tag_);
1022
1023 // Also set up the Finish tag and op set.
1024 finish_ops_.RecvMessage(response);
1025 finish_ops_.AllowNoMessage();
1026 finish_tag_.Set(
1027 call_.call(),
1028 [this](bool /*ok*/) { MaybeFinish(/*from_reaction=*/true); },
1029 &finish_ops_,
1030 /*can_inline=*/false);
1031 finish_ops_.ClientRecvStatus(context_, &finish_status_);
1032 finish_ops_.set_core_cq_tag(&finish_tag_);
1033 }
1034
1035 // MaybeFinish behaves as in ClientCallbackReaderWriterImpl.
MaybeFinish(bool from_reaction)1036 void MaybeFinish(bool from_reaction) {
1037 if (GPR_UNLIKELY(callbacks_outstanding_.fetch_sub(
1038 1, std::memory_order_acq_rel) == 1)) {
1039 grpc::Status s = std::move(finish_status_);
1040 auto* reactor = reactor_;
1041 auto* call = call_.call();
1042 this->~ClientCallbackWriterImpl();
1043 if (GPR_LIKELY(from_reaction)) {
1044 grpc_call_unref(call);
1045 reactor->OnDone(s);
1046 } else {
1047 grpc_call_run_in_event_engine(
1048 call, [reactor, s = std::move(s)]() { reactor->OnDone(s); });
1049 grpc_call_unref(call);
1050 }
1051 }
1052 }
1053
1054 grpc::ClientContext* const context_;
1055 grpc::internal::Call call_;
1056 ClientWriteReactor<Request>* const reactor_;
1057
1058 grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata,
1059 grpc::internal::CallOpRecvInitialMetadata>
1060 start_ops_;
1061 grpc::internal::CallbackWithSuccessTag start_tag_;
1062 const bool start_corked_;
1063 bool corked_write_needed_; // no lock needed since only accessed in
1064 // Write/WritesDone which cannot be concurrent
1065
1066 grpc::internal::CallOpSet<grpc::internal::CallOpGenericRecvMessage,
1067 grpc::internal::CallOpClientRecvStatus>
1068 finish_ops_;
1069 grpc::internal::CallbackWithSuccessTag finish_tag_;
1070 grpc::Status finish_status_;
1071
1072 grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata,
1073 grpc::internal::CallOpSendMessage,
1074 grpc::internal::CallOpClientSendClose>
1075 write_ops_;
1076 grpc::internal::CallbackWithSuccessTag write_tag_;
1077
1078 grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata,
1079 grpc::internal::CallOpClientSendClose>
1080 writes_done_ops_;
1081 grpc::internal::CallbackWithSuccessTag writes_done_tag_;
1082
1083 struct StartCallBacklog {
1084 bool write_ops = false;
1085 bool writes_done_ops = false;
1086 };
1087 StartCallBacklog backlog_ ABSL_GUARDED_BY(start_mu_);
1088
1089 // Minimum of 3 callbacks to pre-register for start ops, StartCall, and finish
1090 std::atomic<intptr_t> callbacks_outstanding_{3};
1091 std::atomic_bool started_{false};
1092 grpc::internal::Mutex start_mu_;
1093 };
1094
1095 template <class Request>
1096 class ClientCallbackWriterFactory {
1097 public:
1098 template <class Response>
Create(grpc::ChannelInterface * channel,const grpc::internal::RpcMethod & method,grpc::ClientContext * context,Response * response,ClientWriteReactor<Request> * reactor)1099 static void Create(grpc::ChannelInterface* channel,
1100 const grpc::internal::RpcMethod& method,
1101 grpc::ClientContext* context, Response* response,
1102 ClientWriteReactor<Request>* reactor) {
1103 grpc::internal::Call call =
1104 channel->CreateCall(method, context, channel->CallbackCQ());
1105
1106 grpc_call_ref(call.call());
1107 new (grpc_call_arena_alloc(call.call(),
1108 sizeof(ClientCallbackWriterImpl<Request>)))
1109 ClientCallbackWriterImpl<Request>(call, context, response, reactor);
1110 }
1111 };
1112
1113 class ClientCallbackUnaryImpl final : public ClientCallbackUnary {
1114 public:
1115 // always allocated against a call arena, no memory free required
delete(void *,std::size_t size)1116 static void operator delete(void* /*ptr*/, std::size_t size) {
1117 ABSL_CHECK_EQ(size, sizeof(ClientCallbackUnaryImpl));
1118 }
1119
1120 // This operator should never be called as the memory should be freed as part
1121 // of the arena destruction. It only exists to provide a matching operator
1122 // delete to the operator new so that some compilers will not complain (see
1123 // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this
1124 // there are no tests catching the compiler warning.
delete(void *,void *)1125 static void operator delete(void*, void*) { ABSL_CHECK(false); }
1126
StartCall()1127 void StartCall() override {
1128 // This call initiates two batches, each with a callback
1129 // 1. Send initial metadata + write + writes done + recv initial metadata
1130 // 2. Read message, recv trailing metadata
1131
1132 start_tag_.Set(
1133 call_.call(),
1134 [this](bool ok) {
1135 reactor_->OnReadInitialMetadataDone(
1136 ok && !reactor_->InternalTrailersOnly(call_.call()));
1137 MaybeFinish();
1138 },
1139 &start_ops_, /*can_inline=*/false);
1140 start_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
1141 context_->initial_metadata_flags());
1142 start_ops_.RecvInitialMetadata(context_);
1143 start_ops_.set_core_cq_tag(&start_tag_);
1144 call_.PerformOps(&start_ops_);
1145
1146 finish_tag_.Set(
1147 call_.call(), [this](bool /*ok*/) { MaybeFinish(); }, &finish_ops_,
1148 /*can_inline=*/false);
1149 finish_ops_.ClientRecvStatus(context_, &finish_status_);
1150 finish_ops_.set_core_cq_tag(&finish_tag_);
1151 call_.PerformOps(&finish_ops_);
1152 }
1153
1154 private:
1155 friend class ClientCallbackUnaryFactory;
1156
1157 template <class Request, class Response>
ClientCallbackUnaryImpl(grpc::internal::Call call,grpc::ClientContext * context,Request * request,Response * response,ClientUnaryReactor * reactor)1158 ClientCallbackUnaryImpl(grpc::internal::Call call,
1159 grpc::ClientContext* context, Request* request,
1160 Response* response, ClientUnaryReactor* reactor)
1161 : context_(context), call_(call), reactor_(reactor) {
1162 this->BindReactor(reactor);
1163 // TODO(vjpai): don't assert
1164 ABSL_CHECK(start_ops_.SendMessagePtr(request).ok());
1165 start_ops_.ClientSendClose();
1166 finish_ops_.RecvMessage(response);
1167 finish_ops_.AllowNoMessage();
1168 }
1169
1170 // In the unary case, MaybeFinish is only ever invoked from a
1171 // library-initiated reaction, so it will just directly call OnDone if this is
1172 // the last reaction for this RPC.
MaybeFinish()1173 void MaybeFinish() {
1174 if (GPR_UNLIKELY(callbacks_outstanding_.fetch_sub(
1175 1, std::memory_order_acq_rel) == 1)) {
1176 grpc::Status s = std::move(finish_status_);
1177 auto* reactor = reactor_;
1178 auto* call = call_.call();
1179 this->~ClientCallbackUnaryImpl();
1180 grpc_call_unref(call);
1181 reactor->OnDone(s);
1182 }
1183 }
1184
1185 grpc::ClientContext* const context_;
1186 grpc::internal::Call call_;
1187 ClientUnaryReactor* const reactor_;
1188
1189 grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata,
1190 grpc::internal::CallOpSendMessage,
1191 grpc::internal::CallOpClientSendClose,
1192 grpc::internal::CallOpRecvInitialMetadata>
1193 start_ops_;
1194 grpc::internal::CallbackWithSuccessTag start_tag_;
1195
1196 grpc::internal::CallOpSet<grpc::internal::CallOpGenericRecvMessage,
1197 grpc::internal::CallOpClientRecvStatus>
1198 finish_ops_;
1199 grpc::internal::CallbackWithSuccessTag finish_tag_;
1200 grpc::Status finish_status_;
1201
1202 // This call will have 2 callbacks: start and finish
1203 std::atomic<intptr_t> callbacks_outstanding_{2};
1204 };
1205
1206 class ClientCallbackUnaryFactory {
1207 public:
1208 template <class Request, class Response, class BaseRequest = Request,
1209 class BaseResponse = Response>
Create(grpc::ChannelInterface * channel,const grpc::internal::RpcMethod & method,grpc::ClientContext * context,const Request * request,Response * response,ClientUnaryReactor * reactor)1210 static void Create(grpc::ChannelInterface* channel,
1211 const grpc::internal::RpcMethod& method,
1212 grpc::ClientContext* context, const Request* request,
1213 Response* response, ClientUnaryReactor* reactor) {
1214 grpc::internal::Call call =
1215 channel->CreateCall(method, context, channel->CallbackCQ());
1216
1217 grpc_call_ref(call.call());
1218
1219 new (grpc_call_arena_alloc(call.call(), sizeof(ClientCallbackUnaryImpl)))
1220 ClientCallbackUnaryImpl(call, context,
1221 static_cast<const BaseRequest*>(request),
1222 static_cast<BaseResponse*>(response), reactor);
1223 }
1224 };
1225
1226 } // namespace internal
1227 } // namespace grpc
1228
1229 #endif // GRPCPP_SUPPORT_CLIENT_CALLBACK_H
1230