/* * * Copyright 2018 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #ifndef GRPCPP_IMPL_CODEGEN_SERVER_CALLBACK_H #define GRPCPP_IMPL_CODEGEN_SERVER_CALLBACK_H #include <atomic> #include <functional> #include <type_traits> #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/config.h> #include <grpcpp/impl/codegen/core_codegen_interface.h> #include <grpcpp/impl/codegen/server_context.h> #include <grpcpp/impl/codegen/server_interface.h> #include <grpcpp/impl/codegen/status.h> namespace grpc { // Declare base class of all reactors as internal namespace internal { class ServerReactor { public: virtual ~ServerReactor() = default; virtual void OnDone() {} virtual void OnCancel() {} }; } // namespace internal namespace experimental { // Forward declarations template <class Request, class Response> class ServerReadReactor; template <class Request, class Response> class ServerWriteReactor; template <class Request, class Response> class ServerBidiReactor; // For unary RPCs, the exposed controller class is only an interface // and the actual implementation is an internal class. class ServerCallbackRpcController { public: virtual ~ServerCallbackRpcController() = default; // The method handler must call this function when it is done so that // the library knows to free its resources virtual void Finish(Status s) = 0; // Allow the method handler to push out the initial metadata before // the response and status are ready virtual void SendInitialMetadata(std::function<void(bool)>) = 0; }; // NOTE: The actual streaming object classes are provided // as API only to support mocking. There are no implementations of // these class interfaces in the API. template <class Request> class ServerCallbackReader { public: virtual ~ServerCallbackReader() {} virtual void Finish(Status s) = 0; virtual void SendInitialMetadata() = 0; virtual void Read(Request* msg) = 0; protected: template <class Response> void BindReactor(ServerReadReactor<Request, Response>* reactor) { reactor->BindReader(this); } }; template <class Response> class ServerCallbackWriter { public: virtual ~ServerCallbackWriter() {} virtual void Finish(Status s) = 0; virtual void SendInitialMetadata() = 0; virtual void Write(const Response* msg, WriteOptions options) = 0; virtual void WriteAndFinish(const Response* msg, WriteOptions options, Status s) { // Default implementation that can/should be overridden Write(msg, std::move(options)); Finish(std::move(s)); }; protected: template <class Request> void BindReactor(ServerWriteReactor<Request, Response>* reactor) { reactor->BindWriter(this); } }; template <class Request, class Response> class ServerCallbackReaderWriter { public: virtual ~ServerCallbackReaderWriter() {} virtual void Finish(Status s) = 0; virtual void SendInitialMetadata() = 0; virtual void Read(Request* msg) = 0; virtual void Write(const Response* msg, WriteOptions options) = 0; virtual void WriteAndFinish(const Response* msg, WriteOptions options, Status s) { // Default implementation that can/should be overridden Write(msg, std::move(options)); Finish(std::move(s)); }; protected: void BindReactor(ServerBidiReactor<Request, Response>* reactor) { reactor->BindStream(this); } }; // The following classes are reactors that are to be implemented // by the user, returned as the result of the method handler for // a callback method, and activated by the call to OnStarted template <class Request, class Response> class ServerBidiReactor : public internal::ServerReactor { public: ~ServerBidiReactor() = default; virtual void OnStarted(ServerContext*) {} virtual void OnSendInitialMetadataDone(bool ok) {} virtual void OnReadDone(bool ok) {} virtual void OnWriteDone(bool ok) {} void StartSendInitialMetadata() { stream_->SendInitialMetadata(); } void StartRead(Request* msg) { stream_->Read(msg); } void StartWrite(const Response* msg) { StartWrite(msg, WriteOptions()); } void StartWrite(const Response* msg, WriteOptions options) { stream_->Write(msg, std::move(options)); } void StartWriteAndFinish(const Response* msg, WriteOptions options, Status s) { stream_->WriteAndFinish(msg, std::move(options), std::move(s)); } void StartWriteLast(const Response* msg, WriteOptions options) { StartWrite(msg, std::move(options.set_last_message())); } void Finish(Status s) { stream_->Finish(std::move(s)); } private: friend class ServerCallbackReaderWriter<Request, Response>; void BindStream(ServerCallbackReaderWriter<Request, Response>* stream) { stream_ = stream; } ServerCallbackReaderWriter<Request, Response>* stream_; }; template <class Request, class Response> class ServerReadReactor : public internal::ServerReactor { public: ~ServerReadReactor() = default; virtual void OnStarted(ServerContext*, Response* resp) {} virtual void OnSendInitialMetadataDone(bool ok) {} virtual void OnReadDone(bool ok) {} void StartSendInitialMetadata() { reader_->SendInitialMetadata(); } void StartRead(Request* msg) { reader_->Read(msg); } void Finish(Status s) { reader_->Finish(std::move(s)); } private: friend class ServerCallbackReader<Request>; void BindReader(ServerCallbackReader<Request>* reader) { reader_ = reader; } ServerCallbackReader<Request>* reader_; }; template <class Request, class Response> class ServerWriteReactor : public internal::ServerReactor { public: ~ServerWriteReactor() = default; virtual void OnStarted(ServerContext*, const Request* req) {} virtual void OnSendInitialMetadataDone(bool ok) {} virtual void OnWriteDone(bool ok) {} void StartSendInitialMetadata() { writer_->SendInitialMetadata(); } void StartWrite(const Response* msg) { StartWrite(msg, WriteOptions()); } void StartWrite(const Response* msg, WriteOptions options) { writer_->Write(msg, std::move(options)); } void StartWriteAndFinish(const Response* msg, WriteOptions options, Status s) { writer_->WriteAndFinish(msg, std::move(options), std::move(s)); } void StartWriteLast(const Response* msg, WriteOptions options) { StartWrite(msg, std::move(options.set_last_message())); } void Finish(Status s) { writer_->Finish(std::move(s)); } private: friend class ServerCallbackWriter<Response>; void BindWriter(ServerCallbackWriter<Response>* writer) { writer_ = writer; } ServerCallbackWriter<Response>* writer_; }; } // namespace experimental namespace internal { template <class Request, class Response> class UnimplementedReadReactor : public experimental::ServerReadReactor<Request, Response> { public: void OnDone() override { delete this; } void OnStarted(ServerContext*, Response*) override { this->Finish(Status(StatusCode::UNIMPLEMENTED, "")); } }; template <class Request, class Response> class UnimplementedWriteReactor : public experimental::ServerWriteReactor<Request, Response> { public: void OnDone() override { delete this; } void OnStarted(ServerContext*, const Request*) override { this->Finish(Status(StatusCode::UNIMPLEMENTED, "")); } }; template <class Request, class Response> class UnimplementedBidiReactor : public experimental::ServerBidiReactor<Request, Response> { public: void OnDone() override { delete this; } void OnStarted(ServerContext*) override { this->Finish(Status(StatusCode::UNIMPLEMENTED, "")); } }; template <class RequestType, class ResponseType> class CallbackUnaryHandler : public MethodHandler { public: CallbackUnaryHandler( std::function<void(ServerContext*, const RequestType*, ResponseType*, experimental::ServerCallbackRpcController*)> func) : func_(func) {} void RunHandler(const HandlerParameter& param) final { // Arena allocate a controller structure (that includes request/response) g_core_codegen_interface->grpc_call_ref(param.call->call()); auto* controller = new (g_core_codegen_interface->grpc_call_arena_alloc( param.call->call(), sizeof(ServerCallbackRpcControllerImpl))) ServerCallbackRpcControllerImpl( param.server_context, param.call, static_cast<RequestType*>(param.request), std::move(param.call_requester)); Status status = param.status; if (status.ok()) { // Call the actual function handler and expect the user to call finish CatchingCallback(func_, param.server_context, controller->request(), controller->response(), controller); } else { // if deserialization failed, we need to fail the call controller->Finish(status); } } void* Deserialize(grpc_call* call, grpc_byte_buffer* req, Status* status) final { ByteBuffer buf; buf.set_buffer(req); auto* request = new (g_core_codegen_interface->grpc_call_arena_alloc( call, sizeof(RequestType))) RequestType(); *status = SerializationTraits<RequestType>::Deserialize(&buf, request); buf.Release(); if (status->ok()) { return request; } request->~RequestType(); return nullptr; } private: std::function<void(ServerContext*, const RequestType*, ResponseType*, experimental::ServerCallbackRpcController*)> func_; // The implementation class of ServerCallbackRpcController is a private member // of CallbackUnaryHandler since it is never exposed anywhere, and this allows // it to take advantage of CallbackUnaryHandler's friendships. class ServerCallbackRpcControllerImpl : public experimental::ServerCallbackRpcController { public: void Finish(Status s) override { finish_tag_.Set(call_.call(), [this](bool) { MaybeDone(); }, &finish_ops_); if (!ctx_->sent_initial_metadata_) { finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_, ctx_->initial_metadata_flags()); if (ctx_->compression_level_set()) { finish_ops_.set_compression_level(ctx_->compression_level()); } ctx_->sent_initial_metadata_ = true; } // The response is dropped if the status is not OK. if (s.ok()) { finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, finish_ops_.SendMessage(resp_)); } else { finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, s); } finish_ops_.set_core_cq_tag(&finish_tag_); call_.PerformOps(&finish_ops_); } void SendInitialMetadata(std::function<void(bool)> f) override { GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); callbacks_outstanding_++; // TODO(vjpai): Consider taking f as a move-capture if we adopt C++14 // and if performance of this operation matters meta_tag_.Set(call_.call(), [this, f](bool ok) { f(ok); MaybeDone(); }, &meta_ops_); meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_, ctx_->initial_metadata_flags()); if (ctx_->compression_level_set()) { meta_ops_.set_compression_level(ctx_->compression_level()); } ctx_->sent_initial_metadata_ = true; meta_ops_.set_core_cq_tag(&meta_tag_); call_.PerformOps(&meta_ops_); } private: friend class CallbackUnaryHandler<RequestType, ResponseType>; ServerCallbackRpcControllerImpl(ServerContext* ctx, Call* call, const RequestType* req, std::function<void()> call_requester) : ctx_(ctx), call_(*call), req_(req), call_requester_(std::move(call_requester)) { ctx_->BeginCompletionOp(call, [this](bool) { MaybeDone(); }, nullptr); } ~ServerCallbackRpcControllerImpl() { req_->~RequestType(); } const RequestType* request() { return req_; } ResponseType* response() { return &resp_; } void MaybeDone() { if (--callbacks_outstanding_ == 0) { grpc_call* call = call_.call(); auto call_requester = std::move(call_requester_); this->~ServerCallbackRpcControllerImpl(); // explicitly call destructor g_core_codegen_interface->grpc_call_unref(call); call_requester(); } } CallOpSet<CallOpSendInitialMetadata> meta_ops_; CallbackWithSuccessTag meta_tag_; CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage, CallOpServerSendStatus> finish_ops_; CallbackWithSuccessTag finish_tag_; ServerContext* ctx_; Call call_; const RequestType* req_; ResponseType resp_; std::function<void()> call_requester_; std::atomic_int callbacks_outstanding_{ 2}; // reserve for Finish and CompletionOp }; }; template <class RequestType, class ResponseType> class CallbackClientStreamingHandler : public MethodHandler { public: CallbackClientStreamingHandler( std::function< experimental::ServerReadReactor<RequestType, ResponseType>*()> func) : func_(std::move(func)) {} void RunHandler(const HandlerParameter& param) final { // Arena allocate a reader structure (that includes response) g_core_codegen_interface->grpc_call_ref(param.call->call()); experimental::ServerReadReactor<RequestType, ResponseType>* reactor = param.status.ok() ? CatchingReactorCreator< experimental::ServerReadReactor<RequestType, ResponseType>>( func_) : nullptr; if (reactor == nullptr) { // if deserialization or reactor creator failed, we need to fail the call reactor = new UnimplementedReadReactor<RequestType, ResponseType>; } auto* reader = new (g_core_codegen_interface->grpc_call_arena_alloc( param.call->call(), sizeof(ServerCallbackReaderImpl))) ServerCallbackReaderImpl(param.server_context, param.call, std::move(param.call_requester), reactor); reader->BindReactor(reactor); reactor->OnStarted(param.server_context, reader->response()); reader->MaybeDone(); } private: std::function<experimental::ServerReadReactor<RequestType, ResponseType>*()> func_; class ServerCallbackReaderImpl : public experimental::ServerCallbackReader<RequestType> { public: void Finish(Status s) override { finish_tag_.Set(call_.call(), [this](bool) { MaybeDone(); }, &finish_ops_); if (!ctx_->sent_initial_metadata_) { finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_, ctx_->initial_metadata_flags()); if (ctx_->compression_level_set()) { finish_ops_.set_compression_level(ctx_->compression_level()); } ctx_->sent_initial_metadata_ = true; } // The response is dropped if the status is not OK. if (s.ok()) { finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, finish_ops_.SendMessage(resp_)); } else { finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, s); } finish_ops_.set_core_cq_tag(&finish_tag_); call_.PerformOps(&finish_ops_); } void SendInitialMetadata() override { GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); callbacks_outstanding_++; meta_tag_.Set(call_.call(), [this](bool ok) { reactor_->OnSendInitialMetadataDone(ok); MaybeDone(); }, &meta_ops_); meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_, ctx_->initial_metadata_flags()); if (ctx_->compression_level_set()) { meta_ops_.set_compression_level(ctx_->compression_level()); } ctx_->sent_initial_metadata_ = true; meta_ops_.set_core_cq_tag(&meta_tag_); call_.PerformOps(&meta_ops_); } void Read(RequestType* req) override { callbacks_outstanding_++; read_ops_.RecvMessage(req); call_.PerformOps(&read_ops_); } private: friend class CallbackClientStreamingHandler<RequestType, ResponseType>; ServerCallbackReaderImpl( ServerContext* ctx, Call* call, std::function<void()> call_requester, experimental::ServerReadReactor<RequestType, ResponseType>* reactor) : ctx_(ctx), call_(*call), call_requester_(std::move(call_requester)), reactor_(reactor) { ctx_->BeginCompletionOp(call, [this](bool) { MaybeDone(); }, reactor); read_tag_.Set(call_.call(), [this](bool ok) { reactor_->OnReadDone(ok); MaybeDone(); }, &read_ops_); read_ops_.set_core_cq_tag(&read_tag_); } ~ServerCallbackReaderImpl() {} ResponseType* response() { return &resp_; } void MaybeDone() { if (--callbacks_outstanding_ == 0) { reactor_->OnDone(); grpc_call* call = call_.call(); auto call_requester = std::move(call_requester_); this->~ServerCallbackReaderImpl(); // explicitly call destructor g_core_codegen_interface->grpc_call_unref(call); call_requester(); } } CallOpSet<CallOpSendInitialMetadata> meta_ops_; CallbackWithSuccessTag meta_tag_; CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage, CallOpServerSendStatus> finish_ops_; CallbackWithSuccessTag finish_tag_; CallOpSet<CallOpRecvMessage<RequestType>> read_ops_; CallbackWithSuccessTag read_tag_; ServerContext* ctx_; Call call_; ResponseType resp_; std::function<void()> call_requester_; experimental::ServerReadReactor<RequestType, ResponseType>* reactor_; std::atomic_int callbacks_outstanding_{ 3}; // reserve for OnStarted, Finish, and CompletionOp }; }; template <class RequestType, class ResponseType> class CallbackServerStreamingHandler : public MethodHandler { public: CallbackServerStreamingHandler( std::function< experimental::ServerWriteReactor<RequestType, ResponseType>*()> func) : func_(std::move(func)) {} void RunHandler(const HandlerParameter& param) final { // Arena allocate a writer structure g_core_codegen_interface->grpc_call_ref(param.call->call()); experimental::ServerWriteReactor<RequestType, ResponseType>* reactor = param.status.ok() ? CatchingReactorCreator< experimental::ServerWriteReactor<RequestType, ResponseType>>( func_) : nullptr; if (reactor == nullptr) { // if deserialization or reactor creator failed, we need to fail the call reactor = new UnimplementedWriteReactor<RequestType, ResponseType>; } auto* writer = new (g_core_codegen_interface->grpc_call_arena_alloc( param.call->call(), sizeof(ServerCallbackWriterImpl))) ServerCallbackWriterImpl(param.server_context, param.call, static_cast<RequestType*>(param.request), std::move(param.call_requester), reactor); writer->BindReactor(reactor); reactor->OnStarted(param.server_context, writer->request()); writer->MaybeDone(); } void* Deserialize(grpc_call* call, grpc_byte_buffer* req, Status* status) final { ByteBuffer buf; buf.set_buffer(req); auto* request = new (g_core_codegen_interface->grpc_call_arena_alloc( call, sizeof(RequestType))) RequestType(); *status = SerializationTraits<RequestType>::Deserialize(&buf, request); buf.Release(); if (status->ok()) { return request; } request->~RequestType(); return nullptr; } private: std::function<experimental::ServerWriteReactor<RequestType, ResponseType>*()> func_; class ServerCallbackWriterImpl : public experimental::ServerCallbackWriter<ResponseType> { public: void Finish(Status s) override { finish_tag_.Set(call_.call(), [this](bool) { MaybeDone(); }, &finish_ops_); finish_ops_.set_core_cq_tag(&finish_tag_); if (!ctx_->sent_initial_metadata_) { finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_, ctx_->initial_metadata_flags()); if (ctx_->compression_level_set()) { finish_ops_.set_compression_level(ctx_->compression_level()); } ctx_->sent_initial_metadata_ = true; } finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, s); call_.PerformOps(&finish_ops_); } void SendInitialMetadata() override { GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); callbacks_outstanding_++; meta_tag_.Set(call_.call(), [this](bool ok) { reactor_->OnSendInitialMetadataDone(ok); MaybeDone(); }, &meta_ops_); meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_, ctx_->initial_metadata_flags()); if (ctx_->compression_level_set()) { meta_ops_.set_compression_level(ctx_->compression_level()); } ctx_->sent_initial_metadata_ = true; meta_ops_.set_core_cq_tag(&meta_tag_); call_.PerformOps(&meta_ops_); } void Write(const ResponseType* resp, WriteOptions options) override { callbacks_outstanding_++; if (options.is_last_message()) { options.set_buffer_hint(); } if (!ctx_->sent_initial_metadata_) { write_ops_.SendInitialMetadata(&ctx_->initial_metadata_, ctx_->initial_metadata_flags()); if (ctx_->compression_level_set()) { write_ops_.set_compression_level(ctx_->compression_level()); } ctx_->sent_initial_metadata_ = true; } // TODO(vjpai): don't assert GPR_CODEGEN_ASSERT(write_ops_.SendMessage(*resp, options).ok()); call_.PerformOps(&write_ops_); } void WriteAndFinish(const ResponseType* resp, WriteOptions options, Status s) override { // This combines the write into the finish callback // Don't send any message if the status is bad if (s.ok()) { // TODO(vjpai): don't assert GPR_CODEGEN_ASSERT(finish_ops_.SendMessage(*resp, options).ok()); } Finish(std::move(s)); } private: friend class CallbackServerStreamingHandler<RequestType, ResponseType>; ServerCallbackWriterImpl( ServerContext* ctx, Call* call, const RequestType* req, std::function<void()> call_requester, experimental::ServerWriteReactor<RequestType, ResponseType>* reactor) : ctx_(ctx), call_(*call), req_(req), call_requester_(std::move(call_requester)), reactor_(reactor) { ctx_->BeginCompletionOp(call, [this](bool) { MaybeDone(); }, reactor); write_tag_.Set(call_.call(), [this](bool ok) { reactor_->OnWriteDone(ok); MaybeDone(); }, &write_ops_); write_ops_.set_core_cq_tag(&write_tag_); } ~ServerCallbackWriterImpl() { req_->~RequestType(); } const RequestType* request() { return req_; } void MaybeDone() { if (--callbacks_outstanding_ == 0) { reactor_->OnDone(); grpc_call* call = call_.call(); auto call_requester = std::move(call_requester_); this->~ServerCallbackWriterImpl(); // explicitly call destructor g_core_codegen_interface->grpc_call_unref(call); call_requester(); } } CallOpSet<CallOpSendInitialMetadata> meta_ops_; CallbackWithSuccessTag meta_tag_; CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage, CallOpServerSendStatus> finish_ops_; CallbackWithSuccessTag finish_tag_; CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage> write_ops_; CallbackWithSuccessTag write_tag_; ServerContext* ctx_; Call call_; const RequestType* req_; std::function<void()> call_requester_; experimental::ServerWriteReactor<RequestType, ResponseType>* reactor_; std::atomic_int callbacks_outstanding_{ 3}; // reserve for OnStarted, Finish, and CompletionOp }; }; template <class RequestType, class ResponseType> class CallbackBidiHandler : public MethodHandler { public: CallbackBidiHandler( std::function< experimental::ServerBidiReactor<RequestType, ResponseType>*()> func) : func_(std::move(func)) {} void RunHandler(const HandlerParameter& param) final { g_core_codegen_interface->grpc_call_ref(param.call->call()); experimental::ServerBidiReactor<RequestType, ResponseType>* reactor = param.status.ok() ? CatchingReactorCreator< experimental::ServerBidiReactor<RequestType, ResponseType>>( func_) : nullptr; if (reactor == nullptr) { // if deserialization or reactor creator failed, we need to fail the call reactor = new UnimplementedBidiReactor<RequestType, ResponseType>; } auto* stream = new (g_core_codegen_interface->grpc_call_arena_alloc( param.call->call(), sizeof(ServerCallbackReaderWriterImpl))) ServerCallbackReaderWriterImpl(param.server_context, param.call, std::move(param.call_requester), reactor); stream->BindReactor(reactor); reactor->OnStarted(param.server_context); stream->MaybeDone(); } private: std::function<experimental::ServerBidiReactor<RequestType, ResponseType>*()> func_; class ServerCallbackReaderWriterImpl : public experimental::ServerCallbackReaderWriter<RequestType, ResponseType> { public: void Finish(Status s) override { finish_tag_.Set(call_.call(), [this](bool) { MaybeDone(); }, &finish_ops_); finish_ops_.set_core_cq_tag(&finish_tag_); if (!ctx_->sent_initial_metadata_) { finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_, ctx_->initial_metadata_flags()); if (ctx_->compression_level_set()) { finish_ops_.set_compression_level(ctx_->compression_level()); } ctx_->sent_initial_metadata_ = true; } finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, s); call_.PerformOps(&finish_ops_); } void SendInitialMetadata() override { GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); callbacks_outstanding_++; meta_tag_.Set(call_.call(), [this](bool ok) { reactor_->OnSendInitialMetadataDone(ok); MaybeDone(); }, &meta_ops_); meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_, ctx_->initial_metadata_flags()); if (ctx_->compression_level_set()) { meta_ops_.set_compression_level(ctx_->compression_level()); } ctx_->sent_initial_metadata_ = true; meta_ops_.set_core_cq_tag(&meta_tag_); call_.PerformOps(&meta_ops_); } void Write(const ResponseType* resp, WriteOptions options) override { callbacks_outstanding_++; if (options.is_last_message()) { options.set_buffer_hint(); } if (!ctx_->sent_initial_metadata_) { write_ops_.SendInitialMetadata(&ctx_->initial_metadata_, ctx_->initial_metadata_flags()); if (ctx_->compression_level_set()) { write_ops_.set_compression_level(ctx_->compression_level()); } ctx_->sent_initial_metadata_ = true; } // TODO(vjpai): don't assert GPR_CODEGEN_ASSERT(write_ops_.SendMessage(*resp, options).ok()); call_.PerformOps(&write_ops_); } void WriteAndFinish(const ResponseType* resp, WriteOptions options, Status s) override { // Don't send any message if the status is bad if (s.ok()) { // TODO(vjpai): don't assert GPR_CODEGEN_ASSERT(finish_ops_.SendMessage(*resp, options).ok()); } Finish(std::move(s)); } void Read(RequestType* req) override { callbacks_outstanding_++; read_ops_.RecvMessage(req); call_.PerformOps(&read_ops_); } private: friend class CallbackBidiHandler<RequestType, ResponseType>; ServerCallbackReaderWriterImpl( ServerContext* ctx, Call* call, std::function<void()> call_requester, experimental::ServerBidiReactor<RequestType, ResponseType>* reactor) : ctx_(ctx), call_(*call), call_requester_(std::move(call_requester)), reactor_(reactor) { ctx_->BeginCompletionOp(call, [this](bool) { MaybeDone(); }, reactor); write_tag_.Set(call_.call(), [this](bool ok) { reactor_->OnWriteDone(ok); MaybeDone(); }, &write_ops_); write_ops_.set_core_cq_tag(&write_tag_); read_tag_.Set(call_.call(), [this](bool ok) { reactor_->OnReadDone(ok); MaybeDone(); }, &read_ops_); read_ops_.set_core_cq_tag(&read_tag_); } ~ServerCallbackReaderWriterImpl() {} void MaybeDone() { if (--callbacks_outstanding_ == 0) { reactor_->OnDone(); grpc_call* call = call_.call(); auto call_requester = std::move(call_requester_); this->~ServerCallbackReaderWriterImpl(); // explicitly call destructor g_core_codegen_interface->grpc_call_unref(call); call_requester(); } } CallOpSet<CallOpSendInitialMetadata> meta_ops_; CallbackWithSuccessTag meta_tag_; CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage, CallOpServerSendStatus> finish_ops_; CallbackWithSuccessTag finish_tag_; CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage> write_ops_; CallbackWithSuccessTag write_tag_; CallOpSet<CallOpRecvMessage<RequestType>> read_ops_; CallbackWithSuccessTag read_tag_; ServerContext* ctx_; Call call_; std::function<void()> call_requester_; experimental::ServerBidiReactor<RequestType, ResponseType>* reactor_; std::atomic_int callbacks_outstanding_{ 3}; // reserve for OnStarted, Finish, and CompletionOp }; }; } // namespace internal } // namespace grpc #endif // GRPCPP_IMPL_CODEGEN_SERVER_CALLBACK_H