diff options
Diffstat (limited to 'include')
-rw-r--r-- | include/grpc++/impl/codegen/async_unary_call.h | 61 | ||||
-rw-r--r-- | include/grpc++/impl/codegen/call.h | 49 | ||||
-rw-r--r-- | include/grpc/grpc.h | 4 |
3 files changed, 89 insertions, 25 deletions
diff --git a/include/grpc++/impl/codegen/async_unary_call.h b/include/grpc++/impl/codegen/async_unary_call.h index b77a16b699..50bb399df6 100644 --- a/include/grpc++/impl/codegen/async_unary_call.h +++ b/include/grpc++/impl/codegen/async_unary_call.h @@ -34,6 +34,7 @@ #ifndef GRPCXX_IMPL_CODEGEN_ASYNC_UNARY_CALL_H #define GRPCXX_IMPL_CODEGEN_ASYNC_UNARY_CALL_H +#include <assert.h> #include <grpc++/impl/codegen/call.h> #include <grpc++/impl/codegen/channel_interface.h> #include <grpc++/impl/codegen/client_context.h> @@ -41,6 +42,8 @@ #include <grpc++/impl/codegen/service_type.h> #include <grpc++/impl/codegen/status.h> +extern "C" void* grpc_call_arena_alloc(grpc_call* call, size_t size); + namespace grpc { class CompletionQueue; @@ -58,20 +61,40 @@ template <class R> class ClientAsyncResponseReader final : public ClientAsyncResponseReaderInterface<R> { public: + ~ClientAsyncResponseReader() { + if (collection_ != nullptr && collection_->Unref()) { + delete collection_; + } + } template <class W> - ClientAsyncResponseReader(ChannelInterface* channel, CompletionQueue* cq, - const RpcMethod& method, ClientContext* context, - const W& request) - : context_(context), - call_(channel->CreateCall(method, context, cq)), - collection_(std::make_shared<CallOpSetCollection>()) { - collection_->init_buf_.SetCollection(collection_); - collection_->init_buf_.SendInitialMetadata( + static ClientAsyncResponseReader* Create(ChannelInterface* channel, + CompletionQueue* cq, + const RpcMethod& method, + ClientContext* context, + const W& request) { + Call call = channel->CreateCall(method, context, cq); + ClientAsyncResponseReader* reader = static_cast<ClientAsyncResponseReader*>( + grpc_call_arena_alloc(call.call(), sizeof(*reader))); + new (&reader->call_) Call(std::move(call)); + reader->context_ = context; + reader->collection_ = + new (grpc_call_arena_alloc(call.call(), sizeof(CallOpSetCollection))) + CallOpSetCollection(); + + reader->collection_->init_buf_.SetCollection(reader->collection_); + reader->collection_->init_buf_.SendInitialMetadata( context->send_initial_metadata_, context->initial_metadata_flags()); // TODO(ctiller): don't assert - GPR_CODEGEN_ASSERT(collection_->init_buf_.SendMessage(request).ok()); - collection_->init_buf_.ClientSendClose(); - call_.PerformOps(&collection_->init_buf_); + GPR_CODEGEN_ASSERT( + reader->collection_->init_buf_.SendMessage(request).ok()); + reader->collection_->init_buf_.ClientSendClose(); + reader->call_.PerformOps(&reader->collection_->init_buf_); + return reader; + } + + // always allocated against a call arena, no memory free required + static void operator delete(void* ptr, std::size_t size) { + assert(size == sizeof(ClientAsyncResponseReader)); } void ReadInitialMetadata(void* tag) { @@ -99,7 +122,10 @@ class ClientAsyncResponseReader final ClientContext* context_; Call call_; - class CallOpSetCollection : public CallOpSetCollectionInterface { + // disable operator new + static void* operator new(std::size_t size); + + class CallOpSetCollection final : public CallOpSetCollectionInterface { public: SneakyCallOpSet<CallOpSendInitialMetadata, CallOpSendMessage, CallOpClientSendClose> @@ -108,8 +134,17 @@ class ClientAsyncResponseReader final CallOpSet<CallOpRecvInitialMetadata, CallOpRecvMessage<R>, CallOpClientRecvStatus> finish_buf_; + + static void* operator new(std::size_t size, void* p) { return p; } + static void operator delete(void* ptr, std::size_t size) { + assert(size == sizeof(CallOpSetCollection)); + } + + private: + // disable operator new + static void* operator new(std::size_t size); }; - std::shared_ptr<CallOpSetCollection> collection_; + CallOpSetCollection* collection_; }; template <class W> diff --git a/include/grpc++/impl/codegen/call.h b/include/grpc++/impl/codegen/call.h index a3f2be6bb1..9c8611a116 100644 --- a/include/grpc++/impl/codegen/call.h +++ b/include/grpc++/impl/codegen/call.h @@ -34,6 +34,7 @@ #ifndef GRPCXX_IMPL_CODEGEN_CALL_H #define GRPCXX_IMPL_CODEGEN_CALL_H +#include <assert.h> #include <cstring> #include <functional> #include <map> @@ -50,6 +51,7 @@ #include <grpc++/impl/codegen/status_helper.h> #include <grpc++/impl/codegen/string_ref.h> +#include <grpc/impl/codegen/atm.h> #include <grpc/impl/codegen/compression_types.h> #include <grpc/impl/codegen/grpc_types.h> @@ -561,14 +563,29 @@ class CallOpClientRecvStatus { /// An abstract collection of CallOpSet's, to be used whenever /// CallOpSet objects must be thought of as a group. Each member -/// of the group should have a shared_ptr back to the collection, -/// as will the object that instantiates the collection, allowing -/// for automatic ref-counting. In practice, any actual use should -/// derive from this base class. This is specifically necessary if -/// some of the CallOpSet's in the collection are "Sneaky" and don't -/// report back to the C++ layer CQ operations -class CallOpSetCollectionInterface - : public std::enable_shared_from_this<CallOpSetCollectionInterface> {}; +/// of the group should reference the collection, as will the object +/// that instantiates the collection, allowing for ref-counting. +/// Any actual use should derive from this base class. This is specifically +/// necessary if some of the CallOpSet's in the collection are "Sneaky" and +/// don't report back to the C++ layer CQ operations +class CallOpSetCollectionInterface { + public: + CallOpSetCollectionInterface() { + gpr_atm_rel_store(&refs_, static_cast<gpr_atm>(1)); + } + // always allocated against a call arena, no memory free required + static void operator delete(void* ptr, std::size_t size) { + } + void Ref() { gpr_atm_no_barrier_fetch_add(&refs_, static_cast<gpr_atm>(1)); } + bool Unref() { + gpr_atm old = + gpr_atm_full_fetch_add(&refs_, static_cast<gpr_atm>(-1)); + return (old == static_cast<gpr_atm>(1)); + } + + private: + gpr_atm refs_; +}; /// An abstract collection of call ops, used to generate the /// grpc_call_op structure to pass down to the lower layers, @@ -577,18 +594,26 @@ class CallOpSetCollectionInterface /// API. class CallOpSetInterface : public CompletionQueueTag { public: - CallOpSetInterface() {} + CallOpSetInterface() : collection_(nullptr) {} + ~CallOpSetInterface() { ResetCollection(); } /// Fills in grpc_op, starting from ops[*nops] and moving /// upwards. virtual void FillOps(grpc_op* ops, size_t* nops) = 0; /// Mark this as belonging to a collection if needed - void SetCollection(std::shared_ptr<CallOpSetCollectionInterface> collection) { + void SetCollection(CallOpSetCollectionInterface* collection) { collection_ = collection; + collection->Ref(); + } + void ResetCollection() { + if (collection_ != nullptr && collection_->Unref()) { + delete collection_; + } + collection_ = nullptr; } protected: - std::shared_ptr<CallOpSetCollectionInterface> collection_; + CallOpSetCollectionInterface* collection_; }; /// Primary implementaiton of CallOpSetInterface. @@ -626,7 +651,7 @@ class CallOpSet : public CallOpSetInterface, this->Op5::FinishOp(status); this->Op6::FinishOp(status); *tag = return_tag_; - collection_.reset(); // drop the ref at this point + ResetCollection(); // drop the ref at this point return true; } diff --git a/include/grpc/grpc.h b/include/grpc/grpc.h index e088435d6c..3430cd31e9 100644 --- a/include/grpc/grpc.h +++ b/include/grpc/grpc.h @@ -263,6 +263,10 @@ GRPCAPI grpc_call *grpc_channel_create_registered_call( grpc_completion_queue *completion_queue, void *registered_call_handle, gpr_timespec deadline, void *reserved); +/** Allocate memory in the grpc_call arena: this memory is automatically + discarded at call completion */ +GRPCAPI void *grpc_call_arena_alloc(grpc_call *call, size_t size); + /** Start a batch of operations defined in the array ops; when complete, post a completion of type 'tag' to the completion queue bound to the call. The order of ops specified in the batch has no significance. |