| 
					
				 | 
			
			
				@@ -22,6 +22,7 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 #include <functional> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 #include <grpcpp/impl/codegen/call.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include <grpcpp/impl/codegen/call_op_set.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 #include <grpcpp/impl/codegen/callback_common.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 #include <grpcpp/impl/codegen/channel_interface.h> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 #include <grpcpp/impl/codegen/config.h> 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -88,6 +89,646 @@ class CallbackUnaryCallImpl { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     call.PerformOps(ops); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}  // namespace internal 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+namespace experimental { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// Forward declarations 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+template <class Request, class Response> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class ClientBidiReactor; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+template <class Response> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class ClientReadReactor; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+template <class Request> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class ClientWriteReactor; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// NOTE: The streaming objects are not actually implemented in the public API. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//       These interfaces are provided for mocking only. Typical applications 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+//       will interact exclusively with the reactors that they define. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+template <class Request, class Response> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class ClientCallbackReaderWriter { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ public: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  virtual ~ClientCallbackReaderWriter() {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  virtual void StartCall() = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  virtual void Write(const Request* req, WriteOptions options) = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  virtual void WritesDone() = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  virtual void Read(Response* resp) = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ protected: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  void BindReactor(ClientBidiReactor<Request, Response>* reactor) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    reactor->BindStream(this); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+template <class Response> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class ClientCallbackReader { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ public: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  virtual ~ClientCallbackReader() {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  virtual void StartCall() = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  virtual void Read(Response* resp) = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ protected: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  void BindReactor(ClientReadReactor<Response>* reactor) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    reactor->BindReader(this); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+template <class Request> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class ClientCallbackWriter { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ public: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  virtual ~ClientCallbackWriter() {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  virtual void StartCall() = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  void Write(const Request* req) { Write(req, WriteOptions()); } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  virtual void Write(const Request* req, WriteOptions options) = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  void WriteLast(const Request* req, WriteOptions options) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    Write(req, options.set_last_message()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  virtual void WritesDone() = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ protected: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  void BindReactor(ClientWriteReactor<Request>* reactor) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    reactor->BindWriter(this); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// The user must implement this reactor interface with reactions to each event 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// type that gets called by the library. An empty reaction is provided by 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// default 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+template <class Request, class Response> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class ClientBidiReactor { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ public: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  virtual ~ClientBidiReactor() {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  virtual void OnDone(const Status& s) {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  virtual void OnReadInitialMetadataDone(bool ok) {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  virtual void OnReadDone(bool ok) {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  virtual void OnWriteDone(bool ok) {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  virtual void OnWritesDoneDone(bool ok) {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  void StartCall() { stream_->StartCall(); } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  void StartRead(Response* resp) { stream_->Read(resp); } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  void StartWrite(const Request* req) { StartWrite(req, WriteOptions()); } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  void StartWrite(const Request* req, WriteOptions options) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    stream_->Write(req, std::move(options)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  void StartWriteLast(const Request* req, WriteOptions options) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    StartWrite(req, std::move(options.set_last_message())); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  void StartWritesDone() { stream_->WritesDone(); } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ private: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  friend class ClientCallbackReaderWriter<Request, Response>; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  void BindStream(ClientCallbackReaderWriter<Request, Response>* stream) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    stream_ = stream; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ClientCallbackReaderWriter<Request, Response>* stream_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+template <class Response> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class ClientReadReactor { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ public: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  virtual ~ClientReadReactor() {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  virtual void OnDone(const Status& s) {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  virtual void OnReadInitialMetadataDone(bool ok) {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  virtual void OnReadDone(bool ok) {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  void StartCall() { reader_->StartCall(); } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  void StartRead(Response* resp) { reader_->Read(resp); } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ private: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  friend class ClientCallbackReader<Response>; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  void BindReader(ClientCallbackReader<Response>* reader) { reader_ = reader; } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ClientCallbackReader<Response>* reader_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+template <class Request> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class ClientWriteReactor { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ public: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  virtual ~ClientWriteReactor() {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  virtual void OnDone(const Status& s) {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  virtual void OnReadInitialMetadataDone(bool ok) {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  virtual void OnWriteDone(bool ok) {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  virtual void OnWritesDoneDone(bool ok) {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  void StartCall() { writer_->StartCall(); } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  void StartWrite(const Request* req) { StartWrite(req, WriteOptions()); } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  void StartWrite(const Request* req, WriteOptions options) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    writer_->Write(req, std::move(options)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  void StartWriteLast(const Request* req, WriteOptions options) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    StartWrite(req, std::move(options.set_last_message())); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  void StartWritesDone() { writer_->WritesDone(); } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ private: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  friend class ClientCallbackWriter<Request>; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  void BindWriter(ClientCallbackWriter<Request>* writer) { writer_ = writer; } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ClientCallbackWriter<Request>* writer_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}  // namespace experimental 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+namespace internal { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// Forward declare factory classes for friendship 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+template <class Request, class Response> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class ClientCallbackReaderWriterFactory; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+template <class Response> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class ClientCallbackReaderFactory; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+template <class Request> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class ClientCallbackWriterFactory; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+template <class Request, class Response> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class ClientCallbackReaderWriterImpl 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    : public ::grpc::experimental::ClientCallbackReaderWriter<Request, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                                              Response> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ public: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // always allocated against a call arena, no memory free required 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  static void operator delete(void* ptr, std::size_t size) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    assert(size == sizeof(ClientCallbackReaderWriterImpl)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // This operator should never be called as the memory should be freed as part 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // of the arena destruction. It only exists to provide a matching operator 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // delete to the operator new so that some compilers will not complain (see 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // there are no tests catching the compiler warning. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  static void operator delete(void*, void*) { assert(0); } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  void MaybeFinish() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (--callbacks_outstanding_ == 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      reactor_->OnDone(finish_status_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      auto* call = call_.call(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      this->~ClientCallbackReaderWriterImpl(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      g_core_codegen_interface->grpc_call_unref(call); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  void StartCall() override { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // This call initiates two batches, plus any backlog, each with a callback 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // 1. Send initial metadata (unless corked) + recv initial metadata 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // 2. Any read backlog 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // 3. Recv trailing metadata, on_completion callback 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // 4. Any write backlog 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    started_ = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    start_tag_.Set(call_.call(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                   [this](bool ok) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                     reactor_->OnReadInitialMetadataDone(ok); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                     MaybeFinish(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                   }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                   &start_ops_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (!start_corked_) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      start_ops_.SendInitialMetadata(&context_->send_initial_metadata_, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                     context_->initial_metadata_flags()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    start_ops_.RecvInitialMetadata(context_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    start_ops_.set_core_cq_tag(&start_tag_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    call_.PerformOps(&start_ops_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // Also set up the read and write tags so that they don't have to be set up 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // each time 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    write_tag_.Set(call_.call(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                   [this](bool ok) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                     reactor_->OnWriteDone(ok); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                     MaybeFinish(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                   }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                   &write_ops_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    write_ops_.set_core_cq_tag(&write_tag_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    read_tag_.Set(call_.call(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  [this](bool ok) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    reactor_->OnReadDone(ok); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    MaybeFinish(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  &read_ops_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    read_ops_.set_core_cq_tag(&read_tag_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (read_ops_at_start_) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      call_.PerformOps(&read_ops_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    finish_tag_.Set(call_.call(), [this](bool ok) { MaybeFinish(); }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    &finish_ops_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    finish_ops_.ClientRecvStatus(context_, &finish_status_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    finish_ops_.set_core_cq_tag(&finish_tag_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    call_.PerformOps(&finish_ops_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (write_ops_at_start_) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      call_.PerformOps(&write_ops_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (writes_done_ops_at_start_) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      call_.PerformOps(&writes_done_ops_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  void Read(Response* msg) override { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    read_ops_.RecvMessage(msg); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    callbacks_outstanding_++; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (started_) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      call_.PerformOps(&read_ops_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      read_ops_at_start_ = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  void Write(const Request* msg, WriteOptions options) override { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (start_corked_) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      write_ops_.SendInitialMetadata(&context_->send_initial_metadata_, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                     context_->initial_metadata_flags()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      start_corked_ = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // TODO(vjpai): don't assert 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    GPR_CODEGEN_ASSERT(write_ops_.SendMessage(*msg).ok()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (options.is_last_message()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      options.set_buffer_hint(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      write_ops_.ClientSendClose(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    callbacks_outstanding_++; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (started_) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      call_.PerformOps(&write_ops_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      write_ops_at_start_ = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  void WritesDone() override { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (start_corked_) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      writes_done_ops_.SendInitialMetadata(&context_->send_initial_metadata_, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                           context_->initial_metadata_flags()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      start_corked_ = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    writes_done_ops_.ClientSendClose(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    writes_done_tag_.Set(call_.call(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                         [this](bool ok) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           reactor_->OnWritesDoneDone(ok); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           MaybeFinish(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                         }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                         &writes_done_ops_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    writes_done_ops_.set_core_cq_tag(&writes_done_tag_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    callbacks_outstanding_++; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (started_) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      call_.PerformOps(&writes_done_ops_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      writes_done_ops_at_start_ = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ private: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  friend class ClientCallbackReaderWriterFactory<Request, Response>; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ClientCallbackReaderWriterImpl( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      Call call, ClientContext* context, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      ::grpc::experimental::ClientBidiReactor<Request, Response>* reactor) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      : context_(context), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        call_(call), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        reactor_(reactor), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        start_corked_(context_->initial_metadata_corked_) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    this->BindReactor(reactor); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ClientContext* context_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  Call call_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ::grpc::experimental::ClientBidiReactor<Request, Response>* reactor_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CallOpSet<CallOpSendInitialMetadata, CallOpRecvInitialMetadata> start_ops_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CallbackWithSuccessTag start_tag_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  bool start_corked_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CallOpSet<CallOpClientRecvStatus> finish_ops_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CallbackWithSuccessTag finish_tag_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  Status finish_status_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage, CallOpClientSendClose> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      write_ops_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CallbackWithSuccessTag write_tag_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  bool write_ops_at_start_{false}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CallOpSet<CallOpSendInitialMetadata, CallOpClientSendClose> writes_done_ops_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CallbackWithSuccessTag writes_done_tag_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  bool writes_done_ops_at_start_{false}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CallOpSet<CallOpRecvMessage<Response>> read_ops_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CallbackWithSuccessTag read_tag_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  bool read_ops_at_start_{false}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // Minimum of 2 outstanding callbacks to pre-register for start and finish 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  std::atomic_int callbacks_outstanding_{2}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  bool started_{false}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+template <class Request, class Response> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class ClientCallbackReaderWriterFactory { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ public: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  static void Create( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      ChannelInterface* channel, const ::grpc::internal::RpcMethod& method, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      ClientContext* context, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      ::grpc::experimental::ClientBidiReactor<Request, Response>* reactor) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    Call call = channel->CreateCall(method, context, channel->CallbackCQ()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    g_core_codegen_interface->grpc_call_ref(call.call()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    new (g_core_codegen_interface->grpc_call_arena_alloc( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        call.call(), sizeof(ClientCallbackReaderWriterImpl<Request, Response>))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ClientCallbackReaderWriterImpl<Request, Response>(call, context, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                                          reactor); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+template <class Response> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class ClientCallbackReaderImpl 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    : public ::grpc::experimental::ClientCallbackReader<Response> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ public: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // always allocated against a call arena, no memory free required 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  static void operator delete(void* ptr, std::size_t size) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    assert(size == sizeof(ClientCallbackReaderImpl)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // This operator should never be called as the memory should be freed as part 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // of the arena destruction. It only exists to provide a matching operator 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // delete to the operator new so that some compilers will not complain (see 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // there are no tests catching the compiler warning. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  static void operator delete(void*, void*) { assert(0); } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  void MaybeFinish() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (--callbacks_outstanding_ == 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      reactor_->OnDone(finish_status_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      auto* call = call_.call(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      this->~ClientCallbackReaderImpl(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      g_core_codegen_interface->grpc_call_unref(call); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  void StartCall() override { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // This call initiates two batches, plus any backlog, each with a callback 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // 1. Send initial metadata (unless corked) + recv initial metadata 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // 2. Any backlog 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // 3. Recv trailing metadata, on_completion callback 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    started_ = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    start_tag_.Set(call_.call(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                   [this](bool ok) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                     reactor_->OnReadInitialMetadataDone(ok); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                     MaybeFinish(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                   }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                   &start_ops_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    start_ops_.SendInitialMetadata(&context_->send_initial_metadata_, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                   context_->initial_metadata_flags()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    start_ops_.RecvInitialMetadata(context_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    start_ops_.set_core_cq_tag(&start_tag_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    call_.PerformOps(&start_ops_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // Also set up the read tag so it doesn't have to be set up each time 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    read_tag_.Set(call_.call(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  [this](bool ok) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    reactor_->OnReadDone(ok); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    MaybeFinish(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  &read_ops_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    read_ops_.set_core_cq_tag(&read_tag_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (read_ops_at_start_) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      call_.PerformOps(&read_ops_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    finish_tag_.Set(call_.call(), [this](bool ok) { MaybeFinish(); }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    &finish_ops_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    finish_ops_.ClientRecvStatus(context_, &finish_status_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    finish_ops_.set_core_cq_tag(&finish_tag_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    call_.PerformOps(&finish_ops_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  void Read(Response* msg) override { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    read_ops_.RecvMessage(msg); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    callbacks_outstanding_++; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (started_) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      call_.PerformOps(&read_ops_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      read_ops_at_start_ = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ private: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  friend class ClientCallbackReaderFactory<Response>; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  template <class Request> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ClientCallbackReaderImpl( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      Call call, ClientContext* context, Request* request, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      ::grpc::experimental::ClientReadReactor<Response>* reactor) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      : context_(context), call_(call), reactor_(reactor) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    this->BindReactor(reactor); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // TODO(vjpai): don't assert 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    GPR_CODEGEN_ASSERT(start_ops_.SendMessage(*request).ok()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    start_ops_.ClientSendClose(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ClientContext* context_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  Call call_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ::grpc::experimental::ClientReadReactor<Response>* reactor_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage, CallOpClientSendClose, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            CallOpRecvInitialMetadata> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      start_ops_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CallbackWithSuccessTag start_tag_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CallOpSet<CallOpClientRecvStatus> finish_ops_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CallbackWithSuccessTag finish_tag_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  Status finish_status_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CallOpSet<CallOpRecvMessage<Response>> read_ops_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CallbackWithSuccessTag read_tag_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  bool read_ops_at_start_{false}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // Minimum of 2 outstanding callbacks to pre-register for start and finish 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  std::atomic_int callbacks_outstanding_{2}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  bool started_{false}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+template <class Response> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class ClientCallbackReaderFactory { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ public: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  template <class Request> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  static void Create( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      ChannelInterface* channel, const ::grpc::internal::RpcMethod& method, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      ClientContext* context, const Request* request, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      ::grpc::experimental::ClientReadReactor<Response>* reactor) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    Call call = channel->CreateCall(method, context, channel->CallbackCQ()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    g_core_codegen_interface->grpc_call_ref(call.call()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    new (g_core_codegen_interface->grpc_call_arena_alloc( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        call.call(), sizeof(ClientCallbackReaderImpl<Response>))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ClientCallbackReaderImpl<Response>(call, context, request, reactor); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+template <class Request> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class ClientCallbackWriterImpl 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    : public ::grpc::experimental::ClientCallbackWriter<Request> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ public: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // always allocated against a call arena, no memory free required 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  static void operator delete(void* ptr, std::size_t size) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    assert(size == sizeof(ClientCallbackWriterImpl)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // This operator should never be called as the memory should be freed as part 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // of the arena destruction. It only exists to provide a matching operator 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // delete to the operator new so that some compilers will not complain (see 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // there are no tests catching the compiler warning. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  static void operator delete(void*, void*) { assert(0); } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  void MaybeFinish() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (--callbacks_outstanding_ == 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      reactor_->OnDone(finish_status_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      auto* call = call_.call(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      this->~ClientCallbackWriterImpl(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      g_core_codegen_interface->grpc_call_unref(call); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  void StartCall() override { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // This call initiates two batches, plus any backlog, each with a callback 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // 1. Send initial metadata (unless corked) + recv initial metadata 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // 2. Recv trailing metadata, on_completion callback 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // 3. Any backlog 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    started_ = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    start_tag_.Set(call_.call(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                   [this](bool ok) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                     reactor_->OnReadInitialMetadataDone(ok); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                     MaybeFinish(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                   }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                   &start_ops_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (!start_corked_) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      start_ops_.SendInitialMetadata(&context_->send_initial_metadata_, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                     context_->initial_metadata_flags()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    start_ops_.RecvInitialMetadata(context_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    start_ops_.set_core_cq_tag(&start_tag_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    call_.PerformOps(&start_ops_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // Also set up the read and write tags so that they don't have to be set up 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // each time 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    write_tag_.Set(call_.call(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                   [this](bool ok) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                     reactor_->OnWriteDone(ok); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                     MaybeFinish(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                   }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                   &write_ops_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    write_ops_.set_core_cq_tag(&write_tag_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    finish_tag_.Set(call_.call(), [this](bool ok) { MaybeFinish(); }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    &finish_ops_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    finish_ops_.ClientRecvStatus(context_, &finish_status_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    finish_ops_.set_core_cq_tag(&finish_tag_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    call_.PerformOps(&finish_ops_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (write_ops_at_start_) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      call_.PerformOps(&write_ops_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (writes_done_ops_at_start_) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      call_.PerformOps(&writes_done_ops_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  void Write(const Request* msg, WriteOptions options) override { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (start_corked_) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      write_ops_.SendInitialMetadata(&context_->send_initial_metadata_, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                     context_->initial_metadata_flags()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      start_corked_ = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // TODO(vjpai): don't assert 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    GPR_CODEGEN_ASSERT(write_ops_.SendMessage(*msg).ok()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (options.is_last_message()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      options.set_buffer_hint(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      write_ops_.ClientSendClose(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    callbacks_outstanding_++; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (started_) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      call_.PerformOps(&write_ops_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      write_ops_at_start_ = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  void WritesDone() override { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (start_corked_) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      writes_done_ops_.SendInitialMetadata(&context_->send_initial_metadata_, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                           context_->initial_metadata_flags()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      start_corked_ = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    writes_done_ops_.ClientSendClose(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    writes_done_tag_.Set(call_.call(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                         [this](bool ok) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           reactor_->OnWritesDoneDone(ok); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           MaybeFinish(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                         }, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                         &writes_done_ops_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    writes_done_ops_.set_core_cq_tag(&writes_done_tag_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    callbacks_outstanding_++; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (started_) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      call_.PerformOps(&writes_done_ops_); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      writes_done_ops_at_start_ = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ private: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  friend class ClientCallbackWriterFactory<Request>; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  template <class Response> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ClientCallbackWriterImpl( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      Call call, ClientContext* context, Response* response, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      ::grpc::experimental::ClientWriteReactor<Request>* reactor) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      : context_(context), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        call_(call), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        reactor_(reactor), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        start_corked_(context_->initial_metadata_corked_) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    this->BindReactor(reactor); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    finish_ops_.RecvMessage(response); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    finish_ops_.AllowNoMessage(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ClientContext* context_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  Call call_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ::grpc::experimental::ClientWriteReactor<Request>* reactor_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CallOpSet<CallOpSendInitialMetadata, CallOpRecvInitialMetadata> start_ops_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CallbackWithSuccessTag start_tag_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  bool start_corked_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CallOpSet<CallOpGenericRecvMessage, CallOpClientRecvStatus> finish_ops_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CallbackWithSuccessTag finish_tag_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  Status finish_status_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage, CallOpClientSendClose> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      write_ops_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CallbackWithSuccessTag write_tag_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  bool write_ops_at_start_{false}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CallOpSet<CallOpSendInitialMetadata, CallOpClientSendClose> writes_done_ops_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CallbackWithSuccessTag writes_done_tag_; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  bool writes_done_ops_at_start_{false}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // Minimum of 2 outstanding callbacks to pre-register for start and finish 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  std::atomic_int callbacks_outstanding_{2}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  bool started_{false}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+template <class Request> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class ClientCallbackWriterFactory { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ public: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  template <class Response> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  static void Create( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      ChannelInterface* channel, const ::grpc::internal::RpcMethod& method, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      ClientContext* context, Response* response, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      ::grpc::experimental::ClientWriteReactor<Request>* reactor) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    Call call = channel->CreateCall(method, context, channel->CallbackCQ()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    g_core_codegen_interface->grpc_call_ref(call.call()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    new (g_core_codegen_interface->grpc_call_arena_alloc( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        call.call(), sizeof(ClientCallbackWriterImpl<Request>))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ClientCallbackWriterImpl<Request>(call, context, response, reactor); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 }  // namespace internal 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 }  // namespace grpc 
			 |