mirror of https://github.com/grpc/grpc.git
parent
d766b9a2a4
commit
8bf52535d1
27 changed files with 772 additions and 7 deletions
@ -0,0 +1,79 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2018 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef GRPCPP_IMPL_CODEGEN_CALLBACK_COMMON_H |
||||
#define GRPCPP_IMPL_CODEGEN_CALLBACK_COMMON_H |
||||
|
||||
#include <functional> |
||||
|
||||
#include <grpcpp/impl/codegen/call.h> |
||||
#include <grpcpp/impl/codegen/channel_interface.h> |
||||
#include <grpcpp/impl/codegen/config.h> |
||||
#include <grpcpp/impl/codegen/core_codegen_interface.h> |
||||
#include <grpcpp/impl/codegen/status.h> |
||||
|
||||
// Forward declarations
|
||||
namespace grpc_core { |
||||
class CQCallbackInterface; |
||||
}; |
||||
|
||||
namespace grpc { |
||||
namespace internal { |
||||
|
||||
class CallbackWithStatusTag { |
||||
public: |
||||
// TODO(vjpai): make impl and ops part of this structure to avoid allocation,
|
||||
// ownership transfer, and delete
|
||||
CallbackWithStatusTag(std::function<void(Status)> f, bool self_delete, |
||||
CompletionQueueTag* ops); |
||||
~CallbackWithStatusTag() { delete ops_; } |
||||
void* tag() { return static_cast<void*>(impl_); } |
||||
Status* status_ptr() { return status_; } |
||||
CompletionQueueTag* ops() { return ops_; } |
||||
|
||||
// force_run can only be performed on a tag before it can ever be active
|
||||
void force_run(Status s); |
||||
|
||||
private: |
||||
grpc_core::CQCallbackInterface* impl_; |
||||
Status* status_; |
||||
CompletionQueueTag* ops_; |
||||
}; |
||||
|
||||
class CallbackWithSuccessTag { |
||||
public: |
||||
// TODO(vjpai): make impl and ops part of this structure to avoid allocation,
|
||||
// ownership transfer, and delete
|
||||
CallbackWithSuccessTag(std::function<void(bool)> f, bool self_delete, |
||||
CompletionQueueTag* ops); |
||||
~CallbackWithSuccessTag() { delete ops_; } |
||||
void* tag() { return static_cast<void*>(impl_); } |
||||
CompletionQueueTag* ops() { return ops_; } |
||||
|
||||
// force_run can only be performed on a tag before it can ever be active
|
||||
void force_run(bool ok); |
||||
|
||||
private: |
||||
grpc_core::CQCallbackInterface* impl_; |
||||
CompletionQueueTag* ops_; |
||||
}; |
||||
|
||||
} // namespace internal
|
||||
} // namespace grpc
|
||||
|
||||
#endif // GRPCPP_IMPL_CODEGEN_CLIENT_CALLBACK_H
|
@ -0,0 +1,92 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2018 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef GRPCPP_IMPL_CODEGEN_CLIENT_CALLBACK_H |
||||
#define GRPCPP_IMPL_CODEGEN_CLIENT_CALLBACK_H |
||||
|
||||
#include <functional> |
||||
|
||||
#include <grpcpp/impl/codegen/call.h> |
||||
#include <grpcpp/impl/codegen/callback_common.h> |
||||
#include <grpcpp/impl/codegen/channel_interface.h> |
||||
#include <grpcpp/impl/codegen/config.h> |
||||
#include <grpcpp/impl/codegen/core_codegen_interface.h> |
||||
#include <grpcpp/impl/codegen/status.h> |
||||
|
||||
namespace grpc { |
||||
|
||||
class Channel; |
||||
class ClientContext; |
||||
class CompletionQueue; |
||||
|
||||
namespace internal { |
||||
class RpcMethod; |
||||
|
||||
/// Perform a callback-based unary call
|
||||
/// TODO(vjpai): Combine as much as possible with the blocking unary call code
|
||||
template <class InputMessage, class OutputMessage> |
||||
void CallbackUnaryCall(ChannelInterface* channel, const RpcMethod& method, |
||||
ClientContext* context, const InputMessage* request, |
||||
OutputMessage* result, |
||||
std::function<void(Status)> on_completion) { |
||||
CallbackUnaryCallImpl<InputMessage, OutputMessage> x( |
||||
channel, method, context, request, result, on_completion); |
||||
} |
||||
|
||||
template <class InputMessage, class OutputMessage> |
||||
class CallbackUnaryCallImpl { |
||||
public: |
||||
CallbackUnaryCallImpl(ChannelInterface* channel, const RpcMethod& method, |
||||
ClientContext* context, const InputMessage* request, |
||||
OutputMessage* result, |
||||
std::function<void(Status)> on_completion) { |
||||
CompletionQueue* cq = channel->CallbackCQ(); |
||||
GPR_CODEGEN_ASSERT(cq != nullptr); |
||||
|
||||
// TODO(vjpai): Allocate this as part of the tag's arena
|
||||
auto* ops = new CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage, |
||||
CallOpRecvInitialMetadata, |
||||
CallOpRecvMessage<OutputMessage>, |
||||
CallOpClientSendClose, CallOpClientRecvStatus>; |
||||
|
||||
// TODO(vjpai): Move to using pre-allocated tags rather than new/self-delete
|
||||
auto* tag = new CallbackWithStatusTag(on_completion, true, ops); |
||||
|
||||
// TODO(vjpai): Unify code with sync API as much as possible
|
||||
Call call(channel->CreateCall(method, context, cq)); |
||||
Status s = ops->SendMessage(*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_cq_tag(tag->tag()); |
||||
call.PerformOps(ops); |
||||
} |
||||
}; |
||||
|
||||
} // namespace internal
|
||||
} // namespace grpc
|
||||
|
||||
#endif // GRPCPP_IMPL_CODEGEN_CLIENT_CALLBACK_H
|
@ -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_CLIENT_CALLBACK_H |
||||
#define GRPCPP_SUPPORT_CLIENT_CALLBACK_H |
||||
|
||||
#include <grpcpp/impl/codegen/client_callback.h> |
||||
|
||||
#endif // GRPCPP_SUPPORT_CLIENT_CALLBACK_H
|
@ -0,0 +1,109 @@ |
||||
/*
|
||||
* |
||||
* 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. |
||||
* |
||||
*/ |
||||
|
||||
#include <functional> |
||||
|
||||
#include <grpcpp/impl/codegen/callback_common.h> |
||||
#include <grpcpp/impl/codegen/status.h> |
||||
|
||||
#include "src/core/lib/gprpp/memory.h" |
||||
#include "src/core/lib/surface/completion_queue.h" |
||||
|
||||
namespace grpc { |
||||
namespace internal { |
||||
|
||||
namespace { |
||||
class CallbackWithSuccessImpl : public grpc_core::CQCallbackInterface { |
||||
public: |
||||
CallbackWithSuccessImpl(CallbackWithSuccessTag* parent, |
||||
std::function<void(bool)> f, bool self_delete) |
||||
: parent_(parent), func_(f), self_delete_(self_delete) {} |
||||
|
||||
void Run(bool ok) override { |
||||
void* ignored = parent_->ops(); |
||||
bool new_ok = ok; |
||||
GPR_ASSERT(parent_->ops()->FinalizeResult(&ignored, &new_ok)); |
||||
GPR_ASSERT(ignored == parent_->ops()); |
||||
func_(ok); |
||||
if (self_delete_) { |
||||
delete parent_; |
||||
// Must use grpc_core::Delete since base is GRPC_ABSTRACT
|
||||
grpc_core::Delete(this); |
||||
} |
||||
} |
||||
|
||||
private: |
||||
CallbackWithSuccessTag* parent_; |
||||
std::function<void(bool)> func_; |
||||
bool self_delete_; |
||||
}; |
||||
|
||||
class CallbackWithStatusImpl : public grpc_core::CQCallbackInterface { |
||||
public: |
||||
CallbackWithStatusImpl(CallbackWithStatusTag* parent, |
||||
std::function<void(Status)> f, bool self_delete) |
||||
: parent_(parent), func_(f), status_(), self_delete_(self_delete) {} |
||||
|
||||
void Run(bool ok) override { |
||||
void* ignored = parent_->ops(); |
||||
|
||||
GPR_ASSERT(parent_->ops()->FinalizeResult(&ignored, &ok)); |
||||
GPR_ASSERT(ignored == parent_->ops()); |
||||
|
||||
func_(status_); |
||||
if (self_delete_) { |
||||
delete parent_; |
||||
// Must use grpc_core::Delete since base is GRPC_ABSTRACT
|
||||
grpc_core::Delete(this); |
||||
} |
||||
} |
||||
Status* status_ptr() { return &status_; } |
||||
|
||||
private: |
||||
CallbackWithStatusTag* parent_; |
||||
std::function<void(Status)> func_; |
||||
Status status_; |
||||
bool self_delete_; |
||||
}; |
||||
|
||||
} // namespace
|
||||
|
||||
CallbackWithSuccessTag::CallbackWithSuccessTag(std::function<void(bool)> f, |
||||
bool self_delete, |
||||
CompletionQueueTag* ops) |
||||
: impl_(grpc_core::New<CallbackWithSuccessImpl>(this, f, self_delete)), |
||||
ops_(ops) {} |
||||
|
||||
void CallbackWithSuccessTag::force_run(bool ok) { impl_->Run(ok); } |
||||
|
||||
CallbackWithStatusTag::CallbackWithStatusTag(std::function<void(Status)> f, |
||||
bool self_delete, |
||||
CompletionQueueTag* ops) |
||||
: ops_(ops) { |
||||
auto* impl = grpc_core::New<CallbackWithStatusImpl>(this, f, self_delete); |
||||
impl_ = impl; |
||||
status_ = impl->status_ptr(); |
||||
} |
||||
|
||||
void CallbackWithStatusTag::force_run(Status s) { |
||||
*status_ = s; |
||||
impl_->Run(true); |
||||
} |
||||
|
||||
} // namespace internal
|
||||
} // namespace grpc
|
@ -0,0 +1,126 @@ |
||||
/*
|
||||
* |
||||
* 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. |
||||
* |
||||
*/ |
||||
|
||||
#include <functional> |
||||
#include <mutex> |
||||
|
||||
#include <grpcpp/channel.h> |
||||
#include <grpcpp/client_context.h> |
||||
#include <grpcpp/create_channel.h> |
||||
#include <grpcpp/generic/generic_stub.h> |
||||
#include <grpcpp/impl/codegen/proto_utils.h> |
||||
#include <grpcpp/server.h> |
||||
#include <grpcpp/server_builder.h> |
||||
#include <grpcpp/server_context.h> |
||||
#include <grpcpp/support/client_callback.h> |
||||
|
||||
#include "src/proto/grpc/testing/echo.grpc.pb.h" |
||||
#include "test/core/util/test_config.h" |
||||
#include "test/cpp/end2end/test_service_impl.h" |
||||
#include "test/cpp/util/byte_buffer_proto_helper.h" |
||||
|
||||
#include <gtest/gtest.h> |
||||
|
||||
namespace grpc { |
||||
namespace testing { |
||||
namespace { |
||||
|
||||
class ClientCallbackEnd2endTest : public ::testing::Test { |
||||
protected: |
||||
ClientCallbackEnd2endTest() {} |
||||
|
||||
void SetUp() override { |
||||
ServerBuilder builder; |
||||
builder.RegisterService(&service_); |
||||
|
||||
server_ = builder.BuildAndStart(); |
||||
is_server_started_ = true; |
||||
} |
||||
|
||||
void ResetStub() { |
||||
ChannelArguments args; |
||||
channel_ = server_->InProcessChannel(args); |
||||
stub_.reset(new GenericStub(channel_)); |
||||
} |
||||
|
||||
void TearDown() override { |
||||
if (is_server_started_) { |
||||
server_->Shutdown(); |
||||
} |
||||
} |
||||
|
||||
void SendRpcs(int num_rpcs) { |
||||
const grpc::string kMethodName("/grpc.testing.EchoTestService/Echo"); |
||||
grpc::string test_string(""); |
||||
for (int i = 0; i < num_rpcs; i++) { |
||||
EchoRequest request; |
||||
std::unique_ptr<ByteBuffer> send_buf; |
||||
ByteBuffer recv_buf; |
||||
ClientContext cli_ctx; |
||||
|
||||
test_string += "Hello world. "; |
||||
request.set_message(test_string); |
||||
send_buf = SerializeToByteBuffer(&request); |
||||
|
||||
std::mutex mu; |
||||
std::condition_variable cv; |
||||
bool done; |
||||
stub_->experimental().UnaryCall( |
||||
&cli_ctx, kMethodName, send_buf.get(), &recv_buf, |
||||
[&request, &recv_buf, &done, &mu, &cv](Status s) { |
||||
GPR_ASSERT(s.ok()); |
||||
|
||||
EchoResponse response; |
||||
EXPECT_TRUE(ParseFromByteBuffer(&recv_buf, &response)); |
||||
EXPECT_EQ(request.message(), response.message()); |
||||
std::lock_guard<std::mutex> l(mu); |
||||
done = true; |
||||
cv.notify_one(); |
||||
}); |
||||
std::unique_lock<std::mutex> l(mu); |
||||
while (!done) { |
||||
cv.wait(l); |
||||
} |
||||
} |
||||
} |
||||
bool is_server_started_; |
||||
std::shared_ptr<Channel> channel_; |
||||
std::unique_ptr<grpc::GenericStub> stub_; |
||||
TestServiceImpl service_; |
||||
std::unique_ptr<Server> server_; |
||||
}; |
||||
|
||||
TEST_F(ClientCallbackEnd2endTest, SimpleRpc) { |
||||
ResetStub(); |
||||
SendRpcs(1); |
||||
} |
||||
|
||||
TEST_F(ClientCallbackEnd2endTest, SequentialRpcs) { |
||||
ResetStub(); |
||||
SendRpcs(10); |
||||
} |
||||
|
||||
} // namespace
|
||||
} // namespace testing
|
||||
} // namespace grpc
|
||||
|
||||
int main(int argc, char** argv) { |
||||
grpc_test_init(argc, argv); |
||||
::testing::InitGoogleTest(&argc, argv); |
||||
return RUN_ALL_TESTS(); |
||||
} |
Loading…
Reference in new issue