This commit is contained in:
Yao
2024-12-20 17:49:45 +08:00
parent 86b0363ce1
commit 654d456c7d
7011 changed files with 1705926 additions and 7 deletions

View File

@@ -0,0 +1,1133 @@
//
//
// Copyright 2015 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_SUPPORT_ASYNC_STREAM_H
#define GRPCPP_SUPPORT_ASYNC_STREAM_H
#include <grpc/grpc.h>
#include <grpc/support/log.h>
#include <grpcpp/impl/call.h>
#include <grpcpp/impl/channel_interface.h>
#include <grpcpp/impl/service_type.h>
#include <grpcpp/server_context.h>
#include <grpcpp/support/status.h>
namespace grpc {
namespace internal {
/// Common interface for all client side asynchronous streaming.
class ClientAsyncStreamingInterface {
public:
virtual ~ClientAsyncStreamingInterface() {}
/// Start the call that was set up by the constructor, but only if the
/// constructor was invoked through the "Prepare" API which doesn't actually
/// start the call
virtual void StartCall(void* tag) = 0;
/// Request notification of the reading of the initial metadata. Completion
/// will be notified by \a tag on the associated completion queue.
/// This call is optional, but if it is used, it cannot be used concurrently
/// with or after the \a AsyncReaderInterface::Read method.
///
/// \param[in] tag Tag identifying this request.
virtual void ReadInitialMetadata(void* tag) = 0;
/// Indicate that the stream is to be finished and request notification for
/// when the call has been ended.
/// Should not be used concurrently with other operations.
///
/// It is appropriate to call this method exactly once when both:
/// * the client side has no more message to send
/// (this can be declared implicitly by calling this method, or
/// explicitly through an earlier call to the <i>WritesDone</i> method
/// of the class in use, e.g. \a ClientAsyncWriterInterface::WritesDone or
/// \a ClientAsyncReaderWriterInterface::WritesDone).
/// * there are no more messages to be received from the server (this can
/// be known implicitly by the calling code, or explicitly from an
/// earlier call to \a AsyncReaderInterface::Read that yielded a failed
/// result, e.g. cq->Next(&read_tag, &ok) filled in 'ok' with 'false').
///
/// The tag will be returned when either:
/// - all incoming messages have been read and the server has returned
/// a status.
/// - the server has returned a non-OK status.
/// - the call failed for some reason and the library generated a
/// status.
///
/// Note that implementations of this method attempt to receive initial
/// metadata from the server if initial metadata hasn't yet been received.
///
/// \param[in] tag Tag identifying this request.
/// \param[out] status To be updated with the operation status.
virtual void Finish(grpc::Status* status, void* tag) = 0;
};
/// An interface that yields a sequence of messages of type \a R.
template <class R>
class AsyncReaderInterface {
public:
virtual ~AsyncReaderInterface() {}
/// Read a message of type \a R into \a msg. Completion will be notified by \a
/// tag on the associated completion queue.
/// This is thread-safe with respect to \a Write or \a WritesDone methods. It
/// should not be called concurrently with other streaming APIs
/// on the same stream. It is not meaningful to call it concurrently
/// with another \a AsyncReaderInterface::Read on the same stream since reads
/// on the same stream are delivered in order.
///
/// \param[out] msg Where to eventually store the read message.
/// \param[in] tag The tag identifying the operation.
///
/// Side effect: note that this method attempt to receive initial metadata for
/// a stream if it hasn't yet been received.
virtual void Read(R* msg, void* tag) = 0;
};
/// An interface that can be fed a sequence of messages of type \a W.
template <class W>
class AsyncWriterInterface {
public:
virtual ~AsyncWriterInterface() {}
/// Request the writing of \a msg with identifying tag \a tag.
///
/// Only one write may be outstanding at any given time. This means that
/// after calling Write, one must wait to receive \a tag from the completion
/// queue BEFORE calling Write again.
/// This is thread-safe with respect to \a AsyncReaderInterface::Read
///
/// gRPC doesn't take ownership or a reference to \a msg, so it is safe to
/// to deallocate once Write returns.
///
/// \param[in] msg The message to be written.
/// \param[in] tag The tag identifying the operation.
virtual void Write(const W& msg, void* tag) = 0;
/// Request the writing of \a msg using WriteOptions \a options with
/// identifying tag \a tag.
///
/// Only one write may be outstanding at any given time. This means that
/// after calling Write, one must wait to receive \a tag from the completion
/// queue BEFORE calling Write again.
/// WriteOptions \a options is used to set the write options of this message.
/// This is thread-safe with respect to \a AsyncReaderInterface::Read
///
/// gRPC doesn't take ownership or a reference to \a msg, so it is safe to
/// to deallocate once Write returns.
///
/// \param[in] msg The message to be written.
/// \param[in] options The WriteOptions to be used to write this message.
/// \param[in] tag The tag identifying the operation.
virtual void Write(const W& msg, grpc::WriteOptions options, void* tag) = 0;
/// Request the writing of \a msg and coalesce it with the writing
/// of trailing metadata, using WriteOptions \a options with
/// identifying tag \a tag.
///
/// For client, WriteLast is equivalent of performing Write and
/// WritesDone in a single step.
/// For server, WriteLast buffers the \a msg. The writing of \a msg is held
/// until Finish is called, where \a msg and trailing metadata are coalesced
/// and write is initiated. Note that WriteLast can only buffer \a msg up to
/// the flow control window size. If \a msg size is larger than the window
/// size, it will be sent on wire without buffering.
///
/// gRPC doesn't take ownership or a reference to \a msg, so it is safe to
/// to deallocate once Write returns.
///
/// \param[in] msg The message to be written.
/// \param[in] options The WriteOptions to be used to write this message.
/// \param[in] tag The tag identifying the operation.
void WriteLast(const W& msg, grpc::WriteOptions options, void* tag) {
Write(msg, options.set_last_message(), tag);
}
};
} // namespace internal
template <class R>
class ClientAsyncReaderInterface
: public internal::ClientAsyncStreamingInterface,
public internal::AsyncReaderInterface<R> {};
namespace internal {
template <class R>
class ClientAsyncReaderFactory {
public:
/// Create a stream object.
/// Write the first request out if \a start is set.
/// \a tag will be notified on \a cq when the call has been started and
/// \a request has been written out. If \a start is not set, \a tag must be
/// nullptr and the actual call must be initiated by StartCall
/// Note that \a context will be used to fill in custom initial metadata
/// used to send to the server when starting the call.
template <class W>
static ClientAsyncReader<R>* Create(grpc::ChannelInterface* channel,
grpc::CompletionQueue* cq,
const grpc::internal::RpcMethod& method,
grpc::ClientContext* context,
const W& request, bool start, void* tag) {
grpc::internal::Call call = channel->CreateCall(method, context, cq);
return new (
grpc_call_arena_alloc(call.call(), sizeof(ClientAsyncReader<R>)))
ClientAsyncReader<R>(call, context, request, start, tag);
}
};
} // namespace internal
/// Async client-side API for doing server-streaming RPCs,
/// where the incoming message stream coming from the server has
/// messages of type \a R.
template <class R>
class ClientAsyncReader final : public ClientAsyncReaderInterface<R> {
public:
// always allocated against a call arena, no memory free required
static void operator delete(void* /*ptr*/, std::size_t size) {
GPR_ASSERT(size == sizeof(ClientAsyncReader));
}
// 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*) { GPR_ASSERT(false); }
void StartCall(void* tag) override {
GPR_ASSERT(!started_);
started_ = true;
StartCallInternal(tag);
}
/// See the \a ClientAsyncStreamingInterface.ReadInitialMetadata
/// method for semantics.
///
/// Side effect:
/// - upon receiving initial metadata from the server,
/// the \a ClientContext associated with this call is updated, and the
/// calling code can access the received metadata through the
/// \a ClientContext.
void ReadInitialMetadata(void* tag) override {
GPR_ASSERT(started_);
GPR_ASSERT(!context_->initial_metadata_received_);
meta_ops_.set_output_tag(tag);
meta_ops_.RecvInitialMetadata(context_);
call_.PerformOps(&meta_ops_);
}
void Read(R* msg, void* tag) override {
GPR_ASSERT(started_);
read_ops_.set_output_tag(tag);
if (!context_->initial_metadata_received_) {
read_ops_.RecvInitialMetadata(context_);
}
read_ops_.RecvMessage(msg);
call_.PerformOps(&read_ops_);
}
/// See the \a ClientAsyncStreamingInterface.Finish method for semantics.
///
/// Side effect:
/// - the \a ClientContext associated with this call is updated with
/// possible initial and trailing metadata received from the server.
void Finish(grpc::Status* status, void* tag) override {
GPR_ASSERT(started_);
finish_ops_.set_output_tag(tag);
if (!context_->initial_metadata_received_) {
finish_ops_.RecvInitialMetadata(context_);
}
finish_ops_.ClientRecvStatus(context_, status);
call_.PerformOps(&finish_ops_);
}
private:
friend class internal::ClientAsyncReaderFactory<R>;
template <class W>
ClientAsyncReader(grpc::internal::Call call, grpc::ClientContext* context,
const W& request, bool start, void* tag)
: context_(context), call_(call), started_(start) {
// TODO(ctiller): don't assert
GPR_ASSERT(init_ops_.SendMessage(request).ok());
init_ops_.ClientSendClose();
if (start) {
StartCallInternal(tag);
} else {
GPR_ASSERT(tag == nullptr);
}
}
void StartCallInternal(void* tag) {
init_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
context_->initial_metadata_flags());
init_ops_.set_output_tag(tag);
call_.PerformOps(&init_ops_);
}
grpc::ClientContext* context_;
grpc::internal::Call call_;
bool started_;
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata,
grpc::internal::CallOpSendMessage,
grpc::internal::CallOpClientSendClose>
init_ops_;
grpc::internal::CallOpSet<grpc::internal::CallOpRecvInitialMetadata>
meta_ops_;
grpc::internal::CallOpSet<grpc::internal::CallOpRecvInitialMetadata,
grpc::internal::CallOpRecvMessage<R>>
read_ops_;
grpc::internal::CallOpSet<grpc::internal::CallOpRecvInitialMetadata,
grpc::internal::CallOpClientRecvStatus>
finish_ops_;
};
/// Common interface for client side asynchronous writing.
template <class W>
class ClientAsyncWriterInterface
: public internal::ClientAsyncStreamingInterface,
public internal::AsyncWriterInterface<W> {
public:
/// Signal the client is done with the writes (half-close the client stream).
/// Thread-safe with respect to \a AsyncReaderInterface::Read
///
/// \param[in] tag The tag identifying the operation.
virtual void WritesDone(void* tag) = 0;
};
namespace internal {
template <class W>
class ClientAsyncWriterFactory {
public:
/// Create a stream object.
/// Start the RPC if \a start is set
/// \a tag will be notified on \a cq when the call has been started (i.e.
/// initial metadata sent) and \a request has been written out.
/// If \a start is not set, \a tag must be nullptr and the actual call
/// must be initiated by StartCall
/// Note that \a context will be used to fill in custom initial metadata
/// used to send to the server when starting the call.
/// \a response will be filled in with the single expected response
/// message from the server upon a successful call to the \a Finish
/// method of this instance.
template <class R>
static ClientAsyncWriter<W>* Create(grpc::ChannelInterface* channel,
grpc::CompletionQueue* cq,
const grpc::internal::RpcMethod& method,
grpc::ClientContext* context, R* response,
bool start, void* tag) {
grpc::internal::Call call = channel->CreateCall(method, context, cq);
return new (
grpc_call_arena_alloc(call.call(), sizeof(ClientAsyncWriter<W>)))
ClientAsyncWriter<W>(call, context, response, start, tag);
}
};
} // namespace internal
/// Async API on the client side for doing client-streaming RPCs,
/// where the outgoing message stream going to the server contains
/// messages of type \a W.
template <class W>
class ClientAsyncWriter final : public ClientAsyncWriterInterface<W> {
public:
// always allocated against a call arena, no memory free required
static void operator delete(void* /*ptr*/, std::size_t size) {
GPR_ASSERT(size == sizeof(ClientAsyncWriter));
}
// 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*) { GPR_ASSERT(false); }
void StartCall(void* tag) override {
GPR_ASSERT(!started_);
started_ = true;
StartCallInternal(tag);
}
/// See the \a ClientAsyncStreamingInterface.ReadInitialMetadata method for
/// semantics.
///
/// Side effect:
/// - upon receiving initial metadata from the server, the \a ClientContext
/// associated with this call is updated, and the calling code can access
/// the received metadata through the \a ClientContext.
void ReadInitialMetadata(void* tag) override {
GPR_ASSERT(started_);
GPR_ASSERT(!context_->initial_metadata_received_);
meta_ops_.set_output_tag(tag);
meta_ops_.RecvInitialMetadata(context_);
call_.PerformOps(&meta_ops_);
}
void Write(const W& msg, void* tag) override {
GPR_ASSERT(started_);
write_ops_.set_output_tag(tag);
// TODO(ctiller): don't assert
GPR_ASSERT(write_ops_.SendMessage(msg).ok());
call_.PerformOps(&write_ops_);
}
void Write(const W& msg, grpc::WriteOptions options, void* tag) override {
GPR_ASSERT(started_);
write_ops_.set_output_tag(tag);
if (options.is_last_message()) {
options.set_buffer_hint();
write_ops_.ClientSendClose();
}
// TODO(ctiller): don't assert
GPR_ASSERT(write_ops_.SendMessage(msg, options).ok());
call_.PerformOps(&write_ops_);
}
void WritesDone(void* tag) override {
GPR_ASSERT(started_);
write_ops_.set_output_tag(tag);
write_ops_.ClientSendClose();
call_.PerformOps(&write_ops_);
}
/// See the \a ClientAsyncStreamingInterface.Finish method for semantics.
///
/// Side effect:
/// - the \a ClientContext associated with this call is updated with
/// possible initial and trailing metadata received from the server.
/// - attempts to fill in the \a response parameter passed to this class's
/// constructor with the server's response message.
void Finish(grpc::Status* status, void* tag) override {
GPR_ASSERT(started_);
finish_ops_.set_output_tag(tag);
if (!context_->initial_metadata_received_) {
finish_ops_.RecvInitialMetadata(context_);
}
finish_ops_.ClientRecvStatus(context_, status);
call_.PerformOps(&finish_ops_);
}
private:
friend class internal::ClientAsyncWriterFactory<W>;
template <class R>
ClientAsyncWriter(grpc::internal::Call call, grpc::ClientContext* context,
R* response, bool start, void* tag)
: context_(context), call_(call), started_(start) {
finish_ops_.RecvMessage(response);
finish_ops_.AllowNoMessage();
if (start) {
StartCallInternal(tag);
} else {
GPR_ASSERT(tag == nullptr);
}
}
void StartCallInternal(void* tag) {
write_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
context_->initial_metadata_flags());
// if corked bit is set in context, we just keep the initial metadata
// buffered up to coalesce with later message send. No op is performed.
if (!context_->initial_metadata_corked_) {
write_ops_.set_output_tag(tag);
call_.PerformOps(&write_ops_);
}
}
grpc::ClientContext* context_;
grpc::internal::Call call_;
bool started_;
grpc::internal::CallOpSet<grpc::internal::CallOpRecvInitialMetadata>
meta_ops_;
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata,
grpc::internal::CallOpSendMessage,
grpc::internal::CallOpClientSendClose>
write_ops_;
grpc::internal::CallOpSet<grpc::internal::CallOpRecvInitialMetadata,
grpc::internal::CallOpGenericRecvMessage,
grpc::internal::CallOpClientRecvStatus>
finish_ops_;
};
/// Async client-side interface for bi-directional streaming,
/// where the client-to-server message stream has messages of type \a W,
/// and the server-to-client message stream has messages of type \a R.
template <class W, class R>
class ClientAsyncReaderWriterInterface
: public internal::ClientAsyncStreamingInterface,
public internal::AsyncWriterInterface<W>,
public internal::AsyncReaderInterface<R> {
public:
/// Signal the client is done with the writes (half-close the client stream).
/// Thread-safe with respect to \a AsyncReaderInterface::Read
///
/// \param[in] tag The tag identifying the operation.
virtual void WritesDone(void* tag) = 0;
};
namespace internal {
template <class W, class R>
class ClientAsyncReaderWriterFactory {
public:
/// Create a stream object.
/// Start the RPC request if \a start is set.
/// \a tag will be notified on \a cq when the call has been started (i.e.
/// initial metadata sent). If \a start is not set, \a tag must be
/// nullptr and the actual call must be initiated by StartCall
/// Note that \a context will be used to fill in custom initial metadata
/// used to send to the server when starting the call.
static ClientAsyncReaderWriter<W, R>* Create(
grpc::ChannelInterface* channel, grpc::CompletionQueue* cq,
const grpc::internal::RpcMethod& method, grpc::ClientContext* context,
bool start, void* tag) {
grpc::internal::Call call = channel->CreateCall(method, context, cq);
return new (grpc_call_arena_alloc(call.call(),
sizeof(ClientAsyncReaderWriter<W, R>)))
ClientAsyncReaderWriter<W, R>(call, context, start, tag);
}
};
} // namespace internal
/// Async client-side interface for bi-directional streaming,
/// where the outgoing message stream going to the server
/// has messages of type \a W, and the incoming message stream coming
/// from the server has messages of type \a R.
template <class W, class R>
class ClientAsyncReaderWriter final
: public ClientAsyncReaderWriterInterface<W, R> {
public:
// always allocated against a call arena, no memory free required
static void operator delete(void* /*ptr*/, std::size_t size) {
GPR_ASSERT(size == sizeof(ClientAsyncReaderWriter));
}
// 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*) { GPR_ASSERT(false); }
void StartCall(void* tag) override {
GPR_ASSERT(!started_);
started_ = true;
StartCallInternal(tag);
}
/// See the \a ClientAsyncStreamingInterface.ReadInitialMetadata method
/// for semantics of this method.
///
/// Side effect:
/// - upon receiving initial metadata from the server, the \a ClientContext
/// is updated with it, and then the receiving initial metadata can
/// be accessed through this \a ClientContext.
void ReadInitialMetadata(void* tag) override {
GPR_ASSERT(started_);
GPR_ASSERT(!context_->initial_metadata_received_);
meta_ops_.set_output_tag(tag);
meta_ops_.RecvInitialMetadata(context_);
call_.PerformOps(&meta_ops_);
}
void Read(R* msg, void* tag) override {
GPR_ASSERT(started_);
read_ops_.set_output_tag(tag);
if (!context_->initial_metadata_received_) {
read_ops_.RecvInitialMetadata(context_);
}
read_ops_.RecvMessage(msg);
call_.PerformOps(&read_ops_);
}
void Write(const W& msg, void* tag) override {
GPR_ASSERT(started_);
write_ops_.set_output_tag(tag);
// TODO(ctiller): don't assert
GPR_ASSERT(write_ops_.SendMessage(msg).ok());
call_.PerformOps(&write_ops_);
}
void Write(const W& msg, grpc::WriteOptions options, void* tag) override {
GPR_ASSERT(started_);
write_ops_.set_output_tag(tag);
if (options.is_last_message()) {
options.set_buffer_hint();
write_ops_.ClientSendClose();
}
// TODO(ctiller): don't assert
GPR_ASSERT(write_ops_.SendMessage(msg, options).ok());
call_.PerformOps(&write_ops_);
}
void WritesDone(void* tag) override {
GPR_ASSERT(started_);
write_ops_.set_output_tag(tag);
write_ops_.ClientSendClose();
call_.PerformOps(&write_ops_);
}
/// See the \a ClientAsyncStreamingInterface.Finish method for semantics.
/// Side effect
/// - the \a ClientContext associated with this call is updated with
/// possible initial and trailing metadata sent from the server.
void Finish(grpc::Status* status, void* tag) override {
GPR_ASSERT(started_);
finish_ops_.set_output_tag(tag);
if (!context_->initial_metadata_received_) {
finish_ops_.RecvInitialMetadata(context_);
}
finish_ops_.ClientRecvStatus(context_, status);
call_.PerformOps(&finish_ops_);
}
private:
friend class internal::ClientAsyncReaderWriterFactory<W, R>;
ClientAsyncReaderWriter(grpc::internal::Call call,
grpc::ClientContext* context, bool start, void* tag)
: context_(context), call_(call), started_(start) {
if (start) {
StartCallInternal(tag);
} else {
GPR_ASSERT(tag == nullptr);
}
}
void StartCallInternal(void* tag) {
write_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
context_->initial_metadata_flags());
// if corked bit is set in context, we just keep the initial metadata
// buffered up to coalesce with later message send. No op is performed.
if (!context_->initial_metadata_corked_) {
write_ops_.set_output_tag(tag);
call_.PerformOps(&write_ops_);
}
}
grpc::ClientContext* context_;
grpc::internal::Call call_;
bool started_;
grpc::internal::CallOpSet<grpc::internal::CallOpRecvInitialMetadata>
meta_ops_;
grpc::internal::CallOpSet<grpc::internal::CallOpRecvInitialMetadata,
grpc::internal::CallOpRecvMessage<R>>
read_ops_;
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata,
grpc::internal::CallOpSendMessage,
grpc::internal::CallOpClientSendClose>
write_ops_;
grpc::internal::CallOpSet<grpc::internal::CallOpRecvInitialMetadata,
grpc::internal::CallOpClientRecvStatus>
finish_ops_;
};
template <class W, class R>
class ServerAsyncReaderInterface
: public grpc::internal::ServerAsyncStreamingInterface,
public internal::AsyncReaderInterface<R> {
public:
/// Indicate that the stream is to be finished with a certain status code
/// and also send out \a msg response to the client.
/// Request notification for when the server has sent the response and the
/// appropriate signals to the client to end the call.
/// Should not be used concurrently with other operations.
///
/// It is appropriate to call this method when:
/// * all messages from the client have been received (either known
/// implicitly, or explicitly because a previous
/// \a AsyncReaderInterface::Read operation with a non-ok result,
/// e.g., cq->Next(&read_tag, &ok) filled in 'ok' with 'false').
///
/// This operation will end when the server has finished sending out initial
/// metadata (if not sent already), response message, and status, or if
/// some failure occurred when trying to do so.
///
/// gRPC doesn't take ownership or a reference to \a msg or \a status, so it
/// is safe to deallocate once Finish returns.
///
/// \param[in] tag Tag identifying this request.
/// \param[in] status To be sent to the client as the result of this call.
/// \param[in] msg To be sent to the client as the response for this call.
virtual void Finish(const W& msg, const grpc::Status& status, void* tag) = 0;
/// Indicate that the stream is to be finished with a certain
/// non-OK status code.
/// Request notification for when the server has sent the appropriate
/// signals to the client to end the call.
/// Should not be used concurrently with other operations.
///
/// This call is meant to end the call with some error, and can be called at
/// any point that the server would like to "fail" the call (though note
/// this shouldn't be called concurrently with any other "sending" call, like
/// \a AsyncWriterInterface::Write).
///
/// This operation will end when the server has finished sending out initial
/// metadata (if not sent already), and status, or if some failure occurred
/// when trying to do so.
///
/// gRPC doesn't take ownership or a reference to \a status, so it is safe to
/// to deallocate once FinishWithError returns.
///
/// \param[in] tag Tag identifying this request.
/// \param[in] status To be sent to the client as the result of this call.
/// - Note: \a status must have a non-OK code.
virtual void FinishWithError(const grpc::Status& status, void* tag) = 0;
};
/// Async server-side API for doing client-streaming RPCs,
/// where the incoming message stream from the client has messages of type \a R,
/// and the single response message sent from the server is type \a W.
template <class W, class R>
class ServerAsyncReader final : public ServerAsyncReaderInterface<W, R> {
public:
explicit ServerAsyncReader(grpc::ServerContext* ctx)
: call_(nullptr, nullptr, nullptr), ctx_(ctx) {}
/// See \a ServerAsyncStreamingInterface::SendInitialMetadata for semantics.
///
/// Implicit input parameter:
/// - The initial metadata that will be sent to the client from this op will
/// be taken from the \a ServerContext associated with the call.
void SendInitialMetadata(void* tag) override {
GPR_ASSERT(!ctx_->sent_initial_metadata_);
meta_ops_.set_output_tag(tag);
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;
call_.PerformOps(&meta_ops_);
}
void Read(R* msg, void* tag) override {
read_ops_.set_output_tag(tag);
read_ops_.RecvMessage(msg);
call_.PerformOps(&read_ops_);
}
/// See the \a ServerAsyncReaderInterface.Read method for semantics
///
/// Side effect:
/// - also sends initial metadata if not already sent.
/// - uses the \a ServerContext associated with this call to send possible
/// initial and trailing metadata.
///
/// Note: \a msg is not sent if \a status has a non-OK code.
///
/// gRPC doesn't take ownership or a reference to \a msg and \a status, so it
/// is safe to deallocate once Finish returns.
void Finish(const W& msg, const grpc::Status& status, void* tag) override {
finish_ops_.set_output_tag(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;
}
// The response is dropped if the status is not OK.
if (status.ok()) {
finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_,
finish_ops_.SendMessage(msg));
} else {
finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, status);
}
call_.PerformOps(&finish_ops_);
}
/// See the \a ServerAsyncReaderInterface.Read method for semantics
///
/// Side effect:
/// - also sends initial metadata if not already sent.
/// - uses the \a ServerContext associated with this call to send possible
/// initial and trailing metadata.
///
/// gRPC doesn't take ownership or a reference to \a status, so it is safe to
/// to deallocate once FinishWithError returns.
void FinishWithError(const grpc::Status& status, void* tag) override {
GPR_ASSERT(!status.ok());
finish_ops_.set_output_tag(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_, status);
call_.PerformOps(&finish_ops_);
}
private:
void BindCall(grpc::internal::Call* call) override { call_ = *call; }
grpc::internal::Call call_;
grpc::ServerContext* ctx_;
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata>
meta_ops_;
grpc::internal::CallOpSet<grpc::internal::CallOpRecvMessage<R>> read_ops_;
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata,
grpc::internal::CallOpSendMessage,
grpc::internal::CallOpServerSendStatus>
finish_ops_;
};
template <class W>
class ServerAsyncWriterInterface
: public grpc::internal::ServerAsyncStreamingInterface,
public internal::AsyncWriterInterface<W> {
public:
/// Indicate that the stream is to be finished with a certain status code.
/// Request notification for when the server has sent the appropriate
/// signals to the client to end the call.
/// Should not be used concurrently with other operations.
///
/// It is appropriate to call this method when either:
/// * all messages from the client have been received (either known
/// implicitly, or explicitly because a previous \a
/// AsyncReaderInterface::Read operation with a non-ok
/// result (e.g., cq->Next(&read_tag, &ok) filled in 'ok' with 'false'.
/// * it is desired to end the call early with some non-OK status code.
///
/// This operation will end when the server has finished sending out initial
/// metadata (if not sent already), response message, and status, or if
/// some failure occurred when trying to do so.
///
/// gRPC doesn't take ownership or a reference to \a status, so it is safe to
/// to deallocate once Finish returns.
///
/// \param[in] tag Tag identifying this request.
/// \param[in] status To be sent to the client as the result of this call.
virtual void Finish(const grpc::Status& status, void* tag) = 0;
/// Request the writing of \a msg and coalesce it with trailing metadata which
/// contains \a status, using WriteOptions options with
/// identifying tag \a tag.
///
/// WriteAndFinish is equivalent of performing WriteLast and Finish
/// in a single step.
///
/// gRPC doesn't take ownership or a reference to \a msg and \a status, so it
/// is safe to deallocate once WriteAndFinish returns.
///
/// \param[in] msg The message to be written.
/// \param[in] options The WriteOptions to be used to write this message.
/// \param[in] status The Status that server returns to client.
/// \param[in] tag The tag identifying the operation.
virtual void WriteAndFinish(const W& msg, grpc::WriteOptions options,
const grpc::Status& status, void* tag) = 0;
};
/// Async server-side API for doing server streaming RPCs,
/// where the outgoing message stream from the server has messages of type \a W.
template <class W>
class ServerAsyncWriter final : public ServerAsyncWriterInterface<W> {
public:
explicit ServerAsyncWriter(grpc::ServerContext* ctx)
: call_(nullptr, nullptr, nullptr), ctx_(ctx) {}
/// See \a ServerAsyncStreamingInterface::SendInitialMetadata for semantics.
///
/// Implicit input parameter:
/// - The initial metadata that will be sent to the client from this op will
/// be taken from the \a ServerContext associated with the call.
///
/// \param[in] tag Tag identifying this request.
void SendInitialMetadata(void* tag) override {
GPR_ASSERT(!ctx_->sent_initial_metadata_);
meta_ops_.set_output_tag(tag);
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;
call_.PerformOps(&meta_ops_);
}
void Write(const W& msg, void* tag) override {
write_ops_.set_output_tag(tag);
EnsureInitialMetadataSent(&write_ops_);
// TODO(ctiller): don't assert
GPR_ASSERT(write_ops_.SendMessage(msg).ok());
call_.PerformOps(&write_ops_);
}
void Write(const W& msg, grpc::WriteOptions options, void* tag) override {
write_ops_.set_output_tag(tag);
if (options.is_last_message()) {
options.set_buffer_hint();
}
EnsureInitialMetadataSent(&write_ops_);
// TODO(ctiller): don't assert
GPR_ASSERT(write_ops_.SendMessage(msg, options).ok());
call_.PerformOps(&write_ops_);
}
/// See the \a ServerAsyncWriterInterface.WriteAndFinish method for semantics.
///
/// Implicit input parameter:
/// - the \a ServerContext associated with this call is used
/// for sending trailing (and initial) metadata to the client.
///
/// Note: \a status must have an OK code.
///
/// gRPC doesn't take ownership or a reference to \a msg and \a status, so it
/// is safe to deallocate once WriteAndFinish returns.
void WriteAndFinish(const W& msg, grpc::WriteOptions options,
const grpc::Status& status, void* tag) override {
write_ops_.set_output_tag(tag);
EnsureInitialMetadataSent(&write_ops_);
options.set_buffer_hint();
GPR_ASSERT(write_ops_.SendMessage(msg, options).ok());
write_ops_.ServerSendStatus(&ctx_->trailing_metadata_, status);
call_.PerformOps(&write_ops_);
}
/// See the \a ServerAsyncWriterInterface.Finish method for semantics.
///
/// Implicit input parameter:
/// - the \a ServerContext associated with this call is used for sending
/// trailing (and initial if not already sent) metadata to the client.
///
/// Note: there are no restrictions are the code of
/// \a status,it may be non-OK
///
/// gRPC doesn't take ownership or a reference to \a status, so it is safe to
/// to deallocate once Finish returns.
void Finish(const grpc::Status& status, void* tag) override {
finish_ops_.set_output_tag(tag);
EnsureInitialMetadataSent(&finish_ops_);
finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, status);
call_.PerformOps(&finish_ops_);
}
private:
void BindCall(grpc::internal::Call* call) override { call_ = *call; }
template <class T>
void EnsureInitialMetadataSent(T* ops) {
if (!ctx_->sent_initial_metadata_) {
ops->SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
ops->set_compression_level(ctx_->compression_level());
}
ctx_->sent_initial_metadata_ = true;
}
}
grpc::internal::Call call_;
grpc::ServerContext* ctx_;
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata>
meta_ops_;
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata,
grpc::internal::CallOpSendMessage,
grpc::internal::CallOpServerSendStatus>
write_ops_;
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata,
grpc::internal::CallOpServerSendStatus>
finish_ops_;
};
/// Server-side interface for asynchronous bi-directional streaming.
template <class W, class R>
class ServerAsyncReaderWriterInterface
: public grpc::internal::ServerAsyncStreamingInterface,
public internal::AsyncWriterInterface<W>,
public internal::AsyncReaderInterface<R> {
public:
/// Indicate that the stream is to be finished with a certain status code.
/// Request notification for when the server has sent the appropriate
/// signals to the client to end the call.
/// Should not be used concurrently with other operations.
///
/// It is appropriate to call this method when either:
/// * all messages from the client have been received (either known
/// implicitly, or explicitly because a previous \a
/// AsyncReaderInterface::Read operation
/// with a non-ok result (e.g., cq->Next(&read_tag, &ok) filled in 'ok'
/// with 'false'.
/// * it is desired to end the call early with some non-OK status code.
///
/// This operation will end when the server has finished sending out initial
/// metadata (if not sent already), response message, and status, or if some
/// failure occurred when trying to do so.
///
/// gRPC doesn't take ownership or a reference to \a status, so it is safe to
/// to deallocate once Finish returns.
///
/// \param[in] tag Tag identifying this request.
/// \param[in] status To be sent to the client as the result of this call.
virtual void Finish(const grpc::Status& status, void* tag) = 0;
/// Request the writing of \a msg and coalesce it with trailing metadata which
/// contains \a status, using WriteOptions options with
/// identifying tag \a tag.
///
/// WriteAndFinish is equivalent of performing WriteLast and Finish in a
/// single step.
///
/// gRPC doesn't take ownership or a reference to \a msg and \a status, so it
/// is safe to deallocate once WriteAndFinish returns.
///
/// \param[in] msg The message to be written.
/// \param[in] options The WriteOptions to be used to write this message.
/// \param[in] status The Status that server returns to client.
/// \param[in] tag The tag identifying the operation.
virtual void WriteAndFinish(const W& msg, grpc::WriteOptions options,
const grpc::Status& status, void* tag) = 0;
};
/// Async server-side API for doing bidirectional streaming RPCs,
/// where the incoming message stream coming from the client has messages of
/// type \a R, and the outgoing message stream coming from the server has
/// messages of type \a W.
template <class W, class R>
class ServerAsyncReaderWriter final
: public ServerAsyncReaderWriterInterface<W, R> {
public:
explicit ServerAsyncReaderWriter(grpc::ServerContext* ctx)
: call_(nullptr, nullptr, nullptr), ctx_(ctx) {}
/// See \a ServerAsyncStreamingInterface::SendInitialMetadata for semantics.
///
/// Implicit input parameter:
/// - The initial metadata that will be sent to the client from this op will
/// be taken from the \a ServerContext associated with the call.
///
/// \param[in] tag Tag identifying this request.
void SendInitialMetadata(void* tag) override {
GPR_ASSERT(!ctx_->sent_initial_metadata_);
meta_ops_.set_output_tag(tag);
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;
call_.PerformOps(&meta_ops_);
}
void Read(R* msg, void* tag) override {
read_ops_.set_output_tag(tag);
read_ops_.RecvMessage(msg);
call_.PerformOps(&read_ops_);
}
void Write(const W& msg, void* tag) override {
write_ops_.set_output_tag(tag);
EnsureInitialMetadataSent(&write_ops_);
// TODO(ctiller): don't assert
GPR_ASSERT(write_ops_.SendMessage(msg).ok());
call_.PerformOps(&write_ops_);
}
void Write(const W& msg, grpc::WriteOptions options, void* tag) override {
write_ops_.set_output_tag(tag);
if (options.is_last_message()) {
options.set_buffer_hint();
}
EnsureInitialMetadataSent(&write_ops_);
GPR_ASSERT(write_ops_.SendMessage(msg, options).ok());
call_.PerformOps(&write_ops_);
}
/// See the \a ServerAsyncReaderWriterInterface.WriteAndFinish
/// method for semantics.
///
/// Implicit input parameter:
/// - the \a ServerContext associated with this call is used
/// for sending trailing (and initial) metadata to the client.
///
/// Note: \a status must have an OK code.
//
/// gRPC doesn't take ownership or a reference to \a msg and \a status, so it
/// is safe to deallocate once WriteAndFinish returns.
void WriteAndFinish(const W& msg, grpc::WriteOptions options,
const grpc::Status& status, void* tag) override {
write_ops_.set_output_tag(tag);
EnsureInitialMetadataSent(&write_ops_);
options.set_buffer_hint();
GPR_ASSERT(write_ops_.SendMessage(msg, options).ok());
write_ops_.ServerSendStatus(&ctx_->trailing_metadata_, status);
call_.PerformOps(&write_ops_);
}
/// See the \a ServerAsyncReaderWriterInterface.Finish method for semantics.
///
/// Implicit input parameter:
/// - the \a ServerContext associated with this call is used for sending
/// trailing (and initial if not already sent) metadata to the client.
///
/// Note: there are no restrictions are the code of \a status,
/// it may be non-OK
//
/// gRPC doesn't take ownership or a reference to \a status, so it is safe to
/// to deallocate once Finish returns.
void Finish(const grpc::Status& status, void* tag) override {
finish_ops_.set_output_tag(tag);
EnsureInitialMetadataSent(&finish_ops_);
finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, status);
call_.PerformOps(&finish_ops_);
}
private:
friend class grpc::Server;
void BindCall(grpc::internal::Call* call) override { call_ = *call; }
template <class T>
void EnsureInitialMetadataSent(T* ops) {
if (!ctx_->sent_initial_metadata_) {
ops->SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
ops->set_compression_level(ctx_->compression_level());
}
ctx_->sent_initial_metadata_ = true;
}
}
grpc::internal::Call call_;
grpc::ServerContext* ctx_;
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata>
meta_ops_;
grpc::internal::CallOpSet<grpc::internal::CallOpRecvMessage<R>> read_ops_;
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata,
grpc::internal::CallOpSendMessage,
grpc::internal::CallOpServerSendStatus>
write_ops_;
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata,
grpc::internal::CallOpServerSendStatus>
finish_ops_;
};
} // namespace grpc
#endif // GRPCPP_SUPPORT_ASYNC_STREAM_H

View File

@@ -0,0 +1,418 @@
//
//
// Copyright 2015 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_SUPPORT_ASYNC_UNARY_CALL_H
#define GRPCPP_SUPPORT_ASYNC_UNARY_CALL_H
#include <grpc/grpc.h>
#include <grpc/support/log.h>
#include <grpcpp/client_context.h>
#include <grpcpp/impl/call.h>
#include <grpcpp/impl/call_op_set.h>
#include <grpcpp/impl/call_op_set_interface.h>
#include <grpcpp/impl/channel_interface.h>
#include <grpcpp/impl/service_type.h>
#include <grpcpp/server_context.h>
#include <grpcpp/support/status.h>
namespace grpc {
// Forward declaration for use in Helper class
template <class R>
class ClientAsyncResponseReader;
/// An interface relevant for async client side unary RPCs (which send
/// one request message to a server and receive one response message).
template <class R>
class ClientAsyncResponseReaderInterface {
public:
virtual ~ClientAsyncResponseReaderInterface() {}
/// Start the call that was set up by the constructor, but only if the
/// constructor was invoked through the "Prepare" API which doesn't actually
/// start the call
virtual void StartCall() = 0;
/// Request notification of the reading of initial metadata. Completion
/// will be notified by \a tag on the associated completion queue.
/// This call is optional, but if it is used, it cannot be used concurrently
/// with or after the \a Finish method.
///
/// \param[in] tag Tag identifying this request.
virtual void ReadInitialMetadata(void* tag) = 0;
/// Request to receive the server's response \a msg and final \a status for
/// the call, and to notify \a tag on this call's completion queue when
/// finished.
///
/// This function will return when either:
/// - when the server's response message and status have been received.
/// - when the server has returned a non-OK status (no message expected in
/// this case).
/// - when the call failed for some reason and the library generated a
/// non-OK status.
///
/// \param[in] tag Tag identifying this request.
/// \param[out] status To be updated with the operation status.
/// \param[out] msg To be filled in with the server's response message.
virtual void Finish(R* msg, grpc::Status* status, void* tag) = 0;
};
namespace internal {
class ClientAsyncResponseReaderHelper {
public:
/// Start a call and write the request out if \a start is set.
/// \a tag will be notified on \a cq when the call has been started (i.e.
/// initial metadata sent) and \a request has been written out.
/// If \a start is not set, the actual call must be initiated by StartCall
/// Note that \a context will be used to fill in custom initial metadata
/// used to send to the server when starting the call.
///
/// Optionally pass in a base class for request and response types so that the
/// internal functions and structs can be templated based on that, allowing
/// reuse across RPCs (e.g., MessageLite for protobuf). Since constructors
/// can't have an explicit template parameter, the last argument is an
/// extraneous parameter just to provide the needed type information.
template <class R, class W, class BaseR = R, class BaseW = W>
static ClientAsyncResponseReader<R>* Create(
grpc::ChannelInterface* channel, grpc::CompletionQueue* cq,
const grpc::internal::RpcMethod& method, grpc::ClientContext* context,
const W& request) /* __attribute__((noinline)) */ {
grpc::internal::Call call = channel->CreateCall(method, context, cq);
ClientAsyncResponseReader<R>* result = new (grpc_call_arena_alloc(
call.call(), sizeof(ClientAsyncResponseReader<R>)))
ClientAsyncResponseReader<R>(call, context);
SetupRequest<BaseR, BaseW>(
call.call(), &result->single_buf_, &result->read_initial_metadata_,
&result->finish_, static_cast<const BaseW&>(request));
return result;
}
// Various helper functions to reduce templating use
template <class R, class W>
static void SetupRequest(
grpc_call* call,
grpc::internal::CallOpSendInitialMetadata** single_buf_ptr,
std::function<void(ClientContext*, internal::Call*,
internal::CallOpSendInitialMetadata*, void*)>*
read_initial_metadata,
std::function<
void(ClientContext*, internal::Call*, bool initial_metadata_read,
internal::CallOpSendInitialMetadata*,
internal::CallOpSetInterface**, void*, Status*, void*)>* finish,
const W& request) {
using SingleBufType =
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata,
grpc::internal::CallOpSendMessage,
grpc::internal::CallOpClientSendClose,
grpc::internal::CallOpRecvInitialMetadata,
grpc::internal::CallOpRecvMessage<R>,
grpc::internal::CallOpClientRecvStatus>;
SingleBufType* single_buf =
new (grpc_call_arena_alloc(call, sizeof(SingleBufType))) SingleBufType;
*single_buf_ptr = single_buf;
// TODO(ctiller): don't assert
GPR_ASSERT(single_buf->SendMessage(request).ok());
single_buf->ClientSendClose();
// The purpose of the following functions is to type-erase the actual
// templated type of the CallOpSet being used by hiding that type inside the
// function definition rather than specifying it as an argument of the
// function or a member of the class. The type-erased CallOpSet will get
// static_cast'ed back to the real type so that it can be used properly.
*read_initial_metadata =
[](ClientContext* context, internal::Call* call,
internal::CallOpSendInitialMetadata* single_buf_view, void* tag) {
auto* single_buf = static_cast<SingleBufType*>(single_buf_view);
single_buf->set_output_tag(tag);
single_buf->RecvInitialMetadata(context);
call->PerformOps(single_buf);
};
// Note that this function goes one step further than the previous one
// because it type-erases the message being written down to a void*. This
// will be static-cast'ed back to the class specified here by hiding that
// class information inside the function definition. Note that this feature
// expects the class being specified here for R to be a base-class of the
// "real" R without any multiple-inheritance (as applies in protobuf wrt
// MessageLite)
*finish = [](ClientContext* context, internal::Call* call,
bool initial_metadata_read,
internal::CallOpSendInitialMetadata* single_buf_view,
internal::CallOpSetInterface** finish_buf_ptr, void* msg,
Status* status, void* tag) {
if (initial_metadata_read) {
using FinishBufType =
grpc::internal::CallOpSet<grpc::internal::CallOpRecvMessage<R>,
grpc::internal::CallOpClientRecvStatus>;
FinishBufType* finish_buf =
new (grpc_call_arena_alloc(call->call(), sizeof(FinishBufType)))
FinishBufType;
*finish_buf_ptr = finish_buf;
finish_buf->set_output_tag(tag);
finish_buf->RecvMessage(static_cast<R*>(msg));
finish_buf->AllowNoMessage();
finish_buf->ClientRecvStatus(context, status);
call->PerformOps(finish_buf);
} else {
auto* single_buf = static_cast<SingleBufType*>(single_buf_view);
single_buf->set_output_tag(tag);
single_buf->RecvInitialMetadata(context);
single_buf->RecvMessage(static_cast<R*>(msg));
single_buf->AllowNoMessage();
single_buf->ClientRecvStatus(context, status);
call->PerformOps(single_buf);
}
};
}
static void StartCall(grpc::ClientContext* context,
grpc::internal::CallOpSendInitialMetadata* single_buf) {
single_buf->SendInitialMetadata(&context->send_initial_metadata_,
context->initial_metadata_flags());
}
};
// TODO(vjpai): This templated factory is deprecated and will be replaced by
//. the non-templated helper as soon as possible.
template <class R>
class ClientAsyncResponseReaderFactory {
public:
template <class W>
static ClientAsyncResponseReader<R>* Create(
grpc::ChannelInterface* channel, grpc::CompletionQueue* cq,
const grpc::internal::RpcMethod& method, grpc::ClientContext* context,
const W& request, bool start) {
auto* result = ClientAsyncResponseReaderHelper::Create<R>(
channel, cq, method, context, request);
if (start) {
result->StartCall();
}
return result;
}
};
} // namespace internal
/// Async API for client-side unary RPCs, where the message response
/// received from the server is of type \a R.
template <class R>
class ClientAsyncResponseReader final
: public ClientAsyncResponseReaderInterface<R> {
public:
// always allocated against a call arena, no memory free required
static void operator delete(void* /*ptr*/, std::size_t size) {
GPR_ASSERT(size == sizeof(ClientAsyncResponseReader));
}
// 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*) { GPR_ASSERT(false); }
void StartCall() override {
GPR_DEBUG_ASSERT(!started_);
started_ = true;
internal::ClientAsyncResponseReaderHelper::StartCall(context_, single_buf_);
}
/// See \a ClientAsyncResponseReaderInterface::ReadInitialMetadata for
/// semantics.
///
/// Side effect:
/// - the \a ClientContext associated with this call is updated with
/// possible initial and trailing metadata sent from the server.
void ReadInitialMetadata(void* tag) override {
GPR_DEBUG_ASSERT(started_);
GPR_DEBUG_ASSERT(!context_->initial_metadata_received_);
read_initial_metadata_(context_, &call_, single_buf_, tag);
initial_metadata_read_ = true;
}
/// See \a ClientAsyncResponseReaderInterface::Finish for semantics.
///
/// Side effect:
/// - the \a ClientContext associated with this call is updated with
/// possible initial and trailing metadata sent from the server.
void Finish(R* msg, grpc::Status* status, void* tag) override {
GPR_DEBUG_ASSERT(started_);
finish_(context_, &call_, initial_metadata_read_, single_buf_, &finish_buf_,
static_cast<void*>(msg), status, tag);
}
private:
friend class internal::ClientAsyncResponseReaderHelper;
grpc::ClientContext* const context_;
grpc::internal::Call call_;
bool started_ = false;
bool initial_metadata_read_ = false;
ClientAsyncResponseReader(grpc::internal::Call call,
grpc::ClientContext* context)
: context_(context), call_(call) {}
// disable operator new
static void* operator new(std::size_t size);
static void* operator new(std::size_t /*size*/, void* p) { return p; }
internal::CallOpSendInitialMetadata* single_buf_;
internal::CallOpSetInterface* finish_buf_ = nullptr;
std::function<void(ClientContext*, internal::Call*,
internal::CallOpSendInitialMetadata*, void*)>
read_initial_metadata_;
std::function<void(ClientContext*, internal::Call*,
bool initial_metadata_read,
internal::CallOpSendInitialMetadata*,
internal::CallOpSetInterface**, void*, Status*, void*)>
finish_;
};
/// Async server-side API for handling unary calls, where the single
/// response message sent to the client is of type \a W.
template <class W>
class ServerAsyncResponseWriter final
: public grpc::internal::ServerAsyncStreamingInterface {
public:
explicit ServerAsyncResponseWriter(grpc::ServerContext* ctx)
: call_(nullptr, nullptr, nullptr), ctx_(ctx) {}
/// See \a ServerAsyncStreamingInterface::SendInitialMetadata for semantics.
///
/// Side effect:
/// The initial metadata that will be sent to the client from this op will
/// be taken from the \a ServerContext associated with the call.
///
/// \param[in] tag Tag identifying this request.
void SendInitialMetadata(void* tag) override {
GPR_ASSERT(!ctx_->sent_initial_metadata_);
meta_buf_.set_output_tag(tag);
meta_buf_.SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
meta_buf_.set_compression_level(ctx_->compression_level());
}
ctx_->sent_initial_metadata_ = true;
call_.PerformOps(&meta_buf_);
}
/// Indicate that the stream is to be finished and request notification
/// when the server has sent the appropriate signals to the client to
/// end the call. Should not be used concurrently with other operations.
///
/// \param[in] tag Tag identifying this request.
/// \param[in] status To be sent to the client as the result of the call.
/// \param[in] msg Message to be sent to the client.
///
/// Side effect:
/// - also sends initial metadata if not already sent (using the
/// \a ServerContext associated with this call).
///
/// Note: if \a status has a non-OK code, then \a msg will not be sent,
/// and the client will receive only the status with possible trailing
/// metadata.
///
/// gRPC doesn't take ownership or a reference to msg and status, so it is
/// safe to deallocate them once the Finish operation is complete (i.e. a
/// result arrives in the completion queue).
void Finish(const W& msg, const grpc::Status& status, void* tag) {
finish_buf_.set_output_tag(tag);
finish_buf_.set_core_cq_tag(&finish_buf_);
if (!ctx_->sent_initial_metadata_) {
finish_buf_.SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
finish_buf_.set_compression_level(ctx_->compression_level());
}
ctx_->sent_initial_metadata_ = true;
}
// The response is dropped if the status is not OK.
if (status.ok()) {
finish_buf_.ServerSendStatus(&ctx_->trailing_metadata_,
finish_buf_.SendMessage(msg));
} else {
finish_buf_.ServerSendStatus(&ctx_->trailing_metadata_, status);
}
call_.PerformOps(&finish_buf_);
}
/// Indicate that the stream is to be finished with a non-OK status,
/// and request notification for when the server has finished sending the
/// appropriate signals to the client to end the call.
/// Should not be used concurrently with other operations.
///
/// \param[in] tag Tag identifying this request.
/// \param[in] status To be sent to the client as the result of the call.
/// - Note: \a status must have a non-OK code.
///
/// Side effect:
/// - also sends initial metadata if not already sent (using the
/// \a ServerContext associated with this call).
///
/// gRPC doesn't take ownership or a reference to status, so it is safe to
/// deallocate them once the Finish operation is complete (i.e. a result
/// arrives in the completion queue).
void FinishWithError(const grpc::Status& status, void* tag) {
GPR_ASSERT(!status.ok());
finish_buf_.set_output_tag(tag);
if (!ctx_->sent_initial_metadata_) {
finish_buf_.SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
finish_buf_.set_compression_level(ctx_->compression_level());
}
ctx_->sent_initial_metadata_ = true;
}
finish_buf_.ServerSendStatus(&ctx_->trailing_metadata_, status);
call_.PerformOps(&finish_buf_);
}
private:
void BindCall(grpc::internal::Call* call) override { call_ = *call; }
grpc::internal::Call call_;
grpc::ServerContext* ctx_;
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata>
meta_buf_;
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata,
grpc::internal::CallOpSendMessage,
grpc::internal::CallOpServerSendStatus>
finish_buf_;
};
} // namespace grpc
namespace std {
template <class R>
class default_delete<grpc::ClientAsyncResponseReader<R>> {
public:
void operator()(void* /*p*/) {}
};
template <class R>
class default_delete<grpc::ClientAsyncResponseReaderInterface<R>> {
public:
void operator()(void* /*p*/) {}
};
} // namespace std
#endif // GRPCPP_SUPPORT_ASYNC_UNARY_CALL_H

View File

@@ -0,0 +1,234 @@
//
//
// Copyright 2015 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_SUPPORT_BYTE_BUFFER_H
#define GRPCPP_SUPPORT_BYTE_BUFFER_H
#include <vector>
#include <grpc/byte_buffer.h>
#include <grpc/grpc.h>
#include <grpc/support/log.h>
#include <grpcpp/impl/serialization_traits.h>
#include <grpcpp/support/config.h>
#include <grpcpp/support/slice.h>
#include <grpcpp/support/status.h>
namespace grpc {
class ServerInterface;
class ByteBuffer;
class ServerInterface;
namespace internal {
template <class RequestType, class ResponseType>
class CallbackUnaryHandler;
template <class RequestType, class ResponseType>
class CallbackServerStreamingHandler;
template <class RequestType>
void* UnaryDeserializeHelper(grpc_byte_buffer*, grpc::Status*, RequestType*);
template <class ServiceType, class RequestType, class ResponseType>
class ServerStreamingHandler;
template <grpc::StatusCode code>
class ErrorMethodHandler;
class CallOpSendMessage;
template <class R>
class CallOpRecvMessage;
class CallOpGenericRecvMessage;
class ExternalConnectionAcceptorImpl;
template <class R>
class DeserializeFuncType;
class GrpcByteBufferPeer;
} // namespace internal
/// A sequence of bytes.
class ByteBuffer final {
public:
/// Constuct an empty buffer.
ByteBuffer() : buffer_(nullptr) {}
/// Construct buffer from \a slices, of which there are \a nslices.
ByteBuffer(const Slice* slices, size_t nslices) {
// The following assertions check that the representation of a grpc::Slice
// is identical to that of a grpc_slice: it has a grpc_slice field, and
// nothing else.
static_assert(std::is_same<decltype(slices[0].slice_), grpc_slice>::value,
"Slice must have same representation as grpc_slice");
static_assert(sizeof(Slice) == sizeof(grpc_slice),
"Slice must have same representation as grpc_slice");
// The following assertions check that the representation of a ByteBuffer is
// identical to grpc_byte_buffer*: it has a grpc_byte_buffer* field,
// and nothing else.
static_assert(std::is_same<decltype(buffer_), grpc_byte_buffer*>::value,
"ByteBuffer must have same representation as "
"grpc_byte_buffer*");
static_assert(sizeof(ByteBuffer) == sizeof(grpc_byte_buffer*),
"ByteBuffer must have same representation as "
"grpc_byte_buffer*");
// The const_cast is legal if grpc_raw_byte_buffer_create() does no more
// than its advertised side effect of increasing the reference count of the
// slices it processes, and such an increase does not affect the semantics
// seen by the caller of this constructor.
buffer_ = grpc_raw_byte_buffer_create(
reinterpret_cast<grpc_slice*>(const_cast<Slice*>(slices)), nslices);
}
/// Construct a byte buffer by referencing elements of existing buffer
/// \a buf. Wrapper of core function grpc_byte_buffer_copy . This is not
/// a deep copy; it is just a referencing. As a result, its performance is
/// size-independent.
ByteBuffer(const ByteBuffer& buf) : buffer_(nullptr) { operator=(buf); }
~ByteBuffer() {
if (buffer_) {
grpc_byte_buffer_destroy(buffer_);
}
}
/// Wrapper of core function grpc_byte_buffer_copy . This is not
/// a deep copy; it is just a referencing. As a result, its performance is
/// size-independent.
ByteBuffer& operator=(const ByteBuffer& buf) {
if (this != &buf) {
Clear(); // first remove existing data
}
if (buf.buffer_) {
// then copy
buffer_ = grpc_byte_buffer_copy(buf.buffer_);
}
return *this;
}
// If this ByteBuffer's representation is a single flat slice, returns a
// slice referencing that array.
Status TrySingleSlice(Slice* slice) const;
/// Dump (read) the buffer contents into \a slics.
Status DumpToSingleSlice(Slice* slice) const;
/// Dump (read) the buffer contents into \a slices.
Status Dump(std::vector<Slice>* slices) const;
/// Remove all data.
void Clear() {
if (buffer_) {
grpc_byte_buffer_destroy(buffer_);
buffer_ = nullptr;
}
}
/// Make a duplicate copy of the internals of this byte
/// buffer so that we have our own owned version of it.
/// bbuf.Duplicate(); is equivalent to bbuf=bbuf; but is actually readable.
/// This is not a deep copy; it is a referencing and its performance
/// is size-independent.
void Duplicate() { buffer_ = grpc_byte_buffer_copy(buffer_); }
/// Forget underlying byte buffer without destroying
/// Use this only for un-owned byte buffers
void Release() { buffer_ = nullptr; }
/// Buffer size in bytes.
size_t Length() const {
return buffer_ == nullptr ? 0 : grpc_byte_buffer_length(buffer_);
}
/// Swap the state of *this and *other.
void Swap(ByteBuffer* other) {
grpc_byte_buffer* tmp = other->buffer_;
other->buffer_ = buffer_;
buffer_ = tmp;
}
/// Is this ByteBuffer valid?
bool Valid() const { return (buffer_ != nullptr); }
private:
friend class SerializationTraits<ByteBuffer, void>;
friend class ServerInterface;
friend class internal::CallOpSendMessage;
template <class R>
friend class internal::CallOpRecvMessage;
friend class internal::CallOpGenericRecvMessage;
template <class RequestType>
friend void* internal::UnaryDeserializeHelper(grpc_byte_buffer*,
grpc::Status*, RequestType*);
template <class ServiceType, class RequestType, class ResponseType>
friend class internal::ServerStreamingHandler;
template <class RequestType, class ResponseType>
friend class internal::CallbackUnaryHandler;
template <class RequestType, class ResponseType>
friend class internal::CallbackServerStreamingHandler;
template <StatusCode code>
friend class internal::ErrorMethodHandler;
template <class R>
friend class internal::DeserializeFuncType;
friend class ProtoBufferReader;
friend class ProtoBufferWriter;
friend class internal::GrpcByteBufferPeer;
friend class internal::ExternalConnectionAcceptorImpl;
grpc_byte_buffer* buffer_;
// takes ownership
void set_buffer(grpc_byte_buffer* buf) {
if (buffer_) {
Clear();
}
buffer_ = buf;
}
grpc_byte_buffer* c_buffer() { return buffer_; }
grpc_byte_buffer** c_buffer_ptr() { return &buffer_; }
class ByteBufferPointer {
public:
// NOLINTNEXTLINE(google-explicit-constructor)
ByteBufferPointer(const ByteBuffer* b)
: bbuf_(const_cast<ByteBuffer*>(b)) {}
// NOLINTNEXTLINE(google-explicit-constructor)
operator ByteBuffer*() { return bbuf_; }
// NOLINTNEXTLINE(google-explicit-constructor)
operator grpc_byte_buffer*() { return bbuf_->buffer_; }
// NOLINTNEXTLINE(google-explicit-constructor)
operator grpc_byte_buffer**() { return &bbuf_->buffer_; }
private:
ByteBuffer* bbuf_;
};
ByteBufferPointer bbuf_ptr() const { return ByteBufferPointer(this); }
};
template <>
class SerializationTraits<ByteBuffer, void> {
public:
static Status Deserialize(ByteBuffer* byte_buffer, ByteBuffer* dest) {
dest->set_buffer(byte_buffer->buffer_);
return Status::OK;
}
static Status Serialize(const ByteBuffer& source, ByteBuffer* buffer,
bool* own_buffer) {
*buffer = source;
*own_buffer = true;
return grpc::Status::OK;
}
};
} // namespace grpc
#endif // GRPCPP_SUPPORT_BYTE_BUFFER_H

View File

@@ -0,0 +1,221 @@
//
//
// 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_SUPPORT_CALLBACK_COMMON_H
#define GRPCPP_SUPPORT_CALLBACK_COMMON_H
#include <functional>
#include <grpc/grpc.h>
#include <grpc/impl/grpc_types.h>
#include <grpc/support/log.h>
#include <grpcpp/impl/call.h>
#include <grpcpp/impl/codegen/channel_interface.h>
#include <grpcpp/impl/completion_queue_tag.h>
#include <grpcpp/support/config.h>
#include <grpcpp/support/status.h>
namespace grpc {
namespace internal {
/// An exception-safe way of invoking a user-specified callback function
// TODO(vjpai): decide whether it is better for this to take a const lvalue
// parameter or an rvalue parameter, or if it even matters
template <class Func, class... Args>
void CatchingCallback(Func&& func, Args&&... args) {
#if GRPC_ALLOW_EXCEPTIONS
try {
func(std::forward<Args>(args)...);
} catch (...) {
// nothing to return or change here, just don't crash the library
}
#else // GRPC_ALLOW_EXCEPTIONS
func(std::forward<Args>(args)...);
#endif // GRPC_ALLOW_EXCEPTIONS
}
template <class Reactor, class Func, class... Args>
Reactor* CatchingReactorGetter(Func&& func, Args&&... args) {
#if GRPC_ALLOW_EXCEPTIONS
try {
return func(std::forward<Args>(args)...);
} catch (...) {
// fail the RPC, don't crash the library
return nullptr;
}
#else // GRPC_ALLOW_EXCEPTIONS
return func(std::forward<Args>(args)...);
#endif // GRPC_ALLOW_EXCEPTIONS
}
// The contract on these tags is that they are single-shot. They must be
// constructed and then fired at exactly one point. There is no expectation
// that they can be reused without reconstruction.
class CallbackWithStatusTag : public grpc_completion_queue_functor {
public:
// always allocated against a call arena, no memory free required
static void operator delete(void* /*ptr*/, std::size_t size) {
GPR_ASSERT(size == sizeof(CallbackWithStatusTag));
}
// 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*) { GPR_ASSERT(false); }
CallbackWithStatusTag(grpc_call* call, std::function<void(Status)> f,
CompletionQueueTag* ops)
: call_(call), func_(std::move(f)), ops_(ops) {
grpc_call_ref(call);
functor_run = &CallbackWithStatusTag::StaticRun;
// A client-side callback should never be run inline since they will always
// have work to do from the user application. So, set the parent's
// inlineable field to false
inlineable = false;
}
~CallbackWithStatusTag() {}
Status* status_ptr() { return &status_; }
// force_run can not be performed on a tag if operations using this tag
// have been sent to PerformOpsOnCall. It is intended for error conditions
// that are detected before the operations are internally processed.
void force_run(Status s) {
status_ = std::move(s);
Run(true);
}
private:
grpc_call* call_;
std::function<void(Status)> func_;
CompletionQueueTag* ops_;
Status status_;
static void StaticRun(grpc_completion_queue_functor* cb, int ok) {
static_cast<CallbackWithStatusTag*>(cb)->Run(static_cast<bool>(ok));
}
void Run(bool ok) {
void* ignored = ops_;
if (!ops_->FinalizeResult(&ignored, &ok)) {
// The tag was swallowed
return;
}
GPR_ASSERT(ignored == ops_);
// Last use of func_ or status_, so ok to move them out
auto func = std::move(func_);
auto status = std::move(status_);
func_ = nullptr; // reset to clear this out for sure
status_ = Status(); // reset to clear this out for sure
CatchingCallback(std::move(func), std::move(status));
grpc_call_unref(call_);
}
};
/// CallbackWithSuccessTag can be reused multiple times, and will be used in
/// this fashion for streaming operations. As a result, it shouldn't clear
/// anything up until its destructor
class CallbackWithSuccessTag : public grpc_completion_queue_functor {
public:
// always allocated against a call arena, no memory free required
static void operator delete(void* /*ptr*/, std::size_t size) {
GPR_ASSERT(size == sizeof(CallbackWithSuccessTag));
}
// 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*) { GPR_ASSERT(false); }
CallbackWithSuccessTag() : call_(nullptr) {}
CallbackWithSuccessTag(const CallbackWithSuccessTag&) = delete;
CallbackWithSuccessTag& operator=(const CallbackWithSuccessTag&) = delete;
~CallbackWithSuccessTag() { Clear(); }
// Set can only be called on a default-constructed or Clear'ed tag.
// It should never be called on a tag that was constructed with arguments
// or on a tag that has been Set before unless the tag has been cleared.
// can_inline indicates that this particular callback can be executed inline
// (without needing a thread hop) and is only used for library-provided server
// callbacks.
void Set(grpc_call* call, std::function<void(bool)> f,
CompletionQueueTag* ops, bool can_inline) {
GPR_ASSERT(call_ == nullptr);
grpc_call_ref(call);
call_ = call;
func_ = std::move(f);
ops_ = ops;
functor_run = &CallbackWithSuccessTag::StaticRun;
inlineable = can_inline;
}
void Clear() {
if (call_ != nullptr) {
grpc_call* call = call_;
call_ = nullptr;
func_ = nullptr;
grpc_call_unref(call);
}
}
CompletionQueueTag* ops() { return ops_; }
// force_run can not be performed on a tag if operations using this tag
// have been sent to PerformOpsOnCall. It is intended for error conditions
// that are detected before the operations are internally processed.
void force_run(bool ok) { Run(ok); }
/// check if this tag is currently set
// NOLINTNEXTLINE(google-explicit-constructor)
operator bool() const { return call_ != nullptr; }
private:
grpc_call* call_;
std::function<void(bool)> func_;
CompletionQueueTag* ops_;
static void StaticRun(grpc_completion_queue_functor* cb, int ok) {
static_cast<CallbackWithSuccessTag*>(cb)->Run(static_cast<bool>(ok));
}
void Run(bool ok) {
void* ignored = ops_;
// Allow a "false" return value from FinalizeResult to silence the
// callback, just as it silences a CQ tag in the async cases
#ifndef NDEBUG
auto* ops = ops_;
#endif
bool do_callback = ops_->FinalizeResult(&ignored, &ok);
GPR_DEBUG_ASSERT(ignored == ops);
if (do_callback) {
CatchingCallback(func_, ok);
}
}
};
} // namespace internal
} // namespace grpc
#endif // GRPCPP_SUPPORT_CALLBACK_COMMON_H

View File

@@ -0,0 +1,146 @@
//
//
// Copyright 2015 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_SUPPORT_CHANNEL_ARGUMENTS_H
#define GRPCPP_SUPPORT_CHANNEL_ARGUMENTS_H
#include <list>
#include <vector>
#include <grpc/compression.h>
#include <grpc/grpc.h>
#include <grpcpp/resource_quota.h>
#include <grpcpp/support/config.h>
namespace grpc {
class SecureChannelCredentials;
namespace testing {
class ChannelArgumentsTest;
} // namespace testing
/// Options for channel creation. The user can use generic setters to pass
/// key value pairs down to C channel creation code. For gRPC related options,
/// concrete setters are provided.
class ChannelArguments {
public:
ChannelArguments();
~ChannelArguments();
ChannelArguments(const ChannelArguments& other);
ChannelArguments& operator=(ChannelArguments other) {
Swap(other);
return *this;
}
void Swap(ChannelArguments& other);
/// Dump arguments in this instance to \a channel_args. Does not take
/// ownership of \a channel_args.
///
/// Note that the underlying arguments are shared. Changes made to either \a
/// channel_args or this instance would be reflected on both.
void SetChannelArgs(grpc_channel_args* channel_args) const;
// gRPC specific channel argument setters
/// Set target name override for SSL host name checking. This option should
/// be used with caution in production.
void SetSslTargetNameOverride(const std::string& name);
// TODO(yangg) add flow control options
/// Set the compression algorithm for the channel.
void SetCompressionAlgorithm(grpc_compression_algorithm algorithm);
/// Set the grpclb fallback timeout (in ms) for the channel. If this amount
/// of time has passed but we have not gotten any non-empty \a serverlist from
/// the balancer, we will fall back to use the backend address(es) returned by
/// the resolver.
void SetGrpclbFallbackTimeout(int fallback_timeout);
/// Set a mutator for the underlying socket.
void SetSocketMutator(grpc_socket_mutator* mutator);
/// Set the string to prepend to the user agent.
void SetUserAgentPrefix(const std::string& user_agent_prefix);
/// Set the buffer pool to be attached to the constructed channel.
void SetResourceQuota(const grpc::ResourceQuota& resource_quota);
/// Set the max receive and send message sizes.
void SetMaxReceiveMessageSize(int size);
void SetMaxSendMessageSize(int size);
/// Set LB policy name.
/// Note that if the name resolver returns only balancer addresses, the
/// grpclb LB policy will be used, regardless of what is specified here.
void SetLoadBalancingPolicyName(const std::string& lb_policy_name);
/// Set service config in JSON form.
/// Primarily meant for use in unit tests.
void SetServiceConfigJSON(const std::string& service_config_json);
// Generic channel argument setter. Only for advanced use cases.
/// Set an integer argument \a value under \a key.
void SetInt(const std::string& key, int value);
// Generic channel argument setter. Only for advanced use cases.
/// Set a pointer argument \a value under \a key. Ownership is not
/// transferred.
void SetPointer(const std::string& key, void* value);
/// Set a pointer argument \a value under \a key, transferring ownership of
/// \a value to the \a ChannelArguments object. The \a vtable::Delete function
/// is responsible for \a value cleanup/destruction when called.
void SetPointerWithVtable(const std::string& key, void* value,
const grpc_arg_pointer_vtable* vtable);
/// Set a textual argument \a value under \a key.
void SetString(const std::string& key, const std::string& value);
/// Return (by value) a C \a grpc_channel_args structure which points to
/// arguments owned by this \a ChannelArguments instance
grpc_channel_args c_channel_args() const {
grpc_channel_args out;
out.num_args = args_.size();
out.args = args_.empty() ? nullptr : const_cast<grpc_arg*>(&args_[0]);
return out;
}
private:
friend class grpc::SecureChannelCredentials;
friend class grpc::testing::ChannelArgumentsTest;
/// Default pointer argument operations.
struct PointerVtableMembers {
static void* Copy(void* in) { return in; }
static void Destroy(void* /*in*/) {}
static int Compare(void* a, void* b) {
if (a < b) return -1;
if (a > b) return 1;
return 0;
}
};
// Returns empty string when it is not set.
std::string GetSslTargetNameOverride() const;
std::vector<grpc_arg> args_;
std::list<std::string> strings_;
};
} // namespace grpc
#endif // GRPCPP_SUPPORT_CHANNEL_ARGUMENTS_H

View File

@@ -0,0 +1,1225 @@
//
//
// 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_SUPPORT_CLIENT_CALLBACK_H
#define GRPCPP_SUPPORT_CLIENT_CALLBACK_H
#include <atomic>
#include <functional>
#include <grpc/grpc.h>
#include <grpc/impl/call.h>
#include <grpc/support/log.h>
#include <grpcpp/impl/call.h>
#include <grpcpp/impl/call_op_set.h>
#include <grpcpp/impl/sync.h>
#include <grpcpp/support/callback_common.h>
#include <grpcpp/support/config.h>
#include <grpcpp/support/status.h>
namespace grpc {
class Channel;
class ClientContext;
namespace internal {
class RpcMethod;
/// Perform a callback-based unary call. May optionally specify the base
/// class of the Request and Response so that the internal calls and structures
/// below this may be based on those base classes and thus achieve code reuse
/// across different RPCs (e.g., for protobuf, MessageLite would be a base
/// class).
/// TODO(vjpai): Combine as much as possible with the blocking unary call code
template <class InputMessage, class OutputMessage,
class BaseInputMessage = InputMessage,
class BaseOutputMessage = OutputMessage>
void CallbackUnaryCall(grpc::ChannelInterface* channel,
const grpc::internal::RpcMethod& method,
grpc::ClientContext* context,
const InputMessage* request, OutputMessage* result,
std::function<void(grpc::Status)> on_completion) {
static_assert(std::is_base_of<BaseInputMessage, InputMessage>::value,
"Invalid input message specification");
static_assert(std::is_base_of<BaseOutputMessage, OutputMessage>::value,
"Invalid output message specification");
CallbackUnaryCallImpl<BaseInputMessage, BaseOutputMessage> x(
channel, method, context, request, result, on_completion);
}
template <class InputMessage, class OutputMessage>
class CallbackUnaryCallImpl {
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) {
grpc::CompletionQueue* cq = channel->CallbackCQ();
GPR_ASSERT(cq != nullptr);
grpc::internal::Call call(channel->CreateCall(method, context, cq));
using FullCallOpSet = grpc::internal::CallOpSet<
grpc::internal::CallOpSendInitialMetadata,
grpc::internal::CallOpSendMessage,
grpc::internal::CallOpRecvInitialMetadata,
grpc::internal::CallOpRecvMessage<OutputMessage>,
grpc::internal::CallOpClientSendClose,
grpc::internal::CallOpClientRecvStatus>;
struct OpSetAndTag {
FullCallOpSet opset;
grpc::internal::CallbackWithStatusTag tag;
};
const size_t alloc_sz = sizeof(OpSetAndTag);
auto* const alloced =
static_cast<OpSetAndTag*>(grpc_call_arena_alloc(call.call(), alloc_sz));
auto* ops = new (&alloced->opset) FullCallOpSet;
auto* tag = new (&alloced->tag)
grpc::internal::CallbackWithStatusTag(call.call(), on_completion, ops);
// TODO(vjpai): Unify code with sync API as much as possible
grpc::Status s = ops->SendMessagePtr(request);
if (!s.ok()) {
tag->force_run(s);
return;
}
ops->SendInitialMetadata(&context->send_initial_metadata_,
context->initial_metadata_flags());
ops->RecvInitialMetadata(context);
ops->RecvMessage(result);
ops->AllowNoMessage();
ops->ClientSendClose();
ops->ClientRecvStatus(context, tag->status_ptr());
ops->set_core_cq_tag(tag);
call.PerformOps(ops);
}
};
// Base class for public API classes.
class ClientReactor {
public:
virtual ~ClientReactor() = default;
/// Called by the library when all operations associated with this RPC have
/// completed and all Holds have been removed. OnDone provides the RPC status
/// outcome for both successful and failed RPCs. If it is never called on an
/// RPC, it indicates an application-level problem (like failure to remove a
/// hold).
///
/// \param[in] s The status outcome of this RPC
virtual void OnDone(const grpc::Status& /*s*/) = 0;
/// InternalTrailersOnly is not part of the API and is not meant to be
/// overridden. It is virtual to allow successful builds for certain bazel
/// build users that only want to depend on gRPC codegen headers and not the
/// full library (although this is not a generally-supported option). Although
/// the virtual call is slower than a direct call, this function is
/// heavyweight and the cost of the virtual call is not much in comparison.
/// This function may be removed or devirtualized in the future.
virtual bool InternalTrailersOnly(const grpc_call* call) const;
};
} // namespace internal
// Forward declarations
template <class Request, class Response>
class ClientBidiReactor;
template <class Response>
class ClientReadReactor;
template <class Request>
class ClientWriteReactor;
class ClientUnaryReactor;
// NOTE: The streaming objects are not actually implemented in the public API.
// These interfaces are provided for mocking only. Typical applications
// 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, grpc::WriteOptions options) = 0;
virtual void WritesDone() = 0;
virtual void Read(Response* resp) = 0;
virtual void AddHold(int holds) = 0;
virtual void RemoveHold() = 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;
virtual void AddHold(int holds) = 0;
virtual void RemoveHold() = 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, grpc::WriteOptions()); }
virtual void Write(const Request* req, grpc::WriteOptions options) = 0;
void WriteLast(const Request* req, grpc::WriteOptions options) {
Write(req, options.set_last_message());
}
virtual void WritesDone() = 0;
virtual void AddHold(int holds) = 0;
virtual void RemoveHold() = 0;
protected:
void BindReactor(ClientWriteReactor<Request>* reactor) {
reactor->BindWriter(this);
}
};
class ClientCallbackUnary {
public:
virtual ~ClientCallbackUnary() {}
virtual void StartCall() = 0;
protected:
void BindReactor(ClientUnaryReactor* reactor);
};
// The following classes are the reactor interfaces that are to be implemented
// by the user. They are passed in to the library as an argument to a call on a
// stub (either a codegen-ed call or a generic call). The streaming RPC is
// activated by calling StartCall, possibly after initiating StartRead,
// StartWrite, or AddHold operations on the streaming object. Note that none of
// the classes are pure; all reactions have a default empty reaction so that the
// user class only needs to override those reactions that it cares about.
// The reactor must be passed to the stub invocation before any of the below
// operations can be called and its reactions will be invoked by the library in
// response to the completion of various operations. Reactions must not include
// blocking operations (such as blocking I/O, starting synchronous RPCs, or
// waiting on condition variables). Reactions may be invoked concurrently,
// except that OnDone is called after all others (assuming proper API usage).
// The reactor may not be deleted until OnDone is called.
/// \a ClientBidiReactor is the interface for a bidirectional streaming RPC.
template <class Request, class Response>
class ClientBidiReactor : public internal::ClientReactor {
public:
/// Activate the RPC and initiate any reads or writes that have been Start'ed
/// before this call. All streaming RPCs issued by the client MUST have
/// StartCall invoked on them (even if they are canceled) as this call is the
/// activation of their lifecycle.
void StartCall() { stream_->StartCall(); }
/// Initiate a read operation (or post it for later initiation if StartCall
/// has not yet been invoked).
///
/// \param[out] resp Where to eventually store the read message. Valid when
/// the library calls OnReadDone
void StartRead(Response* resp) { stream_->Read(resp); }
/// Initiate a write operation (or post it for later initiation if StartCall
/// has not yet been invoked).
///
/// \param[in] req The message to be written. The library does not take
/// ownership but the caller must ensure that the message is
/// not deleted or modified until OnWriteDone is called.
void StartWrite(const Request* req) { StartWrite(req, grpc::WriteOptions()); }
/// Initiate/post a write operation with specified options.
///
/// \param[in] req The message to be written. The library does not take
/// ownership but the caller must ensure that the message is
/// not deleted or modified until OnWriteDone is called.
/// \param[in] options The WriteOptions to use for writing this message
void StartWrite(const Request* req, grpc::WriteOptions options) {
stream_->Write(req, options);
}
/// Initiate/post a write operation with specified options and an indication
/// that this is the last write (like StartWrite and StartWritesDone, merged).
/// Note that calling this means that no more calls to StartWrite,
/// StartWriteLast, or StartWritesDone are allowed.
///
/// \param[in] req The message to be written. The library does not take
/// ownership but the caller must ensure that the message is
/// not deleted or modified until OnWriteDone is called.
/// \param[in] options The WriteOptions to use for writing this message
void StartWriteLast(const Request* req, grpc::WriteOptions options) {
StartWrite(req, options.set_last_message());
}
/// Indicate that the RPC will have no more write operations. This can only be
/// issued once for a given RPC. This is not required or allowed if
/// StartWriteLast is used since that already has the same implication.
/// Note that calling this means that no more calls to StartWrite,
/// StartWriteLast, or StartWritesDone are allowed.
void StartWritesDone() { stream_->WritesDone(); }
/// Holds are needed if (and only if) this stream has operations that take
/// place on it after StartCall but from outside one of the reactions
/// (OnReadDone, etc). This is _not_ a common use of the streaming API.
///
/// Holds must be added before calling StartCall. If a stream still has a hold
/// in place, its resources will not be destroyed even if the status has
/// already come in from the wire and there are currently no active callbacks
/// outstanding. Similarly, the stream will not call OnDone if there are still
/// holds on it.
///
/// For example, if a StartRead or StartWrite operation is going to be
/// initiated from elsewhere in the application, the application should call
/// AddHold or AddMultipleHolds before StartCall. If there is going to be,
/// for example, a read-flow and a write-flow taking place outside the
/// reactions, then call AddMultipleHolds(2) before StartCall. When the
/// application knows that it won't issue any more read operations (such as
/// when a read comes back as not ok), it should issue a RemoveHold(). It
/// should also call RemoveHold() again after it does StartWriteLast or
/// StartWritesDone that indicates that there will be no more write ops.
/// The number of RemoveHold calls must match the total number of AddHold
/// calls plus the number of holds added by AddMultipleHolds.
/// The argument to AddMultipleHolds must be positive.
void AddHold() { AddMultipleHolds(1); }
void AddMultipleHolds(int holds) {
GPR_DEBUG_ASSERT(holds > 0);
stream_->AddHold(holds);
}
void RemoveHold() { stream_->RemoveHold(); }
/// Notifies the application that all operations associated with this RPC
/// have completed and all Holds have been removed. OnDone provides the RPC
/// status outcome for both successful and failed RPCs and will be called in
/// all cases. If it is not called, it indicates an application-level problem
/// (like failure to remove a hold).
///
/// \param[in] s The status outcome of this RPC
void OnDone(const grpc::Status& /*s*/) override {}
/// Notifies the application that a read of initial metadata from the
/// server is done. If the application chooses not to implement this method,
/// it can assume that the initial metadata has been read before the first
/// call of OnReadDone or OnDone.
///
/// \param[in] ok Was the initial metadata read successfully? If false, no
/// new read/write operation will succeed, and any further
/// Start* operations should not be called.
virtual void OnReadInitialMetadataDone(bool /*ok*/) {}
/// Notifies the application that a StartRead operation completed.
///
/// \param[in] ok Was it successful? If false, no new read/write operation
/// will succeed, and any further Start* should not be called.
virtual void OnReadDone(bool /*ok*/) {}
/// Notifies the application that a StartWrite or StartWriteLast operation
/// completed.
///
/// \param[in] ok Was it successful? If false, no new read/write operation
/// will succeed, and any further Start* should not be called.
virtual void OnWriteDone(bool /*ok*/) {}
/// Notifies the application that a StartWritesDone operation completed. Note
/// that this is only used on explicit StartWritesDone operations and not for
/// those that are implicitly invoked as part of a StartWriteLast.
///
/// \param[in] ok Was it successful? If false, the application will later see
/// the failure reflected as a bad status in OnDone and no
/// further Start* should be called.
virtual void OnWritesDoneDone(bool /*ok*/) {}
private:
friend class ClientCallbackReaderWriter<Request, Response>;
void BindStream(ClientCallbackReaderWriter<Request, Response>* stream) {
stream_ = stream;
}
ClientCallbackReaderWriter<Request, Response>* stream_;
};
/// \a ClientReadReactor is the interface for a server-streaming RPC.
/// All public methods behave as in ClientBidiReactor.
template <class Response>
class ClientReadReactor : public internal::ClientReactor {
public:
void StartCall() { reader_->StartCall(); }
void StartRead(Response* resp) { reader_->Read(resp); }
void AddHold() { AddMultipleHolds(1); }
void AddMultipleHolds(int holds) {
GPR_DEBUG_ASSERT(holds > 0);
reader_->AddHold(holds);
}
void RemoveHold() { reader_->RemoveHold(); }
void OnDone(const grpc::Status& /*s*/) override {}
virtual void OnReadInitialMetadataDone(bool /*ok*/) {}
virtual void OnReadDone(bool /*ok*/) {}
private:
friend class ClientCallbackReader<Response>;
void BindReader(ClientCallbackReader<Response>* reader) { reader_ = reader; }
ClientCallbackReader<Response>* reader_;
};
/// \a ClientWriteReactor is the interface for a client-streaming RPC.
/// All public methods behave as in ClientBidiReactor.
template <class Request>
class ClientWriteReactor : public internal::ClientReactor {
public:
void StartCall() { writer_->StartCall(); }
void StartWrite(const Request* req) { StartWrite(req, grpc::WriteOptions()); }
void StartWrite(const Request* req, grpc::WriteOptions options) {
writer_->Write(req, options);
}
void StartWriteLast(const Request* req, grpc::WriteOptions options) {
StartWrite(req, options.set_last_message());
}
void StartWritesDone() { writer_->WritesDone(); }
void AddHold() { AddMultipleHolds(1); }
void AddMultipleHolds(int holds) {
GPR_DEBUG_ASSERT(holds > 0);
writer_->AddHold(holds);
}
void RemoveHold() { writer_->RemoveHold(); }
void OnDone(const grpc::Status& /*s*/) override {}
virtual void OnReadInitialMetadataDone(bool /*ok*/) {}
virtual void OnWriteDone(bool /*ok*/) {}
virtual void OnWritesDoneDone(bool /*ok*/) {}
private:
friend class ClientCallbackWriter<Request>;
void BindWriter(ClientCallbackWriter<Request>* writer) { writer_ = writer; }
ClientCallbackWriter<Request>* writer_;
};
/// \a ClientUnaryReactor is a reactor-style interface for a unary RPC.
/// This is _not_ a common way of invoking a unary RPC. In practice, this
/// option should be used only if the unary RPC wants to receive initial
/// metadata without waiting for the response to complete. Most deployments of
/// RPC systems do not use this option, but it is needed for generality.
/// All public methods behave as in ClientBidiReactor.
/// StartCall is included for consistency with the other reactor flavors: even
/// though there are no StartRead or StartWrite operations to queue before the
/// call (that is part of the unary call itself) and there is no reactor object
/// being created as a result of this call, we keep a consistent 2-phase
/// initiation API among all the reactor flavors.
class ClientUnaryReactor : public internal::ClientReactor {
public:
void StartCall() { call_->StartCall(); }
void OnDone(const grpc::Status& /*s*/) override {}
virtual void OnReadInitialMetadataDone(bool /*ok*/) {}
private:
friend class ClientCallbackUnary;
void BindCall(ClientCallbackUnary* call) { call_ = call; }
ClientCallbackUnary* call_;
};
// Define function out-of-line from class to avoid forward declaration issue
inline void ClientCallbackUnary::BindReactor(ClientUnaryReactor* reactor) {
reactor->BindCall(this);
}
namespace 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 ClientCallbackReaderWriter<Request, Response> {
public:
// always allocated against a call arena, no memory free required
static void operator delete(void* /*ptr*/, std::size_t size) {
GPR_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*) { GPR_ASSERT(false); }
void StartCall() ABSL_LOCKS_EXCLUDED(start_mu_) 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. Any write backlog
// 4. Recv trailing metadata (unless corked)
if (!start_corked_) {
start_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
context_->initial_metadata_flags());
}
call_.PerformOps(&start_ops_);
{
grpc::internal::MutexLock lock(&start_mu_);
if (backlog_.read_ops) {
call_.PerformOps(&read_ops_);
}
if (backlog_.write_ops) {
call_.PerformOps(&write_ops_);
}
if (backlog_.writes_done_ops) {
call_.PerformOps(&writes_done_ops_);
}
call_.PerformOps(&finish_ops_);
// The last thing in this critical section is to set started_ so that it
// can be used lock-free as well.
started_.store(true, std::memory_order_release);
}
// MaybeFinish outside the lock to make sure that destruction of this object
// doesn't take place while holding the lock (which would cause the lock to
// be released after destruction)
this->MaybeFinish(/*from_reaction=*/false);
}
void Read(Response* msg) override {
read_ops_.RecvMessage(msg);
callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed);
if (GPR_UNLIKELY(!started_.load(std::memory_order_acquire))) {
grpc::internal::MutexLock lock(&start_mu_);
if (GPR_LIKELY(!started_.load(std::memory_order_relaxed))) {
backlog_.read_ops = true;
return;
}
}
call_.PerformOps(&read_ops_);
}
void Write(const Request* msg, grpc::WriteOptions options)
ABSL_LOCKS_EXCLUDED(start_mu_) override {
if (options.is_last_message()) {
options.set_buffer_hint();
write_ops_.ClientSendClose();
}
// TODO(vjpai): don't assert
GPR_ASSERT(write_ops_.SendMessagePtr(msg, options).ok());
callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed);
if (GPR_UNLIKELY(corked_write_needed_)) {
write_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
context_->initial_metadata_flags());
corked_write_needed_ = false;
}
if (GPR_UNLIKELY(!started_.load(std::memory_order_acquire))) {
grpc::internal::MutexLock lock(&start_mu_);
if (GPR_LIKELY(!started_.load(std::memory_order_relaxed))) {
backlog_.write_ops = true;
return;
}
}
call_.PerformOps(&write_ops_);
}
void WritesDone() ABSL_LOCKS_EXCLUDED(start_mu_) override {
writes_done_ops_.ClientSendClose();
writes_done_tag_.Set(
call_.call(),
[this](bool ok) {
reactor_->OnWritesDoneDone(ok);
MaybeFinish(/*from_reaction=*/true);
},
&writes_done_ops_, /*can_inline=*/false);
writes_done_ops_.set_core_cq_tag(&writes_done_tag_);
callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed);
if (GPR_UNLIKELY(corked_write_needed_)) {
writes_done_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
context_->initial_metadata_flags());
corked_write_needed_ = false;
}
if (GPR_UNLIKELY(!started_.load(std::memory_order_acquire))) {
grpc::internal::MutexLock lock(&start_mu_);
if (GPR_LIKELY(!started_.load(std::memory_order_relaxed))) {
backlog_.writes_done_ops = true;
return;
}
}
call_.PerformOps(&writes_done_ops_);
}
void AddHold(int holds) override {
callbacks_outstanding_.fetch_add(holds, std::memory_order_relaxed);
}
void RemoveHold() override { MaybeFinish(/*from_reaction=*/false); }
private:
friend class ClientCallbackReaderWriterFactory<Request, Response>;
ClientCallbackReaderWriterImpl(grpc::internal::Call call,
grpc::ClientContext* context,
ClientBidiReactor<Request, Response>* reactor)
: context_(context),
call_(call),
reactor_(reactor),
start_corked_(context_->initial_metadata_corked_),
corked_write_needed_(start_corked_) {
this->BindReactor(reactor);
// Set up the unchanging parts of the start, read, and write tags and ops.
start_tag_.Set(
call_.call(),
[this](bool ok) {
reactor_->OnReadInitialMetadataDone(
ok && !reactor_->InternalTrailersOnly(call_.call()));
MaybeFinish(/*from_reaction=*/true);
},
&start_ops_, /*can_inline=*/false);
start_ops_.RecvInitialMetadata(context_);
start_ops_.set_core_cq_tag(&start_tag_);
write_tag_.Set(
call_.call(),
[this](bool ok) {
reactor_->OnWriteDone(ok);
MaybeFinish(/*from_reaction=*/true);
},
&write_ops_, /*can_inline=*/false);
write_ops_.set_core_cq_tag(&write_tag_);
read_tag_.Set(
call_.call(),
[this](bool ok) {
reactor_->OnReadDone(ok);
MaybeFinish(/*from_reaction=*/true);
},
&read_ops_, /*can_inline=*/false);
read_ops_.set_core_cq_tag(&read_tag_);
// Also set up the Finish tag and op set.
finish_tag_.Set(
call_.call(),
[this](bool /*ok*/) { MaybeFinish(/*from_reaction=*/true); },
&finish_ops_,
/*can_inline=*/false);
finish_ops_.ClientRecvStatus(context_, &finish_status_);
finish_ops_.set_core_cq_tag(&finish_tag_);
}
// MaybeFinish can be called from reactions or from user-initiated operations
// like StartCall or RemoveHold. If this is the last operation or hold on this
// object, it will invoke the OnDone reaction. If MaybeFinish was called from
// a reaction, it can call OnDone directly. If not, it would need to schedule
// OnDone onto an executor thread to avoid the possibility of deadlocking with
// any locks in the user code that invoked it.
void MaybeFinish(bool from_reaction) {
if (GPR_UNLIKELY(callbacks_outstanding_.fetch_sub(
1, std::memory_order_acq_rel) == 1)) {
grpc::Status s = std::move(finish_status_);
auto* reactor = reactor_;
auto* call = call_.call();
this->~ClientCallbackReaderWriterImpl();
if (GPR_LIKELY(from_reaction)) {
grpc_call_unref(call);
reactor->OnDone(s);
} else {
grpc_call_run_in_event_engine(
call, [reactor, s = std::move(s)]() { reactor->OnDone(s); });
grpc_call_unref(call);
}
}
}
grpc::ClientContext* const context_;
grpc::internal::Call call_;
ClientBidiReactor<Request, Response>* const reactor_;
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata,
grpc::internal::CallOpRecvInitialMetadata>
start_ops_;
grpc::internal::CallbackWithSuccessTag start_tag_;
const bool start_corked_;
bool corked_write_needed_; // no lock needed since only accessed in
// Write/WritesDone which cannot be concurrent
grpc::internal::CallOpSet<grpc::internal::CallOpClientRecvStatus> finish_ops_;
grpc::internal::CallbackWithSuccessTag finish_tag_;
grpc::Status finish_status_;
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata,
grpc::internal::CallOpSendMessage,
grpc::internal::CallOpClientSendClose>
write_ops_;
grpc::internal::CallbackWithSuccessTag write_tag_;
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata,
grpc::internal::CallOpClientSendClose>
writes_done_ops_;
grpc::internal::CallbackWithSuccessTag writes_done_tag_;
grpc::internal::CallOpSet<grpc::internal::CallOpRecvMessage<Response>>
read_ops_;
grpc::internal::CallbackWithSuccessTag read_tag_;
struct StartCallBacklog {
bool write_ops = false;
bool writes_done_ops = false;
bool read_ops = false;
};
StartCallBacklog backlog_ ABSL_GUARDED_BY(start_mu_);
// Minimum of 3 callbacks to pre-register for start ops, StartCall, and finish
std::atomic<intptr_t> callbacks_outstanding_{3};
std::atomic_bool started_{false};
grpc::internal::Mutex start_mu_;
};
template <class Request, class Response>
class ClientCallbackReaderWriterFactory {
public:
static void Create(grpc::ChannelInterface* channel,
const grpc::internal::RpcMethod& method,
grpc::ClientContext* context,
ClientBidiReactor<Request, Response>* reactor) {
grpc::internal::Call call =
channel->CreateCall(method, context, channel->CallbackCQ());
grpc_call_ref(call.call());
new (grpc_call_arena_alloc(
call.call(), sizeof(ClientCallbackReaderWriterImpl<Request, Response>)))
ClientCallbackReaderWriterImpl<Request, Response>(call, context,
reactor);
}
};
template <class Response>
class ClientCallbackReaderImpl : public ClientCallbackReader<Response> {
public:
// always allocated against a call arena, no memory free required
static void operator delete(void* /*ptr*/, std::size_t size) {
GPR_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*) { GPR_ASSERT(false); }
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
start_tag_.Set(
call_.call(),
[this](bool ok) {
reactor_->OnReadInitialMetadataDone(
ok && !reactor_->InternalTrailersOnly(call_.call()));
MaybeFinish(/*from_reaction=*/true);
},
&start_ops_, /*can_inline=*/false);
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(/*from_reaction=*/true);
},
&read_ops_, /*can_inline=*/false);
read_ops_.set_core_cq_tag(&read_tag_);
{
grpc::internal::MutexLock lock(&start_mu_);
if (backlog_.read_ops) {
call_.PerformOps(&read_ops_);
}
started_.store(true, std::memory_order_release);
}
finish_tag_.Set(
call_.call(),
[this](bool /*ok*/) { MaybeFinish(/*from_reaction=*/true); },
&finish_ops_, /*can_inline=*/false);
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_.fetch_add(1, std::memory_order_relaxed);
if (GPR_UNLIKELY(!started_.load(std::memory_order_acquire))) {
grpc::internal::MutexLock lock(&start_mu_);
if (GPR_LIKELY(!started_.load(std::memory_order_relaxed))) {
backlog_.read_ops = true;
return;
}
}
call_.PerformOps(&read_ops_);
}
void AddHold(int holds) override {
callbacks_outstanding_.fetch_add(holds, std::memory_order_relaxed);
}
void RemoveHold() override { MaybeFinish(/*from_reaction=*/false); }
private:
friend class ClientCallbackReaderFactory<Response>;
template <class Request>
ClientCallbackReaderImpl(grpc::internal::Call call,
grpc::ClientContext* context, Request* request,
ClientReadReactor<Response>* reactor)
: context_(context), call_(call), reactor_(reactor) {
this->BindReactor(reactor);
// TODO(vjpai): don't assert
GPR_ASSERT(start_ops_.SendMessagePtr(request).ok());
start_ops_.ClientSendClose();
}
// MaybeFinish behaves as in ClientCallbackReaderWriterImpl.
void MaybeFinish(bool from_reaction) {
if (GPR_UNLIKELY(callbacks_outstanding_.fetch_sub(
1, std::memory_order_acq_rel) == 1)) {
grpc::Status s = std::move(finish_status_);
auto* reactor = reactor_;
auto* call = call_.call();
this->~ClientCallbackReaderImpl();
if (GPR_LIKELY(from_reaction)) {
grpc_call_unref(call);
reactor->OnDone(s);
} else {
grpc_call_run_in_event_engine(
call, [reactor, s = std::move(s)]() { reactor->OnDone(s); });
grpc_call_unref(call);
}
}
}
grpc::ClientContext* const context_;
grpc::internal::Call call_;
ClientReadReactor<Response>* const reactor_;
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata,
grpc::internal::CallOpSendMessage,
grpc::internal::CallOpClientSendClose,
grpc::internal::CallOpRecvInitialMetadata>
start_ops_;
grpc::internal::CallbackWithSuccessTag start_tag_;
grpc::internal::CallOpSet<grpc::internal::CallOpClientRecvStatus> finish_ops_;
grpc::internal::CallbackWithSuccessTag finish_tag_;
grpc::Status finish_status_;
grpc::internal::CallOpSet<grpc::internal::CallOpRecvMessage<Response>>
read_ops_;
grpc::internal::CallbackWithSuccessTag read_tag_;
struct StartCallBacklog {
bool read_ops = false;
};
StartCallBacklog backlog_ ABSL_GUARDED_BY(start_mu_);
// Minimum of 2 callbacks to pre-register for start and finish
std::atomic<intptr_t> callbacks_outstanding_{2};
std::atomic_bool started_{false};
grpc::internal::Mutex start_mu_;
};
template <class Response>
class ClientCallbackReaderFactory {
public:
template <class Request>
static void Create(grpc::ChannelInterface* channel,
const grpc::internal::RpcMethod& method,
grpc::ClientContext* context, const Request* request,
ClientReadReactor<Response>* reactor) {
grpc::internal::Call call =
channel->CreateCall(method, context, channel->CallbackCQ());
grpc_call_ref(call.call());
new (grpc_call_arena_alloc(call.call(),
sizeof(ClientCallbackReaderImpl<Response>)))
ClientCallbackReaderImpl<Response>(call, context, request, reactor);
}
};
template <class Request>
class ClientCallbackWriterImpl : public ClientCallbackWriter<Request> {
public:
// always allocated against a call arena, no memory free required
static void operator delete(void* /*ptr*/, std::size_t size) {
GPR_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*) { GPR_ASSERT(false); }
void StartCall() ABSL_LOCKS_EXCLUDED(start_mu_) 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
if (!start_corked_) {
start_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
context_->initial_metadata_flags());
}
call_.PerformOps(&start_ops_);
{
grpc::internal::MutexLock lock(&start_mu_);
if (backlog_.write_ops) {
call_.PerformOps(&write_ops_);
}
if (backlog_.writes_done_ops) {
call_.PerformOps(&writes_done_ops_);
}
call_.PerformOps(&finish_ops_);
// The last thing in this critical section is to set started_ so that it
// can be used lock-free as well.
started_.store(true, std::memory_order_release);
}
// MaybeFinish outside the lock to make sure that destruction of this object
// doesn't take place while holding the lock (which would cause the lock to
// be released after destruction)
this->MaybeFinish(/*from_reaction=*/false);
}
void Write(const Request* msg, grpc::WriteOptions options)
ABSL_LOCKS_EXCLUDED(start_mu_) override {
if (GPR_UNLIKELY(options.is_last_message())) {
options.set_buffer_hint();
write_ops_.ClientSendClose();
}
// TODO(vjpai): don't assert
GPR_ASSERT(write_ops_.SendMessagePtr(msg, options).ok());
callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed);
if (GPR_UNLIKELY(corked_write_needed_)) {
write_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
context_->initial_metadata_flags());
corked_write_needed_ = false;
}
if (GPR_UNLIKELY(!started_.load(std::memory_order_acquire))) {
grpc::internal::MutexLock lock(&start_mu_);
if (GPR_LIKELY(!started_.load(std::memory_order_relaxed))) {
backlog_.write_ops = true;
return;
}
}
call_.PerformOps(&write_ops_);
}
void WritesDone() ABSL_LOCKS_EXCLUDED(start_mu_) override {
writes_done_ops_.ClientSendClose();
writes_done_tag_.Set(
call_.call(),
[this](bool ok) {
reactor_->OnWritesDoneDone(ok);
MaybeFinish(/*from_reaction=*/true);
},
&writes_done_ops_, /*can_inline=*/false);
writes_done_ops_.set_core_cq_tag(&writes_done_tag_);
callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed);
if (GPR_UNLIKELY(corked_write_needed_)) {
writes_done_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
context_->initial_metadata_flags());
corked_write_needed_ = false;
}
if (GPR_UNLIKELY(!started_.load(std::memory_order_acquire))) {
grpc::internal::MutexLock lock(&start_mu_);
if (GPR_LIKELY(!started_.load(std::memory_order_relaxed))) {
backlog_.writes_done_ops = true;
return;
}
}
call_.PerformOps(&writes_done_ops_);
}
void AddHold(int holds) override {
callbacks_outstanding_.fetch_add(holds, std::memory_order_relaxed);
}
void RemoveHold() override { MaybeFinish(/*from_reaction=*/false); }
private:
friend class ClientCallbackWriterFactory<Request>;
template <class Response>
ClientCallbackWriterImpl(grpc::internal::Call call,
grpc::ClientContext* context, Response* response,
ClientWriteReactor<Request>* reactor)
: context_(context),
call_(call),
reactor_(reactor),
start_corked_(context_->initial_metadata_corked_),
corked_write_needed_(start_corked_) {
this->BindReactor(reactor);
// Set up the unchanging parts of the start and write tags and ops.
start_tag_.Set(
call_.call(),
[this](bool ok) {
reactor_->OnReadInitialMetadataDone(
ok && !reactor_->InternalTrailersOnly(call_.call()));
MaybeFinish(/*from_reaction=*/true);
},
&start_ops_, /*can_inline=*/false);
start_ops_.RecvInitialMetadata(context_);
start_ops_.set_core_cq_tag(&start_tag_);
write_tag_.Set(
call_.call(),
[this](bool ok) {
reactor_->OnWriteDone(ok);
MaybeFinish(/*from_reaction=*/true);
},
&write_ops_, /*can_inline=*/false);
write_ops_.set_core_cq_tag(&write_tag_);
// Also set up the Finish tag and op set.
finish_ops_.RecvMessage(response);
finish_ops_.AllowNoMessage();
finish_tag_.Set(
call_.call(),
[this](bool /*ok*/) { MaybeFinish(/*from_reaction=*/true); },
&finish_ops_,
/*can_inline=*/false);
finish_ops_.ClientRecvStatus(context_, &finish_status_);
finish_ops_.set_core_cq_tag(&finish_tag_);
}
// MaybeFinish behaves as in ClientCallbackReaderWriterImpl.
void MaybeFinish(bool from_reaction) {
if (GPR_UNLIKELY(callbacks_outstanding_.fetch_sub(
1, std::memory_order_acq_rel) == 1)) {
grpc::Status s = std::move(finish_status_);
auto* reactor = reactor_;
auto* call = call_.call();
this->~ClientCallbackWriterImpl();
if (GPR_LIKELY(from_reaction)) {
grpc_call_unref(call);
reactor->OnDone(s);
} else {
grpc_call_run_in_event_engine(
call, [reactor, s = std::move(s)]() { reactor->OnDone(s); });
grpc_call_unref(call);
}
}
}
grpc::ClientContext* const context_;
grpc::internal::Call call_;
ClientWriteReactor<Request>* const reactor_;
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata,
grpc::internal::CallOpRecvInitialMetadata>
start_ops_;
grpc::internal::CallbackWithSuccessTag start_tag_;
const bool start_corked_;
bool corked_write_needed_; // no lock needed since only accessed in
// Write/WritesDone which cannot be concurrent
grpc::internal::CallOpSet<grpc::internal::CallOpGenericRecvMessage,
grpc::internal::CallOpClientRecvStatus>
finish_ops_;
grpc::internal::CallbackWithSuccessTag finish_tag_;
grpc::Status finish_status_;
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata,
grpc::internal::CallOpSendMessage,
grpc::internal::CallOpClientSendClose>
write_ops_;
grpc::internal::CallbackWithSuccessTag write_tag_;
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata,
grpc::internal::CallOpClientSendClose>
writes_done_ops_;
grpc::internal::CallbackWithSuccessTag writes_done_tag_;
struct StartCallBacklog {
bool write_ops = false;
bool writes_done_ops = false;
};
StartCallBacklog backlog_ ABSL_GUARDED_BY(start_mu_);
// Minimum of 3 callbacks to pre-register for start ops, StartCall, and finish
std::atomic<intptr_t> callbacks_outstanding_{3};
std::atomic_bool started_{false};
grpc::internal::Mutex start_mu_;
};
template <class Request>
class ClientCallbackWriterFactory {
public:
template <class Response>
static void Create(grpc::ChannelInterface* channel,
const grpc::internal::RpcMethod& method,
grpc::ClientContext* context, Response* response,
ClientWriteReactor<Request>* reactor) {
grpc::internal::Call call =
channel->CreateCall(method, context, channel->CallbackCQ());
grpc_call_ref(call.call());
new (grpc_call_arena_alloc(call.call(),
sizeof(ClientCallbackWriterImpl<Request>)))
ClientCallbackWriterImpl<Request>(call, context, response, reactor);
}
};
class ClientCallbackUnaryImpl final : public ClientCallbackUnary {
public:
// always allocated against a call arena, no memory free required
static void operator delete(void* /*ptr*/, std::size_t size) {
GPR_ASSERT(size == sizeof(ClientCallbackUnaryImpl));
}
// This operator should never be called as the memory should be freed as part
// of the arena destruction. It only exists to provide a matching operator
// delete to the operator new so that some compilers will not complain (see
// https://github.com/grpc/grpc/issues/11301) Note at the time of adding this
// there are no tests catching the compiler warning.
static void operator delete(void*, void*) { GPR_ASSERT(false); }
void StartCall() override {
// This call initiates two batches, each with a callback
// 1. Send initial metadata + write + writes done + recv initial metadata
// 2. Read message, recv trailing metadata
start_tag_.Set(
call_.call(),
[this](bool ok) {
reactor_->OnReadInitialMetadataDone(
ok && !reactor_->InternalTrailersOnly(call_.call()));
MaybeFinish();
},
&start_ops_, /*can_inline=*/false);
start_ops_.SendInitialMetadata(&context_->send_initial_metadata_,
context_->initial_metadata_flags());
start_ops_.RecvInitialMetadata(context_);
start_ops_.set_core_cq_tag(&start_tag_);
call_.PerformOps(&start_ops_);
finish_tag_.Set(
call_.call(), [this](bool /*ok*/) { MaybeFinish(); }, &finish_ops_,
/*can_inline=*/false);
finish_ops_.ClientRecvStatus(context_, &finish_status_);
finish_ops_.set_core_cq_tag(&finish_tag_);
call_.PerformOps(&finish_ops_);
}
private:
friend class ClientCallbackUnaryFactory;
template <class Request, class Response>
ClientCallbackUnaryImpl(grpc::internal::Call call,
grpc::ClientContext* context, Request* request,
Response* response, ClientUnaryReactor* reactor)
: context_(context), call_(call), reactor_(reactor) {
this->BindReactor(reactor);
// TODO(vjpai): don't assert
GPR_ASSERT(start_ops_.SendMessagePtr(request).ok());
start_ops_.ClientSendClose();
finish_ops_.RecvMessage(response);
finish_ops_.AllowNoMessage();
}
// In the unary case, MaybeFinish is only ever invoked from a
// library-initiated reaction, so it will just directly call OnDone if this is
// the last reaction for this RPC.
void MaybeFinish() {
if (GPR_UNLIKELY(callbacks_outstanding_.fetch_sub(
1, std::memory_order_acq_rel) == 1)) {
grpc::Status s = std::move(finish_status_);
auto* reactor = reactor_;
auto* call = call_.call();
this->~ClientCallbackUnaryImpl();
grpc_call_unref(call);
reactor->OnDone(s);
}
}
grpc::ClientContext* const context_;
grpc::internal::Call call_;
ClientUnaryReactor* const reactor_;
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata,
grpc::internal::CallOpSendMessage,
grpc::internal::CallOpClientSendClose,
grpc::internal::CallOpRecvInitialMetadata>
start_ops_;
grpc::internal::CallbackWithSuccessTag start_tag_;
grpc::internal::CallOpSet<grpc::internal::CallOpGenericRecvMessage,
grpc::internal::CallOpClientRecvStatus>
finish_ops_;
grpc::internal::CallbackWithSuccessTag finish_tag_;
grpc::Status finish_status_;
// This call will have 2 callbacks: start and finish
std::atomic<intptr_t> callbacks_outstanding_{2};
};
class ClientCallbackUnaryFactory {
public:
template <class Request, class Response, class BaseRequest = Request,
class BaseResponse = Response>
static void Create(grpc::ChannelInterface* channel,
const grpc::internal::RpcMethod& method,
grpc::ClientContext* context, const Request* request,
Response* response, ClientUnaryReactor* reactor) {
grpc::internal::Call call =
channel->CreateCall(method, context, channel->CallbackCQ());
grpc_call_ref(call.call());
new (grpc_call_arena_alloc(call.call(), sizeof(ClientCallbackUnaryImpl)))
ClientCallbackUnaryImpl(call, context,
static_cast<const BaseRequest*>(request),
static_cast<BaseResponse*>(response), reactor);
}
};
} // namespace internal
} // namespace grpc
#endif // GRPCPP_SUPPORT_CLIENT_CALLBACK_H

View File

@@ -0,0 +1,213 @@
//
//
// Copyright 2015 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_SUPPORT_CLIENT_INTERCEPTOR_H
#define GRPCPP_SUPPORT_CLIENT_INTERCEPTOR_H
#include <memory>
#include <vector>
#include <grpc/support/log.h>
#include <grpcpp/impl/rpc_method.h>
#include <grpcpp/support/interceptor.h>
#include <grpcpp/support/string_ref.h>
namespace grpc {
class Channel;
class ClientContext;
namespace internal {
class InterceptorBatchMethodsImpl;
}
namespace experimental {
class ClientRpcInfo;
// A factory interface for creation of client interceptors. A vector of
// factories can be provided at channel creation which will be used to create a
// new vector of client interceptors per RPC. Client interceptor authors should
// create a subclass of ClientInterceptorFactorInterface which creates objects
// of their interceptors.
class ClientInterceptorFactoryInterface {
public:
virtual ~ClientInterceptorFactoryInterface() {}
// Returns a pointer to an Interceptor object on successful creation, nullptr
// otherwise. If nullptr is returned, this server interceptor factory is
// ignored for the purposes of that RPC.
virtual Interceptor* CreateClientInterceptor(ClientRpcInfo* info) = 0;
};
} // namespace experimental
namespace internal {
extern experimental::ClientInterceptorFactoryInterface*
g_global_client_interceptor_factory;
extern experimental::ClientInterceptorFactoryInterface*
g_global_client_stats_interceptor_factory;
} // namespace internal
/// ClientRpcInfo represents the state of a particular RPC as it
/// appears to an interceptor. It is created and owned by the library and
/// passed to the CreateClientInterceptor method of the application's
/// ClientInterceptorFactoryInterface implementation
namespace experimental {
class ClientRpcInfo {
public:
// TODO(yashykt): Stop default-constructing ClientRpcInfo and remove UNKNOWN
// from the list of possible Types.
/// Type categorizes RPCs by unary or streaming type
enum class Type {
UNARY,
CLIENT_STREAMING,
SERVER_STREAMING,
BIDI_STREAMING,
UNKNOWN // UNKNOWN is not API and will be removed later
};
~ClientRpcInfo() {}
// Delete copy constructor but allow default move constructor
ClientRpcInfo(const ClientRpcInfo&) = delete;
ClientRpcInfo(ClientRpcInfo&&) = default;
// Getter methods
/// Return the fully-specified method name
const char* method() const { return method_; }
/// Return an identifying suffix for the client stub, or nullptr if one wasn't
/// specified.
const char* suffix_for_stats() const { return suffix_for_stats_; }
/// Return a pointer to the channel on which the RPC is being sent
ChannelInterface* channel() { return channel_; }
/// Return a pointer to the underlying ClientContext structure associated
/// with the RPC to support features that apply to it
grpc::ClientContext* client_context() { return ctx_; }
/// Return the type of the RPC (unary or a streaming flavor)
Type type() const { return type_; }
private:
static_assert(Type::UNARY ==
static_cast<Type>(internal::RpcMethod::NORMAL_RPC),
"violated expectation about Type enum");
static_assert(Type::CLIENT_STREAMING ==
static_cast<Type>(internal::RpcMethod::CLIENT_STREAMING),
"violated expectation about Type enum");
static_assert(Type::SERVER_STREAMING ==
static_cast<Type>(internal::RpcMethod::SERVER_STREAMING),
"violated expectation about Type enum");
static_assert(Type::BIDI_STREAMING ==
static_cast<Type>(internal::RpcMethod::BIDI_STREAMING),
"violated expectation about Type enum");
// Default constructor should only be used by ClientContext
ClientRpcInfo() = default;
// Constructor will only be called from ClientContext
ClientRpcInfo(grpc::ClientContext* ctx, internal::RpcMethod::RpcType type,
const char* method, const char* suffix_for_stats,
grpc::ChannelInterface* channel)
: ctx_(ctx),
type_(static_cast<Type>(type)),
method_(method),
suffix_for_stats_(suffix_for_stats),
channel_(channel) {}
// Move assignment should only be used by ClientContext
// TODO(yashykt): Delete move assignment
ClientRpcInfo& operator=(ClientRpcInfo&&) = default;
// Runs interceptor at pos \a pos.
void RunInterceptor(
experimental::InterceptorBatchMethods* interceptor_methods, size_t pos) {
GPR_ASSERT(pos < interceptors_.size());
interceptors_[pos]->Intercept(interceptor_methods);
}
void RegisterInterceptors(
const std::vector<std::unique_ptr<
experimental::ClientInterceptorFactoryInterface>>& creators,
size_t interceptor_pos) {
// TODO(yashykt): This calculation seems broken for the case where an
// interceptor factor returns nullptr.
size_t num_interceptors =
creators.size() +
(internal::g_global_client_stats_interceptor_factory != nullptr) +
(internal::g_global_client_interceptor_factory != nullptr);
if (interceptor_pos > num_interceptors) {
// No interceptors to register
return;
}
if (internal::g_global_client_stats_interceptor_factory != nullptr) {
interceptors_.push_back(std::unique_ptr<experimental::Interceptor>(
internal::g_global_client_stats_interceptor_factory
->CreateClientInterceptor(this)));
--interceptor_pos;
}
// NOTE: The following is not a range-based for loop because it will only
// iterate over a portion of the creators vector.
for (auto it = creators.begin() + interceptor_pos; it != creators.end();
++it) {
auto* interceptor = (*it)->CreateClientInterceptor(this);
if (interceptor != nullptr) {
interceptors_.push_back(
std::unique_ptr<experimental::Interceptor>(interceptor));
}
}
if (internal::g_global_client_interceptor_factory != nullptr) {
interceptors_.push_back(std::unique_ptr<experimental::Interceptor>(
internal::g_global_client_interceptor_factory
->CreateClientInterceptor(this)));
}
}
grpc::ClientContext* ctx_ = nullptr;
// TODO(yashykt): make type_ const once move-assignment is deleted
Type type_{Type::UNKNOWN};
const char* method_ = nullptr;
const char* suffix_for_stats_ = nullptr;
grpc::ChannelInterface* channel_ = nullptr;
std::vector<std::unique_ptr<experimental::Interceptor>> interceptors_;
bool hijacked_ = false;
size_t hijacked_interceptor_ = 0;
friend class internal::InterceptorBatchMethodsImpl;
friend class grpc::ClientContext;
};
// PLEASE DO NOT USE THIS. ALWAYS PREFER PER CHANNEL INTERCEPTORS OVER A GLOBAL
// INTERCEPTOR. IF USAGE IS ABSOLUTELY NECESSARY, PLEASE READ THE SAFETY NOTES.
// Registers a global client interceptor factory object, which is used for all
// RPCs made in this process. The application is responsible for maintaining the
// life of the object while gRPC operations are in progress. The global
// interceptor factory should only be registered once at the start of the
// process before any gRPC operations have begun.
void RegisterGlobalClientInterceptorFactory(
ClientInterceptorFactoryInterface* factory);
// For testing purposes only
void TestOnlyResetGlobalClientInterceptorFactory();
} // namespace experimental
} // namespace grpc
#endif // GRPCPP_SUPPORT_CLIENT_INTERCEPTOR_H

View File

@@ -0,0 +1,44 @@
//
//
// Copyright 2015 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_SUPPORT_CONFIG_H
#define GRPCPP_SUPPORT_CONFIG_H
#include <string>
/// The following macros are deprecated and appear only for users
/// with PB files generated using gRPC 1.0.x plugins. They should
/// not be used in new code
#define GRPC_OVERRIDE override // deprecated
#define GRPC_FINAL final // deprecated
#ifdef GRPC_CUSTOM_STRING
#warning GRPC_CUSTOM_STRING is no longer supported. Please use std::string.
#endif
namespace grpc {
// Using grpc::string and grpc::to_string is discouraged in favor of
// std::string and std::to_string. This is only for legacy code using
// them explictly.
using std::string; // deprecated // NOLINT(misc-unused-using-decls)
using std::to_string; // deprecated // NOLINT(misc-unused-using-decls)
} // namespace grpc
#endif // GRPCPP_SUPPORT_CONFIG_H

View File

@@ -0,0 +1,230 @@
//
//
// Copyright 2015 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_SUPPORT_INTERCEPTOR_H
#define GRPCPP_SUPPORT_INTERCEPTOR_H
#include <map>
#include <memory>
#include <string>
#include <grpc/impl/grpc_types.h>
#include <grpcpp/impl/metadata_map.h>
#include <grpcpp/support/byte_buffer.h>
#include <grpcpp/support/config.h>
#include <grpcpp/support/string_ref.h>
namespace grpc {
class ChannelInterface;
class Status;
namespace experimental {
/// An enumeration of different possible points at which the \a Intercept
/// method of the \a Interceptor interface may be called. Any given call
/// to \a Intercept will include one or more of these hook points, and
/// each hook point makes certain types of information available to the
/// interceptor.
/// In these enumeration names, PRE_SEND means that an interception has taken
/// place between the time the application provided a certain type of data
/// (e.g., initial metadata, status) and the time that that data goes to the
/// other side. POST_SEND means that the data has been committed for going to
/// the other side (even if it has not yet been received at the other side).
/// PRE_RECV means an interception between the time that a certain
/// operation has been requested and it is available. POST_RECV means that a
/// result is available but has not yet been passed back to the application.
/// A batch of interception points will only contain either PRE or POST hooks
/// but not both types. For example, a batch with PRE_SEND hook points will not
/// contain POST_RECV or POST_SEND ops. Likewise, a batch with POST_* ops can
/// not contain PRE_* ops.
enum class InterceptionHookPoints {
/// The first three in this list are for clients and servers
PRE_SEND_INITIAL_METADATA,
PRE_SEND_MESSAGE,
POST_SEND_MESSAGE,
PRE_SEND_STATUS, // server only
PRE_SEND_CLOSE, // client only: WritesDone for stream; after write in unary
/// The following three are for hijacked clients only. A batch with PRE_RECV_*
/// hook points will never contain hook points of other types.
PRE_RECV_INITIAL_METADATA,
PRE_RECV_MESSAGE,
PRE_RECV_STATUS,
/// The following two are for all clients and servers
POST_RECV_INITIAL_METADATA,
POST_RECV_MESSAGE,
POST_RECV_STATUS, // client only
POST_RECV_CLOSE, // server only
/// This is a special hook point available to both clients and servers when
/// TryCancel() is performed.
/// - No other hook points will be present along with this.
/// - It is illegal for an interceptor to block/delay this operation.
/// - ALL interceptors see this hook point irrespective of whether the
/// RPC was hijacked or not.
PRE_SEND_CANCEL,
NUM_INTERCEPTION_HOOKS
};
/// Class that is passed as an argument to the \a Intercept method
/// of the application's \a Interceptor interface implementation. It has five
/// purposes:
/// 1. Indicate which hook points are present at a specific interception
/// 2. Allow an interceptor to inform the library that an RPC should
/// continue to the next stage of its processing (which may be another
/// interceptor or the main path of the library)
/// 3. Allow an interceptor to hijack the processing of the RPC (only for
/// client-side RPCs with PRE_SEND_INITIAL_METADATA) so that it does not
/// proceed with normal processing beyond that stage
/// 4. Access the relevant fields of an RPC at each interception point
/// 5. Set some fields of an RPC at each interception point, when possible
class InterceptorBatchMethods {
public:
virtual ~InterceptorBatchMethods() {}
/// Determine whether the current batch has an interception hook point
/// of type \a type
virtual bool QueryInterceptionHookPoint(InterceptionHookPoints type) = 0;
/// Signal that the interceptor is done intercepting the current batch of the
/// RPC. Every interceptor must either call Proceed or Hijack on each
/// interception. In most cases, only Proceed will be used. Explicit use of
/// Proceed is what enables interceptors to delay the processing of RPCs
/// while they perform other work.
/// Proceed is a no-op if the batch contains PRE_SEND_CANCEL. Simply returning
/// from the Intercept method does the job of continuing the RPC in this case.
/// This is because PRE_SEND_CANCEL is always in a separate batch and is not
/// allowed to be delayed.
virtual void Proceed() = 0;
/// Indicate that the interceptor has hijacked the RPC (only valid if the
/// batch contains send_initial_metadata on the client side). Later
/// interceptors in the interceptor list will not be called. Later batches
/// on the same RPC will go through interception, but only up to the point
/// of the hijacking interceptor.
virtual void Hijack() = 0;
/// Send Message Methods
/// GetSerializedSendMessage and GetSendMessage/ModifySendMessage are the
/// available methods to view and modify the request payload. An interceptor
/// can access the payload in either serialized form or non-serialized form
/// but not both at the same time.
/// gRPC performs serialization in a lazy manner, which means
/// that a call to GetSerializedSendMessage will result in a serialization
/// operation if the payload stored is not in the serialized form already; the
/// non-serialized form will be lost and GetSendMessage will no longer return
/// a valid pointer, and this will remain true for later interceptors too.
/// This can change however if ModifySendMessage is used to replace the
/// current payload. Note that ModifySendMessage requires a new payload
/// message in the non-serialized form. This will overwrite the existing
/// payload irrespective of whether it had been serialized earlier. Also note
/// that gRPC Async API requires early serialization of the payload which
/// means that the payload would be available in the serialized form only
/// unless an interceptor replaces the payload with ModifySendMessage.
/// Returns a modifable ByteBuffer holding the serialized form of the message
/// that is going to be sent. Valid for PRE_SEND_MESSAGE interceptions.
/// A return value of nullptr indicates that this ByteBuffer is not valid.
virtual ByteBuffer* GetSerializedSendMessage() = 0;
/// Returns a non-modifiable pointer to the non-serialized form of the message
/// to be sent. Valid for PRE_SEND_MESSAGE interceptions. A return value of
/// nullptr indicates that this field is not valid.
virtual const void* GetSendMessage() = 0;
/// Overwrites the message to be sent with \a message. \a message should be in
/// the non-serialized form expected by the method. Valid for PRE_SEND_MESSAGE
/// interceptions. Note that the interceptor is responsible for maintaining
/// the life of the message till it is serialized or it receives the
/// POST_SEND_MESSAGE interception point, whichever happens earlier. The
/// modifying interceptor may itself force early serialization by calling
/// GetSerializedSendMessage.
virtual void ModifySendMessage(const void* message) = 0;
/// Checks whether the SEND MESSAGE op succeeded. Valid for POST_SEND_MESSAGE
/// interceptions.
virtual bool GetSendMessageStatus() = 0;
/// Returns a modifiable multimap of the initial metadata to be sent. Valid
/// for PRE_SEND_INITIAL_METADATA interceptions. A value of nullptr indicates
/// that this field is not valid.
virtual std::multimap<std::string, std::string>* GetSendInitialMetadata() = 0;
/// Returns the status to be sent. Valid for PRE_SEND_STATUS interceptions.
virtual Status GetSendStatus() = 0;
/// Overwrites the status with \a status. Valid for PRE_SEND_STATUS
/// interceptions.
virtual void ModifySendStatus(const Status& status) = 0;
/// Returns a modifiable multimap of the trailing metadata to be sent. Valid
/// for PRE_SEND_STATUS interceptions. A value of nullptr indicates
/// that this field is not valid.
virtual std::multimap<std::string, std::string>*
GetSendTrailingMetadata() = 0;
/// Returns a pointer to the modifiable received message. Note that the
/// message is already deserialized but the type is not set; the interceptor
/// should static_cast to the appropriate type before using it. This is valid
/// for PRE_RECV_MESSAGE and POST_RECV_MESSAGE interceptions; nullptr for not
/// valid
virtual void* GetRecvMessage() = 0;
/// Returns a modifiable multimap of the received initial metadata.
/// Valid for PRE_RECV_INITIAL_METADATA and POST_RECV_INITIAL_METADATA
/// interceptions; nullptr if not valid
virtual std::multimap<grpc::string_ref, grpc::string_ref>*
GetRecvInitialMetadata() = 0;
/// Returns a modifiable view of the received status on PRE_RECV_STATUS and
/// POST_RECV_STATUS interceptions; nullptr if not valid.
virtual Status* GetRecvStatus() = 0;
/// Returns a modifiable multimap of the received trailing metadata on
/// PRE_RECV_STATUS and POST_RECV_STATUS interceptions; nullptr if not valid
virtual std::multimap<grpc::string_ref, grpc::string_ref>*
GetRecvTrailingMetadata() = 0;
/// Gets an intercepted channel. When a call is started on this interceptor,
/// only interceptors after the current interceptor are created from the
/// factory objects registered with the channel. This allows calls to be
/// started from interceptors without infinite regress through the interceptor
/// list.
virtual std::unique_ptr<ChannelInterface> GetInterceptedChannel() = 0;
/// On a hijacked RPC, an interceptor can decide to fail a PRE_RECV_MESSAGE
/// op. This would be a signal to the reader that there will be no more
/// messages, or the stream has failed or been cancelled.
virtual void FailHijackedRecvMessage() = 0;
/// On a hijacked RPC/ to-be hijacked RPC, this can be called to fail a SEND
/// MESSAGE op
virtual void FailHijackedSendMessage() = 0;
};
/// Interface for an interceptor. Interceptor authors must create a class
/// that derives from this parent class.
class Interceptor {
public:
virtual ~Interceptor() {}
/// The one public method of an Interceptor interface. Override this to
/// trigger the desired actions at the hook points described above.
virtual void Intercept(InterceptorBatchMethods* methods) = 0;
};
} // namespace experimental
} // namespace grpc
#endif // GRPCPP_SUPPORT_INTERCEPTOR_H

View File

@@ -0,0 +1,71 @@
//
//
// Copyright 2019 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_SUPPORT_MESSAGE_ALLOCATOR_H
#define GRPCPP_SUPPORT_MESSAGE_ALLOCATOR_H
namespace grpc {
// NOTE: This is an API for advanced users who need custom allocators.
// Per rpc struct for the allocator. This is the interface to return to user.
class RpcAllocatorState {
public:
virtual ~RpcAllocatorState() = default;
// Optionally deallocate request early to reduce the size of working set.
// A custom MessageAllocator needs to be registered to make use of this.
// This is not abstract because implementing it is optional.
virtual void FreeRequest() {}
};
// This is the interface returned by the allocator.
// grpc library will call the methods to get request/response pointers and to
// release the object when it is done.
template <typename RequestT, typename ResponseT>
class MessageHolder : public RpcAllocatorState {
public:
// Release this object. For example, if the custom allocator's
// AllocateMessasge creates an instance of a subclass with new, the Release()
// should do a "delete this;".
virtual void Release() = 0;
RequestT* request() { return request_; }
ResponseT* response() { return response_; }
protected:
void set_request(RequestT* request) { request_ = request; }
void set_response(ResponseT* response) { response_ = response; }
private:
// NOTE: subclasses should set these pointers.
RequestT* request_;
ResponseT* response_;
};
// A custom allocator can be set via the generated code to a callback unary
// method, such as SetMessageAllocatorFor_Echo(custom_allocator). The allocator
// needs to be alive for the lifetime of the server.
// Implementations need to be thread-safe.
template <typename RequestT, typename ResponseT>
class MessageAllocator {
public:
virtual ~MessageAllocator() = default;
virtual MessageHolder<RequestT, ResponseT>* AllocateMessages() = 0;
};
} // namespace grpc
#endif // GRPCPP_SUPPORT_MESSAGE_ALLOCATOR_H

View File

@@ -0,0 +1,405 @@
//
//
// Copyright 2015 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_SUPPORT_METHOD_HANDLER_H
#define GRPCPP_SUPPORT_METHOD_HANDLER_H
#include <grpc/byte_buffer.h>
#include <grpc/support/log.h>
#include <grpcpp/impl/rpc_service_method.h>
#include <grpcpp/support/byte_buffer.h>
#include <grpcpp/support/sync_stream.h>
namespace grpc {
namespace internal {
// Invoke the method handler, fill in the status, and
// return whether or not we finished safely (without an exception).
// Note that exception handling is 0-cost in most compiler/library
// implementations (except when an exception is actually thrown),
// so this process doesn't require additional overhead in the common case.
// Additionally, we don't need to return if we caught an exception or not;
// the handling is the same in either case.
template <class Callable>
::grpc::Status CatchingFunctionHandler(Callable&& handler) {
#if GRPC_ALLOW_EXCEPTIONS
try {
return handler();
} catch (...) {
return grpc::Status(grpc::StatusCode::UNKNOWN,
"Unexpected error in RPC handling");
}
#else // GRPC_ALLOW_EXCEPTIONS
return handler();
#endif // GRPC_ALLOW_EXCEPTIONS
}
/// A helper function with reduced templating to do the common work needed to
/// actually send the server response. Uses non-const parameter for Status since
/// this should only ever be called from the end of the RunHandler method.
template <class ResponseType>
void UnaryRunHandlerHelper(const MethodHandler::HandlerParameter& param,
ResponseType* rsp, grpc::Status& status) {
GPR_ASSERT(!param.server_context->sent_initial_metadata_);
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata,
grpc::internal::CallOpSendMessage,
grpc::internal::CallOpServerSendStatus>
ops;
ops.SendInitialMetadata(&param.server_context->initial_metadata_,
param.server_context->initial_metadata_flags());
if (param.server_context->compression_level_set()) {
ops.set_compression_level(param.server_context->compression_level());
}
if (status.ok()) {
status = ops.SendMessagePtr(rsp);
}
ops.ServerSendStatus(&param.server_context->trailing_metadata_, status);
param.call->PerformOps(&ops);
param.call->cq()->Pluck(&ops);
}
/// A helper function with reduced templating to do deserializing.
template <class RequestType>
void* UnaryDeserializeHelper(grpc_byte_buffer* req, grpc::Status* status,
RequestType* request) {
grpc::ByteBuffer buf;
buf.set_buffer(req);
*status = grpc::SerializationTraits<RequestType>::Deserialize(
&buf, static_cast<RequestType*>(request));
buf.Release();
if (status->ok()) {
return request;
}
request->~RequestType();
return nullptr;
}
/// A wrapper class of an application provided rpc method handler.
template <class ServiceType, class RequestType, class ResponseType,
class BaseRequestType = RequestType,
class BaseResponseType = ResponseType>
class RpcMethodHandler : public grpc::internal::MethodHandler {
public:
RpcMethodHandler(
std::function<grpc::Status(ServiceType*, grpc::ServerContext*,
const RequestType*, ResponseType*)>
func,
ServiceType* service)
: func_(func), service_(service) {}
void RunHandler(const HandlerParameter& param) final {
ResponseType rsp;
grpc::Status status = param.status;
if (status.ok()) {
status = CatchingFunctionHandler([this, &param, &rsp] {
return func_(service_,
static_cast<grpc::ServerContext*>(param.server_context),
static_cast<RequestType*>(param.request), &rsp);
});
static_cast<RequestType*>(param.request)->~RequestType();
}
UnaryRunHandlerHelper(param, static_cast<BaseResponseType*>(&rsp), status);
}
void* Deserialize(grpc_call* call, grpc_byte_buffer* req,
grpc::Status* status, void** /*handler_data*/) final {
auto* request =
new (grpc_call_arena_alloc(call, sizeof(RequestType))) RequestType;
return UnaryDeserializeHelper(req, status,
static_cast<BaseRequestType*>(request));
}
private:
/// Application provided rpc handler function.
std::function<grpc::Status(ServiceType*, grpc::ServerContext*,
const RequestType*, ResponseType*)>
func_;
// The class the above handler function lives in.
ServiceType* service_;
};
/// A wrapper class of an application provided client streaming handler.
template <class ServiceType, class RequestType, class ResponseType>
class ClientStreamingHandler : public grpc::internal::MethodHandler {
public:
ClientStreamingHandler(
std::function<grpc::Status(ServiceType*, grpc::ServerContext*,
ServerReader<RequestType>*, ResponseType*)>
func,
ServiceType* service)
: func_(func), service_(service) {}
void RunHandler(const HandlerParameter& param) final {
ServerReader<RequestType> reader(
param.call, static_cast<grpc::ServerContext*>(param.server_context));
ResponseType rsp;
grpc::Status status =
CatchingFunctionHandler([this, &param, &reader, &rsp] {
return func_(service_,
static_cast<grpc::ServerContext*>(param.server_context),
&reader, &rsp);
});
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata,
grpc::internal::CallOpSendMessage,
grpc::internal::CallOpServerSendStatus>
ops;
if (!param.server_context->sent_initial_metadata_) {
ops.SendInitialMetadata(&param.server_context->initial_metadata_,
param.server_context->initial_metadata_flags());
if (param.server_context->compression_level_set()) {
ops.set_compression_level(param.server_context->compression_level());
}
}
if (status.ok()) {
status = ops.SendMessagePtr(&rsp);
}
ops.ServerSendStatus(&param.server_context->trailing_metadata_, status);
param.call->PerformOps(&ops);
param.call->cq()->Pluck(&ops);
}
private:
std::function<grpc::Status(ServiceType*, grpc::ServerContext*,
ServerReader<RequestType>*, ResponseType*)>
func_;
ServiceType* service_;
};
/// A wrapper class of an application provided server streaming handler.
template <class ServiceType, class RequestType, class ResponseType>
class ServerStreamingHandler : public grpc::internal::MethodHandler {
public:
ServerStreamingHandler(std::function<grpc::Status(
ServiceType*, grpc::ServerContext*,
const RequestType*, ServerWriter<ResponseType>*)>
func,
ServiceType* service)
: func_(func), service_(service) {}
void RunHandler(const HandlerParameter& param) final {
grpc::Status status = param.status;
if (status.ok()) {
ServerWriter<ResponseType> writer(
param.call, static_cast<grpc::ServerContext*>(param.server_context));
status = CatchingFunctionHandler([this, &param, &writer] {
return func_(service_,
static_cast<grpc::ServerContext*>(param.server_context),
static_cast<RequestType*>(param.request), &writer);
});
static_cast<RequestType*>(param.request)->~RequestType();
}
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata,
grpc::internal::CallOpServerSendStatus>
ops;
if (!param.server_context->sent_initial_metadata_) {
ops.SendInitialMetadata(&param.server_context->initial_metadata_,
param.server_context->initial_metadata_flags());
if (param.server_context->compression_level_set()) {
ops.set_compression_level(param.server_context->compression_level());
}
}
ops.ServerSendStatus(&param.server_context->trailing_metadata_, status);
param.call->PerformOps(&ops);
if (param.server_context->has_pending_ops_) {
param.call->cq()->Pluck(&param.server_context->pending_ops_);
}
param.call->cq()->Pluck(&ops);
}
void* Deserialize(grpc_call* call, grpc_byte_buffer* req,
grpc::Status* status, void** /*handler_data*/) final {
grpc::ByteBuffer buf;
buf.set_buffer(req);
auto* request =
new (grpc_call_arena_alloc(call, sizeof(RequestType))) RequestType();
*status =
grpc::SerializationTraits<RequestType>::Deserialize(&buf, request);
buf.Release();
if (status->ok()) {
return request;
}
request->~RequestType();
return nullptr;
}
private:
std::function<grpc::Status(ServiceType*, grpc::ServerContext*,
const RequestType*, ServerWriter<ResponseType>*)>
func_;
ServiceType* service_;
};
/// A wrapper class of an application provided bidi-streaming handler.
/// This also applies to server-streamed implementation of a unary method
/// with the additional requirement that such methods must have done a
/// write for status to be ok
/// Since this is used by more than 1 class, the service is not passed in.
/// Instead, it is expected to be an implicitly-captured argument of func
/// (through bind or something along those lines)
template <class Streamer, bool WriteNeeded>
class TemplatedBidiStreamingHandler : public grpc::internal::MethodHandler {
public:
explicit TemplatedBidiStreamingHandler(
std::function<grpc::Status(grpc::ServerContext*, Streamer*)> func)
: func_(func), write_needed_(WriteNeeded) {}
void RunHandler(const HandlerParameter& param) final {
Streamer stream(param.call,
static_cast<grpc::ServerContext*>(param.server_context));
grpc::Status status = CatchingFunctionHandler([this, &param, &stream] {
return func_(static_cast<grpc::ServerContext*>(param.server_context),
&stream);
});
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata,
grpc::internal::CallOpServerSendStatus>
ops;
if (!param.server_context->sent_initial_metadata_) {
ops.SendInitialMetadata(&param.server_context->initial_metadata_,
param.server_context->initial_metadata_flags());
if (param.server_context->compression_level_set()) {
ops.set_compression_level(param.server_context->compression_level());
}
if (write_needed_ && status.ok()) {
// If we needed a write but never did one, we need to mark the
// status as a fail
status = grpc::Status(grpc::StatusCode::INTERNAL,
"Service did not provide response message");
}
}
ops.ServerSendStatus(&param.server_context->trailing_metadata_, status);
param.call->PerformOps(&ops);
if (param.server_context->has_pending_ops_) {
param.call->cq()->Pluck(&param.server_context->pending_ops_);
}
param.call->cq()->Pluck(&ops);
}
private:
std::function<grpc::Status(grpc::ServerContext*, Streamer*)> func_;
const bool write_needed_;
};
template <class ServiceType, class RequestType, class ResponseType>
class BidiStreamingHandler
: public TemplatedBidiStreamingHandler<
ServerReaderWriter<ResponseType, RequestType>, false> {
public:
BidiStreamingHandler(std::function<grpc::Status(
ServiceType*, grpc::ServerContext*,
ServerReaderWriter<ResponseType, RequestType>*)>
func,
ServiceType* service)
// TODO(vjpai): When gRPC supports C++14, move-capture func in the below
: TemplatedBidiStreamingHandler<
ServerReaderWriter<ResponseType, RequestType>, false>(
[func, service](
grpc::ServerContext* ctx,
ServerReaderWriter<ResponseType, RequestType>* streamer) {
return func(service, ctx, streamer);
}) {}
};
template <class RequestType, class ResponseType>
class StreamedUnaryHandler
: public TemplatedBidiStreamingHandler<
ServerUnaryStreamer<RequestType, ResponseType>, true> {
public:
explicit StreamedUnaryHandler(
std::function<
grpc::Status(grpc::ServerContext*,
ServerUnaryStreamer<RequestType, ResponseType>*)>
func)
: TemplatedBidiStreamingHandler<
ServerUnaryStreamer<RequestType, ResponseType>, true>(
std::move(func)) {}
};
template <class RequestType, class ResponseType>
class SplitServerStreamingHandler
: public TemplatedBidiStreamingHandler<
ServerSplitStreamer<RequestType, ResponseType>, false> {
public:
explicit SplitServerStreamingHandler(
std::function<
grpc::Status(grpc::ServerContext*,
ServerSplitStreamer<RequestType, ResponseType>*)>
func)
: TemplatedBidiStreamingHandler<
ServerSplitStreamer<RequestType, ResponseType>, false>(
std::move(func)) {}
};
/// General method handler class for errors that prevent real method use
/// e.g., handle unknown method by returning UNIMPLEMENTED error.
template <grpc::StatusCode code>
class ErrorMethodHandler : public grpc::internal::MethodHandler {
public:
explicit ErrorMethodHandler(const std::string& message) : message_(message) {}
template <class T>
static void FillOps(grpc::ServerContextBase* context,
const std::string& message, T* ops) {
grpc::Status status(code, message);
if (!context->sent_initial_metadata_) {
ops->SendInitialMetadata(&context->initial_metadata_,
context->initial_metadata_flags());
if (context->compression_level_set()) {
ops->set_compression_level(context->compression_level());
}
context->sent_initial_metadata_ = true;
}
ops->ServerSendStatus(&context->trailing_metadata_, status);
}
void RunHandler(const HandlerParameter& param) final {
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata,
grpc::internal::CallOpServerSendStatus>
ops;
FillOps(param.server_context, message_, &ops);
param.call->PerformOps(&ops);
param.call->cq()->Pluck(&ops);
}
void* Deserialize(grpc_call* /*call*/, grpc_byte_buffer* req,
grpc::Status* /*status*/, void** /*handler_data*/) final {
// We have to destroy any request payload
if (req != nullptr) {
grpc_byte_buffer_destroy(req);
}
return nullptr;
}
private:
const std::string message_;
};
typedef ErrorMethodHandler<grpc::StatusCode::UNIMPLEMENTED>
UnknownMethodHandler;
typedef ErrorMethodHandler<grpc::StatusCode::RESOURCE_EXHAUSTED>
ResourceExhaustedHandler;
} // namespace internal
} // namespace grpc
#endif // GRPCPP_SUPPORT_METHOD_HANDLER_H

View File

@@ -0,0 +1,223 @@
//
//
// 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_SUPPORT_PROTO_BUFFER_READER_H
#define GRPCPP_SUPPORT_PROTO_BUFFER_READER_H
#include <type_traits>
#include "absl/strings/cord.h"
#include <grpc/byte_buffer.h>
#include <grpc/byte_buffer_reader.h>
#include <grpc/impl/grpc_types.h>
#include <grpc/slice.h>
#include <grpc/support/log.h>
#include <grpcpp/impl/codegen/config_protobuf.h>
#include <grpcpp/impl/serialization_traits.h>
#include <grpcpp/support/byte_buffer.h>
#include <grpcpp/support/status.h>
/// This header provides an object that reads bytes directly from a
/// grpc::ByteBuffer, via the ZeroCopyInputStream interface
namespace grpc {
/// This is a specialization of the protobuf class ZeroCopyInputStream
/// The principle is to get one chunk of data at a time from the proto layer,
/// with options to backup (re-see some bytes) or skip (forward past some bytes)
///
/// Read more about ZeroCopyInputStream interface here:
/// https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.io.zero_copy_stream#ZeroCopyInputStream
class ProtoBufferReader : public grpc::protobuf::io::ZeroCopyInputStream {
public:
/// Constructs buffer reader from \a buffer. Will set \a status() to non ok
/// if \a buffer is invalid (the internal buffer has not been initialized).
explicit ProtoBufferReader(ByteBuffer* buffer)
: byte_count_(0), backup_count_(0), status_() {
/// Implemented through a grpc_byte_buffer_reader which iterates
/// over the slices that make up a byte buffer
if (!buffer->Valid() ||
!grpc_byte_buffer_reader_init(&reader_, buffer->c_buffer())) {
status_ = Status(StatusCode::INTERNAL,
"Couldn't initialize byte buffer reader");
}
}
~ProtoBufferReader() override {
if (status_.ok()) {
grpc_byte_buffer_reader_destroy(&reader_);
}
}
/// Give the proto library a chunk of data from the stream. The caller
/// may safely read from data[0, size - 1].
bool Next(const void** data, int* size) override {
if (!status_.ok()) {
return false;
}
/// If we have backed up previously, we need to return the backed-up slice
if (backup_count_ > 0) {
*data = GRPC_SLICE_START_PTR(*slice_) + GRPC_SLICE_LENGTH(*slice_) -
backup_count_;
GPR_ASSERT(backup_count_ <= INT_MAX);
*size = static_cast<int>(backup_count_);
backup_count_ = 0;
return true;
}
/// Otherwise get the next slice from the byte buffer reader
if (!grpc_byte_buffer_reader_peek(&reader_, &slice_)) {
return false;
}
*data = GRPC_SLICE_START_PTR(*slice_);
// On win x64, int is only 32bit
GPR_ASSERT(GRPC_SLICE_LENGTH(*slice_) <= INT_MAX);
byte_count_ += * size = static_cast<int>(GRPC_SLICE_LENGTH(*slice_));
return true;
}
/// Returns the status of the buffer reader.
Status status() const { return status_; }
/// The proto library calls this to indicate that we should back up \a count
/// bytes that have already been returned by the last call of Next.
/// So do the backup and have that ready for a later Next.
void BackUp(int count) override {
GPR_ASSERT(count <= static_cast<int>(GRPC_SLICE_LENGTH(*slice_)));
backup_count_ = count;
}
/// The proto library calls this to skip over \a count bytes. Implement this
/// using Next and BackUp combined.
bool Skip(int count) override {
const void* data;
int size;
while (Next(&data, &size)) {
if (size >= count) {
BackUp(size - count);
return true;
}
// size < count;
count -= size;
}
// error or we have too large count;
return false;
}
/// Returns the total number of bytes read since this object was created.
int64_t ByteCount() const override { return byte_count_ - backup_count_; }
#ifdef GRPC_PROTOBUF_CORD_SUPPORT_ENABLED
/// Read the next `count` bytes and append it to the given Cord.
// (override is conditionally omitted here to support old Protobuf which
// doesn't have ReadCord method)
// NOLINTBEGIN(modernize-use-override,
// clang-diagnostic-inconsistent-missing-override)
virtual bool ReadCord(absl::Cord* cord, int count)
#if GOOGLE_PROTOBUF_VERSION >= 4022000
override
#endif
// NOLINTEND(modernize-use-override,
// clang-diagnostic-inconsistent-missing-override)
{
if (!status().ok()) {
return false;
}
// check for backed up data
if (backup_count() > 0) {
if (backup_count() <= count) {
cord->Append(MakeCordFromSlice(grpc_slice_split_tail(
slice(), GRPC_SLICE_LENGTH(*slice()) - backup_count())));
} else {
cord->Append(MakeCordFromSlice(grpc_slice_sub(
*slice(), GRPC_SLICE_LENGTH(*slice()) - backup_count(),
GRPC_SLICE_LENGTH(*slice()) - backup_count() + count)));
}
int64_t take = (std::min)(backup_count(), static_cast<int64_t>(count));
set_backup_count(backup_count() - take);
// This cast is safe as the size of a serialized protobuf message
// should be smaller than 2GiB.
// (https://protobuf.dev/programming-guides/encoding/#size-limit)
count -= static_cast<int>(take);
if (count == 0) {
return true;
}
}
while (count > 0) {
if (!grpc_byte_buffer_reader_peek(reader(), mutable_slice_ptr())) {
return false;
}
uint64_t slice_length = GRPC_SLICE_LENGTH(*slice());
set_byte_count(ByteCount() + slice_length);
if (slice_length <= static_cast<uint64_t>(count)) {
cord->Append(MakeCordFromSlice(grpc_slice_ref(*slice())));
// This cast is safe as above.
count -= static_cast<int>(slice_length);
} else {
cord->Append(MakeCordFromSlice(grpc_slice_split_head(slice(), count)));
set_backup_count(slice_length - count);
return true;
}
}
GPR_ASSERT(count == 0);
return true;
}
#endif // GRPC_PROTOBUF_CORD_SUPPORT_ENABLED
// These protected members are needed to support internal optimizations.
// they expose internal bits of grpc core that are NOT stable. If you have
// a use case needs to use one of these functions, please send an email to
// https://groups.google.com/forum/#!forum/grpc-io.
protected:
void set_byte_count(int64_t byte_count) { byte_count_ = byte_count; }
int64_t backup_count() { return backup_count_; }
void set_backup_count(int64_t backup_count) { backup_count_ = backup_count; }
grpc_byte_buffer_reader* reader() { return &reader_; }
grpc_slice* slice() { return slice_; }
grpc_slice** mutable_slice_ptr() { return &slice_; }
private:
#ifdef GRPC_PROTOBUF_CORD_SUPPORT_ENABLED
// This function takes ownership of slice and return a newly created Cord off
// of it.
static absl::Cord MakeCordFromSlice(grpc_slice slice) {
// slice_for_cord is created to keep inlined data of the given slice
grpc_slice* slice_for_cord = new grpc_slice;
*slice_for_cord = slice;
return absl::MakeCordFromExternal(
absl::string_view(
reinterpret_cast<char*>(GRPC_SLICE_START_PTR(*slice_for_cord)),
GRPC_SLICE_LENGTH(*slice_for_cord)),
[slice_for_cord](absl::string_view /* view */) {
grpc_slice_unref(*slice_for_cord);
delete slice_for_cord;
});
}
#endif // GRPC_PROTOBUF_CORD_SUPPORT_ENABLED
int64_t byte_count_; ///< total bytes read since object creation
int64_t backup_count_; ///< how far backed up in the stream we are
grpc_byte_buffer_reader reader_; ///< internal object to read \a grpc_slice
///< from the \a grpc_byte_buffer
grpc_slice* slice_; ///< current slice passed back to the caller
Status status_; ///< status of the entire object
};
} // namespace grpc
#endif // GRPCPP_SUPPORT_PROTO_BUFFER_READER_H

View File

@@ -0,0 +1,220 @@
//
//
// 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_SUPPORT_PROTO_BUFFER_WRITER_H
#define GRPCPP_SUPPORT_PROTO_BUFFER_WRITER_H
#include <type_traits>
#include "absl/strings/cord.h"
#include <grpc/byte_buffer.h>
#include <grpc/impl/grpc_types.h>
#include <grpc/slice.h>
#include <grpc/slice_buffer.h>
#include <grpc/support/log.h>
#include <grpcpp/impl/codegen/config_protobuf.h>
#include <grpcpp/impl/serialization_traits.h>
#include <grpcpp/support/byte_buffer.h>
#include <grpcpp/support/status.h>
/// This header provides an object that writes bytes directly into a
/// grpc::ByteBuffer, via the ZeroCopyOutputStream interface
namespace grpc {
// Forward declaration for testing use only
namespace internal {
class ProtoBufferWriterPeer;
} // namespace internal
const int kProtoBufferWriterMaxBufferLength = 1024 * 1024;
/// This is a specialization of the protobuf class ZeroCopyOutputStream.
/// The principle is to give the proto layer one buffer of bytes at a time
/// that it can use to serialize the next portion of the message, with the
/// option to "backup" if more buffer is given than required at the last buffer.
///
/// Read more about ZeroCopyOutputStream interface here:
/// https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.io.zero_copy_stream#ZeroCopyOutputStream
class ProtoBufferWriter : public grpc::protobuf::io::ZeroCopyOutputStream {
public:
/// Constructor for this derived class
///
/// \param[out] byte_buffer A pointer to the grpc::ByteBuffer created
/// \param block_size How big are the chunks to allocate at a time
/// \param total_size How many total bytes are required for this proto
ProtoBufferWriter(ByteBuffer* byte_buffer, int block_size, int total_size)
: block_size_(block_size),
total_size_(total_size),
byte_count_(0),
have_backup_(false) {
GPR_ASSERT(!byte_buffer->Valid());
/// Create an empty raw byte buffer and look at its underlying slice buffer
grpc_byte_buffer* bp = grpc_raw_byte_buffer_create(nullptr, 0);
byte_buffer->set_buffer(bp);
slice_buffer_ = &bp->data.raw.slice_buffer;
}
~ProtoBufferWriter() override {
if (have_backup_) {
grpc_slice_unref(backup_slice_);
}
}
/// Give the proto library the next buffer of bytes and its size. It is
/// safe for the caller to write from data[0, size - 1].
bool Next(void** data, int* size) override {
// Protobuf should not ask for more memory than total_size_.
GPR_ASSERT(byte_count_ < total_size_);
// 1. Use the remaining backup slice if we have one
// 2. Otherwise allocate a slice, up to the remaining length needed
// or our maximum allocation size
// 3. Provide the slice start and size available
// 4. Add the slice being returned to the slice buffer
size_t remain = static_cast<size_t>(total_size_ - byte_count_);
if (have_backup_) {
/// If we have a backup slice, we should use it first
slice_ = backup_slice_;
have_backup_ = false;
if (GRPC_SLICE_LENGTH(slice_) > remain) {
GRPC_SLICE_SET_LENGTH(slice_, remain);
}
} else {
// When less than a whole block is needed, only allocate that much.
// But make sure the allocated slice is not inlined.
size_t allocate_length =
remain > static_cast<size_t>(block_size_) ? block_size_ : remain;
slice_ = grpc_slice_malloc(allocate_length > GRPC_SLICE_INLINED_SIZE
? allocate_length
: GRPC_SLICE_INLINED_SIZE + 1);
}
*data = GRPC_SLICE_START_PTR(slice_);
// On win x64, int is only 32bit
GPR_ASSERT(GRPC_SLICE_LENGTH(slice_) <= INT_MAX);
byte_count_ += * size = static_cast<int>(GRPC_SLICE_LENGTH(slice_));
// Using grpc_slice_buffer_add could modify slice_ and merge it with the
// previous slice. Therefore, use grpc_slice_buffer_add_indexed method to
// ensure the slice gets added at a separate index. It can then be kept
// around and popped later in the BackUp function.
grpc_slice_buffer_add_indexed(slice_buffer_, slice_);
return true;
}
/// Backup by \a count bytes because Next returned more bytes than needed
/// (only used in the last buffer). \a count must be less than or equal too
/// the last buffer returned from next.
void BackUp(int count) override {
// count == 0 is invoked by ZeroCopyOutputStream users indicating that any
// potential buffer obtained through a previous call to Next() is final.
// ZeroCopyOutputStream implementations such as streaming output can use
// these calls to flush any temporary buffer and flush the output. The logic
// below is not robust against count == 0 invocations, so directly return.
if (count == 0) return;
/// 1. Remove the partially-used last slice from the slice buffer
/// 2. Split it into the needed (if any) and unneeded part
/// 3. Add the needed part back to the slice buffer
/// 4. Mark that we still have the remaining part (for later use/unref)
GPR_ASSERT(count <= static_cast<int>(GRPC_SLICE_LENGTH(slice_)));
grpc_slice_buffer_pop(slice_buffer_);
if (static_cast<size_t>(count) == GRPC_SLICE_LENGTH(slice_)) {
backup_slice_ = slice_;
} else {
backup_slice_ =
grpc_slice_split_tail(&slice_, GRPC_SLICE_LENGTH(slice_) - count);
grpc_slice_buffer_add(slice_buffer_, slice_);
}
// It's dangerous to keep an inlined grpc_slice as the backup slice, since
// on a following Next() call, a reference will be returned to this slice
// via GRPC_SLICE_START_PTR, which will not be an address held by
// slice_buffer_.
have_backup_ = backup_slice_.refcount != nullptr;
byte_count_ -= count;
}
/// Returns the total number of bytes written since this object was created.
int64_t ByteCount() const override { return byte_count_; }
#ifdef GRPC_PROTOBUF_CORD_SUPPORT_ENABLED
/// Writes cord to the backing byte_buffer, sharing the memory between the
/// blocks of the cord, and the slices of the byte_buffer.
// (override is conditionally omitted here to support old Protobuf which
// doesn't have ReadCord method)
// NOLINTBEGIN(modernize-use-override,
// clang-diagnostic-inconsistent-missing-override)
virtual bool WriteCord(const absl::Cord& cord)
#if GOOGLE_PROTOBUF_VERSION >= 4022000
override
#endif
// NOLINTEND(modernize-use-override,
// clang-diagnostic-inconsistent-missing-override)
{
grpc_slice_buffer* buffer = slice_buffer();
size_t cur = 0;
for (absl::string_view chunk : cord.Chunks()) {
// TODO(veblush): Revisit this 512 threadhold which could be smaller.
if (chunk.size() < 512) {
// If chunk is small enough, just copy it.
grpc_slice slice =
grpc_slice_from_copied_buffer(chunk.data(), chunk.size());
grpc_slice_buffer_add(buffer, slice);
} else {
// If chunk is large, just use the pointer instead of copying.
// To make sure it's alive while being used, a subcord for chunk is
// created and attached to a grpc_slice instance.
absl::Cord* subcord = new absl::Cord(cord.Subcord(cur, chunk.size()));
grpc_slice slice = grpc_slice_new_with_user_data(
const_cast<uint8_t*>(
reinterpret_cast<const uint8_t*>(chunk.data())),
chunk.size(), [](void* p) { delete static_cast<absl::Cord*>(p); },
subcord);
grpc_slice_buffer_add(buffer, slice);
}
cur += chunk.size();
}
set_byte_count(ByteCount() + cur);
return true;
}
#endif // GRPC_PROTOBUF_CORD_SUPPORT_ENABLED
// These protected members are needed to support internal optimizations.
// they expose internal bits of grpc core that are NOT stable. If you have
// a use case needs to use one of these functions, please send an email to
// https://groups.google.com/forum/#!forum/grpc-io.
protected:
grpc_slice_buffer* slice_buffer() { return slice_buffer_; }
void set_byte_count(int64_t byte_count) { byte_count_ = byte_count; }
private:
// friend for testing purposes only
friend class internal::ProtoBufferWriterPeer;
const int block_size_; ///< size to alloc for each new \a grpc_slice needed
const int total_size_; ///< byte size of proto being serialized
int64_t byte_count_; ///< bytes written since this object was created
grpc_slice_buffer*
slice_buffer_; ///< internal buffer of slices holding the serialized data
bool have_backup_; ///< if we are holding a backup slice or not
grpc_slice backup_slice_; ///< holds space we can still write to, if the
///< caller has called BackUp
grpc_slice slice_; ///< current slice passed back to the caller
};
} // namespace grpc
#endif // GRPCPP_SUPPORT_PROTO_BUFFER_WRITER_H

View File

@@ -0,0 +1,797 @@
//
//
// 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_SUPPORT_SERVER_CALLBACK_H
#define GRPCPP_SUPPORT_SERVER_CALLBACK_H
#include <atomic>
#include <functional>
#include <type_traits>
#include <grpcpp/impl/call.h>
#include <grpcpp/impl/call_op_set.h>
#include <grpcpp/impl/sync.h>
#include <grpcpp/support/callback_common.h>
#include <grpcpp/support/config.h>
#include <grpcpp/support/message_allocator.h>
#include <grpcpp/support/status.h>
namespace grpc {
// Declare base class of all reactors as internal
namespace internal {
// Forward declarations
template <class Request, class Response>
class CallbackUnaryHandler;
template <class Request, class Response>
class CallbackClientStreamingHandler;
template <class Request, class Response>
class CallbackServerStreamingHandler;
template <class Request, class Response>
class CallbackBidiHandler;
class ServerReactor {
public:
virtual ~ServerReactor() = default;
virtual void OnDone() = 0;
virtual void OnCancel() = 0;
// The following is not API. It is for internal use only and specifies whether
// all reactions of this Reactor can be run without an extra executor
// scheduling. This should only be used for internally-defined reactors with
// trivial reactions.
virtual bool InternalInlineable() { return false; }
private:
template <class Request, class Response>
friend class CallbackUnaryHandler;
template <class Request, class Response>
friend class CallbackClientStreamingHandler;
template <class Request, class Response>
friend class CallbackServerStreamingHandler;
template <class Request, class Response>
friend class CallbackBidiHandler;
};
/// The base class of ServerCallbackUnary etc.
class ServerCallbackCall {
public:
virtual ~ServerCallbackCall() {}
// This object is responsible for tracking when it is safe to call OnDone and
// OnCancel. OnDone should not be called until the method handler is complete,
// Finish has been called, the ServerContext CompletionOp (which tracks
// cancellation or successful completion) has completed, and all outstanding
// Read/Write actions have seen their reactions. OnCancel should not be called
// until after the method handler is done and the RPC has completed with a
// cancellation. This is tracked by counting how many of these conditions have
// been met and calling OnCancel when none remain unmet.
// Public versions of MaybeDone: one where we don't know the reactor in
// advance (used for the ServerContext CompletionOp), and one for where we
// know the inlineability of the OnDone reaction. You should set the inline
// flag to true if either the Reactor is InternalInlineable() or if this
// callback is already being forced to run dispatched to an executor
// (typically because it contains additional work than just the MaybeDone).
void MaybeDone() {
if (GPR_UNLIKELY(Unref() == 1)) {
ScheduleOnDone(reactor()->InternalInlineable());
}
}
void MaybeDone(bool inline_ondone) {
if (GPR_UNLIKELY(Unref() == 1)) {
ScheduleOnDone(inline_ondone);
}
}
// Fast version called with known reactor passed in, used from derived
// classes, typically in non-cancel case
void MaybeCallOnCancel(ServerReactor* reactor) {
if (GPR_UNLIKELY(UnblockCancellation())) {
CallOnCancel(reactor);
}
}
// Slower version called from object that doesn't know the reactor a priori
// (such as the ServerContext CompletionOp which is formed before the
// reactor). This is used in cancel cases only, so it's ok to be slower and
// invoke a virtual function.
void MaybeCallOnCancel() {
if (GPR_UNLIKELY(UnblockCancellation())) {
CallOnCancel(reactor());
}
}
protected:
/// Increases the reference count
void Ref() { callbacks_outstanding_.fetch_add(1, std::memory_order_relaxed); }
private:
virtual ServerReactor* reactor() = 0;
// CallOnDone performs the work required at completion of the RPC: invoking
// the OnDone function and doing all necessary cleanup. This function is only
// ever invoked on a fully-Unref'fed ServerCallbackCall.
virtual void CallOnDone() = 0;
// If the OnDone reaction is inlineable, execute it inline. Otherwise send it
// to an executor.
void ScheduleOnDone(bool inline_ondone);
// If the OnCancel reaction is inlineable, execute it inline. Otherwise send
// it to an executor.
void CallOnCancel(ServerReactor* reactor);
// Implement the cancellation constraint counter. Return true if OnCancel
// should be called, false otherwise.
bool UnblockCancellation() {
return on_cancel_conditions_remaining_.fetch_sub(
1, std::memory_order_acq_rel) == 1;
}
/// Decreases the reference count and returns the previous value
int Unref() {
return callbacks_outstanding_.fetch_sub(1, std::memory_order_acq_rel);
}
std::atomic_int on_cancel_conditions_remaining_{2};
std::atomic_int callbacks_outstanding_{
3}; // reserve for start, Finish, and CompletionOp
};
template <class Request, class Response>
class DefaultMessageHolder : public MessageHolder<Request, Response> {
public:
DefaultMessageHolder() {
this->set_request(&request_obj_);
this->set_response(&response_obj_);
}
void Release() override {
// the object is allocated in the call arena.
this->~DefaultMessageHolder<Request, Response>();
}
private:
Request request_obj_;
Response response_obj_;
};
} // namespace internal
// Forward declarations
class ServerUnaryReactor;
template <class Request>
class ServerReadReactor;
template <class Response>
class ServerWriteReactor;
template <class Request, class Response>
class ServerBidiReactor;
// NOTE: The actual call/stream object classes are provided as API only to
// support mocking. There are no implementations of these class interfaces in
// the API.
class ServerCallbackUnary : public internal::ServerCallbackCall {
public:
~ServerCallbackUnary() override {}
virtual void Finish(grpc::Status s) = 0;
virtual void SendInitialMetadata() = 0;
protected:
// Use a template rather than explicitly specifying ServerUnaryReactor to
// delay binding and avoid a circular forward declaration issue
template <class Reactor>
void BindReactor(Reactor* reactor) {
reactor->InternalBindCall(this);
}
};
template <class Request>
class ServerCallbackReader : public internal::ServerCallbackCall {
public:
~ServerCallbackReader() override {}
virtual void Finish(grpc::Status s) = 0;
virtual void SendInitialMetadata() = 0;
virtual void Read(Request* msg) = 0;
protected:
void BindReactor(ServerReadReactor<Request>* reactor) {
reactor->InternalBindReader(this);
}
};
template <class Response>
class ServerCallbackWriter : public internal::ServerCallbackCall {
public:
~ServerCallbackWriter() override {}
virtual void Finish(grpc::Status s) = 0;
virtual void SendInitialMetadata() = 0;
virtual void Write(const Response* msg, grpc::WriteOptions options) = 0;
virtual void WriteAndFinish(const Response* msg, grpc::WriteOptions options,
grpc::Status s) = 0;
protected:
void BindReactor(ServerWriteReactor<Response>* reactor) {
reactor->InternalBindWriter(this);
}
};
template <class Request, class Response>
class ServerCallbackReaderWriter : public internal::ServerCallbackCall {
public:
~ServerCallbackReaderWriter() override {}
virtual void Finish(grpc::Status s) = 0;
virtual void SendInitialMetadata() = 0;
virtual void Read(Request* msg) = 0;
virtual void Write(const Response* msg, grpc::WriteOptions options) = 0;
virtual void WriteAndFinish(const Response* msg, grpc::WriteOptions options,
grpc::Status s) = 0;
protected:
void BindReactor(ServerBidiReactor<Request, Response>* reactor) {
reactor->InternalBindStream(this);
}
};
// The following classes are the reactor interfaces that are to be implemented
// by the user, returned as the output parameter of the method handler for a
// callback method. Note that none of the classes are pure; all reactions have a
// default empty reaction so that the user class only needs to override those
// reactions that it cares about. The reaction methods will be invoked by the
// library in response to the completion of various operations. Reactions must
// not include blocking operations (such as blocking I/O, starting synchronous
// RPCs, or waiting on condition variables). Reactions may be invoked
// concurrently, except that OnDone is called after all others (assuming proper
// API usage). The reactor may not be deleted until OnDone is called.
/// \a ServerBidiReactor is the interface for a bidirectional streaming RPC.
template <class Request, class Response>
class ServerBidiReactor : public internal::ServerReactor {
public:
// NOTE: Initializing stream_ as a constructor initializer rather than a
// default initializer because gcc-4.x requires a copy constructor for
// default initializing a templated member, which isn't ok for atomic.
// TODO(vjpai): Switch to default constructor and default initializer when
// gcc-4.x is no longer supported
ServerBidiReactor() : stream_(nullptr) {}
~ServerBidiReactor() override = default;
/// Send any initial metadata stored in the RPC context. If not invoked,
/// any initial metadata will be passed along with the first Write or the
/// Finish (if there are no writes).
void StartSendInitialMetadata() ABSL_LOCKS_EXCLUDED(stream_mu_) {
ServerCallbackReaderWriter<Request, Response>* stream =
stream_.load(std::memory_order_acquire);
if (stream == nullptr) {
grpc::internal::MutexLock l(&stream_mu_);
stream = stream_.load(std::memory_order_relaxed);
if (stream == nullptr) {
backlog_.send_initial_metadata_wanted = true;
return;
}
}
stream->SendInitialMetadata();
}
/// Initiate a read operation.
///
/// \param[out] req Where to eventually store the read message. Valid when
/// the library calls OnReadDone
void StartRead(Request* req) ABSL_LOCKS_EXCLUDED(stream_mu_) {
ServerCallbackReaderWriter<Request, Response>* stream =
stream_.load(std::memory_order_acquire);
if (stream == nullptr) {
grpc::internal::MutexLock l(&stream_mu_);
stream = stream_.load(std::memory_order_relaxed);
if (stream == nullptr) {
backlog_.read_wanted = req;
return;
}
}
stream->Read(req);
}
/// Initiate a write operation.
///
/// \param[in] resp The message to be written. The library does not take
/// ownership but the caller must ensure that the message is
/// not deleted or modified until OnWriteDone is called.
void StartWrite(const Response* resp) {
StartWrite(resp, grpc::WriteOptions());
}
/// Initiate a write operation with specified options.
///
/// \param[in] resp The message to be written. The library does not take
/// ownership but the caller must ensure that the message is
/// not deleted or modified until OnWriteDone is called.
/// \param[in] options The WriteOptions to use for writing this message
void StartWrite(const Response* resp, grpc::WriteOptions options)
ABSL_LOCKS_EXCLUDED(stream_mu_) {
ServerCallbackReaderWriter<Request, Response>* stream =
stream_.load(std::memory_order_acquire);
if (stream == nullptr) {
grpc::internal::MutexLock l(&stream_mu_);
stream = stream_.load(std::memory_order_relaxed);
if (stream == nullptr) {
backlog_.write_wanted = resp;
backlog_.write_options_wanted = options;
return;
}
}
stream->Write(resp, options);
}
/// Initiate a write operation with specified options and final RPC Status,
/// which also causes any trailing metadata for this RPC to be sent out.
/// StartWriteAndFinish is like merging StartWriteLast and Finish into a
/// single step. A key difference, though, is that this operation doesn't have
/// an OnWriteDone reaction - it is considered complete only when OnDone is
/// available. An RPC can either have StartWriteAndFinish or Finish, but not
/// both.
///
/// \param[in] resp The message to be written. The library does not take
/// ownership but the caller must ensure that the message is
/// not deleted or modified until OnDone is called.
/// \param[in] options The WriteOptions to use for writing this message
/// \param[in] s The status outcome of this RPC
void StartWriteAndFinish(const Response* resp, grpc::WriteOptions options,
grpc::Status s) ABSL_LOCKS_EXCLUDED(stream_mu_) {
ServerCallbackReaderWriter<Request, Response>* stream =
stream_.load(std::memory_order_acquire);
if (stream == nullptr) {
grpc::internal::MutexLock l(&stream_mu_);
stream = stream_.load(std::memory_order_relaxed);
if (stream == nullptr) {
backlog_.write_and_finish_wanted = true;
backlog_.write_wanted = resp;
backlog_.write_options_wanted = options;
backlog_.status_wanted = std::move(s);
return;
}
}
stream->WriteAndFinish(resp, options, std::move(s));
}
/// Inform system of a planned write operation with specified options, but
/// allow the library to schedule the actual write coalesced with the writing
/// of trailing metadata (which takes place on a Finish call).
///
/// \param[in] resp The message to be written. The library does not take
/// ownership but the caller must ensure that the message is
/// not deleted or modified until OnWriteDone is called.
/// \param[in] options The WriteOptions to use for writing this message
void StartWriteLast(const Response* resp, grpc::WriteOptions options) {
StartWrite(resp, options.set_last_message());
}
/// Indicate that the stream is to be finished and the trailing metadata and
/// RPC status are to be sent. Every RPC MUST be finished using either Finish
/// or StartWriteAndFinish (but not both), even if the RPC is already
/// cancelled.
///
/// \param[in] s The status outcome of this RPC
void Finish(grpc::Status s) ABSL_LOCKS_EXCLUDED(stream_mu_) {
ServerCallbackReaderWriter<Request, Response>* stream =
stream_.load(std::memory_order_acquire);
if (stream == nullptr) {
grpc::internal::MutexLock l(&stream_mu_);
stream = stream_.load(std::memory_order_relaxed);
if (stream == nullptr) {
backlog_.finish_wanted = true;
backlog_.status_wanted = std::move(s);
return;
}
}
stream->Finish(std::move(s));
}
/// Notifies the application that an explicit StartSendInitialMetadata
/// operation completed. Not used when the sending of initial metadata
/// piggybacks onto the first write.
///
/// \param[in] ok Was it successful? If false, no further write-side operation
/// will succeed.
virtual void OnSendInitialMetadataDone(bool /*ok*/) {}
/// Notifies the application that a StartRead operation completed.
///
/// \param[in] ok Was it successful? If false, no further read-side operation
/// will succeed.
virtual void OnReadDone(bool /*ok*/) {}
/// Notifies the application that a StartWrite (or StartWriteLast) operation
/// completed.
///
/// \param[in] ok Was it successful? If false, no further write-side operation
/// will succeed.
virtual void OnWriteDone(bool /*ok*/) {}
/// Notifies the application that all operations associated with this RPC
/// have completed. This is an override (from the internal base class) but
/// still abstract, so derived classes MUST override it to be instantiated.
void OnDone() override = 0;
/// Notifies the application that this RPC has been cancelled. This is an
/// override (from the internal base class) but not final, so derived classes
/// should override it if they want to take action.
void OnCancel() override {}
private:
friend class ServerCallbackReaderWriter<Request, Response>;
// May be overridden by internal implementation details. This is not a public
// customization point.
virtual void InternalBindStream(
ServerCallbackReaderWriter<Request, Response>* stream) {
grpc::internal::MutexLock l(&stream_mu_);
if (GPR_UNLIKELY(backlog_.send_initial_metadata_wanted)) {
stream->SendInitialMetadata();
}
if (GPR_UNLIKELY(backlog_.read_wanted != nullptr)) {
stream->Read(backlog_.read_wanted);
}
if (GPR_UNLIKELY(backlog_.write_and_finish_wanted)) {
stream->WriteAndFinish(backlog_.write_wanted,
std::move(backlog_.write_options_wanted),
std::move(backlog_.status_wanted));
} else {
if (GPR_UNLIKELY(backlog_.write_wanted != nullptr)) {
stream->Write(backlog_.write_wanted,
std::move(backlog_.write_options_wanted));
}
if (GPR_UNLIKELY(backlog_.finish_wanted)) {
stream->Finish(std::move(backlog_.status_wanted));
}
}
// Set stream_ last so that other functions can use it lock-free
stream_.store(stream, std::memory_order_release);
}
grpc::internal::Mutex stream_mu_;
// TODO(vjpai): Make stream_or_backlog_ into a std::variant or absl::variant
// once C++17 or ABSL is supported since stream and backlog are
// mutually exclusive in this class. Do likewise with the
// remaining reactor classes and their backlogs as well.
std::atomic<ServerCallbackReaderWriter<Request, Response>*> stream_{nullptr};
struct PreBindBacklog {
bool send_initial_metadata_wanted = false;
bool write_and_finish_wanted = false;
bool finish_wanted = false;
Request* read_wanted = nullptr;
const Response* write_wanted = nullptr;
grpc::WriteOptions write_options_wanted;
grpc::Status status_wanted;
};
PreBindBacklog backlog_ ABSL_GUARDED_BY(stream_mu_);
};
/// \a ServerReadReactor is the interface for a client-streaming RPC.
template <class Request>
class ServerReadReactor : public internal::ServerReactor {
public:
ServerReadReactor() : reader_(nullptr) {}
~ServerReadReactor() override = default;
/// The following operation initiations are exactly like ServerBidiReactor.
void StartSendInitialMetadata() ABSL_LOCKS_EXCLUDED(reader_mu_) {
ServerCallbackReader<Request>* reader =
reader_.load(std::memory_order_acquire);
if (reader == nullptr) {
grpc::internal::MutexLock l(&reader_mu_);
reader = reader_.load(std::memory_order_relaxed);
if (reader == nullptr) {
backlog_.send_initial_metadata_wanted = true;
return;
}
}
reader->SendInitialMetadata();
}
void StartRead(Request* req) ABSL_LOCKS_EXCLUDED(reader_mu_) {
ServerCallbackReader<Request>* reader =
reader_.load(std::memory_order_acquire);
if (reader == nullptr) {
grpc::internal::MutexLock l(&reader_mu_);
reader = reader_.load(std::memory_order_relaxed);
if (reader == nullptr) {
backlog_.read_wanted = req;
return;
}
}
reader->Read(req);
}
void Finish(grpc::Status s) ABSL_LOCKS_EXCLUDED(reader_mu_) {
ServerCallbackReader<Request>* reader =
reader_.load(std::memory_order_acquire);
if (reader == nullptr) {
grpc::internal::MutexLock l(&reader_mu_);
reader = reader_.load(std::memory_order_relaxed);
if (reader == nullptr) {
backlog_.finish_wanted = true;
backlog_.status_wanted = std::move(s);
return;
}
}
reader->Finish(std::move(s));
}
/// The following notifications are exactly like ServerBidiReactor.
virtual void OnSendInitialMetadataDone(bool /*ok*/) {}
virtual void OnReadDone(bool /*ok*/) {}
void OnDone() override = 0;
void OnCancel() override {}
private:
friend class ServerCallbackReader<Request>;
// May be overridden by internal implementation details. This is not a public
// customization point.
virtual void InternalBindReader(ServerCallbackReader<Request>* reader)
ABSL_LOCKS_EXCLUDED(reader_mu_) {
grpc::internal::MutexLock l(&reader_mu_);
if (GPR_UNLIKELY(backlog_.send_initial_metadata_wanted)) {
reader->SendInitialMetadata();
}
if (GPR_UNLIKELY(backlog_.read_wanted != nullptr)) {
reader->Read(backlog_.read_wanted);
}
if (GPR_UNLIKELY(backlog_.finish_wanted)) {
reader->Finish(std::move(backlog_.status_wanted));
}
// Set reader_ last so that other functions can use it lock-free
reader_.store(reader, std::memory_order_release);
}
grpc::internal::Mutex reader_mu_;
std::atomic<ServerCallbackReader<Request>*> reader_{nullptr};
struct PreBindBacklog {
bool send_initial_metadata_wanted = false;
bool finish_wanted = false;
Request* read_wanted = nullptr;
grpc::Status status_wanted;
};
PreBindBacklog backlog_ ABSL_GUARDED_BY(reader_mu_);
};
/// \a ServerWriteReactor is the interface for a server-streaming RPC.
template <class Response>
class ServerWriteReactor : public internal::ServerReactor {
public:
ServerWriteReactor() : writer_(nullptr) {}
~ServerWriteReactor() override = default;
/// The following operation initiations are exactly like ServerBidiReactor.
void StartSendInitialMetadata() ABSL_LOCKS_EXCLUDED(writer_mu_) {
ServerCallbackWriter<Response>* writer =
writer_.load(std::memory_order_acquire);
if (writer == nullptr) {
grpc::internal::MutexLock l(&writer_mu_);
writer = writer_.load(std::memory_order_relaxed);
if (writer == nullptr) {
backlog_.send_initial_metadata_wanted = true;
return;
}
}
writer->SendInitialMetadata();
}
void StartWrite(const Response* resp) {
StartWrite(resp, grpc::WriteOptions());
}
void StartWrite(const Response* resp, grpc::WriteOptions options)
ABSL_LOCKS_EXCLUDED(writer_mu_) {
ServerCallbackWriter<Response>* writer =
writer_.load(std::memory_order_acquire);
if (writer == nullptr) {
grpc::internal::MutexLock l(&writer_mu_);
writer = writer_.load(std::memory_order_relaxed);
if (writer == nullptr) {
backlog_.write_wanted = resp;
backlog_.write_options_wanted = options;
return;
}
}
writer->Write(resp, options);
}
void StartWriteAndFinish(const Response* resp, grpc::WriteOptions options,
grpc::Status s) ABSL_LOCKS_EXCLUDED(writer_mu_) {
ServerCallbackWriter<Response>* writer =
writer_.load(std::memory_order_acquire);
if (writer == nullptr) {
grpc::internal::MutexLock l(&writer_mu_);
writer = writer_.load(std::memory_order_relaxed);
if (writer == nullptr) {
backlog_.write_and_finish_wanted = true;
backlog_.write_wanted = resp;
backlog_.write_options_wanted = options;
backlog_.status_wanted = std::move(s);
return;
}
}
writer->WriteAndFinish(resp, options, std::move(s));
}
void StartWriteLast(const Response* resp, grpc::WriteOptions options) {
StartWrite(resp, options.set_last_message());
}
void Finish(grpc::Status s) ABSL_LOCKS_EXCLUDED(writer_mu_) {
ServerCallbackWriter<Response>* writer =
writer_.load(std::memory_order_acquire);
if (writer == nullptr) {
grpc::internal::MutexLock l(&writer_mu_);
writer = writer_.load(std::memory_order_relaxed);
if (writer == nullptr) {
backlog_.finish_wanted = true;
backlog_.status_wanted = std::move(s);
return;
}
}
writer->Finish(std::move(s));
}
/// The following notifications are exactly like ServerBidiReactor.
virtual void OnSendInitialMetadataDone(bool /*ok*/) {}
virtual void OnWriteDone(bool /*ok*/) {}
void OnDone() override = 0;
void OnCancel() override {}
private:
friend class ServerCallbackWriter<Response>;
// May be overridden by internal implementation details. This is not a public
// customization point.
virtual void InternalBindWriter(ServerCallbackWriter<Response>* writer)
ABSL_LOCKS_EXCLUDED(writer_mu_) {
grpc::internal::MutexLock l(&writer_mu_);
if (GPR_UNLIKELY(backlog_.send_initial_metadata_wanted)) {
writer->SendInitialMetadata();
}
if (GPR_UNLIKELY(backlog_.write_and_finish_wanted)) {
writer->WriteAndFinish(backlog_.write_wanted,
std::move(backlog_.write_options_wanted),
std::move(backlog_.status_wanted));
} else {
if (GPR_UNLIKELY(backlog_.write_wanted != nullptr)) {
writer->Write(backlog_.write_wanted,
std::move(backlog_.write_options_wanted));
}
if (GPR_UNLIKELY(backlog_.finish_wanted)) {
writer->Finish(std::move(backlog_.status_wanted));
}
}
// Set writer_ last so that other functions can use it lock-free
writer_.store(writer, std::memory_order_release);
}
grpc::internal::Mutex writer_mu_;
std::atomic<ServerCallbackWriter<Response>*> writer_{nullptr};
struct PreBindBacklog {
bool send_initial_metadata_wanted = false;
bool write_and_finish_wanted = false;
bool finish_wanted = false;
const Response* write_wanted = nullptr;
grpc::WriteOptions write_options_wanted;
grpc::Status status_wanted;
};
PreBindBacklog backlog_ ABSL_GUARDED_BY(writer_mu_);
};
class ServerUnaryReactor : public internal::ServerReactor {
public:
ServerUnaryReactor() : call_(nullptr) {}
~ServerUnaryReactor() override = default;
/// StartSendInitialMetadata is exactly like ServerBidiReactor.
void StartSendInitialMetadata() ABSL_LOCKS_EXCLUDED(call_mu_) {
ServerCallbackUnary* call = call_.load(std::memory_order_acquire);
if (call == nullptr) {
grpc::internal::MutexLock l(&call_mu_);
call = call_.load(std::memory_order_relaxed);
if (call == nullptr) {
backlog_.send_initial_metadata_wanted = true;
return;
}
}
call->SendInitialMetadata();
}
/// Finish is similar to ServerBidiReactor except for one detail.
/// If the status is non-OK, any message will not be sent. Instead,
/// the client will only receive the status and any trailing metadata.
void Finish(grpc::Status s) ABSL_LOCKS_EXCLUDED(call_mu_) {
ServerCallbackUnary* call = call_.load(std::memory_order_acquire);
if (call == nullptr) {
grpc::internal::MutexLock l(&call_mu_);
call = call_.load(std::memory_order_relaxed);
if (call == nullptr) {
backlog_.finish_wanted = true;
backlog_.status_wanted = std::move(s);
return;
}
}
call->Finish(std::move(s));
}
/// The following notifications are exactly like ServerBidiReactor.
virtual void OnSendInitialMetadataDone(bool /*ok*/) {}
void OnDone() override = 0;
void OnCancel() override {}
private:
friend class ServerCallbackUnary;
// May be overridden by internal implementation details. This is not a public
// customization point.
virtual void InternalBindCall(ServerCallbackUnary* call)
ABSL_LOCKS_EXCLUDED(call_mu_) {
grpc::internal::MutexLock l(&call_mu_);
if (GPR_UNLIKELY(backlog_.send_initial_metadata_wanted)) {
call->SendInitialMetadata();
}
if (GPR_UNLIKELY(backlog_.finish_wanted)) {
call->Finish(std::move(backlog_.status_wanted));
}
// Set call_ last so that other functions can use it lock-free
call_.store(call, std::memory_order_release);
}
grpc::internal::Mutex call_mu_;
std::atomic<ServerCallbackUnary*> call_{nullptr};
struct PreBindBacklog {
bool send_initial_metadata_wanted = false;
bool finish_wanted = false;
grpc::Status status_wanted;
};
PreBindBacklog backlog_ ABSL_GUARDED_BY(call_mu_);
};
namespace internal {
template <class Base>
class FinishOnlyReactor : public Base {
public:
explicit FinishOnlyReactor(grpc::Status s) { this->Finish(std::move(s)); }
void OnDone() override { this->~FinishOnlyReactor(); }
};
using UnimplementedUnaryReactor = FinishOnlyReactor<ServerUnaryReactor>;
template <class Request>
using UnimplementedReadReactor = FinishOnlyReactor<ServerReadReactor<Request>>;
template <class Response>
using UnimplementedWriteReactor =
FinishOnlyReactor<ServerWriteReactor<Response>>;
template <class Request, class Response>
using UnimplementedBidiReactor =
FinishOnlyReactor<ServerBidiReactor<Request, Response>>;
} // namespace internal
// TODO(vjpai): Remove namespace experimental when last known users are migrated
// off.
namespace experimental {
template <class Request, class Response>
using ServerBidiReactor = ::grpc::ServerBidiReactor<Request, Response>;
} // namespace experimental
} // namespace grpc
#endif // GRPCPP_SUPPORT_SERVER_CALLBACK_H

View File

@@ -0,0 +1,140 @@
//
//
// Copyright 2015 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_SUPPORT_SERVER_INTERCEPTOR_H
#define GRPCPP_SUPPORT_SERVER_INTERCEPTOR_H
#include <atomic>
#include <vector>
#include <grpc/support/log.h>
#include <grpcpp/impl/rpc_method.h>
#include <grpcpp/support/interceptor.h>
#include <grpcpp/support/string_ref.h>
namespace grpc {
class ServerContextBase;
namespace internal {
class InterceptorBatchMethodsImpl;
}
namespace experimental {
class ServerRpcInfo;
// A factory interface for creation of server interceptors. A vector of
// factories can be provided to ServerBuilder which will be used to create a new
// vector of server interceptors per RPC. Server interceptor authors should
// create a subclass of ServerInterceptorFactorInterface which creates objects
// of their interceptors.
class ServerInterceptorFactoryInterface {
public:
virtual ~ServerInterceptorFactoryInterface() {}
// Returns a pointer to an Interceptor object on successful creation, nullptr
// otherwise. If nullptr is returned, this server interceptor factory is
// ignored for the purposes of that RPC.
virtual Interceptor* CreateServerInterceptor(ServerRpcInfo* info) = 0;
};
/// ServerRpcInfo represents the state of a particular RPC as it
/// appears to an interceptor. It is created and owned by the library and
/// passed to the CreateServerInterceptor method of the application's
/// ServerInterceptorFactoryInterface implementation
class ServerRpcInfo {
public:
/// Type categorizes RPCs by unary or streaming type
enum class Type { UNARY, CLIENT_STREAMING, SERVER_STREAMING, BIDI_STREAMING };
~ServerRpcInfo() {}
// Delete all copy and move constructors and assignments
ServerRpcInfo(const ServerRpcInfo&) = delete;
ServerRpcInfo& operator=(const ServerRpcInfo&) = delete;
ServerRpcInfo(ServerRpcInfo&&) = delete;
ServerRpcInfo& operator=(ServerRpcInfo&&) = delete;
// Getter methods
/// Return the fully-specified method name
const char* method() const { return method_; }
/// Return the type of the RPC (unary or a streaming flavor)
Type type() const { return type_; }
/// Return a pointer to the underlying ServerContext structure associated
/// with the RPC to support features that apply to it
ServerContextBase* server_context() { return ctx_; }
private:
static_assert(Type::UNARY ==
static_cast<Type>(internal::RpcMethod::NORMAL_RPC),
"violated expectation about Type enum");
static_assert(Type::CLIENT_STREAMING ==
static_cast<Type>(internal::RpcMethod::CLIENT_STREAMING),
"violated expectation about Type enum");
static_assert(Type::SERVER_STREAMING ==
static_cast<Type>(internal::RpcMethod::SERVER_STREAMING),
"violated expectation about Type enum");
static_assert(Type::BIDI_STREAMING ==
static_cast<Type>(internal::RpcMethod::BIDI_STREAMING),
"violated expectation about Type enum");
ServerRpcInfo(ServerContextBase* ctx, const char* method,
internal::RpcMethod::RpcType type)
: ctx_(ctx), method_(method), type_(static_cast<Type>(type)) {}
// Runs interceptor at pos \a pos.
void RunInterceptor(
experimental::InterceptorBatchMethods* interceptor_methods, size_t pos) {
GPR_ASSERT(pos < interceptors_.size());
interceptors_[pos]->Intercept(interceptor_methods);
}
void RegisterInterceptors(
const std::vector<
std::unique_ptr<experimental::ServerInterceptorFactoryInterface>>&
creators) {
for (const auto& creator : creators) {
auto* interceptor = creator->CreateServerInterceptor(this);
if (interceptor != nullptr) {
interceptors_.push_back(
std::unique_ptr<experimental::Interceptor>(interceptor));
}
}
}
void Ref() { ref_.fetch_add(1, std::memory_order_relaxed); }
void Unref() {
if (GPR_UNLIKELY(ref_.fetch_sub(1, std::memory_order_acq_rel) == 1)) {
delete this;
}
}
ServerContextBase* ctx_ = nullptr;
const char* method_ = nullptr;
const Type type_;
std::atomic<intptr_t> ref_{1};
std::vector<std::unique_ptr<experimental::Interceptor>> interceptors_;
friend class internal::InterceptorBatchMethodsImpl;
friend class grpc::ServerContextBase;
};
} // namespace experimental
} // namespace grpc
#endif // GRPCPP_SUPPORT_SERVER_INTERCEPTOR_H

View File

@@ -0,0 +1,142 @@
//
//
// Copyright 2015 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_SUPPORT_SLICE_H
#define GRPCPP_SUPPORT_SLICE_H
#include <grpc/slice.h>
#include <grpcpp/support/config.h>
#include <grpcpp/support/string_ref.h>
namespace grpc {
/// A wrapper around \a grpc_slice.
///
/// A slice represents a contiguous reference counted array of bytes.
/// It is cheap to take references to a slice, and it is cheap to create a
/// slice pointing to a subset of another slice.
class Slice final {
public:
/// Construct an empty slice.
Slice() : slice_(grpc_empty_slice()) {}
/// Destructor - drops one reference.
~Slice() { grpc_slice_unref(slice_); }
enum AddRef { ADD_REF };
/// Construct a slice from \a slice, adding a reference.
Slice(grpc_slice slice, AddRef) : slice_(grpc_slice_ref(slice)) {}
enum StealRef { STEAL_REF };
/// Construct a slice from \a slice, stealing a reference.
Slice(grpc_slice slice, StealRef) : slice_(slice) {}
/// Allocate a slice of specified size
explicit Slice(size_t len) : slice_(grpc_slice_malloc(len)) {}
/// Construct a slice from a copied buffer
Slice(const void* buf, size_t len)
: slice_(grpc_slice_from_copied_buffer(reinterpret_cast<const char*>(buf),
len)) {}
/// Construct a slice from a copied string
// NOLINTNEXTLINE(google-explicit-constructor)
Slice(const std::string& str)
: slice_(grpc_slice_from_copied_buffer(str.c_str(), str.length())) {}
enum StaticSlice { STATIC_SLICE };
/// Construct a slice from a static buffer
Slice(const void* buf, size_t len, StaticSlice)
: slice_(grpc_slice_from_static_buffer(reinterpret_cast<const char*>(buf),
len)) {}
/// Copy constructor, adds a reference.
Slice(const Slice& other) : slice_(grpc_slice_ref(other.slice_)) {}
/// Move constructor, steals a reference.
Slice(Slice&& other) noexcept : slice_(other.slice_) {
other.slice_ = grpc_empty_slice();
}
/// Assignment, reference count is unchanged.
Slice& operator=(Slice other) {
std::swap(slice_, other.slice_);
return *this;
}
/// Create a slice pointing at some data. Calls malloc to allocate a refcount
/// for the object, and arranges that destroy will be called with the
/// user data pointer passed in at destruction. Can be the same as buf or
/// different (e.g., if data is part of a larger structure that must be
/// destroyed when the data is no longer needed)
Slice(void* buf, size_t len, void (*destroy)(void*), void* user_data)
: slice_(grpc_slice_new_with_user_data(buf, len, destroy, user_data)) {}
/// Specialization of above for common case where buf == user_data
Slice(void* buf, size_t len, void (*destroy)(void*))
: Slice(buf, len, destroy, buf) {}
/// Similar to the above but has a destroy that also takes slice length
Slice(void* buf, size_t len, void (*destroy)(void*, size_t))
: slice_(grpc_slice_new_with_len(buf, len, destroy)) {}
/// Byte size.
size_t size() const { return GRPC_SLICE_LENGTH(slice_); }
/// Raw pointer to the beginning (first element) of the slice.
const uint8_t* begin() const { return GRPC_SLICE_START_PTR(slice_); }
/// Raw pointer to the end (one byte \em past the last element) of the slice.
const uint8_t* end() const { return GRPC_SLICE_END_PTR(slice_); }
/// Returns a substring of the `slice` as another slice.
Slice sub(size_t begin, size_t end) const {
return Slice(grpc_slice_sub(slice_, begin, end), STEAL_REF);
}
/// Raw C slice. Caller needs to call grpc_slice_unref when done.
grpc_slice c_slice() const { return grpc_slice_ref(slice_); }
private:
friend class ByteBuffer;
grpc_slice slice_;
};
inline grpc::string_ref StringRefFromSlice(const grpc_slice* slice) {
return grpc::string_ref(
reinterpret_cast<const char*>(GRPC_SLICE_START_PTR(*slice)),
GRPC_SLICE_LENGTH(*slice));
}
inline std::string StringFromCopiedSlice(grpc_slice slice) {
return std::string(reinterpret_cast<char*>(GRPC_SLICE_START_PTR(slice)),
GRPC_SLICE_LENGTH(slice));
}
inline grpc_slice SliceReferencingString(const std::string& str) {
return grpc_slice_from_static_buffer(str.data(), str.length());
}
inline grpc_slice SliceFromCopiedString(const std::string& str) {
return grpc_slice_from_copied_buffer(str.data(), str.length());
}
} // namespace grpc
#endif // GRPCPP_SUPPORT_SLICE_H

View File

@@ -0,0 +1,24 @@
//
//
// Copyright 2015 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_SUPPORT_STATUS_H
#define GRPCPP_SUPPORT_STATUS_H
#include <grpcpp/impl/status.h> // IWYU pragma: export
#endif // GRPCPP_SUPPORT_STATUS_H

View File

@@ -0,0 +1,145 @@
//
//
// Copyright 2015 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_SUPPORT_STATUS_CODE_ENUM_H
#define GRPCPP_SUPPORT_STATUS_CODE_ENUM_H
// IWYU pragma: private, include <grpcpp/support/status.h>
namespace grpc {
enum StatusCode {
/// Not an error; returned on success.
OK = 0,
/// The operation was cancelled (typically by the caller).
CANCELLED = 1,
/// Unknown error. An example of where this error may be returned is if a
/// Status value received from another address space belongs to an error-space
/// that is not known in this address space. Also errors raised by APIs that
/// do not return enough error information may be converted to this error.
UNKNOWN = 2,
/// Client specified an invalid argument. Note that this differs from
/// FAILED_PRECONDITION. INVALID_ARGUMENT indicates arguments that are
/// problematic regardless of the state of the system (e.g., a malformed file
/// name).
INVALID_ARGUMENT = 3,
/// Deadline expired before operation could complete. For operations that
/// change the state of the system, this error may be returned even if the
/// operation has completed successfully. For example, a successful response
/// from a server could have been delayed long enough for the deadline to
/// expire.
DEADLINE_EXCEEDED = 4,
/// Some requested entity (e.g., file or directory) was not found.
NOT_FOUND = 5,
/// Some entity that we attempted to create (e.g., file or directory) already
/// exists.
ALREADY_EXISTS = 6,
/// The caller does not have permission to execute the specified operation.
/// PERMISSION_DENIED must not be used for rejections caused by exhausting
/// some resource (use RESOURCE_EXHAUSTED instead for those errors).
/// PERMISSION_DENIED must not be used if the caller can not be identified
/// (use UNAUTHENTICATED instead for those errors).
PERMISSION_DENIED = 7,
/// The request does not have valid authentication credentials for the
/// operation.
UNAUTHENTICATED = 16,
/// Some resource has been exhausted, perhaps a per-user quota, or perhaps the
/// entire file system is out of space.
RESOURCE_EXHAUSTED = 8,
/// Operation was rejected because the system is not in a state required for
/// the operation's execution. For example, directory to be deleted may be
/// non-empty, an rmdir operation is applied to a non-directory, etc.
///
/// A litmus test that may help a service implementor in deciding
/// between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE:
/// (a) Use UNAVAILABLE if the client can retry just the failing call.
/// (b) Use ABORTED if the client should retry at a higher-level
/// (e.g., restarting a read-modify-write sequence).
/// (c) Use FAILED_PRECONDITION if the client should not retry until
/// the system state has been explicitly fixed. E.g., if an "rmdir"
/// fails because the directory is non-empty, FAILED_PRECONDITION
/// should be returned since the client should not retry unless
/// they have first fixed up the directory by deleting files from it.
/// (d) Use FAILED_PRECONDITION if the client performs conditional
/// REST Get/Update/Delete on a resource and the resource on the
/// server does not match the condition. E.g., conflicting
/// read-modify-write on the same resource.
FAILED_PRECONDITION = 9,
/// The operation was aborted, typically due to a concurrency issue like
/// sequencer check failures, transaction aborts, etc.
///
/// See litmus test above for deciding between FAILED_PRECONDITION, ABORTED,
/// and UNAVAILABLE.
ABORTED = 10,
/// Operation was attempted past the valid range. E.g., seeking or reading
/// past end of file.
///
/// Unlike INVALID_ARGUMENT, this error indicates a problem that may be fixed
/// if the system state changes. For example, a 32-bit file system will
/// generate INVALID_ARGUMENT if asked to read at an offset that is not in the
/// range [0,2^32-1], but it will generate OUT_OF_RANGE if asked to read from
/// an offset past the current file size.
///
/// There is a fair bit of overlap between FAILED_PRECONDITION and
/// OUT_OF_RANGE. We recommend using OUT_OF_RANGE (the more specific error)
/// when it applies so that callers who are iterating through a space can
/// easily look for an OUT_OF_RANGE error to detect when they are done.
OUT_OF_RANGE = 11,
/// Operation is not implemented or not supported/enabled in this service.
UNIMPLEMENTED = 12,
/// Internal errors. Means some invariants expected by underlying System has
/// been broken. If you see one of these errors, Something is very broken.
INTERNAL = 13,
/// The service is currently unavailable. This is a most likely a transient
/// condition and may be corrected by retrying with a backoff. Note that it is
/// not always safe to retry non-idempotent operations.
///
/// \warning Although data MIGHT not have been transmitted when this
/// status occurs, there is NOT A GUARANTEE that the server has not seen
/// anything. So in general it is unsafe to retry on this status code
/// if the call is non-idempotent.
///
/// See litmus test above for deciding between FAILED_PRECONDITION, ABORTED,
/// and UNAVAILABLE.
UNAVAILABLE = 14,
/// Unrecoverable data loss or corruption.
DATA_LOSS = 15,
/// Force users to include a default branch:
DO_NOT_USE = -1
};
} // namespace grpc
#endif // GRPCPP_SUPPORT_STATUS_CODE_ENUM_H

View File

@@ -0,0 +1,149 @@
//
//
// Copyright 2015 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_SUPPORT_STRING_REF_H
#define GRPCPP_SUPPORT_STRING_REF_H
#include <string.h>
#include <algorithm>
#include <iosfwd>
#include <iostream>
#include <iterator>
#include <grpcpp/support/config.h>
namespace grpc {
/// This class is a non owning reference to a string.
///
/// It should be a strict subset of the upcoming std::string_ref.
///
/// \see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3442.html
///
/// The constexpr is dropped or replaced with const for legacy compiler
/// compatibility.
class string_ref {
public:
/// types
typedef const char* const_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
/// constants
const static size_t npos;
/// construct/copy.
string_ref() : data_(nullptr), length_(0) {}
string_ref(const string_ref& other)
: data_(other.data_), length_(other.length_) {}
// NOLINTNEXTLINE(bugprone-unhandled-self-assignment)
string_ref& operator=(const string_ref& rhs) {
data_ = rhs.data_;
length_ = rhs.length_;
return *this;
}
// NOLINTNEXTLINE(google-explicit-constructor)
string_ref(const char* s) : data_(s), length_(strlen(s)) {}
string_ref(const char* s, size_t l) : data_(s), length_(l) {}
// NOLINTNEXTLINE(google-explicit-constructor)
string_ref(const std::string& s) : data_(s.data()), length_(s.length()) {}
/// iterators
const_iterator begin() const { return data_; }
const_iterator end() const { return data_ + length_; }
const_iterator cbegin() const { return data_; }
const_iterator cend() const { return data_ + length_; }
const_reverse_iterator rbegin() const {
return const_reverse_iterator(end());
}
const_reverse_iterator rend() const {
return const_reverse_iterator(begin());
}
const_reverse_iterator crbegin() const {
return const_reverse_iterator(end());
}
const_reverse_iterator crend() const {
return const_reverse_iterator(begin());
}
/// capacity
size_t size() const { return length_; }
size_t length() const { return length_; }
size_t max_size() const { return length_; }
bool empty() const { return length_ == 0; }
/// element access
const char* data() const { return data_; }
/// string operations
int compare(string_ref x) const {
size_t min_size = length_ < x.length_ ? length_ : x.length_;
int r = memcmp(data_, x.data_, min_size);
if (r < 0) return -1;
if (r > 0) return 1;
if (length_ < x.length_) return -1;
if (length_ > x.length_) return 1;
return 0;
}
bool starts_with(string_ref x) const {
return length_ >= x.length_ && (memcmp(data_, x.data_, x.length_) == 0);
}
bool ends_with(string_ref x) const {
return length_ >= x.length_ &&
(memcmp(data_ + (length_ - x.length_), x.data_, x.length_) == 0);
}
size_t find(string_ref s) const {
auto it = std::search(cbegin(), cend(), s.cbegin(), s.cend());
return it == cend() ? npos : std::distance(cbegin(), it);
}
size_t find(char c) const {
auto it = std::find(cbegin(), cend(), c);
return it == cend() ? npos : std::distance(cbegin(), it);
}
string_ref substr(size_t pos, size_t n = npos) const {
if (pos > length_) pos = length_;
if (n > (length_ - pos)) n = length_ - pos;
return string_ref(data_ + pos, n);
}
private:
const char* data_;
size_t length_;
};
/// Comparison operators
inline bool operator==(string_ref x, string_ref y) { return x.compare(y) == 0; }
inline bool operator!=(string_ref x, string_ref y) { return x.compare(y) != 0; }
inline bool operator<(string_ref x, string_ref y) { return x.compare(y) < 0; }
inline bool operator<=(string_ref x, string_ref y) { return x.compare(y) <= 0; }
inline bool operator>(string_ref x, string_ref y) { return x.compare(y) > 0; }
inline bool operator>=(string_ref x, string_ref y) { return x.compare(y) >= 0; }
inline std::ostream& operator<<(std::ostream& out, const string_ref& string) {
return out << std::string(string.begin(), string.end());
}
} // namespace grpc
#endif // GRPCPP_SUPPORT_STRING_REF_H

View File

@@ -0,0 +1,42 @@
//
//
// Copyright 2015 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_SUPPORT_STUB_OPTIONS_H
#define GRPCPP_SUPPORT_STUB_OPTIONS_H
namespace grpc {
/// Useful interface for generated stubs
class StubOptions {
public:
StubOptions() = default;
explicit StubOptions(const char* suffix_for_stats)
: suffix_for_stats_(suffix_for_stats) {}
void set_suffix_for_stats(const char* suffix_for_stats) {
suffix_for_stats_ = suffix_for_stats;
}
const char* suffix_for_stats() const { return suffix_for_stats_; }
private:
const char* suffix_for_stats_ = nullptr;
};
} // namespace grpc
#endif // GRPCPP_SUPPORT_STUB_OPTIONS_H

View File

@@ -0,0 +1,949 @@
//
//
// Copyright 2015 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_SUPPORT_SYNC_STREAM_H
#define GRPCPP_SUPPORT_SYNC_STREAM_H
#include <grpc/support/log.h>
#include <grpcpp/client_context.h>
#include <grpcpp/completion_queue.h>
#include <grpcpp/impl/call.h>
#include <grpcpp/impl/codegen/channel_interface.h>
#include <grpcpp/impl/service_type.h>
#include <grpcpp/server_context.h>
#include <grpcpp/support/status.h>
namespace grpc {
namespace internal {
/// Common interface for all synchronous client side streaming.
class ClientStreamingInterface {
public:
virtual ~ClientStreamingInterface() {}
/// Block waiting until the stream finishes and a final status of the call is
/// available.
///
/// It is appropriate to call this method exactly once when both:
/// * the calling code (client-side) has no more message to send
/// (this can be declared implicitly by calling this method, or
/// explicitly through an earlier call to <i>WritesDone</i> method of the
/// class in use, e.g. \a ClientWriterInterface::WritesDone or
/// \a ClientReaderWriterInterface::WritesDone).
/// * there are no more messages to be received from the server (which can
/// be known implicitly, or explicitly from an earlier call to \a
/// ReaderInterface::Read that returned "false").
///
/// This function will return either:
/// - when all incoming messages have been read and the server has
/// returned status.
/// - when the server has returned a non-OK status.
/// - OR when the call failed for some reason and the library generated a
/// status.
///
/// Return values:
/// - \a Status contains the status code, message and details for the call
/// - the \a ClientContext associated with this call is updated with
/// possible trailing metadata sent from the server.
virtual grpc::Status Finish() = 0;
};
/// Common interface for all synchronous server side streaming.
class ServerStreamingInterface {
public:
virtual ~ServerStreamingInterface() {}
/// Block to send initial metadata to client.
/// This call is optional, but if it is used, it cannot be used concurrently
/// with or after the \a Finish method.
///
/// The initial metadata that will be sent to the client will be
/// taken from the \a ServerContext associated with the call.
virtual void SendInitialMetadata() = 0;
};
/// An interface that yields a sequence of messages of type \a R.
template <class R>
class ReaderInterface {
public:
virtual ~ReaderInterface() {}
/// Get an upper bound on the next message size available for reading on this
/// stream.
virtual bool NextMessageSize(uint32_t* sz) = 0;
/// Block to read a message and parse to \a msg. Returns \a true on success.
/// This is thread-safe with respect to \a Write or \WritesDone methods on
/// the same stream. It should not be called concurrently with another \a
/// Read on the same stream as the order of delivery will not be defined.
///
/// \param[out] msg The read message.
///
/// \return \a false when there will be no more incoming messages, either
/// because the other side has called \a WritesDone() or the stream has failed
/// (or been cancelled).
virtual bool Read(R* msg) = 0;
};
/// An interface that can be fed a sequence of messages of type \a W.
template <class W>
class WriterInterface {
public:
virtual ~WriterInterface() {}
/// Block to write \a msg to the stream with WriteOptions \a options.
/// This is thread-safe with respect to \a ReaderInterface::Read
///
/// \param msg The message to be written to the stream.
/// \param options The WriteOptions affecting the write operation.
///
/// \return \a true on success, \a false when the stream has been closed.
virtual bool Write(const W& msg, grpc::WriteOptions options) = 0;
/// Block to write \a msg to the stream with default write options.
/// This is thread-safe with respect to \a ReaderInterface::Read
///
/// \param msg The message to be written to the stream.
///
/// \return \a true on success, \a false when the stream has been closed.
inline bool Write(const W& msg) { return Write(msg, grpc::WriteOptions()); }
/// Write \a msg and coalesce it with the writing of trailing metadata, using
/// WriteOptions \a options.
///
/// For client, WriteLast is equivalent of performing Write and WritesDone in
/// a single step. \a msg and trailing metadata are coalesced and sent on wire
/// by calling this function. For server, WriteLast buffers the \a msg.
/// The writing of \a msg is held until the service handler returns,
/// where \a msg and trailing metadata are coalesced and sent on wire.
/// Note that WriteLast can only buffer \a msg up to the flow control window
/// size. If \a msg size is larger than the window size, it will be sent on
/// wire without buffering.
///
/// \param[in] msg The message to be written to the stream.
/// \param[in] options The WriteOptions to be used to write this message.
void WriteLast(const W& msg, grpc::WriteOptions options) {
Write(msg, options.set_last_message());
}
};
} // namespace internal
/// Client-side interface for streaming reads of message of type \a R.
template <class R>
class ClientReaderInterface : public internal::ClientStreamingInterface,
public internal::ReaderInterface<R> {
public:
/// Block to wait for initial metadata from server. The received metadata
/// can only be accessed after this call returns. Should only be called before
/// the first read. Calling this method is optional, and if it is not called
/// the metadata will be available in ClientContext after the first read.
virtual void WaitForInitialMetadata() = 0;
};
namespace internal {
template <class R>
class ClientReaderFactory {
public:
template <class W>
static ClientReader<R>* Create(grpc::ChannelInterface* channel,
const grpc::internal::RpcMethod& method,
grpc::ClientContext* context,
const W& request) {
return new ClientReader<R>(channel, method, context, request);
}
};
} // namespace internal
/// Synchronous (blocking) client-side API for doing server-streaming RPCs,
/// where the stream of messages coming from the server has messages
/// of type \a R.
template <class R>
class ClientReader final : public ClientReaderInterface<R> {
public:
/// See the \a ClientStreamingInterface.WaitForInitialMetadata method for
/// semantics.
///
// Side effect:
/// Once complete, the initial metadata read from
/// the server will be accessible through the \a ClientContext used to
/// construct this object.
void WaitForInitialMetadata() override {
GPR_ASSERT(!context_->initial_metadata_received_);
grpc::internal::CallOpSet<grpc::internal::CallOpRecvInitialMetadata> ops;
ops.RecvInitialMetadata(context_);
call_.PerformOps(&ops);
cq_.Pluck(&ops); /// status ignored
}
bool NextMessageSize(uint32_t* sz) override {
int result = call_.max_receive_message_size();
*sz = (result > 0) ? result : UINT32_MAX;
return true;
}
/// See the \a ReaderInterface.Read method for semantics.
/// Side effect:
/// This also receives initial metadata from the server, if not
/// already received (if initial metadata is received, it can be then
/// accessed through the \a ClientContext associated with this call).
bool Read(R* msg) override {
grpc::internal::CallOpSet<grpc::internal::CallOpRecvInitialMetadata,
grpc::internal::CallOpRecvMessage<R>>
ops;
if (!context_->initial_metadata_received_) {
ops.RecvInitialMetadata(context_);
}
ops.RecvMessage(msg);
call_.PerformOps(&ops);
return cq_.Pluck(&ops) && ops.got_message;
}
/// See the \a ClientStreamingInterface.Finish method for semantics.
///
/// Side effect:
/// The \a ClientContext associated with this call is updated with
/// possible metadata received from the server.
grpc::Status Finish() override {
grpc::internal::CallOpSet<grpc::internal::CallOpRecvInitialMetadata,
grpc::internal::CallOpClientRecvStatus>
ops;
if (!context_->initial_metadata_received_) {
ops.RecvInitialMetadata(context_);
}
grpc::Status status;
ops.ClientRecvStatus(context_, &status);
call_.PerformOps(&ops);
GPR_ASSERT(cq_.Pluck(&ops));
return status;
}
private:
friend class internal::ClientReaderFactory<R>;
grpc::ClientContext* context_;
grpc::CompletionQueue cq_;
grpc::internal::Call call_;
/// Block to create a stream and write the initial metadata and \a request
/// out. Note that \a context will be used to fill in custom initial
/// metadata used to send to the server when starting the call.
template <class W>
ClientReader(grpc::ChannelInterface* channel,
const grpc::internal::RpcMethod& method,
grpc::ClientContext* context, const W& request)
: context_(context),
cq_(grpc_completion_queue_attributes{
GRPC_CQ_CURRENT_VERSION, GRPC_CQ_PLUCK, GRPC_CQ_DEFAULT_POLLING,
nullptr}), // Pluckable cq
call_(channel->CreateCall(method, context, &cq_)) {
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata,
grpc::internal::CallOpSendMessage,
grpc::internal::CallOpClientSendClose>
ops;
ops.SendInitialMetadata(&context->send_initial_metadata_,
context->initial_metadata_flags());
// TODO(ctiller): don't assert
GPR_ASSERT(ops.SendMessagePtr(&request).ok());
ops.ClientSendClose();
call_.PerformOps(&ops);
cq_.Pluck(&ops);
}
};
/// Client-side interface for streaming writes of message type \a W.
template <class W>
class ClientWriterInterface : public internal::ClientStreamingInterface,
public internal::WriterInterface<W> {
public:
/// Half close writing from the client. (signal that the stream of messages
/// coming from the client is complete).
/// Blocks until currently-pending writes are completed.
/// Thread safe with respect to \a ReaderInterface::Read operations only
///
/// \return Whether the writes were successful.
virtual bool WritesDone() = 0;
};
namespace internal {
template <class W>
class ClientWriterFactory {
public:
template <class R>
static ClientWriter<W>* Create(grpc::ChannelInterface* channel,
const grpc::internal::RpcMethod& method,
grpc::ClientContext* context, R* response) {
return new ClientWriter<W>(channel, method, context, response);
}
};
} // namespace internal
/// Synchronous (blocking) client-side API for doing client-streaming RPCs,
/// where the outgoing message stream coming from the client has messages of
/// type \a W.
template <class W>
class ClientWriter : public ClientWriterInterface<W> {
public:
/// See the \a ClientStreamingInterface.WaitForInitialMetadata method for
/// semantics.
///
// Side effect:
/// Once complete, the initial metadata read from the server will be
/// accessible through the \a ClientContext used to construct this object.
void WaitForInitialMetadata() {
GPR_ASSERT(!context_->initial_metadata_received_);
grpc::internal::CallOpSet<grpc::internal::CallOpRecvInitialMetadata> ops;
ops.RecvInitialMetadata(context_);
call_.PerformOps(&ops);
cq_.Pluck(&ops); // status ignored
}
/// See the WriterInterface.Write(const W& msg, WriteOptions options) method
/// for semantics.
///
/// Side effect:
/// Also sends initial metadata if not already sent (using the
/// \a ClientContext associated with this call).
using internal::WriterInterface<W>::Write;
bool Write(const W& msg, grpc::WriteOptions options) override {
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata,
grpc::internal::CallOpSendMessage,
grpc::internal::CallOpClientSendClose>
ops;
if (options.is_last_message()) {
options.set_buffer_hint();
ops.ClientSendClose();
}
if (context_->initial_metadata_corked_) {
ops.SendInitialMetadata(&context_->send_initial_metadata_,
context_->initial_metadata_flags());
context_->set_initial_metadata_corked(false);
}
if (!ops.SendMessagePtr(&msg, options).ok()) {
return false;
}
call_.PerformOps(&ops);
return cq_.Pluck(&ops);
}
bool WritesDone() override {
grpc::internal::CallOpSet<grpc::internal::CallOpClientSendClose> ops;
ops.ClientSendClose();
call_.PerformOps(&ops);
return cq_.Pluck(&ops);
}
/// See the ClientStreamingInterface.Finish method for semantics.
/// Side effects:
/// - Also receives initial metadata if not already received.
/// - Attempts to fill in the \a response parameter passed
/// to the constructor of this instance with the response
/// message from the server.
grpc::Status Finish() override {
grpc::Status status;
if (!context_->initial_metadata_received_) {
finish_ops_.RecvInitialMetadata(context_);
}
finish_ops_.ClientRecvStatus(context_, &status);
call_.PerformOps(&finish_ops_);
GPR_ASSERT(cq_.Pluck(&finish_ops_));
return status;
}
private:
friend class internal::ClientWriterFactory<W>;
/// Block to create a stream (i.e. send request headers and other initial
/// metadata to the server). Note that \a context will be used to fill
/// in custom initial metadata. \a response will be filled in with the
/// single expected response message from the server upon a successful
/// call to the \a Finish method of this instance.
template <class R>
ClientWriter(grpc::ChannelInterface* channel,
const grpc::internal::RpcMethod& method,
grpc::ClientContext* context, R* response)
: context_(context),
cq_(grpc_completion_queue_attributes{
GRPC_CQ_CURRENT_VERSION, GRPC_CQ_PLUCK, GRPC_CQ_DEFAULT_POLLING,
nullptr}), // Pluckable cq
call_(channel->CreateCall(method, context, &cq_)) {
finish_ops_.RecvMessage(response);
finish_ops_.AllowNoMessage();
if (!context_->initial_metadata_corked_) {
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata> ops;
ops.SendInitialMetadata(&context->send_initial_metadata_,
context->initial_metadata_flags());
call_.PerformOps(&ops);
cq_.Pluck(&ops);
}
}
grpc::ClientContext* context_;
grpc::internal::CallOpSet<grpc::internal::CallOpRecvInitialMetadata,
grpc::internal::CallOpGenericRecvMessage,
grpc::internal::CallOpClientRecvStatus>
finish_ops_;
grpc::CompletionQueue cq_;
grpc::internal::Call call_;
};
/// Client-side interface for bi-directional streaming with
/// client-to-server stream messages of type \a W and
/// server-to-client stream messages of type \a R.
template <class W, class R>
class ClientReaderWriterInterface : public internal::ClientStreamingInterface,
public internal::WriterInterface<W>,
public internal::ReaderInterface<R> {
public:
/// Block to wait for initial metadata from server. The received metadata
/// can only be accessed after this call returns. Should only be called before
/// the first read. Calling this method is optional, and if it is not called
/// the metadata will be available in ClientContext after the first read.
virtual void WaitForInitialMetadata() = 0;
/// Half close writing from the client. (signal that the stream of messages
/// coming from the client is complete).
/// Blocks until currently-pending writes are completed.
/// Thread-safe with respect to \a ReaderInterface::Read
///
/// \return Whether the writes were successful.
virtual bool WritesDone() = 0;
};
namespace internal {
template <class W, class R>
class ClientReaderWriterFactory {
public:
static ClientReaderWriter<W, R>* Create(
grpc::ChannelInterface* channel, const grpc::internal::RpcMethod& method,
grpc::ClientContext* context) {
return new ClientReaderWriter<W, R>(channel, method, context);
}
};
} // namespace internal
/// Synchronous (blocking) client-side API for bi-directional streaming RPCs,
/// where the outgoing message stream coming from the client has messages of
/// type \a W, and the incoming messages stream coming from the server has
/// messages of type \a R.
template <class W, class R>
class ClientReaderWriter final : public ClientReaderWriterInterface<W, R> {
public:
/// Block waiting to read initial metadata from the server.
/// This call is optional, but if it is used, it cannot be used concurrently
/// with or after the \a Finish method.
///
/// Once complete, the initial metadata read from the server will be
/// accessible through the \a ClientContext used to construct this object.
void WaitForInitialMetadata() override {
GPR_ASSERT(!context_->initial_metadata_received_);
grpc::internal::CallOpSet<grpc::internal::CallOpRecvInitialMetadata> ops;
ops.RecvInitialMetadata(context_);
call_.PerformOps(&ops);
cq_.Pluck(&ops); // status ignored
}
bool NextMessageSize(uint32_t* sz) override {
int result = call_.max_receive_message_size();
*sz = (result > 0) ? result : UINT32_MAX;
return true;
}
/// See the \a ReaderInterface.Read method for semantics.
/// Side effect:
/// Also receives initial metadata if not already received (updates the \a
/// ClientContext associated with this call in that case).
bool Read(R* msg) override {
grpc::internal::CallOpSet<grpc::internal::CallOpRecvInitialMetadata,
grpc::internal::CallOpRecvMessage<R>>
ops;
if (!context_->initial_metadata_received_) {
ops.RecvInitialMetadata(context_);
}
ops.RecvMessage(msg);
call_.PerformOps(&ops);
return cq_.Pluck(&ops) && ops.got_message;
}
/// See the \a WriterInterface.Write method for semantics.
///
/// Side effect:
/// Also sends initial metadata if not already sent (using the
/// \a ClientContext associated with this call to fill in values).
using internal::WriterInterface<W>::Write;
bool Write(const W& msg, grpc::WriteOptions options) override {
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata,
grpc::internal::CallOpSendMessage,
grpc::internal::CallOpClientSendClose>
ops;
if (options.is_last_message()) {
options.set_buffer_hint();
ops.ClientSendClose();
}
if (context_->initial_metadata_corked_) {
ops.SendInitialMetadata(&context_->send_initial_metadata_,
context_->initial_metadata_flags());
context_->set_initial_metadata_corked(false);
}
if (!ops.SendMessagePtr(&msg, options).ok()) {
return false;
}
call_.PerformOps(&ops);
return cq_.Pluck(&ops);
}
bool WritesDone() override {
grpc::internal::CallOpSet<grpc::internal::CallOpClientSendClose> ops;
ops.ClientSendClose();
call_.PerformOps(&ops);
return cq_.Pluck(&ops);
}
/// See the ClientStreamingInterface.Finish method for semantics.
///
/// Side effect:
/// - the \a ClientContext associated with this call is updated with
/// possible trailing metadata sent from the server.
grpc::Status Finish() override {
grpc::internal::CallOpSet<grpc::internal::CallOpRecvInitialMetadata,
grpc::internal::CallOpClientRecvStatus>
ops;
if (!context_->initial_metadata_received_) {
ops.RecvInitialMetadata(context_);
}
grpc::Status status;
ops.ClientRecvStatus(context_, &status);
call_.PerformOps(&ops);
GPR_ASSERT(cq_.Pluck(&ops));
return status;
}
private:
friend class internal::ClientReaderWriterFactory<W, R>;
grpc::ClientContext* context_;
grpc::CompletionQueue cq_;
grpc::internal::Call call_;
/// Block to create a stream and write the initial metadata and \a request
/// out. Note that \a context will be used to fill in custom initial metadata
/// used to send to the server when starting the call.
ClientReaderWriter(grpc::ChannelInterface* channel,
const grpc::internal::RpcMethod& method,
grpc::ClientContext* context)
: context_(context),
cq_(grpc_completion_queue_attributes{
GRPC_CQ_CURRENT_VERSION, GRPC_CQ_PLUCK, GRPC_CQ_DEFAULT_POLLING,
nullptr}), // Pluckable cq
call_(channel->CreateCall(method, context, &cq_)) {
if (!context_->initial_metadata_corked_) {
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata> ops;
ops.SendInitialMetadata(&context->send_initial_metadata_,
context->initial_metadata_flags());
call_.PerformOps(&ops);
cq_.Pluck(&ops);
}
}
};
/// Server-side interface for streaming reads of message of type \a R.
template <class R>
class ServerReaderInterface : public internal::ServerStreamingInterface,
public internal::ReaderInterface<R> {};
/// Synchronous (blocking) server-side API for doing client-streaming RPCs,
/// where the incoming message stream coming from the client has messages of
/// type \a R.
template <class R>
class ServerReader final : public ServerReaderInterface<R> {
public:
/// See the \a ServerStreamingInterface.SendInitialMetadata method
/// for semantics. Note that initial metadata will be affected by the
/// \a ServerContext associated with this call.
void SendInitialMetadata() override {
GPR_ASSERT(!ctx_->sent_initial_metadata_);
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata> ops;
ops.SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
ops.set_compression_level(ctx_->compression_level());
}
ctx_->sent_initial_metadata_ = true;
call_->PerformOps(&ops);
call_->cq()->Pluck(&ops);
}
bool NextMessageSize(uint32_t* sz) override {
int result = call_->max_receive_message_size();
*sz = (result > 0) ? result : UINT32_MAX;
return true;
}
bool Read(R* msg) override {
grpc::internal::CallOpSet<grpc::internal::CallOpRecvMessage<R>> ops;
ops.RecvMessage(msg);
call_->PerformOps(&ops);
bool ok = call_->cq()->Pluck(&ops) && ops.got_message;
if (!ok) {
ctx_->MaybeMarkCancelledOnRead();
}
return ok;
}
private:
grpc::internal::Call* const call_;
ServerContext* const ctx_;
template <class ServiceType, class RequestType, class ResponseType>
friend class internal::ClientStreamingHandler;
ServerReader(grpc::internal::Call* call, grpc::ServerContext* ctx)
: call_(call), ctx_(ctx) {}
};
/// Server-side interface for streaming writes of message of type \a W.
template <class W>
class ServerWriterInterface : public internal::ServerStreamingInterface,
public internal::WriterInterface<W> {};
/// Synchronous (blocking) server-side API for doing for doing a
/// server-streaming RPCs, where the outgoing message stream coming from the
/// server has messages of type \a W.
template <class W>
class ServerWriter final : public ServerWriterInterface<W> {
public:
/// See the \a ServerStreamingInterface.SendInitialMetadata method
/// for semantics.
/// Note that initial metadata will be affected by the
/// \a ServerContext associated with this call.
void SendInitialMetadata() override {
GPR_ASSERT(!ctx_->sent_initial_metadata_);
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata> ops;
ops.SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
ops.set_compression_level(ctx_->compression_level());
}
ctx_->sent_initial_metadata_ = true;
call_->PerformOps(&ops);
call_->cq()->Pluck(&ops);
}
/// See the \a WriterInterface.Write method for semantics.
///
/// Side effect:
/// Also sends initial metadata if not already sent (using the
/// \a ClientContext associated with this call to fill in values).
using internal::WriterInterface<W>::Write;
bool Write(const W& msg, grpc::WriteOptions options) override {
if (options.is_last_message()) {
options.set_buffer_hint();
}
if (!ctx_->pending_ops_.SendMessagePtr(&msg, options).ok()) {
return false;
}
if (!ctx_->sent_initial_metadata_) {
ctx_->pending_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
ctx_->pending_ops_.set_compression_level(ctx_->compression_level());
}
ctx_->sent_initial_metadata_ = true;
}
call_->PerformOps(&ctx_->pending_ops_);
// if this is the last message we defer the pluck until AFTER we start
// the trailing md op. This prevents hangs. See
// https://github.com/grpc/grpc/issues/11546
if (options.is_last_message()) {
ctx_->has_pending_ops_ = true;
return true;
}
ctx_->has_pending_ops_ = false;
return call_->cq()->Pluck(&ctx_->pending_ops_);
}
private:
grpc::internal::Call* const call_;
grpc::ServerContext* const ctx_;
template <class ServiceType, class RequestType, class ResponseType>
friend class internal::ServerStreamingHandler;
ServerWriter(grpc::internal::Call* call, grpc::ServerContext* ctx)
: call_(call), ctx_(ctx) {}
};
/// Server-side interface for bi-directional streaming.
template <class W, class R>
class ServerReaderWriterInterface : public internal::ServerStreamingInterface,
public internal::WriterInterface<W>,
public internal::ReaderInterface<R> {};
/// Actual implementation of bi-directional streaming
namespace internal {
template <class W, class R>
class ServerReaderWriterBody final {
public:
ServerReaderWriterBody(grpc::internal::Call* call, grpc::ServerContext* ctx)
: call_(call), ctx_(ctx) {}
void SendInitialMetadata() {
GPR_ASSERT(!ctx_->sent_initial_metadata_);
grpc::internal::CallOpSet<grpc::internal::CallOpSendInitialMetadata> ops;
ops.SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
ops.set_compression_level(ctx_->compression_level());
}
ctx_->sent_initial_metadata_ = true;
call_->PerformOps(&ops);
call_->cq()->Pluck(&ops);
}
bool NextMessageSize(uint32_t* sz) {
int result = call_->max_receive_message_size();
*sz = (result > 0) ? result : UINT32_MAX;
return true;
}
bool Read(R* msg) {
grpc::internal::CallOpSet<grpc::internal::CallOpRecvMessage<R>> ops;
ops.RecvMessage(msg);
call_->PerformOps(&ops);
bool ok = call_->cq()->Pluck(&ops) && ops.got_message;
if (!ok) {
ctx_->MaybeMarkCancelledOnRead();
}
return ok;
}
bool Write(const W& msg, grpc::WriteOptions options) {
if (options.is_last_message()) {
options.set_buffer_hint();
}
if (!ctx_->pending_ops_.SendMessagePtr(&msg, options).ok()) {
return false;
}
if (!ctx_->sent_initial_metadata_) {
ctx_->pending_ops_.SendInitialMetadata(&ctx_->initial_metadata_,
ctx_->initial_metadata_flags());
if (ctx_->compression_level_set()) {
ctx_->pending_ops_.set_compression_level(ctx_->compression_level());
}
ctx_->sent_initial_metadata_ = true;
}
call_->PerformOps(&ctx_->pending_ops_);
// if this is the last message we defer the pluck until AFTER we start
// the trailing md op. This prevents hangs. See
// https://github.com/grpc/grpc/issues/11546
if (options.is_last_message()) {
ctx_->has_pending_ops_ = true;
return true;
}
ctx_->has_pending_ops_ = false;
return call_->cq()->Pluck(&ctx_->pending_ops_);
}
private:
grpc::internal::Call* const call_;
grpc::ServerContext* const ctx_;
};
} // namespace internal
/// Synchronous (blocking) server-side API for a bidirectional
/// streaming call, where the incoming message stream coming from the client has
/// messages of type \a R, and the outgoing message streaming coming from
/// the server has messages of type \a W.
template <class W, class R>
class ServerReaderWriter final : public ServerReaderWriterInterface<W, R> {
public:
/// See the \a ServerStreamingInterface.SendInitialMetadata method
/// for semantics. Note that initial metadata will be affected by the
/// \a ServerContext associated with this call.
void SendInitialMetadata() override { body_.SendInitialMetadata(); }
bool NextMessageSize(uint32_t* sz) override {
return body_.NextMessageSize(sz);
}
bool Read(R* msg) override { return body_.Read(msg); }
/// See the \a WriterInterface.Write(const W& msg, WriteOptions options)
/// method for semantics.
/// Side effect:
/// Also sends initial metadata if not already sent (using the \a
/// ServerContext associated with this call).
using internal::WriterInterface<W>::Write;
bool Write(const W& msg, grpc::WriteOptions options) override {
return body_.Write(msg, options);
}
private:
internal::ServerReaderWriterBody<W, R> body_;
friend class internal::TemplatedBidiStreamingHandler<ServerReaderWriter<W, R>,
false>;
ServerReaderWriter(grpc::internal::Call* call, grpc::ServerContext* ctx)
: body_(call, ctx) {}
};
/// A class to represent a flow-controlled unary call. This is something
/// of a hybrid between conventional unary and streaming. This is invoked
/// through a unary call on the client side, but the server responds to it
/// as though it were a single-ping-pong streaming call. The server can use
/// the \a NextMessageSize method to determine an upper-bound on the size of
/// the message. A key difference relative to streaming: ServerUnaryStreamer
/// must have exactly 1 Read and exactly 1 Write, in that order, to function
/// correctly. Otherwise, the RPC is in error.
template <class RequestType, class ResponseType>
class ServerUnaryStreamer final
: public ServerReaderWriterInterface<ResponseType, RequestType> {
public:
/// Block to send initial metadata to client.
/// Implicit input parameter:
/// - the \a ServerContext associated with this call will be used for
/// sending initial metadata.
void SendInitialMetadata() override { body_.SendInitialMetadata(); }
/// Get an upper bound on the request message size from the client.
bool NextMessageSize(uint32_t* sz) override {
return body_.NextMessageSize(sz);
}
/// Read a message of type \a R into \a msg. Completion will be notified by \a
/// tag on the associated completion queue.
/// This is thread-safe with respect to \a Write or \a WritesDone methods. It
/// should not be called concurrently with other streaming APIs
/// on the same stream. It is not meaningful to call it concurrently
/// with another \a ReaderInterface::Read on the same stream since reads on
/// the same stream are delivered in order.
///
/// \param[out] msg Where to eventually store the read message.
/// \param[in] tag The tag identifying the operation.
bool Read(RequestType* request) override {
if (read_done_) {
return false;
}
read_done_ = true;
return body_.Read(request);
}
/// Block to write \a msg to the stream with WriteOptions \a options.
/// This is thread-safe with respect to \a ReaderInterface::Read
///
/// \param msg The message to be written to the stream.
/// \param options The WriteOptions affecting the write operation.
///
/// \return \a true on success, \a false when the stream has been closed.
using internal::WriterInterface<ResponseType>::Write;
bool Write(const ResponseType& response,
grpc::WriteOptions options) override {
if (write_done_ || !read_done_) {
return false;
}
write_done_ = true;
return body_.Write(response, options);
}
private:
internal::ServerReaderWriterBody<ResponseType, RequestType> body_;
bool read_done_;
bool write_done_;
friend class internal::TemplatedBidiStreamingHandler<
ServerUnaryStreamer<RequestType, ResponseType>, true>;
ServerUnaryStreamer(grpc::internal::Call* call, grpc::ServerContext* ctx)
: body_(call, ctx), read_done_(false), write_done_(false) {}
};
/// A class to represent a flow-controlled server-side streaming call.
/// This is something of a hybrid between server-side and bidi streaming.
/// This is invoked through a server-side streaming call on the client side,
/// but the server responds to it as though it were a bidi streaming call that
/// must first have exactly 1 Read and then any number of Writes.
template <class RequestType, class ResponseType>
class ServerSplitStreamer final
: public ServerReaderWriterInterface<ResponseType, RequestType> {
public:
/// Block to send initial metadata to client.
/// Implicit input parameter:
/// - the \a ServerContext associated with this call will be used for
/// sending initial metadata.
void SendInitialMetadata() override { body_.SendInitialMetadata(); }
/// Get an upper bound on the request message size from the client.
bool NextMessageSize(uint32_t* sz) override {
return body_.NextMessageSize(sz);
}
/// Read a message of type \a R into \a msg. Completion will be notified by \a
/// tag on the associated completion queue.
/// This is thread-safe with respect to \a Write or \a WritesDone methods. It
/// should not be called concurrently with other streaming APIs
/// on the same stream. It is not meaningful to call it concurrently
/// with another \a ReaderInterface::Read on the same stream since reads on
/// the same stream are delivered in order.
///
/// \param[out] msg Where to eventually store the read message.
/// \param[in] tag The tag identifying the operation.
bool Read(RequestType* request) override {
if (read_done_) {
return false;
}
read_done_ = true;
return body_.Read(request);
}
/// Block to write \a msg to the stream with WriteOptions \a options.
/// This is thread-safe with respect to \a ReaderInterface::Read
///
/// \param msg The message to be written to the stream.
/// \param options The WriteOptions affecting the write operation.
///
/// \return \a true on success, \a false when the stream has been closed.
using internal::WriterInterface<ResponseType>::Write;
bool Write(const ResponseType& response,
grpc::WriteOptions options) override {
return read_done_ && body_.Write(response, options);
}
private:
internal::ServerReaderWriterBody<ResponseType, RequestType> body_;
bool read_done_;
friend class internal::TemplatedBidiStreamingHandler<
ServerSplitStreamer<RequestType, ResponseType>, false>;
ServerSplitStreamer(grpc::internal::Call* call, grpc::ServerContext* ctx)
: body_(call, ctx), read_done_(false) {}
};
} // namespace grpc
#endif // GRPCPP_SUPPORT_SYNC_STREAM_H

View File

@@ -0,0 +1,89 @@
//
//
// Copyright 2015 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_SUPPORT_TIME_H
#define GRPCPP_SUPPORT_TIME_H
#include <chrono>
#include <grpc/impl/grpc_types.h>
#include <grpcpp/support/config.h>
namespace grpc {
/// If you are trying to use CompletionQueue::AsyncNext with a time class that
/// isn't either gpr_timespec or std::chrono::system_clock::time_point, you
/// will most likely be looking at this comment as your compiler will have
/// fired an error below. In order to fix this issue, you have two potential
/// solutions:
/// 1. Use gpr_timespec or std::chrono::system_clock::time_point instead
/// 2. Specialize the TimePoint class with whichever time class that you
/// want to use here. See below for two examples of how to do this.
///
template <typename T>
class TimePoint {
public:
// If you see the error with methods below, you may need either
// i) using the existing types having a conversion class such as
// gpr_timespec and std::chrono::system_clock::time_point or
// ii) writing a new TimePoint<YourType> to address your case.
TimePoint(const T& /*time*/) = delete;
gpr_timespec raw_time() = delete;
};
template <>
class TimePoint<gpr_timespec> {
public:
// NOLINTNEXTLINE(google-explicit-constructor)
TimePoint(const gpr_timespec& time) : time_(time) {}
gpr_timespec raw_time() { return time_; }
private:
gpr_timespec time_;
};
} // namespace grpc
namespace grpc {
// from and to should be absolute time.
void Timepoint2Timespec(const std::chrono::system_clock::time_point& from,
gpr_timespec* to);
void TimepointHR2Timespec(
const std::chrono::high_resolution_clock::time_point& from,
gpr_timespec* to);
std::chrono::system_clock::time_point Timespec2Timepoint(gpr_timespec t);
template <>
class TimePoint<std::chrono::system_clock::time_point> {
public:
// NOLINTNEXTLINE(google-explicit-constructor)
TimePoint(const std::chrono::system_clock::time_point& time) {
Timepoint2Timespec(time, &time_);
}
gpr_timespec raw_time() const { return time_; }
private:
gpr_timespec time_;
};
} // namespace grpc
#endif // GRPCPP_SUPPORT_TIME_H

View File

@@ -0,0 +1,36 @@
//
//
// Copyright 2019 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_SUPPORT_VALIDATE_SERVICE_CONFIG_H
#define GRPCPP_SUPPORT_VALIDATE_SERVICE_CONFIG_H
#include <grpcpp/support/config.h>
namespace grpc {
namespace experimental {
/// Validates \a service_config_json. If valid, returns an empty string.
/// Otherwise, returns the validation error.
/// TODO(yashykt): Promote it to out of experimental once it is proved useful
/// and gRFC is accepted.
std::string ValidateServiceConfigJSON(const std::string& service_config_json);
} // namespace experimental
} // namespace grpc
#endif // GRPCPP_SUPPORT_VALIDATE_SERVICE_CONFIG_H