• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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