Merge branch 'we-dont-need-no-backup' into one-shouldnt-depend-on-protobufs

Conflicts:
	include/grpc++/async_unary_call.h
	include/grpc++/config.h
	include/grpc++/config_protobuf.h
	include/grpc++/impl/service_type.h
	include/grpc++/stream.h
	src/cpp/common/call.cc
pull/1969/head
Craig Tiller 10 years ago
commit 2622ddbcfd
  1. 4
      BUILD
  2. 395
      Makefile
  3. 45
      build.json
  4. 147
      doc/connectivity-semantics-and-api.md
  5. 2
      gRPC.podspec
  6. 6
      include/grpc++/async_unary_call.h
  7. 1
      include/grpc++/byte_buffer.h
  8. 2
      include/grpc++/client_context.h
  9. 8
      include/grpc++/impl/call.h
  10. 6
      include/grpc++/impl/client_unary_call.h
  11. 8
      include/grpc++/impl/rpc_service_method.h
  12. 2
      include/grpc++/impl/service_type.h
  13. 2
      include/grpc++/server.h
  14. 9
      include/grpc++/status.h
  15. 24
      include/grpc++/stream.h
  16. 14
      include/grpc++/time.h
  17. 66
      include/grpc/byte_buffer.h
  18. 11
      include/grpc/byte_buffer_reader.h
  19. 8
      include/grpc/census.h
  20. 6
      include/grpc/compression.h
  21. 47
      include/grpc/grpc.h
  22. 6
      include/grpc/grpc_security.h
  23. 5
      include/grpc/support/slice.h
  24. 8
      include/grpc/support/tls_pthread.h
  25. 19
      src/compiler/cpp_generator.cc
  26. 12
      src/core/channel/client_channel.c
  27. 20
      src/core/channel/client_setup.c
  28. 2
      src/core/compression/algorithm.c
  29. 2
      src/core/compression/message_compress.h
  30. 5
      src/core/iomgr/pollset_multipoller_with_epoll.c
  31. 5
      src/core/iomgr/pollset_multipoller_with_poll_posix.c
  32. 22
      src/core/iomgr/pollset_posix.c
  33. 1
      src/core/iomgr/pollset_posix.h
  34. 6
      src/core/iomgr/pollset_set_posix.c
  35. 3
      src/core/iomgr/pollset_set_windows.c
  36. 1
      src/core/iomgr/pollset_windows.h
  37. 3
      src/core/support/log_win32.c
  38. 32
      src/core/surface/byte_buffer.c
  39. 54
      src/core/surface/byte_buffer_reader.c
  40. 90
      src/core/surface/call.c
  41. 3
      src/core/surface/call.h
  42. 34
      src/core/surface/channel.c
  43. 10
      src/core/surface/channel.h
  44. 5
      src/core/surface/completion_queue.c
  45. 3
      src/core/surface/completion_queue.h
  46. 12
      src/core/surface/server.c
  47. 44
      src/core/transport/chttp2_transport.c
  48. 2
      src/core/transport/metadata.c
  49. 17
      src/core/transport/stream_op.h
  50. 4
      src/cpp/proto/proto_utils.cc
  51. 2
      src/cpp/util/byte_buffer.cc
  52. 2
      src/cpp/util/status.cc
  53. 15
      src/cpp/util/time.cc
  54. 105
      src/csharp/Grpc.Core.Tests/ChannelOptionsTest.cs
  55. 2
      src/csharp/Grpc.Core.Tests/ClientServerTest.cs
  56. 6
      src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj
  57. 75
      src/csharp/Grpc.Core.Tests/Internal/ChannelArgsSafeHandleTest.cs
  58. 52
      src/csharp/Grpc.Core.Tests/Internal/CompletionQueueEventTest.cs
  59. 64
      src/csharp/Grpc.Core.Tests/Internal/CompletionQueueSafeHandleTest.cs
  60. 8
      src/csharp/Grpc.Core.Tests/PInvokeTest.cs
  61. 62
      src/csharp/Grpc.Core/Channel.cs
  62. 115
      src/csharp/Grpc.Core/ChannelArgs.cs
  63. 178
      src/csharp/Grpc.Core/ChannelOptions.cs
  64. 8
      src/csharp/Grpc.Core/Grpc.Core.csproj
  65. 29
      src/csharp/Grpc.Core/GrpcEnvironment.cs
  66. 35
      src/csharp/Grpc.Core/Internal/AsyncCall.cs
  67. 43
      src/csharp/Grpc.Core/Internal/AsyncCallBase.cs
  68. 9
      src/csharp/Grpc.Core/Internal/AsyncCallServer.cs
  69. 44
      src/csharp/Grpc.Core/Internal/BatchContextSafeHandle.cs
  70. 100
      src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
  71. 8
      src/csharp/Grpc.Core/Internal/ChannelArgsSafeHandle.cs
  72. 60
      src/csharp/Grpc.Core/Internal/CompletionQueueEvent.cs
  73. 14
      src/csharp/Grpc.Core/Internal/CompletionQueueSafeHandle.cs
  74. 89
      src/csharp/Grpc.Core/Internal/CompletionRegistry.cs
  75. 2
      src/csharp/Grpc.Core/Internal/DebugStats.cs
  76. 40
      src/csharp/Grpc.Core/Internal/Enums.cs
  77. 20
      src/csharp/Grpc.Core/Internal/GrpcThreadPool.cs
  78. 2
      src/csharp/Grpc.Core/Internal/ServerCallHandler.cs
  79. 29
      src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs
  80. 2
      src/csharp/Grpc.Core/Internal/Timespec.cs
  81. 46
      src/csharp/Grpc.Core/Server.cs
  82. 2
      src/csharp/Grpc.Examples.MathClient/MathClient.cs
  83. 2
      src/csharp/Grpc.Examples.Tests/MathClientServerTests.cs
  84. 12
      src/csharp/Grpc.IntegrationTesting/InteropClient.cs
  85. 9
      src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs
  86. 156
      src/csharp/ext/grpc_csharp_ext.c
  87. 2
      src/node/ext/byte_buffer.cc
  88. 1
      src/node/ext/call.cc
  89. 2
      src/node/package.json
  90. 4
      src/node/src/common.js
  91. 90
      src/node/test/common_test.js
  92. 38
      src/node/test/test_messages.proto
  93. 58
      src/objective-c/GRPCClient/GRPCCall.h
  94. 48
      src/objective-c/GRPCClient/GRPCCall.m
  95. 2
      src/objective-c/GRPCClient/private/GRPCWrappedCall.h
  96. 29
      src/objective-c/GRPCClient/private/GRPCWrappedCall.m
  97. 4
      src/objective-c/GRPCClient/private/NSData+GRPC.m
  98. 3
      src/objective-c/GRPCClient/private/NSDictionary+GRPC.h
  99. 13
      src/objective-c/GRPCClient/private/NSDictionary+GRPC.m
  100. 16
      src/objective-c/GRPCClient/private/NSError+GRPC.h
  101. Some files were not shown because too many files have changed in this diff Show More

@ -154,7 +154,6 @@ cc_library(
"src/core/channel/http_client_filter.h",
"src/core/channel/http_server_filter.h",
"src/core/channel/noop_filter.h",
"src/core/compression/algorithm.h",
"src/core/compression/message_compress.h",
"src/core/debug/trace.h",
"src/core/iomgr/alarm.h",
@ -349,6 +348,7 @@ cc_library(
"include/grpc/grpc_security.h",
"include/grpc/byte_buffer.h",
"include/grpc/byte_buffer_reader.h",
"include/grpc/compression.h",
"include/grpc/grpc.h",
"include/grpc/status.h",
"include/grpc/census.h",
@ -377,7 +377,6 @@ cc_library(
"src/core/channel/http_client_filter.h",
"src/core/channel/http_server_filter.h",
"src/core/channel/noop_filter.h",
"src/core/compression/algorithm.h",
"src/core/compression/message_compress.h",
"src/core/debug/trace.h",
"src/core/iomgr/alarm.h",
@ -549,6 +548,7 @@ cc_library(
hdrs = [
"include/grpc/byte_buffer.h",
"include/grpc/byte_buffer_reader.h",
"include/grpc/compression.h",
"include/grpc/grpc.h",
"include/grpc/status.h",
"include/grpc/census.h",

File diff suppressed because one or more lines are too long

@ -101,6 +101,7 @@
"public_headers": [
"include/grpc/byte_buffer.h",
"include/grpc/byte_buffer_reader.h",
"include/grpc/compression.h",
"include/grpc/grpc.h",
"include/grpc/status.h"
],
@ -115,7 +116,6 @@
"src/core/channel/http_client_filter.h",
"src/core/channel/http_server_filter.h",
"src/core/channel/noop_filter.h",
"src/core/compression/algorithm.h",
"src/core/compression/message_compress.h",
"src/core/debug/trace.h",
"src/core/iomgr/alarm.h",
@ -703,6 +703,7 @@
"language": "c++",
"headers": [
"test/cpp/qps/driver.h",
"test/cpp/qps/interarrival.h",
"test/cpp/qps/qps_worker.h",
"test/cpp/qps/report.h",
"test/cpp/qps/timer.h"
@ -1362,6 +1363,9 @@
"grpc",
"gpr_test_util",
"gpr"
],
"platforms": [
"posix"
]
},
{
@ -2075,9 +2079,27 @@
"grpc++_benchmark_config"
]
},
{
"name": "qps_interarrival_test",
"build": "test",
"run": false,
"language": "c++",
"src": [
"test/cpp/qps/qps_interarrival_test.cc"
],
"deps": [
"qps",
"grpc++_test_util",
"grpc_test_util",
"grpc++",
"grpc",
"gpr_test_util",
"gpr"
]
},
{
"name": "qps_test",
"build": "benchmark",
"build": "test",
"language": "c++",
"src": [
"test/cpp/qps/qps_test.cc"
@ -2094,6 +2116,25 @@
"grpc++_test_config"
]
},
{
"name": "qps_test_openloop",
"build": "test",
"language": "c++",
"src": [
"test/cpp/qps/qps_test_openloop.cc"
],
"deps": [
"qps",
"grpc++_test_util",
"grpc++_benchmark_config",
"grpc_test_util",
"grpc++",
"grpc",
"gpr_test_util",
"gpr",
"grpc++_test_config"
]
},
{
"name": "qps_worker",
"build": "benchmark",

@ -0,0 +1,147 @@
gRPC Connectivity Semantics and API
===================================
This document describes the connectivity semantics for gRPC channels and the
corresponding impact on RPCs. We then discuss an API.
States of Connectivity
----------------------
gRPC Channels provide the abstraction over which clients can communicate with
servers.The client-side channel object can be constructed using little more
than a DNS name. Channels encapsulate a range of functionality including name
resolution, establishing a TCP connection (with retries and backoff) and TLS
handshakes. Channels can also handle errors on established connections and
reconnect, or in the case of HTTP/2 GO_AWAY, re-resolve the name and reconnect.
To hide the details of all this activity from the user of the gRPC API (i.e.,
application code) while exposing meaningful information about the state of a
channel, we use a state machine with four states, defined below:
CONNECTING: The channel is trying to establish a connection and is waiting to
make progress on one of the steps involved in name resolution, TCP connection
establishment or TLS handshake. This may be used as the initial state for channels upon
creation.
READY: The channel has successfully established a connection all the way
through TLS handshake (or equivalent) and all subsequent attempt to communicate
have succeeded (or are pending without any known failure ).
TRANSIENT_FAILURE: There has been some transient failure (such as a TCP 3-way
handshake timing out or a socket error). Channels in this state will eventually
switch to the CONNECTING state and try to establish a connection again. Since
retries are done with exponential backoff, channels that fail to connect will
start out spending very little time in this state but as the attempts fail
repeatedly, the channel will spend increasingly large amounts of time in this
state. For many non-fatal failures (e.g., TCP connection attempts timing out
because the server is not yet available), the channel may spend increasingly
large amounts of time in this state.
IDLE: This is the state where the channel is not even trying to create a
connection because of a lack of new or pending RPCs. New channels MAY be created
in this state. Any attempt to start an RPC on the channel will push the channel
out of this state to connecting. When there has been no RPC activity on a channel
for a specified IDLE_TIMEOUT, i.e., no new or pending (active) RPCs for this
period, channels that are READY or CONNECTING switch to IDLE. Additionaly,
channels that receive a GOAWAY when there are no active or pending RPCs should
also switch to IDLE to avoid connection overload at servers that are attempting
to shed connections. We will use a default IDLE_TIMEOUT of 300 seconds (5 minutes).
SHUTDOWN: This channel has started shutting down. Any new RPCs should fail
immediately. Pending RPCs may continue running till the application cancels them.
Channels may enter this state either because the application explicitly requested
a shutdown or if a non-recoverable error has happened during attempts to connect
communicate . (As of 6/12/2015, there are no known errors (while connecting or
communicating) that are classified as non-recoverable)
Channels that enter this state never leave this state.
The following table lists the legal transitions from one state to another and
corresponding reasons. Empty cells denote disallowed transitions.
<table style='border: 1px solid black'>
<tr>
<th>From/To</th>
<th>CONNECTING</th>
<th>READY</th>
<th>TRANSIENT_FAILURE</th>
<th>IDLE</th>
<th>SHUTDOWN</th>
</tr>
<tr>
<th>CONNECTING</th>
<td>Incremental progress during connection establishment</td>
<td>All steps needed to establish a connection succeeded</td>
<td>Any failure in any of the steps needed to establish connection</td>
<td>No RPC activity on channel for IDLE_TIMEOUT</td>
<td>Shutdown triggered by application.</td>
</tr>
<tr>
<th>READY</th>
<td></td>
<td>Incremental successful communication on established channel.</td>
<td>Any failure encountered while expecting successful communication on
established channel.</td>
<td>No RPC activity on channel for IDLE_TIMEOUT <br>OR<br>upon receiving a GOAWAY while there are no pending RPCs.</td>
<td>Shutdown triggered by application.</td>
</tr>
<tr>
<th>TRANSIENT_FAILURE</th>
<td>Wait time required to implement (exponential) backoff is over.</td>
<td></td>
<td></td>
<td></td>
<td>Shutdown triggered by application.</td>
</tr>
<tr>
<th>IDLE</th>
<td>Any new RPC activity on the channel</td>
<td></td>
<td></td>
<td></td>
<td>Shutdown triggered by application.</td>
</tr>
<tr>
<th>FATAL_FAILURE</th>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
Channel State API
-----------------
All gRPC libraries will expose a channel-level API method to poll the current
state of a channel. In C++, this method is called GetCurrentState and returns
an enum for one of the four legal states.
All libraries should also expose an API that enables the application (user of
the gRPC API) to be notified when the channel state changes. Since state
changes can be rapid and race with any such notification, the notification
should just inform the user that some state change has happened, leaving it to
the user to poll the channel for the current state.
The synchronous version of this API is:
```cpp
bool WaitForStateChange(gpr_timespec deadline, ChannelState source_state);
```
which returns true when the state changes to something other than the
source_state and false if the deadline expires. Asynchronous and futures based
APIs should have a corresponding method that allows the application to be
notified when the state of a channel changes.
Note that a notification is delivered every time there is a transition from any
state to any *other* state. On the other hand the rules for legal state
transition, require a transition from CONNECTING to TRANSIENT_FAILURE and back
to CONNECTING for every recoverable failure, even if the corresponding
exponential backoff requires no wait before retry. The combined effect is that
the application may receive state change notifications that appear spurious.
e.g., an application waiting for state changes on a channel that is CONNECTING
may receive a state change notification but find the channel in the same
CONNECTING state on polling for current state because the channel may have
spent infinitesimally small amount of time in the TRANSIENT_FAILURE state.

@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'gRPC'
s.version = '0.5.1'
s.version = '0.6.0'
s.summary = 'gRPC client library for iOS/OSX'
s.homepage = 'http://www.grpc.io'
s.license = 'New BSD'

@ -64,7 +64,7 @@ class ClientAsyncResponseReader GRPC_FINAL
: context_(context), call_(channel->CreateCall(method, context, cq)) {
init_buf_.SendInitialMetadata(context->send_initial_metadata_);
// TODO(ctiller): don't assert
GPR_ASSERT(init_buf_.SendMessage(request).IsOk());
GPR_ASSERT(init_buf_.SendMessage(request).ok());
init_buf_.ClientSendClose();
call_.PerformOps(&init_buf_);
}
@ -120,7 +120,7 @@ class ServerAsyncResponseWriter GRPC_FINAL
ctx_->sent_initial_metadata_ = true;
}
// The response is dropped if the status is not OK.
if (status.IsOk()) {
if (status.ok()) {
finish_buf_.ServerSendStatus(
ctx_->trailing_metadata_, finish_buf_.SendMessage(msg));
} else {
@ -130,7 +130,7 @@ class ServerAsyncResponseWriter GRPC_FINAL
}
void FinishWithError(const Status& status, void* tag) {
GPR_ASSERT(!status.IsOk());
GPR_ASSERT(!status.ok());
finish_buf_.set_output_tag(tag);
if (!ctx_->sent_initial_metadata_) {
finish_buf_.SendInitialMetadata(ctx_->initial_metadata_);

@ -35,6 +35,7 @@
#define GRPCXX_BYTE_BUFFER_H
#include <grpc/grpc.h>
#include <grpc/byte_buffer.h>
#include <grpc/support/log.h>
#include <grpc++/config.h>
#include <grpc++/slice.h>

@ -41,6 +41,7 @@
#include <grpc/support/log.h>
#include <grpc/support/time.h>
#include <grpc++/config.h>
#include <grpc++/status.h>
#include <grpc++/time.h>
struct grpc_call;
@ -52,7 +53,6 @@ class ChannelInterface;
class CompletionQueue;
class Credentials;
class RpcMethod;
class Status;
template <class R>
class ClientReader;
template <class W>

@ -151,7 +151,7 @@ class CallOpRecvMessage {
got_message = true;
*status = SerializationTraits<R>::Deserialize(recv_buf_, message_,
max_message_size)
.IsOk();
.ok();
} else {
got_message = false;
grpc_byte_buffer_destroy(recv_buf_);
@ -196,7 +196,7 @@ class CallOpGenericRecvMessage {
if (recv_buf_) {
if (*status) {
got_message = true;
*status = deserialize_(recv_buf_, max_message_size).IsOk();
*status = deserialize_(recv_buf_, max_message_size).ok();
} else {
got_message = false;
grpc_byte_buffer_destroy(recv_buf_);
@ -241,8 +241,8 @@ class CallOpServerSendStatus {
trailing_metadata_count_ = trailing_metadata.size();
trailing_metadata_ = FillMetadataArray(trailing_metadata);
send_status_available_ = true;
send_status_code_ = static_cast<grpc_status_code>(status.code());
send_status_details_ = status.details();
send_status_code_ = static_cast<grpc_status_code>(status.error_code());
send_status_details_ = status.error_message();
}
protected:

@ -35,6 +35,7 @@
#define GRPCXX_IMPL_CLIENT_UNARY_CALL_H
#include <grpc++/config.h>
#include <grpc++/status.h>
#include <grpc++/impl/call.h>
@ -44,7 +45,6 @@ class ChannelInterface;
class ClientContext;
class CompletionQueue;
class RpcMethod;
class Status;
// Wrapper that performs a blocking unary call
template <class InputMessage, class OutputMessage>
@ -57,7 +57,7 @@ Status BlockingUnaryCall(ChannelInterface* channel, const RpcMethod& method,
CallOpRecvInitialMetadata, CallOpRecvMessage<OutputMessage>,
CallOpClientSendClose, CallOpClientRecvStatus> ops;
Status status = ops.SendMessage(request);
if (!status.IsOk()) {
if (!status.ok()) {
return status;
}
ops.SendInitialMetadata(context->send_initial_metadata_);
@ -66,7 +66,7 @@ Status BlockingUnaryCall(ChannelInterface* channel, const RpcMethod& method,
ops.ClientSendClose();
ops.ClientRecvStatus(context, &status);
call.PerformOps(&ops);
GPR_ASSERT((cq.Pluck(&ops) && ops.got_message) || !status.IsOk());
GPR_ASSERT((cq.Pluck(&ops) && ops.got_message) || !status.ok());
return status;
}

@ -85,7 +85,7 @@ class RpcMethodHandler : public MethodHandler {
Status status = SerializationTraits<RequestType>::Deserialize(
param.request, &req, param.max_message_size);
ResponseType rsp;
if (status.IsOk()) {
if (status.ok()) {
status = func_(service_, param.server_context, &req, &rsp);
}
@ -93,7 +93,7 @@ class RpcMethodHandler : public MethodHandler {
CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage,
CallOpServerSendStatus> ops;
ops.SendInitialMetadata(param.server_context->initial_metadata_);
if (status.IsOk()) {
if (status.ok()) {
status = ops.SendMessage(rsp);
}
ops.ServerSendStatus(param.server_context->trailing_metadata_, status);
@ -128,7 +128,7 @@ class ClientStreamingHandler : public MethodHandler {
CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage,
CallOpServerSendStatus> ops;
ops.SendInitialMetadata(param.server_context->initial_metadata_);
if (status.IsOk()) {
if (status.ok()) {
status = ops.SendMessage(rsp);
}
ops.ServerSendStatus(param.server_context->trailing_metadata_, status);
@ -157,7 +157,7 @@ class ServerStreamingHandler : public MethodHandler {
Status status = SerializationTraits<RequestType>::Deserialize(
param.request, &req, param.max_message_size);
if (status.IsOk()) {
if (status.ok()) {
ServerWriter<ResponseType> writer(param.call, param.server_context);
status = func_(service_, param.server_context, &req, &writer);
}

@ -37,6 +37,7 @@
#include <grpc++/config.h>
#include <grpc++/impl/serialization_traits.h>
#include <grpc++/server.h>
#include <grpc++/status.h>
namespace grpc {
@ -46,7 +47,6 @@ class RpcService;
class Server;
class ServerCompletionQueue;
class ServerContext;
class Status;
class SynchronousService {
public:

@ -163,7 +163,7 @@ class Server GRPC_FINAL : public GrpcLibrary, private CallHook {
*status && payload_ &&
SerializationTraits<Message>::Deserialize(payload_, request_,
server_->max_message_size_)
.IsOk();
.ok();
bool ret = RegisteredAsyncRequest::FinalizeResult(tag, status);
*status = serialization_status && *status;
return ret;

@ -42,18 +42,17 @@ namespace grpc {
class Status {
public:
Status() : code_(StatusCode::OK) {}
explicit Status(StatusCode code) : code_(code) {}
Status(StatusCode code, const grpc::string& details)
: code_(code), details_(details) {}
// Pre-defined special status objects.
static const Status& OK;
static const Status& Cancelled;
static const Status& CANCELLED;
StatusCode code() const { return code_; }
grpc::string details() const { return details_; }
StatusCode error_code() const { return code_; }
grpc::string error_message() const { return details_; }
bool IsOk() const { return code_ == StatusCode::OK; }
bool ok() const { return code_ == StatusCode::OK; }
private:
StatusCode code_;

@ -101,7 +101,7 @@ class ClientReader GRPC_FINAL : public ClientReaderInterface<R> {
CallOpClientSendClose> ops;
ops.SendInitialMetadata(context->send_initial_metadata_);
// TODO(ctiller): don't assert
GPR_ASSERT(ops.SendMessage(request).IsOk());
GPR_ASSERT(ops.SendMessage(request).ok());
ops.ClientSendClose();
call_.PerformOps(&ops);
cq_.Pluck(&ops);
@ -170,7 +170,7 @@ class ClientWriter : public ClientWriterInterface<W> {
bool Write(const W& msg) GRPC_OVERRIDE {
CallOpSet<CallOpSendMessage> ops;
if (!ops.SendMessage(msg).IsOk()) {
if (!ops.SendMessage(msg).ok()) {
return false;
}
call_.PerformOps(&ops);
@ -248,7 +248,7 @@ class ClientReaderWriter GRPC_FINAL : public ClientReaderWriterInterface<W, R> {
bool Write(const W& msg) GRPC_OVERRIDE {
CallOpSet<CallOpSendMessage> ops;
if (!ops.SendMessage(msg).IsOk()) return false;
if (!ops.SendMessage(msg).ok()) return false;
call_.PerformOps(&ops);
return cq_.Pluck(&ops);
}
@ -319,7 +319,7 @@ class ServerWriter GRPC_FINAL : public WriterInterface<W> {
bool Write(const W& msg) GRPC_OVERRIDE {
CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage> ops;
if (!ops.SendMessage(msg).IsOk()) {
if (!ops.SendMessage(msg).ok()) {
return false;
}
if (!ctx_->sent_initial_metadata_) {
@ -361,7 +361,7 @@ class ServerReaderWriter GRPC_FINAL : public WriterInterface<W>,
bool Write(const W& msg) GRPC_OVERRIDE {
CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage> ops;
if (!ops.SendMessage(msg).IsOk()) {
if (!ops.SendMessage(msg).ok()) {
return false;
}
if (!ctx_->sent_initial_metadata_) {
@ -422,7 +422,7 @@ class ClientAsyncReader GRPC_FINAL : public ClientAsyncReaderInterface<R> {
init_ops_.set_output_tag(tag);
init_ops_.SendInitialMetadata(context->send_initial_metadata_);
// TODO(ctiller): don't assert
GPR_ASSERT(init_ops_.SendMessage(request).IsOk());
GPR_ASSERT(init_ops_.SendMessage(request).ok());
init_ops_.ClientSendClose();
call_.PerformOps(&init_ops_);
}
@ -496,7 +496,7 @@ class ClientAsyncWriter GRPC_FINAL : public ClientAsyncWriterInterface<W> {
void Write(const W& msg, void* tag) GRPC_OVERRIDE {
write_ops_.set_output_tag(tag);
// TODO(ctiller): don't assert
GPR_ASSERT(write_ops_.SendMessage(msg).IsOk());
GPR_ASSERT(write_ops_.SendMessage(msg).ok());
call_.PerformOps(&write_ops_);
}
@ -568,7 +568,7 @@ class ClientAsyncReaderWriter GRPC_FINAL
void Write(const W& msg, void* tag) GRPC_OVERRIDE {
write_ops_.set_output_tag(tag);
// TODO(ctiller): don't assert
GPR_ASSERT(write_ops_.SendMessage(msg).IsOk());
GPR_ASSERT(write_ops_.SendMessage(msg).ok());
call_.PerformOps(&write_ops_);
}
@ -627,7 +627,7 @@ class ServerAsyncReader GRPC_FINAL : public ServerAsyncStreamingInterface,
ctx_->sent_initial_metadata_ = true;
}
// The response is dropped if the status is not OK.
if (status.IsOk()) {
if (status.ok()) {
finish_ops_.ServerSendStatus(
ctx_->trailing_metadata_,
finish_ops_.SendMessage(msg));
@ -638,7 +638,7 @@ class ServerAsyncReader GRPC_FINAL : public ServerAsyncStreamingInterface,
}
void FinishWithError(const Status& status, void* tag) {
GPR_ASSERT(!status.IsOk());
GPR_ASSERT(!status.ok());
finish_ops_.set_output_tag(tag);
if (!ctx_->sent_initial_metadata_) {
finish_ops_.SendInitialMetadata(ctx_->initial_metadata_);
@ -682,7 +682,7 @@ class ServerAsyncWriter GRPC_FINAL : public ServerAsyncStreamingInterface,
ctx_->sent_initial_metadata_ = true;
}
// TODO(ctiller): don't assert
GPR_ASSERT(write_ops_.SendMessage(msg).IsOk());
GPR_ASSERT(write_ops_.SendMessage(msg).ok());
call_.PerformOps(&write_ops_);
}
@ -737,7 +737,7 @@ class ServerAsyncReaderWriter GRPC_FINAL : public ServerAsyncStreamingInterface,
ctx_->sent_initial_metadata_ = true;
}
// TODO(ctiller): don't assert
GPR_ASSERT(write_ops_.SendMessage(msg).IsOk());
GPR_ASSERT(write_ops_.SendMessage(msg).ok());
call_.PerformOps(&write_ops_);
}

@ -52,22 +52,22 @@ namespace grpc {
template <typename T>
class TimePoint {
public:
TimePoint(const T& time) {
you_need_a_specialization_of_TimePoint();
}
TimePoint(const T& time) { you_need_a_specialization_of_TimePoint(); }
gpr_timespec raw_time() {
gpr_timespec t;
return t;
}
private:
void you_need_a_specialization_of_TimePoint();
};
template<>
template <>
class TimePoint<gpr_timespec> {
public:
TimePoint(const gpr_timespec& time) : time_(time) { }
TimePoint(const gpr_timespec& time) : time_(time) {}
gpr_timespec raw_time() { return time_; }
private:
gpr_timespec time_;
};
@ -85,6 +85,9 @@ 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);
@ -95,6 +98,7 @@ class TimePoint<std::chrono::system_clock::time_point> {
Timepoint2Timespec(time, &time_);
}
gpr_timespec raw_time() const { return time_; }
private:
gpr_timespec time_;
};

@ -34,17 +34,77 @@
#ifndef GRPC_BYTE_BUFFER_H
#define GRPC_BYTE_BUFFER_H
#include <grpc/grpc.h>
#include <grpc/compression.h>
#include <grpc/support/slice_buffer.h>
typedef enum { GRPC_BB_SLICE_BUFFER } grpc_byte_buffer_type;
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
GRPC_BB_RAW
/* Future types may include GRPC_BB_PROTOBUF, etc. */
} grpc_byte_buffer_type;
/* byte buffers are containers for messages passed in from the public api's */
struct grpc_byte_buffer {
grpc_byte_buffer_type type;
union {
struct {
grpc_compression_algorithm compression;
gpr_slice_buffer slice_buffer;
} raw;
} data;
};
typedef struct grpc_byte_buffer grpc_byte_buffer;
/** Returns a RAW byte buffer instance over the given slices (up to \a nslices).
*
* Increases the reference count for all \a slices processed. The user is
* responsible for invoking grpc_byte_buffer_destroy on the returned instance.*/
grpc_byte_buffer *grpc_raw_byte_buffer_create(gpr_slice *slices,
size_t nslices);
/** Returns a *compressed* RAW byte buffer instance over the given slices (up to
* \a nslices). The \a compression argument defines the compression algorithm
* used to generate the data in \a slices.
*
* Increases the reference count for all \a slices processed. The user is
* responsible for invoking grpc_byte_buffer_destroy on the returned instance.*/
grpc_byte_buffer *grpc_raw_compressed_byte_buffer_create(
gpr_slice *slices, size_t nslices, grpc_compression_algorithm compression);
/** Copies input byte buffer \a bb.
*
* Increases the reference count of all the source slices. The user is
* responsible for calling grpc_byte_buffer_destroy over the returned copy. */
grpc_byte_buffer *grpc_byte_buffer_copy(grpc_byte_buffer *bb);
/** Returns the size of the given byte buffer, in bytes. */
size_t grpc_byte_buffer_length(grpc_byte_buffer *bb);
/** Destroys \a byte_buffer deallocating all its memory. */
void grpc_byte_buffer_destroy(grpc_byte_buffer *byte_buffer);
/** Reader for byte buffers. Iterates over slices in the byte buffer */
struct grpc_byte_buffer_reader;
typedef struct grpc_byte_buffer_reader grpc_byte_buffer_reader;
/** Initialize \a reader to read over \a buffer */
void grpc_byte_buffer_reader_init(grpc_byte_buffer_reader *reader,
grpc_byte_buffer *buffer);
/** Cleanup and destroy \a reader */
void grpc_byte_buffer_reader_destroy(grpc_byte_buffer_reader *reader);
/** Updates \a slice with the next piece of data from from \a reader and returns
* 1. Returns 0 at the end of the stream. Caller is responsible for calling
* gpr_slice_unref on the result. */
int grpc_byte_buffer_reader_next(grpc_byte_buffer_reader *reader,
gpr_slice *slice);
#ifdef __cplusplus
}
#endif
#endif /* GRPC_BYTE_BUFFER_H */

@ -37,8 +37,13 @@
#include <grpc/grpc.h>
#include <grpc/byte_buffer.h>
#ifdef __cplusplus
extern "C" {
#endif
struct grpc_byte_buffer_reader {
grpc_byte_buffer *buffer;
grpc_byte_buffer *buffer_in;
grpc_byte_buffer *buffer_out;
/* Different current objects correspond to different types of byte buffers */
union {
/* Index into a slice buffer's array of slices */
@ -46,4 +51,8 @@ struct grpc_byte_buffer_reader {
} current;
};
#ifdef __cplusplus
}
#endif
#endif /* GRPC_BYTE_BUFFER_READER_H */

@ -40,6 +40,10 @@
#include <grpc/grpc.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Identify census functionality that can be enabled via census_initialize(). */
enum census_functions {
CENSUS_NONE = 0, /* Do not enable census. */
@ -92,4 +96,8 @@ int census_context_deserialize(const char *buffer, census_context **context);
* future census calls will result in undefined behavior. */
void census_context_destroy(census_context *context);
#ifdef __cplusplus
}
#endif
#endif /* CENSUS_CENSUS_H */

@ -31,8 +31,8 @@
*
*/
#ifndef GRPC_INTERNAL_CORE_COMPRESSION_ALGORITHM_H
#define GRPC_INTERNAL_CORE_COMPRESSION_ALGORITHM_H
#ifndef GRPC_COMPRESSION_H
#define GRPC_COMPRESSION_H
/* The various compression algorithms supported by GRPC */
typedef enum {
@ -46,4 +46,4 @@ typedef enum {
const char *grpc_compression_algorithm_name(
grpc_compression_algorithm algorithm);
#endif /* GRPC_INTERNAL_CORE_COMPRESSION_ALGORITHM_H */
#endif /* GRPC_COMPRESSION_H */

@ -37,6 +37,7 @@
#include <grpc/status.h>
#include <stddef.h>
#include <grpc/byte_buffer.h>
#include <grpc/support/slice.h>
#include <grpc/support/time.h>
@ -143,7 +144,10 @@ typedef enum grpc_call_error {
/* the flags value was illegal for this call */
GRPC_CALL_ERROR_INVALID_FLAGS,
/* invalid metadata was passed to this call */
GRPC_CALL_ERROR_INVALID_METADATA
GRPC_CALL_ERROR_INVALID_METADATA,
/* completion queue for notification has not been registered with the server
*/
GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE
} grpc_call_error;
/* Write Flags: */
@ -154,34 +158,8 @@ typedef enum grpc_call_error {
/* Force compression to be disabled for a particular write
(start_write/add_metadata). Illegal on invoke/accept. */
#define GRPC_WRITE_NO_COMPRESS (0x00000002u)
/* A buffer of bytes */
struct grpc_byte_buffer;
typedef struct grpc_byte_buffer grpc_byte_buffer;
/* Sample helpers to obtain byte buffers (these will certainly move
someplace else) */
grpc_byte_buffer *grpc_byte_buffer_create(gpr_slice *slices, size_t nslices);
grpc_byte_buffer *grpc_byte_buffer_copy(grpc_byte_buffer *bb);
size_t grpc_byte_buffer_length(grpc_byte_buffer *bb);
void grpc_byte_buffer_destroy(grpc_byte_buffer *byte_buffer);
/* Reader for byte buffers. Iterates over slices in the byte buffer */
struct grpc_byte_buffer_reader;
typedef struct grpc_byte_buffer_reader grpc_byte_buffer_reader;
/** Initialize \a reader to read over \a buffer */
void grpc_byte_buffer_reader_init(grpc_byte_buffer_reader *reader,
grpc_byte_buffer *buffer);
/** Cleanup and destroy \a reader */
void grpc_byte_buffer_reader_destroy(grpc_byte_buffer_reader *reader);
/* At the end of the stream, returns 0. Otherwise, returns 1 and sets slice to
be the returned slice. Caller is responsible for calling gpr_slice_unref on
the result. */
int grpc_byte_buffer_reader_next(grpc_byte_buffer_reader *reader,
gpr_slice *slice);
/* Mask of all valid flags. */
#define GRPC_WRITE_USED_MASK (GRPC_WRITE_BUFFER_HINT | GRPC_WRITE_NO_COMPRESS)
/* A single metadata element */
typedef struct grpc_metadata {
@ -248,7 +226,7 @@ typedef enum {
GRPC_OP_SEND_INITIAL_METADATA = 0,
/* Send a message: 0 or more of these operations can occur for each call */
GRPC_OP_SEND_MESSAGE,
/* Send a close from the server: one and only one instance MUST be sent from
/* Send a close from the client: one and only one instance MUST be sent from
the client,
unless the call was cancelled - in which case this can be skipped */
GRPC_OP_SEND_CLOSE_FROM_CLIENT,
@ -267,7 +245,7 @@ typedef enum {
the status will indicate some failure.
*/
GRPC_OP_RECV_STATUS_ON_CLIENT,
/* Receive status on the server: one and only one must be made on the server
/* Receive close on the server: one and only one must be made on the server
*/
GRPC_OP_RECV_CLOSE_ON_SERVER
} grpc_op_type;
@ -277,6 +255,7 @@ typedef enum {
no arguments) */
typedef struct grpc_op {
grpc_op_type op;
gpr_uint32 flags; /**< Write flags bitset for grpc_begin_messages */
union {
struct {
size_t count;
@ -361,7 +340,8 @@ grpc_completion_queue *grpc_completion_queue_create(void);
/** Blocks until an event is available, the completion queue is being shut down,
or deadline is reached.
Returns NULL on timeout, otherwise the event that occurred.
Returns a grpc_event with type GRPC_QUEUE_TIMEOUT on timeout,
otherwise a grpc_event describing the event that occurred.
Callers must not call grpc_completion_queue_next and
grpc_completion_queue_pluck simultaneously on the same completion queue. */
@ -371,7 +351,8 @@ grpc_event grpc_completion_queue_next(grpc_completion_queue *cq,
/** Blocks until an event with tag 'tag' is available, the completion queue is
being shutdown or deadline is reached.
Returns NULL on timeout, or a pointer to the event that occurred.
Returns a grpc_event with type GRPC_QUEUE_TIMEOUT on timeout,
otherwise a grpc_event describing the event that occurred.
Callers must not call grpc_completion_queue_next and
grpc_completion_queue_pluck simultaneously on the same completion queue. */

@ -34,8 +34,8 @@
#ifndef GRPC_GRPC_SECURITY_H
#define GRPC_GRPC_SECURITY_H
#include "grpc.h"
#include "status.h"
#include <grpc/grpc.h>
#include <grpc/status.h>
#ifdef __cplusplus
extern "C" {
@ -117,7 +117,7 @@ grpc_credentials *grpc_service_account_credentials_create(
grpc_credentials *grpc_jwt_credentials_create(const char *json_key,
gpr_timespec token_lifetime);
/* Creates an Oauth2 Refresh Token crednetials object. May return NULL if the
/* Creates an Oauth2 Refresh Token credentials object. May return NULL if the
input is invalid.
WARNING: Do NOT use this credentials to connect to a non-google service as
this could result in an oauth2 token leak.

@ -110,8 +110,9 @@ gpr_slice gpr_slice_ref(gpr_slice s);
/* Decrement the ref count of s. If the ref count of s reaches zero, all
slices sharing the ref count are destroyed, and considered no longer
initialized. If s is ultimately derived from a call to gpr_slice_new(start,
len, dest) where dest!=NULL , then (*dest)(start, len) is called. Requires
s initialized. */
len, dest) where dest!=NULL , then (*dest)(start) is called, else if s is
ultimately derived from a call to gpr_slice_new_with_len(start, len, dest)
where dest!=NULL , then (*dest)(start, len). Requires s initialized. */
void gpr_slice_unref(gpr_slice s);
/* Create a slice pointing at some data. Calls malloc to allocate a refcount

@ -49,7 +49,13 @@ struct gpr_pthread_thread_local {
#define gpr_tls_init(tls) GPR_ASSERT(0 == pthread_key_create(&(tls)->key, NULL))
#define gpr_tls_destroy(tls) pthread_key_delete((tls)->key)
gpr_intptr gpr_tls_set(struct gpr_pthread_thread_local *tls, gpr_intptr value);
#define gpr_tls_get(tls) ((gpr_intptr)pthread_getspecific((tls)->key))
#ifdef __cplusplus
extern "C" {
#endif
gpr_intptr gpr_tls_set(struct gpr_pthread_thread_local *tls, gpr_intptr value);
#ifdef __cplusplus
}
#endif
#endif

@ -839,9 +839,12 @@ void PrintSourceServerMethod(grpc::protobuf::io::Printer *printer,
"::grpc::Status $ns$$Service$::Service::$Method$("
"::grpc::ServerContext* context, "
"const $Request$* request, $Response$* response) {\n");
printer->Print(" (void) context;\n");
printer->Print(" (void) request;\n");
printer->Print(" (void) response;\n");
printer->Print(
" return ::grpc::Status("
"::grpc::StatusCode::UNIMPLEMENTED);\n");
"::grpc::StatusCode::UNIMPLEMENTED, \"\");\n");
printer->Print("}\n\n");
} else if (ClientOnlyStreaming(method)) {
printer->Print(*vars,
@ -849,9 +852,12 @@ void PrintSourceServerMethod(grpc::protobuf::io::Printer *printer,
"::grpc::ServerContext* context, "
"::grpc::ServerReader< $Request$>* reader, "
"$Response$* response) {\n");
printer->Print(" (void) context;\n");
printer->Print(" (void) reader;\n");
printer->Print(" (void) response;\n");
printer->Print(
" return ::grpc::Status("
"::grpc::StatusCode::UNIMPLEMENTED);\n");
"::grpc::StatusCode::UNIMPLEMENTED, \"\");\n");
printer->Print("}\n\n");
} else if (ServerOnlyStreaming(method)) {
printer->Print(*vars,
@ -859,9 +865,12 @@ void PrintSourceServerMethod(grpc::protobuf::io::Printer *printer,
"::grpc::ServerContext* context, "
"const $Request$* request, "
"::grpc::ServerWriter< $Response$>* writer) {\n");
printer->Print(" (void) context;\n");
printer->Print(" (void) request;\n");
printer->Print(" (void) writer;\n");
printer->Print(
" return ::grpc::Status("
"::grpc::StatusCode::UNIMPLEMENTED);\n");
"::grpc::StatusCode::UNIMPLEMENTED, \"\");\n");
printer->Print("}\n\n");
} else if (BidiStreaming(method)) {
printer->Print(*vars,
@ -869,9 +878,11 @@ void PrintSourceServerMethod(grpc::protobuf::io::Printer *printer,
"::grpc::ServerContext* context, "
"::grpc::ServerReaderWriter< $Response$, $Request$>* "
"stream) {\n");
printer->Print(" (void) context;\n");
printer->Print(" (void) stream;\n");
printer->Print(
" return ::grpc::Status("
"::grpc::StatusCode::UNIMPLEMENTED);\n");
"::grpc::StatusCode::UNIMPLEMENTED, \"\");\n");
printer->Print("}\n\n");
}
}

@ -102,10 +102,17 @@ struct call_data {
static int prepare_activate(grpc_call_element *elem,
grpc_child_channel *on_child) {
call_data *calld = elem->call_data;
channel_data *chand = elem->channel_data;
if (calld->state == CALL_CANCELLED) return 0;
/* no more access to calld->s.waiting allowed */
GPR_ASSERT(calld->state == CALL_WAITING);
if (calld->s.waiting_op.bind_pollset) {
grpc_transport_setup_del_interested_party(chand->transport_setup,
calld->s.waiting_op.bind_pollset);
}
calld->state = CALL_ACTIVE;
/* create a child call */
@ -199,6 +206,7 @@ static void cc_start_transport_op(grpc_call_element *elem,
handle_op_after_cancellation(elem, op);
} else {
calld->state = CALL_WAITING;
calld->s.waiting_op.bind_pollset = NULL;
if (chand->active_child) {
/* channel is connected - use the connected stack */
if (prepare_activate(elem, chand->active_child)) {
@ -230,14 +238,14 @@ static void cc_start_transport_op(grpc_call_element *elem,
}
calld->s.waiting_op = *op;
chand->waiting_children[chand->waiting_child_count++] = calld;
grpc_transport_setup_add_interested_party(chand->transport_setup,
op->bind_pollset);
gpr_mu_unlock(&chand->mu);
/* finally initiate transport setup if needed */
if (initiate_transport_setup) {
grpc_transport_setup_initiate(chand->transport_setup);
}
grpc_transport_setup_add_interested_party(chand->transport_setup,
op->bind_pollset);
}
}
break;

@ -56,12 +56,12 @@ struct grpc_client_setup {
gpr_cv cv;
grpc_client_setup_request *active_request;
int refs;
grpc_pollset_set interested_parties;
};
struct grpc_client_setup_request {
/* pointer back to the setup object */
grpc_client_setup *setup;
grpc_pollset_set interested_parties;
gpr_timespec deadline;
};
@ -71,7 +71,7 @@ gpr_timespec grpc_client_setup_request_deadline(grpc_client_setup_request *r) {
grpc_pollset_set *grpc_client_setup_get_interested_parties(
grpc_client_setup_request *r) {
return &r->interested_parties;
return &r->setup->interested_parties;
}
static void destroy_setup(grpc_client_setup *s) {
@ -79,11 +79,11 @@ static void destroy_setup(grpc_client_setup *s) {
gpr_cv_destroy(&s->cv);
s->done(s->user_data);
grpc_channel_args_destroy(s->args);
grpc_pollset_set_destroy(&s->interested_parties);
gpr_free(s);
}
static void destroy_request(grpc_client_setup_request *r) {
grpc_pollset_set_destroy(&r->interested_parties);
gpr_free(r);
}
@ -94,7 +94,6 @@ static void setup_initiate(grpc_transport_setup *sp) {
int in_alarm = 0;
r->setup = s;
grpc_pollset_set_init(&r->interested_parties);
/* TODO(klempner): Actually set a deadline */
r->deadline = gpr_time_add(gpr_now(), gpr_time_from_seconds(60));
@ -125,12 +124,8 @@ static void setup_add_interested_party(grpc_transport_setup *sp,
grpc_client_setup *s = (grpc_client_setup *)sp;
gpr_mu_lock(&s->mu);
if (!s->active_request) {
gpr_mu_unlock(&s->mu);
return;
}
grpc_pollset_set_add_pollset(&s->active_request->interested_parties, pollset);
grpc_pollset_set_add_pollset(&s->interested_parties, pollset);
gpr_mu_unlock(&s->mu);
}
@ -140,12 +135,8 @@ static void setup_del_interested_party(grpc_transport_setup *sp,
grpc_client_setup *s = (grpc_client_setup *)sp;
gpr_mu_lock(&s->mu);
if (!s->active_request) {
gpr_mu_unlock(&s->mu);
return;
}
grpc_pollset_set_del_pollset(&s->active_request->interested_parties, pollset);
grpc_pollset_set_del_pollset(&s->interested_parties, pollset);
gpr_mu_unlock(&s->mu);
}
@ -225,6 +216,7 @@ void grpc_client_setup_create_and_attach(
s->in_alarm = 0;
s->in_cb = 0;
s->cancelled = 0;
grpc_pollset_set_init(&s->interested_parties);
grpc_client_channel_set_transport_setup(newly_minted_channel, &s->base);
}

@ -31,7 +31,7 @@
*
*/
#include "src/core/compression/algorithm.h"
#include <grpc/compression.h>
const char *grpc_compression_algorithm_name(
grpc_compression_algorithm algorithm) {

@ -34,7 +34,7 @@
#ifndef GRPC_INTERNAL_CORE_COMPRESSION_MESSAGE_COMPRESS_H
#define GRPC_INTERNAL_CORE_COMPRESSION_MESSAGE_COMPRESS_H
#include "src/core/compression/algorithm.h"
#include <grpc/compression.h>
#include <grpc/support/slice_buffer.h>
/* compress 'input' to 'output' using 'algorithm'.

@ -100,8 +100,9 @@ static int multipoll_with_epoll_pollset_maybe_work(
if (gpr_time_cmp(deadline, gpr_inf_future) == 0) {
timeout_ms = -1;
} else {
timeout_ms = gpr_time_to_millis(gpr_time_sub(deadline, now));
if (timeout_ms <= 0) {
timeout_ms = gpr_time_to_millis(
gpr_time_add(gpr_time_sub(deadline, now), gpr_time_from_micros(500)));
if (timeout_ms < 0) {
return 1;
}
}

@ -116,8 +116,9 @@ static int multipoll_with_poll_pollset_maybe_work(
if (gpr_time_cmp(deadline, gpr_inf_future) == 0) {
timeout = -1;
} else {
timeout = gpr_time_to_millis(gpr_time_sub(deadline, now));
if (timeout <= 0) {
timeout = gpr_time_to_millis(
gpr_time_add(gpr_time_sub(deadline, now), gpr_time_from_micros(500)));
if (timeout < 0) {
return 1;
}
}

@ -99,6 +99,7 @@ void grpc_pollset_init(grpc_pollset *pollset) {
grpc_pollset_kick_init(&pollset->kick_state);
pollset->in_flight_cbs = 0;
pollset->shutting_down = 0;
pollset->called_shutdown = 0;
become_basic_pollset(pollset, NULL);
}
@ -141,7 +142,8 @@ int grpc_pollset_work(grpc_pollset *pollset, gpr_timespec deadline) {
if (pollset->shutting_down) {
if (pollset->counter > 0) {
grpc_pollset_kick(pollset);
} else if (pollset->in_flight_cbs == 0) {
} else if (!pollset->called_shutdown && pollset->in_flight_cbs == 0) {
pollset->called_shutdown = 1;
gpr_mu_unlock(&pollset->mu);
finish_shutdown(pollset);
/* Continuing to access pollset here is safe -- it is the caller's
@ -157,21 +159,22 @@ int grpc_pollset_work(grpc_pollset *pollset, gpr_timespec deadline) {
void grpc_pollset_shutdown(grpc_pollset *pollset,
void (*shutdown_done)(void *arg),
void *shutdown_done_arg) {
int in_flight_cbs;
int counter;
int call_shutdown = 0;
gpr_mu_lock(&pollset->mu);
GPR_ASSERT(!pollset->shutting_down);
pollset->shutting_down = 1;
in_flight_cbs = pollset->in_flight_cbs;
counter = pollset->counter;
if (!pollset->called_shutdown && pollset->in_flight_cbs == 0 && pollset->counter == 0) {
pollset->called_shutdown = 1;
call_shutdown = 1;
}
pollset->shutdown_done_cb = shutdown_done;
pollset->shutdown_done_arg = shutdown_done_arg;
if (counter > 0) {
if (pollset->counter > 0) {
grpc_pollset_kick(pollset);
}
gpr_mu_unlock(&pollset->mu);
if (in_flight_cbs == 0 && counter == 0) {
if (call_shutdown) {
finish_shutdown(pollset);
}
}
@ -343,8 +346,9 @@ static int basic_pollset_maybe_work(grpc_pollset *pollset,
if (gpr_time_cmp(deadline, gpr_inf_future) == 0) {
timeout = -1;
} else {
timeout = gpr_time_to_millis(gpr_time_sub(deadline, now));
if (timeout <= 0) {
timeout = gpr_time_to_millis(
gpr_time_add(gpr_time_sub(deadline, now), gpr_time_from_micros(500)));
if (timeout < 0) {
return 1;
}
}

@ -56,6 +56,7 @@ typedef struct grpc_pollset {
int counter;
int in_flight_cbs;
int shutting_down;
int called_shutdown;
void (*shutdown_done_cb)(void *arg);
void *shutdown_done_arg;
union {

@ -49,7 +49,11 @@ void grpc_pollset_set_init(grpc_pollset_set *pollset_set) {
}
void grpc_pollset_set_destroy(grpc_pollset_set *pollset_set) {
size_t i;
gpr_mu_destroy(&pollset_set->mu);
for (i = 0; i < pollset_set->fd_count; i++) {
GRPC_FD_UNREF(pollset_set->fds[i], "pollset");
}
gpr_free(pollset_set->pollsets);
gpr_free(pollset_set->fds);
}
@ -95,6 +99,7 @@ void grpc_pollset_set_add_fd(grpc_pollset_set *pollset_set, grpc_fd *fd) {
pollset_set->fds = gpr_realloc(
pollset_set->fds, pollset_set->fd_capacity * sizeof(*pollset_set->fds));
}
GRPC_FD_REF(fd, "pollset_set");
pollset_set->fds[pollset_set->fd_count++] = fd;
for (i = 0; i < pollset_set->pollset_count; i++) {
grpc_pollset_add_fd(pollset_set->pollsets[i], fd);
@ -110,6 +115,7 @@ void grpc_pollset_set_del_fd(grpc_pollset_set *pollset_set, grpc_fd *fd) {
pollset_set->fd_count--;
GPR_SWAP(grpc_fd *, pollset_set->fds[i],
pollset_set->fds[pollset_set->pollset_count]);
GRPC_FD_UNREF(fd, "pollset_set");
break;
}
}

@ -44,4 +44,7 @@ void grpc_pollset_set_destroy(grpc_pollset_set *pollset_set) {}
void grpc_pollset_set_add_pollset(grpc_pollset_set *pollset_set,
grpc_pollset *pollset) {}
void grpc_pollset_set_del_pollset(grpc_pollset_set *pollset_set,
grpc_pollset *pollset) {}
#endif /* GPR_WINSOCK_SOCKET */

@ -37,7 +37,6 @@
#include <windows.h>
#include <grpc/support/sync.h>
#include "src/core/iomgr/pollset_kick.h"
#include "src/core/iomgr/socket_windows.h"
/* There isn't really any such thing as a pollset under Windows, due to the

@ -42,6 +42,7 @@
#include <grpc/support/log_win32.h>
#include <grpc/support/log.h>
#include <grpc/support/time.h>
#include <grpc/support/string_util.h>
#include "src/core/support/string.h"
#include "src/core/support/string_win32.h"
@ -106,7 +107,7 @@ char *gpr_format_message(DWORD messageid) {
NULL, messageid,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)(&tmessage), 0, NULL);
if (status == 0) return gpr_strdup("Unable to retreive error string");
if (status == 0) return gpr_strdup("Unable to retrieve error string");
message = gpr_tchar_to_char(tmessage);
LocalFree(tmessage);
return message;

@ -35,25 +35,31 @@
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
grpc_byte_buffer *grpc_byte_buffer_create(gpr_slice *slices, size_t nslices) {
grpc_byte_buffer *grpc_raw_byte_buffer_create(gpr_slice *slices,
size_t nslices) {
return grpc_raw_compressed_byte_buffer_create(slices, nslices,
GRPC_COMPRESS_NONE);
}
grpc_byte_buffer *grpc_raw_compressed_byte_buffer_create(
gpr_slice *slices, size_t nslices, grpc_compression_algorithm compression) {
size_t i;
grpc_byte_buffer *bb = malloc(sizeof(grpc_byte_buffer));
bb->type = GRPC_BB_SLICE_BUFFER;
gpr_slice_buffer_init(&bb->data.slice_buffer);
bb->type = GRPC_BB_RAW;
bb->data.raw.compression = compression;
gpr_slice_buffer_init(&bb->data.raw.slice_buffer);
for (i = 0; i < nslices; i++) {
gpr_slice_ref(slices[i]);
gpr_slice_buffer_add(&bb->data.slice_buffer, slices[i]);
gpr_slice_buffer_add(&bb->data.raw.slice_buffer, slices[i]);
}
return bb;
}
grpc_byte_buffer *grpc_byte_buffer_copy(grpc_byte_buffer *bb) {
switch (bb->type) {
case GRPC_BB_SLICE_BUFFER:
return grpc_byte_buffer_create(bb->data.slice_buffer.slices,
bb->data.slice_buffer.count);
case GRPC_BB_RAW:
return grpc_raw_byte_buffer_create(bb->data.raw.slice_buffer.slices,
bb->data.raw.slice_buffer.count);
}
gpr_log(GPR_INFO, "should never get here");
abort();
@ -63,8 +69,8 @@ grpc_byte_buffer *grpc_byte_buffer_copy(grpc_byte_buffer *bb) {
void grpc_byte_buffer_destroy(grpc_byte_buffer *bb) {
if (!bb) return;
switch (bb->type) {
case GRPC_BB_SLICE_BUFFER:
gpr_slice_buffer_destroy(&bb->data.slice_buffer);
case GRPC_BB_RAW:
gpr_slice_buffer_destroy(&bb->data.raw.slice_buffer);
break;
}
free(bb);
@ -72,8 +78,8 @@ void grpc_byte_buffer_destroy(grpc_byte_buffer *bb) {
size_t grpc_byte_buffer_length(grpc_byte_buffer *bb) {
switch (bb->type) {
case GRPC_BB_SLICE_BUFFER:
return bb->data.slice_buffer.length;
case GRPC_BB_RAW:
return bb->data.raw.slice_buffer.length;
}
gpr_log(GPR_ERROR, "should never reach here");
abort();

@ -33,41 +33,73 @@
#include <grpc/byte_buffer_reader.h>
#include <grpc/compression.h>
#include <grpc/grpc.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/slice_buffer.h>
#include <grpc/byte_buffer.h>
#include "src/core/compression/message_compress.h"
static int is_compressed(grpc_byte_buffer *buffer) {
switch (buffer->type) {
case GRPC_BB_RAW:
if (buffer->data.raw.compression == GRPC_COMPRESS_NONE) {
return 0 /* GPR_FALSE */;
}
break;
}
return 1 /* GPR_TRUE */;
}
void grpc_byte_buffer_reader_init(grpc_byte_buffer_reader *reader,
grpc_byte_buffer *buffer) {
reader->buffer = buffer;
switch (buffer->type) {
case GRPC_BB_SLICE_BUFFER:
gpr_slice_buffer decompressed_slices_buffer;
reader->buffer_in = buffer;
switch (reader->buffer_in->type) {
case GRPC_BB_RAW:
gpr_slice_buffer_init(&decompressed_slices_buffer);
if (is_compressed(reader->buffer_in)) {
grpc_msg_decompress(reader->buffer_in->data.raw.compression,
&reader->buffer_in->data.raw.slice_buffer,
&decompressed_slices_buffer);
reader->buffer_out = grpc_raw_byte_buffer_create(
decompressed_slices_buffer.slices,
decompressed_slices_buffer.count);
gpr_slice_buffer_destroy(&decompressed_slices_buffer);
} else { /* not compressed, use the input buffer as output */
reader->buffer_out = reader->buffer_in;
}
reader->current.index = 0;
break;
}
}
void grpc_byte_buffer_reader_destroy(grpc_byte_buffer_reader *reader) {
/* no-op: the user is responsible for memory deallocation.
* Other cleanup operations would go here if needed. */
switch (reader->buffer_in->type) {
case GRPC_BB_RAW:
/* keeping the same if-else structure as in the init function */
if (is_compressed(reader->buffer_in)) {
grpc_byte_buffer_destroy(reader->buffer_out);
}
break;
}
}
int grpc_byte_buffer_reader_next(grpc_byte_buffer_reader *reader,
gpr_slice *slice) {
grpc_byte_buffer *buffer = reader->buffer;
switch (reader->buffer_in->type) {
case GRPC_BB_RAW: {
gpr_slice_buffer *slice_buffer;
switch (buffer->type) {
case GRPC_BB_SLICE_BUFFER:
slice_buffer = &buffer->data.slice_buffer;
slice_buffer = &reader->buffer_out->data.raw.slice_buffer;
if (reader->current.index < slice_buffer->count) {
*slice = gpr_slice_ref(slice_buffer->slices[reader->current.index]);
reader->current.index += 1;
return 1;
} else {
return 0;
}
break;
}
}
return 0;
}

@ -99,6 +99,8 @@ typedef enum {
/* Status came from 'the wire' - or somewhere below the surface
layer */
STATUS_FROM_WIRE,
/* Status came from the server sending status */
STATUS_FROM_SERVER_STATUS,
STATUS_SOURCE_COUNT
} status_source;
@ -189,6 +191,7 @@ struct grpc_call {
and a strong upper bound of a count of masters to be calculated. */
gpr_uint8 request_set[GRPC_IOREQ_OP_COUNT];
grpc_ioreq_data request_data[GRPC_IOREQ_OP_COUNT];
gpr_uint32 request_flags[GRPC_IOREQ_OP_COUNT];
reqinfo_master masters[GRPC_IOREQ_OP_COUNT];
/* Dynamic array of ioreq's that have completed: the count of
@ -232,6 +235,7 @@ struct grpc_call {
gpr_slice_buffer incoming_message;
gpr_uint32 incoming_message_length;
gpr_uint32 incoming_message_flags;
grpc_iomgr_closure destroy_closure;
};
@ -578,10 +582,18 @@ static void finish_live_ioreq_op(grpc_call *call, grpc_ioreq_op op,
call->write_state = WRITE_STATE_WRITE_CLOSED;
}
break;
case GRPC_IOREQ_SEND_STATUS:
if (call->request_data[GRPC_IOREQ_SEND_STATUS].send_status.details !=
NULL) {
grpc_mdstr_unref(
call->request_data[GRPC_IOREQ_SEND_STATUS].send_status.details);
call->request_data[GRPC_IOREQ_SEND_STATUS].send_status.details =
NULL;
}
break;
case GRPC_IOREQ_RECV_CLOSE:
case GRPC_IOREQ_SEND_INITIAL_METADATA:
case GRPC_IOREQ_SEND_TRAILING_METADATA:
case GRPC_IOREQ_SEND_STATUS:
case GRPC_IOREQ_SEND_CLOSE:
break;
case GRPC_IOREQ_RECV_STATUS:
@ -665,7 +677,7 @@ static void call_on_done_send(void *pc, int success) {
static void finish_message(grpc_call *call) {
/* TODO(ctiller): this could be a lot faster if coded directly */
grpc_byte_buffer *byte_buffer = grpc_byte_buffer_create(
grpc_byte_buffer *byte_buffer = grpc_raw_byte_buffer_create(
call->incoming_message.slices, call->incoming_message.count);
gpr_slice_buffer_reset_and_unref(&call->incoming_message);
@ -699,6 +711,7 @@ static int begin_message(grpc_call *call, grpc_begin_message msg) {
} else if (msg.length > 0) {
call->reading_message = 1;
call->incoming_message_length = msg.length;
call->incoming_message_flags = msg.flags;
return 1;
} else {
finish_message(call);
@ -788,7 +801,7 @@ static void call_on_done_recv(void *pc, int success) {
unlock(call);
GRPC_CALL_INTERNAL_UNREF(call, "receiving", 0);
GRPC_TIMER_BEGIN(GRPC_PTAG_CALL_ON_DONE_RECV, 0);
GRPC_TIMER_END(GRPC_PTAG_CALL_ON_DONE_RECV, 0);
}
static int prepare_application_metadata(grpc_call *call, size_t count,
@ -835,9 +848,9 @@ static void copy_byte_buffer_to_stream_ops(grpc_byte_buffer *byte_buffer,
size_t i;
switch (byte_buffer->type) {
case GRPC_BB_SLICE_BUFFER:
for (i = 0; i < byte_buffer->data.slice_buffer.count; i++) {
gpr_slice slice = byte_buffer->data.slice_buffer.slices[i];
case GRPC_BB_RAW:
for (i = 0; i < byte_buffer->data.raw.slice_buffer.count; i++) {
gpr_slice slice = byte_buffer->data.raw.slice_buffer.slices[i];
gpr_slice_ref(slice);
grpc_sopb_add_slice(sopb, slice);
}
@ -847,9 +860,9 @@ static void copy_byte_buffer_to_stream_ops(grpc_byte_buffer *byte_buffer,
static int fill_send_ops(grpc_call *call, grpc_transport_op *op) {
grpc_ioreq_data data;
gpr_uint32 flags;
grpc_metadata_batch mdb;
size_t i;
char status_str[GPR_LTOA_MIN_BUFSIZE];
GPR_ASSERT(op->send_ops == NULL);
switch (call->write_state) {
@ -873,8 +886,9 @@ static int fill_send_ops(grpc_call *call, grpc_transport_op *op) {
case WRITE_STATE_STARTED:
if (is_op_live(call, GRPC_IOREQ_SEND_MESSAGE)) {
data = call->request_data[GRPC_IOREQ_SEND_MESSAGE];
flags = call->request_flags[GRPC_IOREQ_SEND_MESSAGE];
grpc_sopb_add_begin_message(
&call->send_ops, grpc_byte_buffer_length(data.send_message), 0);
&call->send_ops, grpc_byte_buffer_length(data.send_message), flags);
copy_byte_buffer_to_stream_ops(data.send_message, &call->send_ops);
op->send_ops = &call->send_ops;
call->last_send_contains |= 1 << GRPC_IOREQ_SEND_MESSAGE;
@ -893,13 +907,10 @@ static int fill_send_ops(grpc_call *call, grpc_transport_op *op) {
/* send status */
/* TODO(ctiller): cache common status values */
data = call->request_data[GRPC_IOREQ_SEND_STATUS];
gpr_ltoa(data.send_status.code, status_str);
grpc_metadata_batch_add_tail(
&mdb, &call->status_link,
grpc_mdelem_from_metadata_strings(
call->metadata_context,
grpc_mdstr_ref(grpc_channel_get_status_string(call->channel)),
grpc_mdstr_from_string(call->metadata_context, status_str)));
grpc_channel_get_reffed_status_elem(call->channel,
data.send_status.code));
if (data.send_status.details) {
grpc_metadata_batch_add_tail(
&mdb, &call->details_link,
@ -907,8 +918,9 @@ static int fill_send_ops(grpc_call *call, grpc_transport_op *op) {
call->metadata_context,
grpc_mdstr_ref(
grpc_channel_get_message_string(call->channel)),
grpc_mdstr_from_string(call->metadata_context,
data.send_status.details)));
data.send_status.details));
call->request_data[GRPC_IOREQ_SEND_STATUS].send_status.details =
NULL;
}
grpc_sopb_add_metadata(&call->send_ops, mdb);
}
@ -1008,9 +1020,18 @@ static grpc_call_error start_ioreq(grpc_call *call, const grpc_ioreq *reqs,
GRPC_CALL_ERROR_INVALID_METADATA);
}
}
if (op == GRPC_IOREQ_SEND_STATUS) {
set_status_code(call, STATUS_FROM_SERVER_STATUS,
reqs[i].data.send_status.code);
if (reqs[i].data.send_status.details) {
set_status_details(call, STATUS_FROM_SERVER_STATUS,
grpc_mdstr_ref(reqs[i].data.send_status.details));
}
}
have_ops |= 1u << op;
call->request_data[op] = data;
call->request_flags[op] = reqs[i].flags;
call->request_set[op] = set;
}
@ -1227,6 +1248,14 @@ static void finish_batch_with_close(grpc_call *call, int success, void *tag) {
grpc_cq_end_op(call->cq, tag, call, 1);
}
static int are_write_flags_valid(gpr_uint32 flags) {
/* check that only bits in GRPC_WRITE_(INTERNAL?)_USED_MASK are set */
const gpr_uint32 allowed_write_positions =
(GRPC_WRITE_USED_MASK | GRPC_WRITE_INTERNAL_USED_MASK);
const gpr_uint32 invalid_positions = ~allowed_write_positions;
return !(flags & invalid_positions);
}
grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops,
size_t nops, void *tag) {
grpc_ioreq reqs[GRPC_IOREQ_OP_COUNT];
@ -1249,30 +1278,43 @@ grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops,
op = &ops[in];
switch (op->op) {
case GRPC_OP_SEND_INITIAL_METADATA:
/* Flag validation: currently allow no flags */
if (op->flags != 0) return GRPC_CALL_ERROR_INVALID_FLAGS;
req = &reqs[out++];
req->op = GRPC_IOREQ_SEND_INITIAL_METADATA;
req->data.send_metadata.count = op->data.send_initial_metadata.count;
req->data.send_metadata.metadata =
op->data.send_initial_metadata.metadata;
req->flags = op->flags;
break;
case GRPC_OP_SEND_MESSAGE:
if (!are_write_flags_valid(op->flags)){
return GRPC_CALL_ERROR_INVALID_FLAGS;
}
req = &reqs[out++];
req->op = GRPC_IOREQ_SEND_MESSAGE;
req->data.send_message = op->data.send_message;
req->flags = ops->flags;
break;
case GRPC_OP_SEND_CLOSE_FROM_CLIENT:
/* Flag validation: currently allow no flags */
if (op->flags != 0) return GRPC_CALL_ERROR_INVALID_FLAGS;
if (!call->is_client) {
return GRPC_CALL_ERROR_NOT_ON_SERVER;
}
req = &reqs[out++];
req->op = GRPC_IOREQ_SEND_CLOSE;
req->flags = op->flags;
break;
case GRPC_OP_SEND_STATUS_FROM_SERVER:
/* Flag validation: currently allow no flags */
if (op->flags != 0) return GRPC_CALL_ERROR_INVALID_FLAGS;
if (call->is_client) {
return GRPC_CALL_ERROR_NOT_ON_CLIENT;
}
req = &reqs[out++];
req->op = GRPC_IOREQ_SEND_TRAILING_METADATA;
req->flags = op->flags;
req->data.send_metadata.count =
op->data.send_status_from_server.trailing_metadata_count;
req->data.send_metadata.metadata =
@ -1281,29 +1323,42 @@ grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops,
req->op = GRPC_IOREQ_SEND_STATUS;
req->data.send_status.code = op->data.send_status_from_server.status;
req->data.send_status.details =
op->data.send_status_from_server.status_details;
op->data.send_status_from_server.status_details != NULL
? grpc_mdstr_from_string(
call->metadata_context,
op->data.send_status_from_server.status_details)
: NULL;
req = &reqs[out++];
req->op = GRPC_IOREQ_SEND_CLOSE;
break;
case GRPC_OP_RECV_INITIAL_METADATA:
/* Flag validation: currently allow no flags */
if (op->flags != 0) return GRPC_CALL_ERROR_INVALID_FLAGS;
if (!call->is_client) {
return GRPC_CALL_ERROR_NOT_ON_SERVER;
}
req = &reqs[out++];
req->op = GRPC_IOREQ_RECV_INITIAL_METADATA;
req->data.recv_metadata = op->data.recv_initial_metadata;
req->flags = op->flags;
break;
case GRPC_OP_RECV_MESSAGE:
/* Flag validation: currently allow no flags */
if (op->flags != 0) return GRPC_CALL_ERROR_INVALID_FLAGS;
req = &reqs[out++];
req->op = GRPC_IOREQ_RECV_MESSAGE;
req->data.recv_message = op->data.recv_message;
req->flags = op->flags;
break;
case GRPC_OP_RECV_STATUS_ON_CLIENT:
/* Flag validation: currently allow no flags */
if (op->flags != 0) return GRPC_CALL_ERROR_INVALID_FLAGS;
if (!call->is_client) {
return GRPC_CALL_ERROR_NOT_ON_SERVER;
}
req = &reqs[out++];
req->op = GRPC_IOREQ_RECV_STATUS;
req->flags = op->flags;
req->data.recv_status.set_value = set_status_value_directly;
req->data.recv_status.user_data = op->data.recv_status_on_client.status;
req = &reqs[out++];
@ -1321,8 +1376,11 @@ grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops,
finish_func = finish_batch_with_close;
break;
case GRPC_OP_RECV_CLOSE_ON_SERVER:
/* Flag validation: currently allow no flags */
if (op->flags != 0) return GRPC_CALL_ERROR_INVALID_FLAGS;
req = &reqs[out++];
req->op = GRPC_IOREQ_RECV_STATUS;
req->flags = op->flags;
req->data.recv_status.set_value = set_cancelled_value;
req->data.recv_status.user_data =
op->data.recv_close_on_server.cancelled;

@ -72,13 +72,14 @@ typedef union {
grpc_byte_buffer *send_message;
struct {
grpc_status_code code;
const char *details;
grpc_mdstr *details;
} send_status;
} grpc_ioreq_data;
typedef struct {
grpc_ioreq_op op;
grpc_ioreq_data data;
gpr_uint32 flags; /**< A copy of the write flags from grpc_op */
} grpc_ioreq;
typedef void (*grpc_ioreq_completion_func)(grpc_call *call, int success,

@ -37,12 +37,20 @@
#include <string.h>
#include "src/core/iomgr/iomgr.h"
#include "src/core/support/string.h"
#include "src/core/surface/call.h"
#include "src/core/surface/client.h"
#include "src/core/surface/init.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
/** Cache grpc-status: X mdelems for X = 0..NUM_CACHED_STATUS_ELEMS.
* Avoids needing to take a metadata context lock for sending status
* if the status code is <= NUM_CACHED_STATUS_ELEMS.
* Sized to allow the most commonly used codes to fit in
* (OK, Cancelled, Unknown). */
#define NUM_CACHED_STATUS_ELEMS 3
typedef struct registered_call {
grpc_mdelem *path;
grpc_mdelem *authority;
@ -54,10 +62,13 @@ struct grpc_channel {
gpr_refcount refs;
gpr_uint32 max_message_length;
grpc_mdctx *metadata_context;
/** mdstr for the grpc-status key */
grpc_mdstr *grpc_status_string;
grpc_mdstr *grpc_message_string;
grpc_mdstr *path_string;
grpc_mdstr *authority_string;
/** mdelem for grpc-status: 0 thru grpc-status: 2 */
grpc_mdelem *grpc_status_elem[NUM_CACHED_STATUS_ELEMS];
gpr_mu registered_call_mu;
registered_call *registered_calls;
@ -88,6 +99,13 @@ grpc_channel *grpc_channel_create_from_filters(
channel->metadata_context = mdctx;
channel->grpc_status_string = grpc_mdstr_from_string(mdctx, "grpc-status");
channel->grpc_message_string = grpc_mdstr_from_string(mdctx, "grpc-message");
for (i = 0; i < NUM_CACHED_STATUS_ELEMS; i++) {
char buf[GPR_LTOA_MIN_BUFSIZE];
gpr_ltoa(i, buf);
channel->grpc_status_elem[i] = grpc_mdelem_from_metadata_strings(
mdctx, grpc_mdstr_ref(channel->grpc_status_string),
grpc_mdstr_from_string(mdctx, buf));
}
channel->path_string = grpc_mdstr_from_string(mdctx, ":path");
channel->authority_string = grpc_mdstr_from_string(mdctx, ":authority");
grpc_channel_stack_init(filters, num_filters, args, channel->metadata_context,
@ -181,7 +199,11 @@ void grpc_channel_internal_ref(grpc_channel *c) {
static void destroy_channel(void *p, int ok) {
grpc_channel *channel = p;
size_t i;
grpc_channel_stack_destroy(CHANNEL_STACK_FROM_CHANNEL(channel));
for (i = 0; i < NUM_CACHED_STATUS_ELEMS; i++) {
grpc_mdelem_unref(channel->grpc_status_elem[i]);
}
grpc_mdstr_unref(channel->grpc_status_string);
grpc_mdstr_unref(channel->grpc_message_string);
grpc_mdstr_unref(channel->path_string);
@ -247,6 +269,18 @@ grpc_mdstr *grpc_channel_get_status_string(grpc_channel *channel) {
return channel->grpc_status_string;
}
grpc_mdelem *grpc_channel_get_reffed_status_elem(grpc_channel *channel, int i) {
if (i >= 0 && i < NUM_CACHED_STATUS_ELEMS) {
return grpc_mdelem_ref(channel->grpc_status_elem[i]);
} else {
char tmp[GPR_LTOA_MIN_BUFSIZE];
gpr_ltoa(i, tmp);
return grpc_mdelem_from_metadata_strings(
channel->metadata_context, grpc_mdstr_ref(channel->grpc_status_string),
grpc_mdstr_from_string(channel->metadata_context, tmp));
}
}
grpc_mdstr *grpc_channel_get_message_string(grpc_channel *channel) {
return channel->grpc_message_string;
}

@ -40,8 +40,18 @@ grpc_channel *grpc_channel_create_from_filters(
const grpc_channel_filter **filters, size_t count,
const grpc_channel_args *args, grpc_mdctx *mdctx, int is_client);
/** Get a (borrowed) pointer to this channels underlying channel stack */
grpc_channel_stack *grpc_channel_get_channel_stack(grpc_channel *channel);
/** Get a (borrowed) pointer to the channel wide metadata context */
grpc_mdctx *grpc_channel_get_metadata_context(grpc_channel *channel);
/** Get a grpc_mdelem of grpc-status: X where X is the numeric value of
status_code.
The returned elem is owned by the caller. */
grpc_mdelem *grpc_channel_get_reffed_status_elem(grpc_channel *channel,
int status_code);
grpc_mdstr *grpc_channel_get_status_string(grpc_channel *channel);
grpc_mdstr *grpc_channel_get_message_string(grpc_channel *channel);
gpr_uint32 grpc_channel_get_max_message_length(grpc_channel *channel);

@ -73,6 +73,7 @@ struct grpc_completion_queue {
event *queue;
/* Fixed size chained hash table of events for pluck() */
event *buckets[NUM_TAG_BUCKETS];
int is_server_cq;
};
grpc_completion_queue *grpc_completion_queue_create(void) {
@ -323,3 +324,7 @@ void grpc_cq_hack_spin_pollset(grpc_completion_queue *cc) {
gpr_time_add(gpr_now(), gpr_time_from_millis(100)));
gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
}
void grpc_cq_mark_server_cq(grpc_completion_queue *cc) { cc->is_server_cq = 1; }
int grpc_cq_is_server_cq(grpc_completion_queue *cc) { return cc->is_server_cq; }

@ -63,4 +63,7 @@ grpc_pollset *grpc_cq_pollset(grpc_completion_queue *cc);
void grpc_cq_hack_spin_pollset(grpc_completion_queue *cc);
void grpc_cq_mark_server_cq(grpc_completion_queue *cc);
int grpc_cq_is_server_cq(grpc_completion_queue *cc);
#endif /* GRPC_INTERNAL_CORE_SURFACE_COMPLETION_QUEUE_H */

@ -709,6 +709,7 @@ void grpc_server_register_completion_queue(grpc_server *server,
if (server->cqs[i] == cq) return;
}
GRPC_CQ_INTERNAL_REF(cq, "server");
grpc_cq_mark_server_cq(cq);
n = server->cq_count++;
server->cqs = gpr_realloc(server->cqs,
server->cq_count * sizeof(grpc_completion_queue *));
@ -1010,7 +1011,7 @@ void grpc_server_destroy(grpc_server *server) {
listener *l;
gpr_mu_lock(&server->mu);
GPR_ASSERT(server->shutdown);
GPR_ASSERT(server->shutdown || !server->listeners);
GPR_ASSERT(server->listeners_destroyed == num_listeners(server));
while (server->listeners) {
@ -1081,6 +1082,9 @@ grpc_call_error grpc_server_request_call(
GRPC_SERVER_LOG_REQUEST_CALL(GPR_INFO, server, call, details,
initial_metadata, cq_bound_to_call,
cq_for_notification, tag);
if (!grpc_cq_is_server_cq(cq_for_notification)) {
return GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE;
}
grpc_cq_begin_op(cq_for_notification, NULL);
rc.type = BATCH_CALL;
rc.tag = tag;
@ -1099,6 +1103,9 @@ grpc_call_error grpc_server_request_registered_call(
grpc_completion_queue *cq_for_notification, void *tag) {
requested_call rc;
registered_method *registered_method = rm;
if (!grpc_cq_is_server_cq(cq_for_notification)) {
return GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE;
}
grpc_cq_begin_op(cq_for_notification, NULL);
rc.type = REGISTERED_CALL;
rc.tag = tag;
@ -1153,6 +1160,7 @@ static void begin_call(grpc_server *server, call_data *calld,
rc->data.batch.details->deadline = calld->deadline;
r->op = GRPC_IOREQ_RECV_INITIAL_METADATA;
r->data.recv_metadata = rc->data.batch.initial_metadata;
r->flags = 0;
r++;
publish = publish_registered_or_batch;
break;
@ -1160,10 +1168,12 @@ static void begin_call(grpc_server *server, call_data *calld,
*rc->data.registered.deadline = calld->deadline;
r->op = GRPC_IOREQ_RECV_INITIAL_METADATA;
r->data.recv_metadata = rc->data.registered.initial_metadata;
r->flags = 0;
r++;
if (rc->data.registered.optional_payload) {
r->op = GRPC_IOREQ_RECV_MESSAGE;
r->data.recv_message = rc->data.registered.optional_payload;
r->flags = 0;
r++;
}
publish = publish_registered_or_batch;

@ -230,7 +230,10 @@ struct transport {
/* basic state management - what are we doing at the moment? */
gpr_uint8 reading;
gpr_uint8 writing;
gpr_uint8 calling_back;
/** are we calling back (via cb) with a channel-level event */
gpr_uint8 calling_back_channel;
/** are we calling back any grpc_transport_op completion events */
gpr_uint8 calling_back_ops;
gpr_uint8 destroying;
gpr_uint8 closed;
error_state error_state;
@ -357,7 +360,7 @@ static void push_setting(transport *t, grpc_chttp2_setting_id id,
gpr_uint32 value);
static int prepare_callbacks(transport *t);
static void run_callbacks(transport *t, const grpc_transport_callbacks *cb);
static void run_callbacks(transport *t);
static void call_cb_closed(transport *t, const grpc_transport_callbacks *cb);
static int prepare_write(transport *t);
@ -565,7 +568,7 @@ static void init_transport(transport *t, grpc_transport_setup_callback setup,
}
gpr_mu_lock(&t->mu);
t->calling_back = 1;
t->calling_back_channel = 1;
ref_transport(t); /* matches unref at end of this function */
gpr_mu_unlock(&t->mu);
@ -574,7 +577,7 @@ static void init_transport(transport *t, grpc_transport_setup_callback setup,
lock(t);
t->cb = sr.callbacks;
t->cb_user_data = sr.user_data;
t->calling_back = 0;
t->calling_back_channel = 0;
if (t->destroying) gpr_cv_signal(&t->cv);
unlock(t);
@ -595,7 +598,7 @@ static void destroy_transport(grpc_transport *gt) {
We need to be not writing as cancellation finalization may produce some
callbacks that NEED to be made to close out some streams when t->writing
becomes 0. */
while (t->calling_back || t->writing) {
while (t->calling_back_channel || t->writing) {
gpr_cv_wait(&t->cv, &t->mu, gpr_inf_future);
}
drop_connection(t);
@ -835,12 +838,15 @@ static void unlock(transport *t) {
finish_reads(t);
/* gather any callbacks that need to be made */
if (!t->calling_back) {
t->calling_back = perform_callbacks = prepare_callbacks(t);
if (cb) {
if (!t->calling_back_ops) {
t->calling_back_ops = perform_callbacks = prepare_callbacks(t);
if (perform_callbacks) ref_transport(t);
}
if (!t->calling_back_channel && cb) {
if (t->error_state == ERROR_STATE_SEEN && !t->writing) {
call_closed = 1;
t->calling_back = 1;
t->calling_back_channel = 1;
t->cb = NULL; /* no more callbacks */
t->error_state = ERROR_STATE_NOTIFIED;
}
@ -850,14 +856,12 @@ static void unlock(transport *t) {
t->pending_goaways = NULL;
t->num_pending_goaways = 0;
t->cap_pending_goaways = 0;
t->calling_back = 1;
}
t->calling_back_channel = 1;
}
}
if (perform_callbacks || call_closed || num_goaways) {
if (call_closed || num_goaways) {
ref_transport(t);
}
}
/* finally unlock */
gpr_mu_unlock(&t->mu);
@ -870,7 +874,11 @@ static void unlock(transport *t) {
}
if (perform_callbacks) {
run_callbacks(t, cb);
run_callbacks(t);
lock(t);
t->calling_back_ops = 0;
unlock(t);
unref_transport(t);
}
if (call_closed) {
@ -883,9 +891,9 @@ static void unlock(transport *t) {
perform_write(t, ep);
}
if (perform_callbacks || call_closed || num_goaways) {
if (call_closed || num_goaways) {
lock(t);
t->calling_back = 0;
t->calling_back_channel = 0;
if (t->destroying) gpr_cv_signal(&t->cv);
unlock(t);
unref_transport(t);
@ -2121,7 +2129,7 @@ static int prepare_callbacks(transport *t) {
return t->executing_callbacks.count > 0;
}
static void run_callbacks(transport *t, const grpc_transport_callbacks *cb) {
static void run_callbacks(transport *t) {
size_t i;
for (i = 0; i < t->executing_callbacks.count; i++) {
op_closure c = t->executing_callbacks.callbacks[i];

@ -120,7 +120,7 @@ static void unlock(grpc_mdctx *ctx) {
if (ctx->refs == 0) {
/* uncomment if you're having trouble diagnosing an mdelem leak to make
things clearer (slows down destruction a lot, however) */
gc_mdtab(ctx);
/* gc_mdtab(ctx); */
if (ctx->mdtab_count && ctx->mdtab_count == ctx->mdtab_free) {
discard_metadata(ctx);
}

@ -58,11 +58,18 @@ typedef enum grpc_stream_op_code {
GRPC_OP_SLICE
} grpc_stream_op_code;
/* Arguments for GRPC_OP_BEGIN */
/** Internal bit flag for grpc_begin_message's \a flags signaling the use of
* compression for the message */
#define GRPC_WRITE_INTERNAL_COMPRESS (0x80000000u)
/** Mask of all valid internal flags. */
#define GRPC_WRITE_INTERNAL_USED_MASK (GRPC_WRITE_INTERNAL_COMPRESS)
/* Arguments for GRPC_OP_BEGIN_MESSAGE */
typedef struct grpc_begin_message {
/* How many bytes of data will this message contain */
gpr_uint32 length;
/* Write flags for the message: see grpc.h GRPC_WRITE_xxx */
/* Write flags for the message: see grpc.h GRPC_WRITE_* for the public bits,
* GRPC_WRITE_INTERNAL_* for the internal ones. */
gpr_uint32 flags;
} grpc_begin_message;
@ -126,10 +133,8 @@ typedef struct grpc_stream_op {
} data;
} grpc_stream_op;
/* A stream op buffer is a wrapper around stream operations that is dynamically
extendable.
TODO(ctiller): inline a few elements into the struct, to avoid common case
per-call allocations. */
/** A stream op buffer is a wrapper around stream operations that is
* dynamically extendable. */
typedef struct grpc_stream_op_buffer {
grpc_stream_op *ops;
size_t nops;

@ -49,8 +49,8 @@ class GrpcBufferWriter GRPC_FINAL
explicit GrpcBufferWriter(grpc_byte_buffer** bp,
int block_size = kMaxBufferLength)
: block_size_(block_size), byte_count_(0), have_backup_(false) {
*bp = grpc_byte_buffer_create(NULL, 0);
slice_buffer_ = &(*bp)->data.slice_buffer;
*bp = grpc_raw_byte_buffer_create(NULL, 0);
slice_buffer_ = &(*bp)->data.raw.slice_buffer;
}
~GrpcBufferWriter() GRPC_OVERRIDE {

@ -42,7 +42,7 @@ ByteBuffer::ByteBuffer(Slice* slices, size_t nslices) {
for (size_t i = 0; i < nslices; i++) {
c_slices[i] = slices[i].slice_;
}
buffer_ = grpc_byte_buffer_create(c_slices.data(), nslices);
buffer_ = grpc_raw_byte_buffer_create(c_slices.data(), nslices);
}
void ByteBuffer::Clear() {

@ -36,6 +36,6 @@
namespace grpc {
const Status& Status::OK = Status();
const Status& Status::Cancelled = Status(StatusCode::CANCELLED);
const Status& Status::CANCELLED = Status(StatusCode::CANCELLED, "");
} // namespace grpc

@ -42,6 +42,7 @@ using std::chrono::duration_cast;
using std::chrono::nanoseconds;
using std::chrono::seconds;
using std::chrono::system_clock;
using std::chrono::high_resolution_clock;
namespace grpc {
@ -59,6 +60,20 @@ void Timepoint2Timespec(const system_clock::time_point& from,
to->tv_nsec = nsecs.count();
}
void TimepointHR2Timespec(const high_resolution_clock::time_point& from,
gpr_timespec* to) {
high_resolution_clock::duration deadline = from.time_since_epoch();
seconds secs = duration_cast<seconds>(deadline);
if (from == high_resolution_clock::time_point::max() ||
secs.count() >= gpr_inf_future.tv_sec || secs.count() < 0) {
*to = gpr_inf_future;
return;
}
nanoseconds nsecs = duration_cast<nanoseconds>(deadline - secs);
to->tv_sec = secs.count();
to->tv_nsec = nsecs.count();
}
system_clock::time_point Timespec2Timepoint(gpr_timespec t) {
if (gpr_time_cmp(t, gpr_inf_future) == 0) {
return system_clock::time_point::max();

@ -0,0 +1,105 @@
#region Copyright notice and license
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using System.Collections.Generic;
using Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
using NUnit.Framework;
namespace Grpc.Core.Internal.Tests
{
public class ChannelOptionsTest
{
[Test]
public void IntOption()
{
var option = new ChannelOption("somename", 1);
Assert.AreEqual(ChannelOption.OptionType.Integer, option.Type);
Assert.AreEqual("somename", option.Name);
Assert.AreEqual(1, option.IntValue);
Assert.Throws(typeof(InvalidOperationException), () => { var s = option.StringValue; });
}
[Test]
public void StringOption()
{
var option = new ChannelOption("somename", "ABCDEF");
Assert.AreEqual(ChannelOption.OptionType.String, option.Type);
Assert.AreEqual("somename", option.Name);
Assert.AreEqual("ABCDEF", option.StringValue);
Assert.Throws(typeof(InvalidOperationException), () => { var s = option.IntValue; });
}
[Test]
public void ConstructorPreconditions()
{
Assert.Throws(typeof(NullReferenceException), () => { new ChannelOption(null, "abc"); });
Assert.Throws(typeof(NullReferenceException), () => { new ChannelOption(null, 1); });
Assert.Throws(typeof(NullReferenceException), () => { new ChannelOption("abc", null); });
}
[Test]
public void CreateChannelArgsNull()
{
var channelArgs = ChannelOptions.CreateChannelArgs(null);
Assert.IsTrue(channelArgs.IsInvalid);
}
[Test]
public void CreateChannelArgsEmpty()
{
var options = new List<ChannelOption>();
var channelArgs = ChannelOptions.CreateChannelArgs(options);
channelArgs.Dispose();
}
[Test]
public void CreateChannelArgs()
{
var options = new List<ChannelOption>
{
new ChannelOption("ABC", "XYZ"),
new ChannelOption("somename", "IJKLM"),
new ChannelOption("intoption", 12345),
new ChannelOption("GHIJK", 12345),
};
var channelArgs = ChannelOptions.CreateChannelArgs(options);
channelArgs.Dispose();
}
}
}

@ -86,7 +86,7 @@ namespace Grpc.Core.Tests
server.AddServiceDefinition(ServiceDefinition);
int port = server.AddListeningPort(Host, Server.PickUnusedPort);
server.Start();
channel = new Channel(Host + ":" + port);
channel = new Channel(Host, port);
}
[TearDown]

@ -3,8 +3,6 @@
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>10.0.0</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{86EC5CB4-4EA2-40A2-8057-86542A0353BB}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>Grpc.Core.Tests</RootNamespace>
@ -46,6 +44,10 @@
<Compile Include="TimespecTest.cs" />
<Compile Include="PInvokeTest.cs" />
<Compile Include="Internal\MetadataArraySafeHandleTest.cs" />
<Compile Include="Internal\CompletionQueueSafeHandleTest.cs" />
<Compile Include="Internal\CompletionQueueEventTest.cs" />
<Compile Include="Internal\ChannelArgsSafeHandleTest.cs" />
<Compile Include="ChannelOptionsTest.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>

@ -0,0 +1,75 @@
#region Copyright notice and license
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
using NUnit.Framework;
namespace Grpc.Core.Internal.Tests
{
public class ChannelArgsSafeHandleTest
{
[Test]
public void CreateEmptyAndDestroy()
{
var channelArgs = ChannelArgsSafeHandle.Create(0);
channelArgs.Dispose();
}
[Test]
public void CreateNonEmptyAndDestroy()
{
var channelArgs = ChannelArgsSafeHandle.Create(5);
channelArgs.Dispose();
}
[Test]
public void CreateNullAndDestroy()
{
var channelArgs = ChannelArgsSafeHandle.CreateNull();
channelArgs.Dispose();
}
[Test]
public void CreateFillAndDestroy()
{
var channelArgs = ChannelArgsSafeHandle.Create(3);
channelArgs.SetInteger(0, "somekey", 12345);
channelArgs.SetString(1, "somekey", "abcdefghijkl");
channelArgs.SetString(2, "somekey", "XYZ");
channelArgs.Dispose();
}
}
}

@ -0,0 +1,52 @@
#region Copyright notice and license
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
using NUnit.Framework;
namespace Grpc.Core.Internal.Tests
{
public class CompletionQueueEventTest
{
[Test]
public void CreateAndDestroy()
{
Assert.AreEqual(CompletionQueueEvent.NativeSize, Marshal.SizeOf(typeof(CompletionQueueEvent)));
}
}
}

@ -0,0 +1,64 @@
#region Copyright notice and license
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
using NUnit.Framework;
namespace Grpc.Core.Internal.Tests
{
public class CompletionQueueSafeHandleTest
{
[Test]
public void CreateAndDestroy()
{
var cq = CompletionQueueSafeHandle.Create();
cq.Dispose();
}
[Test]
public void CreateAndShutdown()
{
var cq = CompletionQueueSafeHandle.Create();
cq.Shutdown();
var ev = cq.Next();
cq.Dispose();
Assert.AreEqual(GRPCCompletionType.Shutdown, ev.type);
Assert.AreNotEqual(IntPtr.Zero, ev.success);
Assert.AreEqual(IntPtr.Zero, ev.tag);
}
}
}

@ -48,7 +48,7 @@ namespace Grpc.Core.Tests
int counter;
[DllImport("grpc_csharp_ext.dll")]
static extern GRPCCallError grpcsharp_test_callback([MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback);
static extern GRPCCallError grpcsharp_test_callback([MarshalAs(UnmanagedType.FunctionPtr)] OpCompletionDelegate callback);
[DllImport("grpc_csharp_ext.dll")]
static extern IntPtr grpcsharp_test_nop(IntPtr ptr);
@ -88,7 +88,7 @@ namespace Grpc.Core.Tests
[Test]
public void NativeCallbackBenchmark()
{
CompletionCallbackDelegate handler = Handler;
OpCompletionDelegate handler = Handler;
counter = 0;
BenchmarkUtil.RunBenchmark(
@ -114,7 +114,7 @@ namespace Grpc.Core.Tests
10000, 10000,
() =>
{
grpcsharp_test_callback(new CompletionCallbackDelegate(Handler));
grpcsharp_test_callback(new OpCompletionDelegate(Handler));
});
Assert.AreNotEqual(0, counter);
}
@ -134,7 +134,7 @@ namespace Grpc.Core.Tests
});
}
private void Handler(bool success, IntPtr ptr)
private void Handler(bool success)
{
counter++;
}

@ -29,6 +29,7 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
@ -45,33 +46,41 @@ namespace Grpc.Core
readonly string target;
/// <summary>
/// Creates a channel.
/// Creates a channel that connects to a specific host.
/// Port will default to 80 for an unsecure channel and to 443 a secure channel.
/// </summary>
public Channel(string target, Credentials credentials = null, ChannelArgs channelArgs = null)
/// <param name="host">The DNS name of IP address of the host.</param>
/// <param name="credentials">Optional credentials to create a secure channel.</param>
/// <param name="options">Channel options.</param>
public Channel(string host, Credentials credentials = null, IEnumerable<ChannelOption> options = null)
{
using (ChannelArgsSafeHandle nativeChannelArgs = CreateNativeChannelArgs(channelArgs))
using (ChannelArgsSafeHandle nativeChannelArgs = ChannelOptions.CreateChannelArgs(options))
{
if (credentials != null)
{
using (CredentialsSafeHandle nativeCredentials = credentials.ToNativeCredentials())
{
this.handle = ChannelSafeHandle.CreateSecure(nativeCredentials, target, nativeChannelArgs);
this.handle = ChannelSafeHandle.CreateSecure(nativeCredentials, host, nativeChannelArgs);
}
}
else
{
this.handle = ChannelSafeHandle.Create(target, nativeChannelArgs);
this.handle = ChannelSafeHandle.Create(host, nativeChannelArgs);
}
}
this.target = GetOverridenTarget(target, channelArgs);
this.target = GetOverridenTarget(host, options);
}
public string Target
{
get
/// <summary>
/// Creates a channel that connects to a specific host and port.
/// </summary>
/// <param name="host">DNS name or IP address</param>
/// <param name="port">the port</param>
/// <param name="credentials">Optional credentials to create a secure channel.</param>
/// <param name="options">Channel options.</param>
public Channel(string host, int port, Credentials credentials = null, IEnumerable<ChannelOption> options = null) :
this(string.Format("{0}:{1}", host, port), credentials, options)
{
return this.target;
}
}
public void Dispose()
@ -80,6 +89,14 @@ namespace Grpc.Core
GC.SuppressFinalize(this);
}
internal string Target
{
get
{
return target;
}
}
internal ChannelSafeHandle Handle
{
get
@ -96,22 +113,25 @@ namespace Grpc.Core
}
}
private static string GetOverridenTarget(string target, ChannelArgs args)
/// <summary>
/// Look for SslTargetNameOverride option and return its value instead of originalTarget
/// if found.
/// </summary>
private static string GetOverridenTarget(string originalTarget, IEnumerable<ChannelOption> options)
{
if (args != null && !string.IsNullOrEmpty(args.GetSslTargetNameOverride()))
if (options == null)
{
return args.GetSslTargetNameOverride();
return originalTarget;
}
return target;
}
private static ChannelArgsSafeHandle CreateNativeChannelArgs(ChannelArgs args)
foreach (var option in options)
{
if (args == null)
if (option.Type == ChannelOption.OptionType.String
&& option.Name == ChannelOptions.SslTargetNameOverride)
{
return ChannelArgsSafeHandle.CreateNull();
return option.StringValue;
}
}
return args.ToNativeChannelArgs();
return originalTarget;
}
}
}

@ -1,115 +0,0 @@
#region Copyright notice and license
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core.Internal;
namespace Grpc.Core
{
/// <summary>
/// gRPC channel options.
/// </summary>
public class ChannelArgs
{
public const string SslTargetNameOverrideKey = "grpc.ssl_target_name_override";
readonly ImmutableDictionary<string, string> stringArgs;
private ChannelArgs(ImmutableDictionary<string, string> stringArgs)
{
this.stringArgs = stringArgs;
}
public string GetSslTargetNameOverride()
{
string result;
if (stringArgs.TryGetValue(SslTargetNameOverrideKey, out result))
{
return result;
}
return null;
}
public static Builder CreateBuilder()
{
return new Builder();
}
public class Builder
{
readonly Dictionary<string, string> stringArgs = new Dictionary<string, string>();
// TODO: AddInteger not supported yet.
public Builder AddString(string key, string value)
{
stringArgs.Add(key, value);
return this;
}
public ChannelArgs Build()
{
return new ChannelArgs(stringArgs.ToImmutableDictionary());
}
}
/// <summary>
/// Creates native object for the channel arguments.
/// </summary>
/// <returns>The native channel arguments.</returns>
internal ChannelArgsSafeHandle ToNativeChannelArgs()
{
ChannelArgsSafeHandle nativeArgs = null;
try
{
nativeArgs = ChannelArgsSafeHandle.Create(stringArgs.Count);
int i = 0;
foreach (var entry in stringArgs)
{
nativeArgs.SetString(i, entry.Key, entry.Value);
i++;
}
return nativeArgs;
}
catch (Exception)
{
if (nativeArgs != null)
{
nativeArgs.Dispose();
}
throw;
}
}
}
}

@ -0,0 +1,178 @@
#region Copyright notice and license
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
namespace Grpc.Core
{
/// <summary>
/// Channel option specified when creating a channel.
/// Corresponds to grpc_channel_args from grpc/grpc.h.
/// </summary>
public sealed class ChannelOption
{
public enum OptionType
{
Integer,
String
}
private readonly OptionType type;
private readonly string name;
private readonly int intValue;
private readonly string stringValue;
/// <summary>
/// Creates a channel option with a string value.
/// </summary>
/// <param name="name">Name.</param>
/// <param name="stringValue">String value.</param>
public ChannelOption(string name, string stringValue)
{
this.type = OptionType.String;
this.name = Preconditions.CheckNotNull(name);
this.stringValue = Preconditions.CheckNotNull(stringValue);
}
/// <summary>
/// Creates a channel option with an integer value.
/// </summary>
/// <param name="name">Name.</param>
/// <param name="stringValue">String value.</param>
public ChannelOption(string name, int intValue)
{
this.type = OptionType.Integer;
this.name = Preconditions.CheckNotNull(name);
this.intValue = intValue;
}
public OptionType Type
{
get
{
return type;
}
}
public string Name
{
get
{
return name;
}
}
public int IntValue
{
get
{
Preconditions.CheckState(type == OptionType.Integer);
return intValue;
}
}
public string StringValue
{
get
{
Preconditions.CheckState(type == OptionType.String);
return stringValue;
}
}
}
public static class ChannelOptions
{
// Override SSL target check. Only to be used for testing.
public const string SslTargetNameOverride = "grpc.ssl_target_name_override";
// Enable census for tracing and stats collection
public const string Census = "grpc.census";
// Maximum number of concurrent incoming streams to allow on a http2 connection
public const string MaxConcurrentStreams = "grpc.max_concurrent_streams";
// Maximum message length that the channel can receive
public const string MaxMessageLength = "grpc.max_message_length";
// Initial sequence number for http2 transports
public const string Http2InitialSequenceNumber = "grpc.http2.initial_sequence_number";
/// <summary>
/// Creates native object for a collection of channel options.
/// </summary>
/// <returns>The native channel arguments.</returns>
internal static ChannelArgsSafeHandle CreateChannelArgs(IEnumerable<ChannelOption> options)
{
if (options == null)
{
return ChannelArgsSafeHandle.CreateNull();
}
var optionList = new List<ChannelOption>(options); // It's better to do defensive copy
ChannelArgsSafeHandle nativeArgs = null;
try
{
nativeArgs = ChannelArgsSafeHandle.Create(optionList.Count);
for (int i = 0; i < optionList.Count; i++)
{
var option = optionList[i];
if (option.Type == ChannelOption.OptionType.Integer)
{
nativeArgs.SetInteger(i, option.Name, option.IntValue);
}
else if (option.Type == ChannelOption.OptionType.String)
{
nativeArgs.SetString(i, option.Name, option.StringValue);
}
else
{
throw new InvalidOperationException("Unknown option type");
}
}
return nativeArgs;
}
catch (Exception)
{
if (nativeArgs != null)
{
nativeArgs.Dispose();
}
throw;
}
}
}
}

@ -5,8 +5,6 @@
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>Grpc.Core</RootNamespace>
@ -73,13 +71,11 @@
<Compile Include="Marshaller.cs" />
<Compile Include="ServerServiceDefinition.cs" />
<Compile Include="Utils\AsyncStreamExtensions.cs" />
<Compile Include="Internal\BatchContextSafeHandleNotOwned.cs" />
<Compile Include="Utils\BenchmarkUtil.cs" />
<Compile Include="Utils\ExceptionHelper.cs" />
<Compile Include="Internal\CredentialsSafeHandle.cs" />
<Compile Include="Credentials.cs" />
<Compile Include="Internal\ChannelArgsSafeHandle.cs" />
<Compile Include="ChannelArgs.cs" />
<Compile Include="Internal\AsyncCompletion.cs" />
<Compile Include="Internal\AsyncCallBase.cs" />
<Compile Include="Internal\AsyncCallServer.cs" />
@ -101,6 +97,10 @@
<Compile Include="Internal\AtomicCounter.cs" />
<Compile Include="Internal\DebugStats.cs" />
<Compile Include="ServerCallContext.cs" />
<Compile Include="Internal\CompletionQueueEvent.cs" />
<Compile Include="Internal\CompletionRegistry.cs" />
<Compile Include="Internal\BatchContextSafeHandle.cs" />
<Compile Include="ChannelOptions.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

@ -54,6 +54,7 @@ namespace Grpc.Core
static volatile GrpcEnvironment instance;
readonly GrpcThreadPool threadPool;
readonly CompletionRegistry completionRegistry;
bool isClosed;
/// <summary>
@ -105,6 +106,19 @@ namespace Grpc.Core
}
}
internal static CompletionRegistry CompletionRegistry
{
get
{
var inst = instance;
if (inst == null)
{
throw new InvalidOperationException("GRPC environment not initialized");
}
return inst.completionRegistry;
}
}
/// <summary>
/// Creates gRPC environment.
/// </summary>
@ -112,6 +126,7 @@ namespace Grpc.Core
{
GrpcLog.RedirectNativeLogs(Console.Error);
grpcsharp_init();
completionRegistry = new CompletionRegistry();
threadPool = new GrpcThreadPool(THREAD_POOL_SIZE);
threadPool.Start();
// TODO: use proper logging here
@ -140,13 +155,23 @@ namespace Grpc.Core
var remainingClientCalls = DebugStats.ActiveClientCalls.Count;
if (remainingClientCalls != 0)
{
Console.WriteLine("Warning: Detected {0} client calls that weren't disposed properly.", remainingClientCalls);
DebugWarning(string.Format("Detected {0} client calls that weren't disposed properly.", remainingClientCalls));
}
var remainingServerCalls = DebugStats.ActiveServerCalls.Count;
if (remainingServerCalls != 0)
{
Console.WriteLine("Warning: Detected {0} server calls that weren't disposed properly.", remainingServerCalls);
DebugWarning(string.Format("Detected {0} server calls that weren't disposed properly.", remainingServerCalls));
}
var pendingBatchCompletions = DebugStats.PendingBatchCompletions.Count;
if (pendingBatchCompletions != 0)
{
DebugWarning(string.Format("Detected {0} pending batch completions.", pendingBatchCompletions));
}
}
private static void DebugWarning(string message)
{
throw new Exception("Shutdown check: " + message);
}
}
}

@ -47,9 +47,6 @@ namespace Grpc.Core.Internal
/// </summary>
internal class AsyncCall<TRequest, TResponse> : AsyncCallBase<TRequest, TResponse>
{
readonly CompletionCallbackDelegate unaryResponseHandler;
readonly CompletionCallbackDelegate finishedHandler;
// Completion of a pending unary response if not null.
TaskCompletionSource<TResponse> unaryResponseTcs;
@ -60,8 +57,6 @@ namespace Grpc.Core.Internal
public AsyncCall(Func<TRequest, byte[]> serializer, Func<byte[], TResponse> deserializer) : base(serializer, deserializer)
{
this.unaryResponseHandler = CreateBatchCompletionCallback(HandleUnaryResponse);
this.finishedHandler = CreateBatchCompletionCallback(HandleFinished);
}
public void Initialize(Channel channel, CompletionQueueSafeHandle cq, string methodName)
@ -96,7 +91,21 @@ namespace Grpc.Core.Internal
using (var metadataArray = MetadataArraySafeHandle.Create(headers))
{
call.BlockingUnary(cq, payload, unaryResponseHandler, metadataArray);
using (var ctx = BatchContextSafeHandle.Create())
{
call.StartUnary(payload, ctx, metadataArray);
var ev = cq.Pluck(ctx.Handle);
bool success = (ev.success != 0);
try
{
HandleUnaryResponse(success, ctx);
}
catch (Exception e)
{
Console.WriteLine("Exception occured while invoking completion delegate: " + e);
}
}
}
try
@ -129,7 +138,7 @@ namespace Grpc.Core.Internal
unaryResponseTcs = new TaskCompletionSource<TResponse>();
using (var metadataArray = MetadataArraySafeHandle.Create(headers))
{
call.StartUnary(payload, unaryResponseHandler, metadataArray);
call.StartUnary(payload, HandleUnaryResponse, metadataArray);
}
return unaryResponseTcs.Task;
}
@ -151,7 +160,7 @@ namespace Grpc.Core.Internal
unaryResponseTcs = new TaskCompletionSource<TResponse>();
using (var metadataArray = MetadataArraySafeHandle.Create(headers))
{
call.StartClientStreaming(unaryResponseHandler, metadataArray);
call.StartClientStreaming(HandleUnaryResponse, metadataArray);
}
return unaryResponseTcs.Task;
@ -175,7 +184,7 @@ namespace Grpc.Core.Internal
using (var metadataArray = MetadataArraySafeHandle.Create(headers))
{
call.StartServerStreaming(payload, finishedHandler, metadataArray);
call.StartServerStreaming(payload, HandleFinished, metadataArray);
}
}
}
@ -194,7 +203,7 @@ namespace Grpc.Core.Internal
using (var metadataArray = MetadataArraySafeHandle.Create(headers))
{
call.StartDuplexStreaming(finishedHandler, metadataArray);
call.StartDuplexStreaming(HandleFinished, metadataArray);
}
}
}
@ -229,7 +238,7 @@ namespace Grpc.Core.Internal
Preconditions.CheckNotNull(completionDelegate, "Completion delegate cannot be null");
CheckSendingAllowed();
call.StartSendCloseFromClient(halfclosedHandler);
call.StartSendCloseFromClient(HandleHalfclosed);
halfcloseRequested = true;
sendCompletionDelegate = completionDelegate;
@ -274,7 +283,7 @@ namespace Grpc.Core.Internal
/// <summary>
/// Handler for unary response completion.
/// </summary>
private void HandleUnaryResponse(bool success, BatchContextSafeHandleNotOwned ctx)
private void HandleUnaryResponse(bool success, BatchContextSafeHandle ctx)
{
lock (myLock)
{
@ -307,7 +316,7 @@ namespace Grpc.Core.Internal
/// <summary>
/// Handles receive status completion for calls with streaming response.
/// </summary>
private void HandleFinished(bool success, BatchContextSafeHandleNotOwned ctx)
private void HandleFinished(bool success, BatchContextSafeHandle ctx)
{
var status = ctx.GetReceivedStatus();

@ -51,13 +51,8 @@ namespace Grpc.Core.Internal
readonly Func<TWrite, byte[]> serializer;
readonly Func<byte[], TRead> deserializer;
protected readonly CompletionCallbackDelegate sendFinishedHandler;
protected readonly CompletionCallbackDelegate readFinishedHandler;
protected readonly CompletionCallbackDelegate halfclosedHandler;
protected readonly object myLock = new object();
protected GCHandle gchandle;
protected CallSafeHandle call;
protected bool disposed;
@ -77,10 +72,6 @@ namespace Grpc.Core.Internal
{
this.serializer = Preconditions.CheckNotNull(serializer);
this.deserializer = Preconditions.CheckNotNull(deserializer);
this.sendFinishedHandler = CreateBatchCompletionCallback(HandleSendFinished);
this.readFinishedHandler = CreateBatchCompletionCallback(HandleReadFinished);
this.halfclosedHandler = CreateBatchCompletionCallback(HandleHalfclosed);
}
/// <summary>
@ -121,9 +112,6 @@ namespace Grpc.Core.Internal
{
lock (myLock)
{
// Make sure this object and the delegated held by it will not be garbage collected
// before we release this handle.
gchandle = GCHandle.Alloc(this);
this.call = call;
}
}
@ -141,7 +129,7 @@ namespace Grpc.Core.Internal
Preconditions.CheckNotNull(completionDelegate, "Completion delegate cannot be null");
CheckSendingAllowed();
call.StartSendMessage(payload, sendFinishedHandler);
call.StartSendMessage(payload, HandleSendFinished);
sendCompletionDelegate = completionDelegate;
}
}
@ -157,7 +145,7 @@ namespace Grpc.Core.Internal
Preconditions.CheckNotNull(completionDelegate, "Completion delegate cannot be null");
CheckReadingAllowed();
call.StartReceiveMessage(readFinishedHandler);
call.StartReceiveMessage(HandleReadFinished);
readCompletionDelegate = completionDelegate;
}
}
@ -197,7 +185,6 @@ namespace Grpc.Core.Internal
{
call.Dispose();
}
gchandle.Free();
disposed = true;
}
@ -281,30 +268,10 @@ namespace Grpc.Core.Internal
}
}
/// <summary>
/// Creates completion callback delegate that wraps the batch completion handler in a try catch block to
/// prevent propagating exceptions accross managed/unmanaged boundary.
/// </summary>
protected CompletionCallbackDelegate CreateBatchCompletionCallback(Action<bool, BatchContextSafeHandleNotOwned> handler)
{
return new CompletionCallbackDelegate((success, batchContextPtr) =>
{
try
{
var ctx = new BatchContextSafeHandleNotOwned(batchContextPtr);
handler(success, ctx);
}
catch (Exception e)
{
Console.WriteLine("Caught exception in a native handler: " + e);
}
});
}
/// <summary>
/// Handles send completion.
/// </summary>
private void HandleSendFinished(bool success, BatchContextSafeHandleNotOwned ctx)
protected void HandleSendFinished(bool success, BatchContextSafeHandle ctx)
{
AsyncCompletionDelegate<object> origCompletionDelegate = null;
lock (myLock)
@ -328,7 +295,7 @@ namespace Grpc.Core.Internal
/// <summary>
/// Handles halfclose completion.
/// </summary>
private void HandleHalfclosed(bool success, BatchContextSafeHandleNotOwned ctx)
protected void HandleHalfclosed(bool success, BatchContextSafeHandle ctx)
{
AsyncCompletionDelegate<object> origCompletionDelegate = null;
lock (myLock)
@ -353,7 +320,7 @@ namespace Grpc.Core.Internal
/// <summary>
/// Handles streaming read completion.
/// </summary>
private void HandleReadFinished(bool success, BatchContextSafeHandleNotOwned ctx)
protected void HandleReadFinished(bool success, BatchContextSafeHandle ctx)
{
var payload = ctx.GetReceivedMessage();

@ -47,12 +47,10 @@ namespace Grpc.Core.Internal
/// </summary>
internal class AsyncCallServer<TRequest, TResponse> : AsyncCallBase<TResponse, TRequest>
{
readonly CompletionCallbackDelegate finishedServersideHandler;
readonly TaskCompletionSource<object> finishedServersideTcs = new TaskCompletionSource<object>();
public AsyncCallServer(Func<TResponse, byte[]> serializer, Func<byte[], TRequest> deserializer) : base(serializer, deserializer)
{
this.finishedServersideHandler = CreateBatchCompletionCallback(HandleFinishedServerside);
}
public void Initialize(CallSafeHandle call)
@ -72,7 +70,7 @@ namespace Grpc.Core.Internal
started = true;
call.StartServerSide(finishedServersideHandler);
call.StartServerSide(HandleFinishedServerside);
return finishedServersideTcs.Task;
}
}
@ -107,8 +105,9 @@ namespace Grpc.Core.Internal
Preconditions.CheckNotNull(completionDelegate, "Completion delegate cannot be null");
CheckSendingAllowed();
call.StartSendStatusFromServer(status, halfclosedHandler);
call.StartSendStatusFromServer(status, HandleHalfclosed);
halfcloseRequested = true;
readingDone = true;
sendCompletionDelegate = completionDelegate;
}
}
@ -121,7 +120,7 @@ namespace Grpc.Core.Internal
/// <summary>
/// Handles the server side close completion.
/// </summary>
private void HandleFinishedServerside(bool success, BatchContextSafeHandleNotOwned ctx)
private void HandleFinishedServerside(bool success, BatchContextSafeHandle ctx)
{
bool cancelled = ctx.GetReceivedCloseOnServerCancelled();

@ -41,32 +41,50 @@ namespace Grpc.Core.Internal
/// Not owned version of
/// grpcsharp_batch_context
/// </summary>
internal class BatchContextSafeHandleNotOwned : SafeHandleZeroIsInvalid
internal class BatchContextSafeHandle : SafeHandleZeroIsInvalid
{
[DllImport("grpc_csharp_ext.dll")]
static extern IntPtr grpcsharp_batch_context_recv_message_length(BatchContextSafeHandleNotOwned ctx);
static extern BatchContextSafeHandle grpcsharp_batch_context_create();
[DllImport("grpc_csharp_ext.dll")]
static extern void grpcsharp_batch_context_recv_message_to_buffer(BatchContextSafeHandleNotOwned ctx, byte[] buffer, UIntPtr bufferLen);
static extern IntPtr grpcsharp_batch_context_recv_message_length(BatchContextSafeHandle ctx);
[DllImport("grpc_csharp_ext.dll")]
static extern StatusCode grpcsharp_batch_context_recv_status_on_client_status(BatchContextSafeHandleNotOwned ctx);
static extern void grpcsharp_batch_context_recv_message_to_buffer(BatchContextSafeHandle ctx, byte[] buffer, UIntPtr bufferLen);
[DllImport("grpc_csharp_ext.dll")]
static extern IntPtr grpcsharp_batch_context_recv_status_on_client_details(BatchContextSafeHandleNotOwned ctx); // returns const char*
static extern StatusCode grpcsharp_batch_context_recv_status_on_client_status(BatchContextSafeHandle ctx);
[DllImport("grpc_csharp_ext.dll")]
static extern CallSafeHandle grpcsharp_batch_context_server_rpc_new_call(BatchContextSafeHandleNotOwned ctx);
static extern IntPtr grpcsharp_batch_context_recv_status_on_client_details(BatchContextSafeHandle ctx); // returns const char*
[DllImport("grpc_csharp_ext.dll")]
static extern IntPtr grpcsharp_batch_context_server_rpc_new_method(BatchContextSafeHandleNotOwned ctx); // returns const char*
static extern CallSafeHandle grpcsharp_batch_context_server_rpc_new_call(BatchContextSafeHandle ctx);
[DllImport("grpc_csharp_ext.dll")]
static extern int grpcsharp_batch_context_recv_close_on_server_cancelled(BatchContextSafeHandleNotOwned ctx);
static extern IntPtr grpcsharp_batch_context_server_rpc_new_method(BatchContextSafeHandle ctx); // returns const char*
public BatchContextSafeHandleNotOwned(IntPtr handle) : base(false)
[DllImport("grpc_csharp_ext.dll")]
static extern int grpcsharp_batch_context_recv_close_on_server_cancelled(BatchContextSafeHandle ctx);
[DllImport("grpc_csharp_ext.dll")]
static extern void grpcsharp_batch_context_destroy(IntPtr ctx);
private BatchContextSafeHandle()
{
SetHandle(handle);
}
public static BatchContextSafeHandle Create()
{
return grpcsharp_batch_context_create();
}
public IntPtr Handle
{
get
{
return handle;
}
}
public Status GetReceivedStatus()
@ -102,5 +120,11 @@ namespace Grpc.Core.Internal
{
return grpcsharp_batch_context_recv_close_on_server_cancelled(this) != 0;
}
protected override bool ReleaseHandle()
{
grpcsharp_batch_context_destroy(handle);
return true;
}
}
}

@ -37,8 +37,6 @@ using Grpc.Core.Utils;
namespace Grpc.Core.Internal
{
internal delegate void CompletionCallbackDelegate(bool success, IntPtr batchContextPtr);
/// <summary>
/// grpc_call from <grpc/grpc.h>
/// </summary>
@ -57,49 +55,40 @@ namespace Grpc.Core.Internal
[DllImport("grpc_csharp_ext.dll")]
static extern GRPCCallError grpcsharp_call_start_unary(CallSafeHandle call,
[MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback,
byte[] send_buffer, UIntPtr send_buffer_len, MetadataArraySafeHandle metadataArray);
[DllImport("grpc_csharp_ext.dll")]
static extern void grpcsharp_call_blocking_unary(CallSafeHandle call, CompletionQueueSafeHandle dedicatedCq,
[MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback,
byte[] send_buffer, UIntPtr send_buffer_len, MetadataArraySafeHandle metadataArray);
BatchContextSafeHandle ctx, byte[] send_buffer, UIntPtr send_buffer_len, MetadataArraySafeHandle metadataArray);
[DllImport("grpc_csharp_ext.dll")]
static extern GRPCCallError grpcsharp_call_start_client_streaming(CallSafeHandle call,
[MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback,
MetadataArraySafeHandle metadataArray);
BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray);
[DllImport("grpc_csharp_ext.dll")]
static extern GRPCCallError grpcsharp_call_start_server_streaming(CallSafeHandle call,
[MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback,
byte[] send_buffer, UIntPtr send_buffer_len,
BatchContextSafeHandle ctx, byte[] send_buffer, UIntPtr send_buffer_len,
MetadataArraySafeHandle metadataArray);
[DllImport("grpc_csharp_ext.dll")]
static extern GRPCCallError grpcsharp_call_start_duplex_streaming(CallSafeHandle call,
[MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback,
MetadataArraySafeHandle metadataArray);
BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray);
[DllImport("grpc_csharp_ext.dll")]
static extern GRPCCallError grpcsharp_call_send_message(CallSafeHandle call,
[MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback,
byte[] send_buffer, UIntPtr send_buffer_len);
BatchContextSafeHandle ctx, byte[] send_buffer, UIntPtr send_buffer_len);
[DllImport("grpc_csharp_ext.dll")]
static extern GRPCCallError grpcsharp_call_send_close_from_client(CallSafeHandle call,
[MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback);
BatchContextSafeHandle ctx);
[DllImport("grpc_csharp_ext.dll")]
static extern GRPCCallError grpcsharp_call_send_status_from_server(CallSafeHandle call, [MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback, StatusCode statusCode, string statusMessage);
static extern GRPCCallError grpcsharp_call_send_status_from_server(CallSafeHandle call,
BatchContextSafeHandle ctx, StatusCode statusCode, string statusMessage);
[DllImport("grpc_csharp_ext.dll")]
static extern GRPCCallError grpcsharp_call_recv_message(CallSafeHandle call,
[MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback);
BatchContextSafeHandle ctx);
[DllImport("grpc_csharp_ext.dll")]
static extern GRPCCallError grpcsharp_call_start_serverside(CallSafeHandle call,
[MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback);
BatchContextSafeHandle ctx);
[DllImport("grpc_csharp_ext.dll")]
static extern void grpcsharp_call_destroy(IntPtr call);
@ -113,64 +102,84 @@ namespace Grpc.Core.Internal
return grpcsharp_channel_create_call(channel, cq, method, host, deadline);
}
public void StartUnary(byte[] payload, CompletionCallbackDelegate callback, MetadataArraySafeHandle metadataArray)
public void StartUnary(byte[] payload, BatchCompletionDelegate callback, MetadataArraySafeHandle metadataArray)
{
AssertCallOk(grpcsharp_call_start_unary(this, callback, payload, new UIntPtr((ulong)payload.Length), metadataArray));
var ctx = BatchContextSafeHandle.Create();
GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback);
grpcsharp_call_start_unary(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray)
.CheckOk();
}
public void BlockingUnary(CompletionQueueSafeHandle dedicatedCq, byte[] payload, CompletionCallbackDelegate callback, MetadataArraySafeHandle metadataArray)
public void StartUnary(byte[] payload, BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray)
{
grpcsharp_call_blocking_unary(this, dedicatedCq, callback, payload, new UIntPtr((ulong)payload.Length), metadataArray);
grpcsharp_call_start_unary(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray)
.CheckOk();
}
public void StartClientStreaming(CompletionCallbackDelegate callback, MetadataArraySafeHandle metadataArray)
public void StartClientStreaming(BatchCompletionDelegate callback, MetadataArraySafeHandle metadataArray)
{
AssertCallOk(grpcsharp_call_start_client_streaming(this, callback, metadataArray));
var ctx = BatchContextSafeHandle.Create();
GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback);
grpcsharp_call_start_client_streaming(this, ctx, metadataArray).CheckOk();
}
public void StartServerStreaming(byte[] payload, CompletionCallbackDelegate callback, MetadataArraySafeHandle metadataArray)
public void StartServerStreaming(byte[] payload, BatchCompletionDelegate callback, MetadataArraySafeHandle metadataArray)
{
AssertCallOk(grpcsharp_call_start_server_streaming(this, callback, payload, new UIntPtr((ulong)payload.Length), metadataArray));
var ctx = BatchContextSafeHandle.Create();
GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback);
grpcsharp_call_start_server_streaming(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray).CheckOk();
}
public void StartDuplexStreaming(CompletionCallbackDelegate callback, MetadataArraySafeHandle metadataArray)
public void StartDuplexStreaming(BatchCompletionDelegate callback, MetadataArraySafeHandle metadataArray)
{
AssertCallOk(grpcsharp_call_start_duplex_streaming(this, callback, metadataArray));
var ctx = BatchContextSafeHandle.Create();
GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback);
grpcsharp_call_start_duplex_streaming(this, ctx, metadataArray).CheckOk();
}
public void StartSendMessage(byte[] payload, CompletionCallbackDelegate callback)
public void StartSendMessage(byte[] payload, BatchCompletionDelegate callback)
{
AssertCallOk(grpcsharp_call_send_message(this, callback, payload, new UIntPtr((ulong)payload.Length)));
var ctx = BatchContextSafeHandle.Create();
GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback);
grpcsharp_call_send_message(this, ctx, payload, new UIntPtr((ulong)payload.Length)).CheckOk();
}
public void StartSendCloseFromClient(CompletionCallbackDelegate callback)
public void StartSendCloseFromClient(BatchCompletionDelegate callback)
{
AssertCallOk(grpcsharp_call_send_close_from_client(this, callback));
var ctx = BatchContextSafeHandle.Create();
GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback);
grpcsharp_call_send_close_from_client(this, ctx).CheckOk();
}
public void StartSendStatusFromServer(Status status, CompletionCallbackDelegate callback)
public void StartSendStatusFromServer(Status status, BatchCompletionDelegate callback)
{
AssertCallOk(grpcsharp_call_send_status_from_server(this, callback, status.StatusCode, status.Detail));
var ctx = BatchContextSafeHandle.Create();
GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback);
grpcsharp_call_send_status_from_server(this, ctx, status.StatusCode, status.Detail).CheckOk();
}
public void StartReceiveMessage(CompletionCallbackDelegate callback)
public void StartReceiveMessage(BatchCompletionDelegate callback)
{
AssertCallOk(grpcsharp_call_recv_message(this, callback));
var ctx = BatchContextSafeHandle.Create();
GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback);
grpcsharp_call_recv_message(this, ctx).CheckOk();
}
public void StartServerSide(CompletionCallbackDelegate callback)
public void StartServerSide(BatchCompletionDelegate callback)
{
AssertCallOk(grpcsharp_call_start_serverside(this, callback));
var ctx = BatchContextSafeHandle.Create();
GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback);
grpcsharp_call_start_serverside(this, ctx).CheckOk();
}
public void Cancel()
{
AssertCallOk(grpcsharp_call_cancel(this));
grpcsharp_call_cancel(this).CheckOk();
}
public void CancelWithStatus(Status status)
{
AssertCallOk(grpcsharp_call_cancel_with_status(this, status.StatusCode, status.Detail));
grpcsharp_call_cancel_with_status(this, status.StatusCode, status.Detail).CheckOk();
}
protected override bool ReleaseHandle()
@ -179,11 +188,6 @@ namespace Grpc.Core.Internal
return true;
}
private static void AssertCallOk(GRPCCallError callError)
{
Preconditions.CheckState(callError == GRPCCallError.GRPC_CALL_OK, "Status not GRPC_CALL_OK");
}
private static uint GetFlags(bool buffered)
{
return buffered ? 0 : GRPC_WRITE_BUFFER_HINT;

@ -45,6 +45,9 @@ namespace Grpc.Core.Internal
[DllImport("grpc_csharp_ext.dll", CharSet = CharSet.Ansi)]
static extern void grpcsharp_channel_args_set_string(ChannelArgsSafeHandle args, UIntPtr index, string key, string value);
[DllImport("grpc_csharp_ext.dll", CharSet = CharSet.Ansi)]
static extern void grpcsharp_channel_args_set_integer(ChannelArgsSafeHandle args, UIntPtr index, string key, int value);
[DllImport("grpc_csharp_ext.dll")]
static extern void grpcsharp_channel_args_destroy(IntPtr args);
@ -67,6 +70,11 @@ namespace Grpc.Core.Internal
grpcsharp_channel_args_set_string(this, new UIntPtr((uint)index), key, value);
}
public void SetInteger(int index, string key, int value)
{
grpcsharp_channel_args_set_integer(this, new UIntPtr((uint)index), key, value);
}
protected override bool ReleaseHandle()
{
grpcsharp_channel_args_destroy(handle);

@ -0,0 +1,60 @@
#region Copyright notice and license
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using System.Runtime.InteropServices;
namespace Grpc.Core.Internal
{
/// <summary>
/// grpc_event from grpc/grpc.h
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct CompletionQueueEvent
{
[DllImport("grpc_csharp_ext.dll")]
static extern int grpcsharp_sizeof_grpc_event();
public GRPCCompletionType type;
public int success;
public IntPtr tag;
internal static int NativeSize
{
get
{
return grpcsharp_sizeof_grpc_event();
}
}
}
}

@ -46,7 +46,10 @@ namespace Grpc.Core.Internal
static extern void grpcsharp_completion_queue_shutdown(CompletionQueueSafeHandle cq);
[DllImport("grpc_csharp_ext.dll")]
static extern GRPCCompletionType grpcsharp_completion_queue_next_with_callback(CompletionQueueSafeHandle cq);
static extern CompletionQueueEvent grpcsharp_completion_queue_next(CompletionQueueSafeHandle cq);
[DllImport("grpc_csharp_ext.dll")]
static extern CompletionQueueEvent grpcsharp_completion_queue_pluck(CompletionQueueSafeHandle cq, IntPtr tag);
[DllImport("grpc_csharp_ext.dll")]
static extern void grpcsharp_completion_queue_destroy(IntPtr cq);
@ -60,9 +63,14 @@ namespace Grpc.Core.Internal
return grpcsharp_completion_queue_create();
}
public GRPCCompletionType NextWithCallback()
public CompletionQueueEvent Next()
{
return grpcsharp_completion_queue_next(this);
}
public CompletionQueueEvent Pluck(IntPtr tag)
{
return grpcsharp_completion_queue_next_with_callback(this);
return grpcsharp_completion_queue_pluck(this, tag);
}
public void Shutdown()

@ -0,0 +1,89 @@
#region Copyright notice and license
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Grpc.Core.Utils;
namespace Grpc.Core.Internal
{
internal delegate void OpCompletionDelegate(bool success);
internal delegate void BatchCompletionDelegate(bool success, BatchContextSafeHandle ctx);
internal class CompletionRegistry
{
readonly ConcurrentDictionary<IntPtr, OpCompletionDelegate> dict = new ConcurrentDictionary<IntPtr, OpCompletionDelegate>();
public void Register(IntPtr key, OpCompletionDelegate callback)
{
DebugStats.PendingBatchCompletions.Increment();
Preconditions.CheckState(dict.TryAdd(key, callback));
}
public void RegisterBatchCompletion(BatchContextSafeHandle ctx, BatchCompletionDelegate callback)
{
OpCompletionDelegate opCallback = ((success) => HandleBatchCompletion(success, ctx, callback));
Register(ctx.Handle, opCallback);
}
public OpCompletionDelegate Extract(IntPtr key)
{
OpCompletionDelegate value;
Preconditions.CheckState(dict.TryRemove(key, out value));
DebugStats.PendingBatchCompletions.Decrement();
return value;
}
private static void HandleBatchCompletion(bool success, BatchContextSafeHandle ctx, BatchCompletionDelegate callback)
{
try
{
callback(success, ctx);
}
catch (Exception e)
{
Console.WriteLine("Exception occured while invoking completion delegate: " + e);
}
finally
{
if (ctx != null)
{
ctx.Dispose();
}
}
}
}
}

@ -41,5 +41,7 @@ namespace Grpc.Core.Internal
public static readonly AtomicCounter ActiveClientCalls = new AtomicCounter();
public static readonly AtomicCounter ActiveServerCalls = new AtomicCounter();
public static readonly AtomicCounter PendingBatchCompletions = new AtomicCounter();
}
}

@ -33,35 +33,47 @@
using System;
using System.Runtime.InteropServices;
using Grpc.Core.Utils;
namespace Grpc.Core.Internal
{
/// <summary>
/// from grpc/grpc.h
/// grpc_call_error from grpc/grpc.h
/// </summary>
internal enum GRPCCallError
{
/* everything went ok */
GRPC_CALL_OK = 0,
OK = 0,
/* something failed, we don't know what */
GRPC_CALL_ERROR,
Error,
/* this method is not available on the server */
GRPC_CALL_ERROR_NOT_ON_SERVER,
NotOnServer,
/* this method is not available on the client */
GRPC_CALL_ERROR_NOT_ON_CLIENT,
NotOnClient,
/* this method must be called before server_accept */
GRPC_CALL_ERROR_ALREADY_ACCEPTED,
AlreadyAccepted,
/* this method must be called before invoke */
GRPC_CALL_ERROR_ALREADY_INVOKED,
AlreadyInvoked,
/* this method must be called after invoke */
GRPC_CALL_ERROR_NOT_INVOKED,
NotInvoked,
/* this call is already finished
(writes_done or write_status has already been called) */
GRPC_CALL_ERROR_ALREADY_FINISHED,
AlreadyFinished,
/* there is already an outstanding read/write operation on the call */
GRPC_CALL_ERROR_TOO_MANY_OPERATIONS,
TooManyOperations,
/* the flags value was illegal for this call */
GRPC_CALL_ERROR_INVALID_FLAGS
InvalidFlags
}
internal static class CallErrorExtensions
{
/// <summary>
/// Checks the call API invocation's result is OK.
/// </summary>
public static void CheckOk(this GRPCCallError callError)
{
Preconditions.CheckState(callError == GRPCCallError.OK, "Call error: " + callError);
}
}
/// <summary>
@ -70,12 +82,12 @@ namespace Grpc.Core.Internal
internal enum GRPCCompletionType
{
/* Shutting down */
GRPC_QUEUE_SHUTDOWN,
Shutdown,
/* No event before timeout */
GRPC_QUEUE_TIMEOUT,
Timeout,
/* operation completion */
GRPC_OP_COMPLETE
OpComplete
}
}

@ -112,12 +112,26 @@ namespace Grpc.Core.Internal
/// </summary>
private void RunHandlerLoop()
{
GRPCCompletionType completionType;
CompletionQueueEvent ev;
do
{
completionType = cq.NextWithCallback();
ev = cq.Next();
if (ev.type == GRPCCompletionType.OpComplete)
{
bool success = (ev.success != 0);
IntPtr tag = ev.tag;
try
{
var callback = GrpcEnvironment.CompletionRegistry.Extract(tag);
callback(success);
}
catch (Exception e)
{
Console.WriteLine("Exception occured while invoking completion delegate: " + e);
}
}
}
while (completionType != GRPCCompletionType.GRPC_QUEUE_SHUTDOWN);
while (ev.type != GRPCCompletionType.Shutdown);
Console.WriteLine("Completion queue has shutdown successfully, thread " + Thread.CurrentThread.Name + " exiting.");
}
}

@ -267,8 +267,6 @@ namespace Grpc.Core.Internal
var responseStream = new ServerResponseStream<byte[], byte[]>(asyncCall);
await responseStream.WriteStatusAsync(new Status(StatusCode.Unimplemented, "No such method."));
// TODO(jtattermusch): if we don't read what client has sent, the server call never gets disposed.
await requestStream.ToList();
await finishedTask;
}
}

@ -45,10 +45,7 @@ namespace Grpc.Core.Internal
internal sealed class ServerSafeHandle : SafeHandleZeroIsInvalid
{
[DllImport("grpc_csharp_ext.dll")]
static extern GRPCCallError grpcsharp_server_request_call(ServerSafeHandle server, CompletionQueueSafeHandle cq, [MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback);
[DllImport("grpc_csharp_ext.dll")]
static extern ServerSafeHandle grpcsharp_server_create(CompletionQueueSafeHandle cq, IntPtr args);
static extern ServerSafeHandle grpcsharp_server_create(CompletionQueueSafeHandle cq, ChannelArgsSafeHandle args);
[DllImport("grpc_csharp_ext.dll")]
static extern int grpcsharp_server_add_http2_port(ServerSafeHandle server, string addr);
@ -60,11 +57,14 @@ namespace Grpc.Core.Internal
static extern void grpcsharp_server_start(ServerSafeHandle server);
[DllImport("grpc_csharp_ext.dll")]
static extern void grpcsharp_server_shutdown_and_notify_callback(ServerSafeHandle server, CompletionQueueSafeHandle cq, [MarshalAs(UnmanagedType.FunctionPtr)] CompletionCallbackDelegate callback);
static extern GRPCCallError grpcsharp_server_request_call(ServerSafeHandle server, CompletionQueueSafeHandle cq, BatchContextSafeHandle ctx);
[DllImport("grpc_csharp_ext.dll")]
static extern void grpcsharp_server_cancel_all_calls(ServerSafeHandle server);
[DllImport("grpc_csharp_ext.dll")]
static extern void grpcsharp_server_shutdown_and_notify_callback(ServerSafeHandle server, CompletionQueueSafeHandle cq, BatchContextSafeHandle ctx);
[DllImport("grpc_csharp_ext.dll")]
static extern void grpcsharp_server_destroy(IntPtr server);
@ -72,7 +72,7 @@ namespace Grpc.Core.Internal
{
}
public static ServerSafeHandle NewServer(CompletionQueueSafeHandle cq, IntPtr args)
public static ServerSafeHandle NewServer(CompletionQueueSafeHandle cq, ChannelArgsSafeHandle args)
{
return grpcsharp_server_create(cq, args);
}
@ -92,14 +92,18 @@ namespace Grpc.Core.Internal
grpcsharp_server_start(this);
}
public void ShutdownAndNotify(CompletionQueueSafeHandle cq, CompletionCallbackDelegate callback)
public void ShutdownAndNotify(CompletionQueueSafeHandle cq, BatchCompletionDelegate callback)
{
grpcsharp_server_shutdown_and_notify_callback(this, cq, callback);
var ctx = BatchContextSafeHandle.Create();
GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback);
grpcsharp_server_shutdown_and_notify_callback(this, cq, ctx);
}
public void RequestCall(CompletionQueueSafeHandle cq, CompletionCallbackDelegate callback)
public void RequestCall(CompletionQueueSafeHandle cq, BatchCompletionDelegate callback)
{
AssertCallOk(grpcsharp_server_request_call(this, cq, callback));
var ctx = BatchContextSafeHandle.Create();
GrpcEnvironment.CompletionRegistry.RegisterBatchCompletion(ctx, callback);
grpcsharp_server_request_call(this, cq, ctx).CheckOk();
}
protected override bool ReleaseHandle()
@ -113,10 +117,5 @@ namespace Grpc.Core.Internal
{
grpcsharp_server_cancel_all_calls(this);
}
private static void AssertCallOk(GRPCCallError callError)
{
Preconditions.CheckState(callError == GRPCCallError.GRPC_CALL_OK, "Status not GRPC_CALL_OK");
}
}
}

@ -51,7 +51,7 @@ namespace Grpc.Core.Internal
[DllImport("grpc_csharp_ext.dll")]
static extern int gprsharp_sizeof_timespec();
// TODO: revisit this.
// NOTE: on linux 64bit sizeof(gpr_timespec) = 16, on windows 32bit sizeof(gpr_timespec) = 8
// so IntPtr seems to have the right size to work on both.
public System.IntPtr tv_sec;

@ -52,11 +52,6 @@ namespace Grpc.Core
/// </summary>
public const int PickUnusedPort = 0;
// TODO(jtattermusch) : make sure the delegate doesn't get garbage collected while
// native callbacks are in the completion queue.
readonly CompletionCallbackDelegate serverShutdownHandler;
readonly CompletionCallbackDelegate newServerRpcHandler;
readonly ServerSafeHandle handle;
readonly object myLock = new object();
@ -66,11 +61,16 @@ namespace Grpc.Core
bool startRequested;
bool shutdownRequested;
public Server()
/// <summary>
/// Create a new server.
/// </summary>
/// <param name="options">Channel options.</param>
public Server(IEnumerable<ChannelOption> options = null)
{
this.handle = ServerSafeHandle.NewServer(GetCompletionQueue(), IntPtr.Zero);
this.newServerRpcHandler = HandleNewServerRpc;
this.serverShutdownHandler = HandleServerShutdown;
using (var channelArgs = ChannelOptions.CreateChannelArgs(options))
{
this.handle = ServerSafeHandle.NewServer(GetCompletionQueue(), channelArgs);
}
}
/// <summary>
@ -108,7 +108,7 @@ namespace Grpc.Core
/// </summary>
/// <returns>The port on which server will be listening.</returns>
/// <param name="host">the host</param>
/// <param name="port">the port. If zero, , an unused port is chosen automatically.</param>
/// <param name="port">the port. If zero, an unused port is chosen automatically.</param>
public int AddListeningPort(string host, int port, ServerCredentials credentials)
{
Preconditions.CheckNotNull(credentials);
@ -144,7 +144,7 @@ namespace Grpc.Core
shutdownRequested = true;
}
handle.ShutdownAndNotify(GetCompletionQueue(), serverShutdownHandler);
handle.ShutdownAndNotify(GetCompletionQueue(), HandleServerShutdown);
await shutdownTcs.Task;
handle.Dispose();
}
@ -173,7 +173,7 @@ namespace Grpc.Core
shutdownRequested = true;
}
handle.ShutdownAndNotify(GetCompletionQueue(), serverShutdownHandler);
handle.ShutdownAndNotify(GetCompletionQueue(), HandleServerShutdown);
handle.CancelAllCalls();
await shutdownTcs.Task;
handle.Dispose();
@ -208,7 +208,7 @@ namespace Grpc.Core
{
if (!shutdownRequested)
{
handle.RequestCall(GetCompletionQueue(), newServerRpcHandler);
handle.RequestCall(GetCompletionQueue(), HandleNewServerRpc);
}
}
}
@ -236,12 +236,8 @@ namespace Grpc.Core
/// <summary>
/// Handles the native callback.
/// </summary>
private void HandleNewServerRpc(bool success, IntPtr batchContextPtr)
{
try
private void HandleNewServerRpc(bool success, BatchContextSafeHandle ctx)
{
var ctx = new BatchContextSafeHandleNotOwned(batchContextPtr);
// TODO: handle error
CallSafeHandle call = ctx.GetServerRpcNewCall();
@ -255,26 +251,14 @@ namespace Grpc.Core
AllowOneRpc();
}
catch (Exception e)
{
Console.WriteLine("Caught exception in a native handler: " + e);
}
}
/// <summary>
/// Handles native callback.
/// </summary>
private void HandleServerShutdown(bool success, IntPtr batchContextPtr)
{
try
private void HandleServerShutdown(bool success, BatchContextSafeHandle ctx)
{
shutdownTcs.SetResult(null);
}
catch (Exception e)
{
Console.WriteLine("Caught exception in a native handler: " + e);
}
}
private static CompletionQueueSafeHandle GetCompletionQueue()
{

@ -41,7 +41,7 @@ namespace math
{
GrpcEnvironment.Initialize();
using (Channel channel = new Channel("127.0.0.1:23456"))
using (Channel channel = new Channel("127.0.0.1", 23456))
{
Math.IMathClient stub = new Math.MathClient(channel);
MathExamples.DivExample(stub);

@ -60,7 +60,7 @@ namespace math.Tests
server.AddServiceDefinition(Math.BindService(new MathServiceImpl()));
int port = server.AddListeningPort(host, Server.PickUnusedPort);
server.Start();
channel = new Channel(host + ":" + port);
channel = new Channel(host, port);
// TODO(jtattermusch): get rid of the custom header here once we have dedicated tests
// for header support.

@ -104,22 +104,22 @@ namespace Grpc.IntegrationTesting
{
GrpcEnvironment.Initialize();
string addr = string.Format("{0}:{1}", options.serverHost, options.serverPort);
Credentials credentials = null;
if (options.useTls)
{
credentials = TestCredentials.CreateTestClientCredentials(options.useTestCa);
}
ChannelArgs channelArgs = null;
List<ChannelOption> channelOptions = null;
if (!string.IsNullOrEmpty(options.serverHostOverride))
{
channelArgs = ChannelArgs.CreateBuilder()
.AddString(ChannelArgs.SslTargetNameOverrideKey, options.serverHostOverride).Build();
channelOptions = new List<ChannelOption>
{
new ChannelOption(ChannelOptions.SslTargetNameOverride, options.serverHostOverride)
};
}
using (Channel channel = new Channel(addr, credentials, channelArgs))
using (Channel channel = new Channel(options.serverHost, options.serverPort.Value, credentials, channelOptions))
{
var stubConfig = StubConfiguration.Default;
if (options.testCase == "service_account_creds" || options.testCase == "compute_engine_creds")

@ -62,10 +62,11 @@ namespace Grpc.IntegrationTesting
int port = server.AddListeningPort(host, Server.PickUnusedPort, TestCredentials.CreateTestServerCredentials());
server.Start();
var channelArgs = ChannelArgs.CreateBuilder()
.AddString(ChannelArgs.SslTargetNameOverrideKey, TestCredentials.DefaultHostOverride).Build();
channel = new Channel(host + ":" + port, TestCredentials.CreateTestClientCredentials(true), channelArgs);
var options = new List<ChannelOption>
{
new ChannelOption(ChannelOptions.SslTargetNameOverride, TestCredentials.DefaultHostOverride)
};
channel = new Channel(host, port, TestCredentials.CreateTestClientCredentials(true), options);
client = TestService.NewStub(channel);
}

@ -60,13 +60,11 @@
grpc_byte_buffer *string_to_byte_buffer(const char *buffer, size_t len) {
gpr_slice slice = gpr_slice_from_copied_buffer(buffer, len);
grpc_byte_buffer *bb = grpc_byte_buffer_create(&slice, 1);
grpc_byte_buffer *bb = grpc_raw_byte_buffer_create(&slice, 1);
gpr_slice_unref(slice);
return bb;
}
typedef void(GPR_CALLTYPE *callback_funcptr)(gpr_int32 success, void *batch_context);
/*
* Helper to maintain lifetime of batch op inputs and store batch op outputs.
*/
@ -91,13 +89,9 @@ typedef struct gprcsharp_batch_context {
grpc_call_details call_details;
grpc_metadata_array request_metadata;
} server_rpc_new;
/* callback will be called upon completion */
callback_funcptr callback;
} grpcsharp_batch_context;
grpcsharp_batch_context *grpcsharp_batch_context_create() {
GPR_EXPORT grpcsharp_batch_context *GPR_CALLTYPE grpcsharp_batch_context_create() {
grpcsharp_batch_context *ctx = gpr_malloc(sizeof(grpcsharp_batch_context));
memset(ctx, 0, sizeof(grpcsharp_batch_context));
return ctx;
@ -192,7 +186,7 @@ void grpcsharp_metadata_array_move(grpc_metadata_array *dest,
src->metadata = NULL;
}
void grpcsharp_batch_context_destroy(grpcsharp_batch_context *ctx) {
GPR_EXPORT void GPR_CALLTYPE grpcsharp_batch_context_destroy(grpcsharp_batch_context *ctx) {
if (!ctx) {
return;
}
@ -306,25 +300,14 @@ grpcsharp_completion_queue_destroy(grpc_completion_queue *cq) {
grpc_completion_queue_destroy(cq);
}
GPR_EXPORT grpc_completion_type GPR_CALLTYPE
grpcsharp_completion_queue_next_with_callback(grpc_completion_queue *cq) {
grpc_event ev;
grpcsharp_batch_context *batch_context;
grpc_completion_type t;
ev = grpc_completion_queue_next(cq, gpr_inf_future);
t = ev.type;
if (t == GRPC_OP_COMPLETE && ev.tag) {
/* NEW API handler */
batch_context = (grpcsharp_batch_context *)ev.tag;
batch_context->callback((gpr_int32) ev.success, batch_context);
grpcsharp_batch_context_destroy(batch_context);
}
GPR_EXPORT grpc_event GPR_CALLTYPE
grpcsharp_completion_queue_next(grpc_completion_queue *cq) {
return grpc_completion_queue_next(cq, gpr_inf_future);
}
/* return completion type to allow some handling for events that have no
* tag - such as GRPC_QUEUE_SHUTDOWN
*/
return t;
GPR_EXPORT grpc_event GPR_CALLTYPE
grpcsharp_completion_queue_pluck(grpc_completion_queue *cq, void *tag) {
return grpc_completion_queue_pluck(cq, tag, gpr_inf_future);
}
/* Channel */
@ -369,6 +352,16 @@ grpcsharp_channel_args_set_string(grpc_channel_args *args, size_t index,
args->args[index].value.string = gpr_strdup(value);
}
GPR_EXPORT void GPR_CALLTYPE
grpcsharp_channel_args_set_integer(grpc_channel_args *args, size_t index,
const char *key, int value) {
GPR_ASSERT(args);
GPR_ASSERT(index < args->num_args);
args->args[index].type = GRPC_ARG_INTEGER;
args->args[index].key = gpr_strdup(key);
args->args[index].value.integer = value;
}
GPR_EXPORT void GPR_CALLTYPE
grpcsharp_channel_args_destroy(grpc_channel_args *args) {
size_t i;
@ -413,32 +406,34 @@ GPR_EXPORT void GPR_CALLTYPE grpcsharp_call_destroy(grpc_call *call) {
}
GPR_EXPORT grpc_call_error GPR_CALLTYPE
grpcsharp_call_start_unary(grpc_call *call, callback_funcptr callback,
grpcsharp_call_start_unary(grpc_call *call, grpcsharp_batch_context *ctx,
const char *send_buffer, size_t send_buffer_len,
grpc_metadata_array *initial_metadata) {
/* TODO: don't use magic number */
grpc_op ops[6];
grpcsharp_batch_context *ctx = grpcsharp_batch_context_create();
ctx->callback = callback;
ops[0].op = GRPC_OP_SEND_INITIAL_METADATA;
grpcsharp_metadata_array_move(&(ctx->send_initial_metadata),
initial_metadata);
ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count;
ops[0].data.send_initial_metadata.metadata =
ctx->send_initial_metadata.metadata;
ops[0].flags = 0;
ops[1].op = GRPC_OP_SEND_MESSAGE;
ctx->send_message = string_to_byte_buffer(send_buffer, send_buffer_len);
ops[1].data.send_message = ctx->send_message;
ops[1].flags = 0;
ops[2].op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
ops[2].flags = 0;
ops[3].op = GRPC_OP_RECV_INITIAL_METADATA;
ops[3].data.recv_initial_metadata = &(ctx->recv_initial_metadata);
ops[3].flags = 0;
ops[4].op = GRPC_OP_RECV_MESSAGE;
ops[4].data.recv_message = &(ctx->recv_message);
ops[4].flags = 0;
ops[5].op = GRPC_OP_RECV_STATUS_ON_CLIENT;
ops[5].data.recv_status_on_client.trailing_metadata =
@ -450,50 +445,32 @@ grpcsharp_call_start_unary(grpc_call *call, callback_funcptr callback,
&(ctx->recv_status_on_client.status_details);
ops[5].data.recv_status_on_client.status_details_capacity =
&(ctx->recv_status_on_client.status_details_capacity);
ops[5].flags = 0;
return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx);
}
/* Synchronous unary call */
GPR_EXPORT void GPR_CALLTYPE
grpcsharp_call_blocking_unary(grpc_call *call,
grpc_completion_queue *dedicated_cq,
callback_funcptr callback,
const char *send_buffer, size_t send_buffer_len,
grpc_metadata_array *initial_metadata) {
GPR_ASSERT(grpcsharp_call_start_unary(call, callback, send_buffer,
send_buffer_len,
initial_metadata) == GRPC_CALL_OK);
/* TODO: we would like to use pluck, but we don't know the tag */
GPR_ASSERT(grpcsharp_completion_queue_next_with_callback(dedicated_cq) ==
GRPC_OP_COMPLETE);
grpc_completion_queue_shutdown(dedicated_cq);
GPR_ASSERT(grpcsharp_completion_queue_next_with_callback(dedicated_cq) ==
GRPC_QUEUE_SHUTDOWN);
}
GPR_EXPORT grpc_call_error GPR_CALLTYPE
grpcsharp_call_start_client_streaming(grpc_call *call,
callback_funcptr callback,
grpcsharp_batch_context *ctx,
grpc_metadata_array *initial_metadata) {
/* TODO: don't use magic number */
grpc_op ops[4];
grpcsharp_batch_context *ctx = grpcsharp_batch_context_create();
ctx->callback = callback;
ops[0].op = GRPC_OP_SEND_INITIAL_METADATA;
grpcsharp_metadata_array_move(&(ctx->send_initial_metadata),
initial_metadata);
ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count;
ops[0].data.send_initial_metadata.metadata =
ctx->send_initial_metadata.metadata;
ops[0].flags = 0;
ops[1].op = GRPC_OP_RECV_INITIAL_METADATA;
ops[1].data.recv_initial_metadata = &(ctx->recv_initial_metadata);
ops[1].flags = 0;
ops[2].op = GRPC_OP_RECV_MESSAGE;
ops[2].data.recv_message = &(ctx->recv_message);
ops[2].flags = 0;
ops[3].op = GRPC_OP_RECV_STATUS_ON_CLIENT;
ops[3].data.recv_status_on_client.trailing_metadata =
@ -505,33 +482,35 @@ grpcsharp_call_start_client_streaming(grpc_call *call,
&(ctx->recv_status_on_client.status_details);
ops[3].data.recv_status_on_client.status_details_capacity =
&(ctx->recv_status_on_client.status_details_capacity);
ops[3].flags = 0;
return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx);
}
GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_start_server_streaming(
grpc_call *call, callback_funcptr callback, const char *send_buffer,
grpc_call *call, grpcsharp_batch_context *ctx, const char *send_buffer,
size_t send_buffer_len, grpc_metadata_array *initial_metadata) {
/* TODO: don't use magic number */
grpc_op ops[5];
grpcsharp_batch_context *ctx = grpcsharp_batch_context_create();
ctx->callback = callback;
ops[0].op = GRPC_OP_SEND_INITIAL_METADATA;
grpcsharp_metadata_array_move(&(ctx->send_initial_metadata),
initial_metadata);
ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count;
ops[0].data.send_initial_metadata.metadata =
ctx->send_initial_metadata.metadata;
ops[0].flags = 0;
ops[1].op = GRPC_OP_SEND_MESSAGE;
ctx->send_message = string_to_byte_buffer(send_buffer, send_buffer_len);
ops[1].data.send_message = ctx->send_message;
ops[1].flags = 0;
ops[2].op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
ops[2].flags = 0;
ops[3].op = GRPC_OP_RECV_INITIAL_METADATA;
ops[3].data.recv_initial_metadata = &(ctx->recv_initial_metadata);
ops[3].flags = 0;
ops[4].op = GRPC_OP_RECV_STATUS_ON_CLIENT;
ops[4].data.recv_status_on_client.trailing_metadata =
@ -543,28 +522,28 @@ GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_start_server_streaming(
&(ctx->recv_status_on_client.status_details);
ops[4].data.recv_status_on_client.status_details_capacity =
&(ctx->recv_status_on_client.status_details_capacity);
ops[4].flags = 0;
return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx);
}
GPR_EXPORT grpc_call_error GPR_CALLTYPE
grpcsharp_call_start_duplex_streaming(grpc_call *call,
callback_funcptr callback,
grpcsharp_batch_context *ctx,
grpc_metadata_array *initial_metadata) {
/* TODO: don't use magic number */
grpc_op ops[3];
grpcsharp_batch_context *ctx = grpcsharp_batch_context_create();
ctx->callback = callback;
ops[0].op = GRPC_OP_SEND_INITIAL_METADATA;
grpcsharp_metadata_array_move(&(ctx->send_initial_metadata),
initial_metadata);
ops[0].data.send_initial_metadata.count = ctx->send_initial_metadata.count;
ops[0].data.send_initial_metadata.metadata =
ctx->send_initial_metadata.metadata;
ops[0].flags = 0;
ops[1].op = GRPC_OP_RECV_INITIAL_METADATA;
ops[1].data.recv_initial_metadata = &(ctx->recv_initial_metadata);
ops[1].flags = 0;
ops[2].op = GRPC_OP_RECV_STATUS_ON_CLIENT;
ops[2].data.recv_status_on_client.trailing_metadata =
@ -576,85 +555,76 @@ grpcsharp_call_start_duplex_streaming(grpc_call *call,
&(ctx->recv_status_on_client.status_details);
ops[2].data.recv_status_on_client.status_details_capacity =
&(ctx->recv_status_on_client.status_details_capacity);
ops[2].flags = 0;
return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx);
}
GPR_EXPORT grpc_call_error GPR_CALLTYPE
grpcsharp_call_send_message(grpc_call *call, callback_funcptr callback,
grpcsharp_call_send_message(grpc_call *call, grpcsharp_batch_context *ctx,
const char *send_buffer, size_t send_buffer_len) {
/* TODO: don't use magic number */
grpc_op ops[1];
grpcsharp_batch_context *ctx = grpcsharp_batch_context_create();
ctx->callback = callback;
ops[0].op = GRPC_OP_SEND_MESSAGE;
ctx->send_message = string_to_byte_buffer(send_buffer, send_buffer_len);
ops[0].data.send_message = ctx->send_message;
ops[0].flags = 0;
return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx);
}
GPR_EXPORT grpc_call_error GPR_CALLTYPE
grpcsharp_call_send_close_from_client(grpc_call *call,
callback_funcptr callback) {
grpcsharp_batch_context *ctx) {
/* TODO: don't use magic number */
grpc_op ops[1];
grpcsharp_batch_context *ctx = grpcsharp_batch_context_create();
ctx->callback = callback;
ops[0].op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
ops[0].flags = 0;
return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx);
}
GPR_EXPORT grpc_call_error GPR_CALLTYPE
grpcsharp_call_send_status_from_server(grpc_call *call,
callback_funcptr callback,
grpcsharp_batch_context *ctx,
grpc_status_code status_code,
const char *status_details) {
/* TODO: don't use magic number */
grpc_op ops[1];
grpcsharp_batch_context *ctx = grpcsharp_batch_context_create();
ctx->callback = callback;
ops[0].op = GRPC_OP_SEND_STATUS_FROM_SERVER;
ops[0].data.send_status_from_server.status = status_code;
ops[0].data.send_status_from_server.status_details =
gpr_strdup(status_details);
ops[0].data.send_status_from_server.trailing_metadata = NULL;
ops[0].data.send_status_from_server.trailing_metadata_count = 0;
ops[0].flags = 0;
return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx);
}
GPR_EXPORT grpc_call_error GPR_CALLTYPE
grpcsharp_call_recv_message(grpc_call *call, callback_funcptr callback) {
grpcsharp_call_recv_message(grpc_call *call, grpcsharp_batch_context *ctx) {
/* TODO: don't use magic number */
grpc_op ops[1];
grpcsharp_batch_context *ctx = grpcsharp_batch_context_create();
ctx->callback = callback;
ops[0].op = GRPC_OP_RECV_MESSAGE;
ops[0].data.recv_message = &(ctx->recv_message);
ops[0].flags = 0;
return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx);
}
GPR_EXPORT grpc_call_error GPR_CALLTYPE
grpcsharp_call_start_serverside(grpc_call *call, callback_funcptr callback) {
grpcsharp_call_start_serverside(grpc_call *call, grpcsharp_batch_context *ctx) {
/* TODO: don't use magic number */
grpc_op ops[2];
grpcsharp_batch_context *ctx = grpcsharp_batch_context_create();
ctx->callback = callback;
ops[0].op = GRPC_OP_SEND_INITIAL_METADATA;
ops[0].data.send_initial_metadata.count = 0;
ops[0].data.send_initial_metadata.metadata = NULL;
ops[0].flags = 0;
ops[1].op = GRPC_OP_RECV_CLOSE_ON_SERVER;
ops[1].data.recv_close_on_server.cancelled =
(&ctx->recv_close_on_server_cancelled);
ops[1].flags = 0;
return grpc_call_start_batch(call, ops, sizeof(ops) / sizeof(ops[0]), ctx);
}
@ -681,9 +651,7 @@ GPR_EXPORT void GPR_CALLTYPE grpcsharp_server_start(grpc_server *server) {
GPR_EXPORT void GPR_CALLTYPE
grpcsharp_server_shutdown_and_notify_callback(grpc_server *server,
grpc_completion_queue *cq,
callback_funcptr callback) {
grpcsharp_batch_context *ctx = grpcsharp_batch_context_create();
ctx->callback = callback;
grpcsharp_batch_context *ctx) {
grpc_server_shutdown_and_notify(server, cq, ctx);
}
@ -697,10 +665,7 @@ GPR_EXPORT void GPR_CALLTYPE grpcsharp_server_destroy(grpc_server *server) {
GPR_EXPORT grpc_call_error GPR_CALLTYPE
grpcsharp_server_request_call(grpc_server *server, grpc_completion_queue *cq,
callback_funcptr callback) {
grpcsharp_batch_context *ctx = grpcsharp_batch_context_create();
ctx->callback = callback;
grpcsharp_batch_context *ctx) {
return grpc_server_request_call(
server, &(ctx->server_rpc_new.call), &(ctx->server_rpc_new.call_details),
&(ctx->server_rpc_new.request_metadata), cq, cq, ctx);
@ -789,11 +754,18 @@ GPR_EXPORT void GPR_CALLTYPE grpcsharp_redirect_log(grpcsharp_log_func func) {
gpr_set_log_function(grpcsharp_log_handler);
}
typedef void(GPR_CALLTYPE *test_callback_funcptr)(gpr_int32 success);
/* For testing */
GPR_EXPORT void GPR_CALLTYPE
grpcsharp_test_callback(callback_funcptr callback) {
callback(1, NULL);
grpcsharp_test_callback(test_callback_funcptr callback) {
callback(1);
}
/* For testing */
GPR_EXPORT void *GPR_CALLTYPE grpcsharp_test_nop(void *ptr) { return ptr; }
/* For testing */
GPR_EXPORT gpr_int32 GPR_CALLTYPE grpcsharp_sizeof_grpc_event(void) {
return sizeof(grpc_event);
}

@ -57,7 +57,7 @@ grpc_byte_buffer *BufferToByteBuffer(Handle<Value> buffer) {
char *data = ::node::Buffer::Data(buffer);
gpr_slice slice = gpr_slice_malloc(length);
memcpy(GPR_SLICE_START_PTR(slice), data, length);
grpc_byte_buffer *byte_buffer(grpc_byte_buffer_create(&slice, 1));
grpc_byte_buffer *byte_buffer(grpc_raw_byte_buffer_create(&slice, 1));
gpr_slice_unref(slice);
return byte_buffer;
}

@ -550,6 +550,7 @@ NAN_METHOD(Call::StartBatch) {
}
uint32_t type = keys->Get(i)->Uint32Value();
ops[i].op = static_cast<grpc_op_type>(type);
ops[i].flags = 0;
switch (type) {
case GRPC_OP_SEND_INITIAL_METADATA:
op.reset(new SendMetadataOp());

@ -1,6 +1,6 @@
{
"name": "grpc",
"version": "0.9.0",
"version": "0.9.1",
"author": "Google Inc.",
"description": "gRPC Library for Node",
"homepage": "http://www.grpc.io/",

@ -47,7 +47,9 @@ function deserializeCls(cls) {
* @return {cls} The resulting object
*/
return function deserialize(arg_buf) {
return cls.decode(arg_buf).toRaw();
// Convert to a native object with binary fields as Buffers (first argument)
// and longs as strings (second argument)
return cls.decode(arg_buf).toRaw(false, true);
};
}

@ -0,0 +1,90 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
'use strict';
var assert = require('assert');
var common = require('../src/common.js');
var ProtoBuf = require('protobufjs');
var messages_proto = ProtoBuf.loadProtoFile(
__dirname + '/test_messages.proto').build();
describe('Proto message serialize and deserialize', function() {
var longSerialize = common.serializeCls(messages_proto.LongValues);
var longDeserialize = common.deserializeCls(messages_proto.LongValues);
var pos_value = '314159265358979';
var neg_value = '-27182818284590';
it('should preserve positive int64 values', function() {
var serialized = longSerialize({int_64: pos_value});
assert.strictEqual(longDeserialize(serialized).int_64.toString(),
pos_value);
});
it('should preserve negative int64 values', function() {
var serialized = longSerialize({int_64: neg_value});
assert.strictEqual(longDeserialize(serialized).int_64.toString(),
neg_value);
});
it('should preserve uint64 values', function() {
var serialized = longSerialize({uint_64: pos_value});
assert.strictEqual(longDeserialize(serialized).uint_64.toString(),
pos_value);
});
it('should preserve positive sint64 values', function() {
var serialized = longSerialize({sint_64: pos_value});
assert.strictEqual(longDeserialize(serialized).sint_64.toString(),
pos_value);
});
it('should preserve negative sint64 values', function() {
var serialized = longSerialize({sint_64: neg_value});
assert.strictEqual(longDeserialize(serialized).sint_64.toString(),
neg_value);
});
it('should preserve fixed64 values', function() {
var serialized = longSerialize({fixed_64: pos_value});
assert.strictEqual(longDeserialize(serialized).fixed_64.toString(),
pos_value);
});
it('should preserve positive sfixed64 values', function() {
var serialized = longSerialize({sfixed_64: pos_value});
assert.strictEqual(longDeserialize(serialized).sfixed_64.toString(),
pos_value);
});
it('should preserve negative sfixed64 values', function() {
var serialized = longSerialize({sfixed_64: neg_value});
assert.strictEqual(longDeserialize(serialized).sfixed_64.toString(),
neg_value);
});
});

@ -0,0 +1,38 @@
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
message LongValues {
int64 int_64 = 1;
uint64 uint_64 = 2;
sint64 sint_64 = 3;
fixed64 fixed_64 = 4;
sfixed64 sfixed_64 = 5;
}

@ -31,43 +31,55 @@
*
*/
// The gRPC protocol is an RPC protocol on top of HTTP2.
//
// While the most common type of RPC receives only one request message and returns only one response
// message, the protocol also supports RPCs that return multiple individual messages in a streaming
// fashion, RPCs that accept a stream of request messages, or RPCs with both streaming requests and
// responses.
//
// Conceptually, each gRPC call consists of a bidirectional stream of binary messages, with RPCs of
// the "non-streaming type" sending only one message in the corresponding direction (the protocol
// doesn't make any distinction).
//
// Each RPC uses a different HTTP2 stream, and thus multiple simultaneous RPCs can be multiplexed
// transparently on the same TCP connection.
#import <Foundation/Foundation.h>
#import <gRPC/GRXWriter.h>
@class GRPCMethodName;
@class GRPCCall;
// Key used in |NSError|'s |userInfo| dictionary to store the response metadata sent by the server.
extern id const kGRPCStatusMetadataKey;
// The gRPC protocol is an RPC protocol on top of HTTP2.
//
// While the most common type of RPC receives only one request message and
// returns only one response message, the protocol also supports RPCs that
// return multiple individual messages in a streaming fashion, RPCs that
// accept a stream of request messages, or RPCs with both streaming requests
// and responses.
//
// Conceptually, each gRPC call consists of a bidirectional stream of binary
// messages, with RPCs of the "non-streaming type" sending only one message in
// the corresponding direction (the protocol doesn't make any distinction).
//
// Each RPC uses a different HTTP2 stream, and thus multiple simultaneous RPCs
// can be multiplexed transparently on the same TCP connection.
// Represents a single gRPC remote call.
@interface GRPCCall : NSObject<GRXWriter>
// These HTTP2 headers will be passed to the server as part of this call. Each
// HTTP2 header is a name-value pair with string names and either string or binary values.
// These HTTP headers will be passed to the server as part of this call. Each HTTP header is a
// name-value pair with string names and either string or binary values.
//
// The passed dictionary has to use NSString keys, corresponding to the header names. The
// value associated to each can be a NSString object or a NSData object. E.g.:
//
// call.requestMetadata = @{
// @"Authorization": @"Bearer ...",
// @"SomeBinaryHeader": someData
// };
// call.requestMetadata = @{@"Authorization": @"Bearer ..."};
//
// call.requestMetadata[@"SomeBinaryHeader"] = someData;
//
// After the call is started, modifying this won't have any effect.
@property(nonatomic, readwrite) NSMutableDictionary *requestMetadata;
//
// For convenience, the property is initialized to an empty NSMutableDictionary, and the setter
// accepts (and copies) both mutable and immutable dictionaries.
- (NSMutableDictionary *)requestMetadata; // nonatomic
- (void)setRequestMetadata:(NSDictionary *)requestMetadata; // nonatomic, copy
// This isn't populated until the first event is delivered to the handler.
// This dictionary is populated with the HTTP headers received from the server. When the RPC ends,
// the HTTP trailers received are added to the dictionary too. It has the same structure as the
// request metadata dictionary.
//
// The first time this object calls |writeValue| on the writeable passed to |startWithWriteable|,
// the |responseMetadata| dictionary already contains the response headers. When it calls
// |writesFinishedWithError|, the dictionary contains both the response headers and trailers.
@property(atomic, readonly) NSDictionary *responseMetadata;
// The request writer has to write NSData objects into the provided Writeable. The server will

@ -46,9 +46,9 @@
#import "private/NSDictionary+GRPC.h"
#import "private/NSError+GRPC.h"
NSString * const kGRPCStatusMetadataKey = @"io.grpc.StatusMetadataKey";
@interface GRPCCall () <GRXWriteable>
// Makes it readwrite.
@property(atomic, strong) NSDictionary *responseMetadata;
@end
// The following methods of a C gRPC call object aren't reentrant, and thus
@ -82,6 +82,9 @@
// correct ordering.
GRPCDelegateWrapper *_responseWriteable;
id<GRXWriter> _requestWriter;
NSMutableDictionary *_requestMetadata;
NSMutableDictionary *_responseMetadata;
}
@synthesize state = _state;
@ -116,10 +119,27 @@
_callQueue = dispatch_queue_create("org.grpc.call", NULL);
_requestWriter = requestWriter;
_requestMetadata = [NSMutableDictionary dictionary];
_responseMetadata = [NSMutableDictionary dictionary];
}
return self;
}
#pragma mark Metadata
- (NSMutableDictionary *)requestMetadata {
return _requestMetadata;
}
- (void)setRequestMetadata:(NSDictionary *)requestMetadata {
_requestMetadata = [NSMutableDictionary dictionaryWithDictionary:requestMetadata];
}
- (NSDictionary *)responseMetadata {
return _responseMetadata;
}
#pragma mark Finish
- (void)finishWithError:(NSError *)errorOrNil {
@ -277,7 +297,7 @@
// The first one (metadataHandler), when the response headers are received.
// The second one (completionHandler), whenever the RPC finishes for any reason.
- (void)invokeCallWithMetadataHandler:(void(^)(NSDictionary *))metadataHandler
completionHandler:(void(^)(NSError *))completionHandler {
completionHandler:(void(^)(NSError *, NSDictionary *))completionHandler {
// TODO(jcanizales): Add error handlers for async failures
[_wrappedCall startBatchWithOperations:@[[[GRPCOpRecvMetadata alloc]
initWithHandler:metadataHandler]]];
@ -287,16 +307,26 @@
- (void)invokeCall {
__weak GRPCCall *weakSelf = self;
[self invokeCallWithMetadataHandler:^(NSDictionary *metadata) {
// Response metadata received.
[self invokeCallWithMetadataHandler:^(NSDictionary *headers) {
// Response headers received.
GRPCCall *strongSelf = weakSelf;
if (strongSelf) {
strongSelf.responseMetadata = metadata;
[strongSelf->_responseMetadata addEntriesFromDictionary:headers];
[strongSelf startNextRead];
}
} completionHandler:^(NSError *error) {
// TODO(jcanizales): Merge HTTP2 trailers into response metadata.
[weakSelf finishWithError:error];
} completionHandler:^(NSError *error, NSDictionary *trailers) {
GRPCCall *strongSelf = weakSelf;
if (strongSelf) {
[strongSelf->_responseMetadata addEntriesFromDictionary:trailers];
if (error) {
NSMutableDictionary *userInfo =
[NSMutableDictionary dictionaryWithDictionary:error.userInfo];
userInfo[kGRPCStatusMetadataKey] = strongSelf->_responseMetadata;
error = [NSError errorWithDomain:error.domain code:error.code userInfo:userInfo];
}
[strongSelf finishWithError:error];
}
}];
// Now that the RPC has been initiated, request writes can start.
[_requestWriter startWithWriteable:self];

@ -79,7 +79,7 @@ typedef void(^GRPCCompletionHandler)(NSDictionary *);
@interface GRPCOpRecvStatus : NSObject <GRPCOp>
- (instancetype)initWithHandler:(void(^)(NSError *))handler NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithHandler:(void(^)(NSError *, NSDictionary *))handler NS_DESIGNATED_INITIALIZER;
@end

@ -165,9 +165,7 @@
}
- (void)finish {
NSDictionary *metadata = [NSDictionary
grpc_dictionaryFromMetadata:_recvInitialMetadata.metadata
count:_recvInitialMetadata.count];
NSDictionary *metadata = [NSDictionary grpc_dictionaryFromMetadataArray:_recvInitialMetadata];
if (_handler) {
_handler(metadata);
}
@ -209,41 +207,44 @@
@end
@implementation GRPCOpRecvStatus{
void(^_handler)(NSError *);
void(^_handler)(NSError *, NSDictionary *);
grpc_status_code _statusCode;
char *_details;
size_t _detailsCapacity;
grpc_status _status;
grpc_metadata_array _metadata;
}
- (instancetype) init {
return [self initWithHandler:nil];
}
- (instancetype) initWithHandler:(void (^)(NSError *))handler {
- (instancetype) initWithHandler:(void (^)(NSError *, NSDictionary *))handler {
if (self = [super init]) {
_handler = handler;
grpc_metadata_array_init(&_status.metadata);
grpc_metadata_array_init(&_metadata);
}
return self;
}
- (void)getOp:(grpc_op *)op {
op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
op->data.recv_status_on_client.status = &_status.status;
op->data.recv_status_on_client.status_details = &_status.details;
op->data.recv_status_on_client.status = &_statusCode;
op->data.recv_status_on_client.status_details = &_details;
op->data.recv_status_on_client.status_details_capacity = &_detailsCapacity;
op->data.recv_status_on_client.trailing_metadata = &_status.metadata;
op->data.recv_status_on_client.trailing_metadata = &_metadata;
}
- (void)finish {
if (_handler) {
NSError *error = [NSError grpc_errorFromStatus:&_status];
_handler(error);
NSError *error = [NSError grpc_errorFromStatusCode:_statusCode details:_details];
NSDictionary *trailers = [NSDictionary grpc_dictionaryFromMetadataArray:_metadata];
_handler(error, trailers);
}
}
- (void)dealloc {
grpc_metadata_array_destroy(&_status.metadata);
gpr_free(_status.details);
grpc_metadata_array_destroy(&_metadata);
gpr_free(_details);
}
@end

@ -55,7 +55,7 @@ static void CopyByteBufferToCharArray(grpc_byte_buffer *buffer, char *array) {
static grpc_byte_buffer *CopyCharArrayToNewByteBuffer(const char *array,
size_t length) {
gpr_slice slice = gpr_slice_from_copied_buffer(array, length);
grpc_byte_buffer *buffer = grpc_byte_buffer_create(&slice, 1);
grpc_byte_buffer *buffer = grpc_raw_byte_buffer_create(&slice, 1);
gpr_slice_unref(slice);
return buffer;
}
@ -85,7 +85,7 @@ static grpc_byte_buffer *CopyCharArrayToNewByteBuffer(const char *array,
// The following implementation is thus not optimal, sometimes requiring two
// copies (one by self.bytes and another by gpr_slice_from_copied_buffer).
// If it turns out to be an issue, we can use enumerateByteRangesUsingblock:
// to create an array of gpr_slice objects to pass to grpc_byte_buffer_create.
// to create an array of gpr_slice objects to pass to grpc_raw_byte_buffer_create.
// That would make it do exactly one copy, always.
return CopyCharArrayToNewByteBuffer((const char *)self.bytes, (size_t)self.length);
}

@ -35,6 +35,7 @@
#include <grpc/grpc.h>
@interface NSDictionary (GRPC)
+ (instancetype)grpc_dictionaryFromMetadata:(struct grpc_metadata *)entries count:(size_t)count;
+ (instancetype)grpc_dictionaryFromMetadataArray:(grpc_metadata_array)array;
+ (instancetype)grpc_dictionaryFromMetadata:(grpc_metadata *)entries count:(size_t)count;
- (grpc_metadata *)grpc_metadataArray;
@end

@ -98,14 +98,18 @@
#pragma mark Category for metadata arrays
@implementation NSDictionary (GRPC)
+ (instancetype)grpc_dictionaryFromMetadataArray:(grpc_metadata_array)array {
return [self grpc_dictionaryFromMetadata:array.metadata count:array.count];
}
+ (instancetype)grpc_dictionaryFromMetadata:(grpc_metadata *)entries count:(size_t)count {
NSMutableDictionary *metadata = [NSMutableDictionary dictionaryWithCapacity:count];
for (grpc_metadata *entry = entries; entry < entries + count; entry++) {
// TODO(jcanizales): Verify in a C library test that it's converting header names to lower case
// automatically.
NSString *name = [NSString stringWithCString:entry->key encoding:NSASCIIStringEncoding];
if (!name) {
// log?
if (!name || metadata[name]) {
// Log if name is nil?
continue;
}
id value;
@ -115,10 +119,7 @@
} else {
value = [NSString grpc_stringFromMetadataValue:entry];
}
if (!metadata[name]) {
metadata[name] = [NSMutableArray array];
}
[metadata[name] addObject:value];
metadata[name] = value;
}
return metadata;
}

@ -32,6 +32,7 @@
*/
#import <Foundation/Foundation.h>
#include <grpc/grpc.h>
// TODO(jcanizales): Make the domain string public.
extern NSString *const kGRPCErrorDomain;
@ -56,17 +57,8 @@ typedef NS_ENUM(NSInteger, GRPCErrorCode) {
GRPCErrorCodeDataLoss = 15
};
// TODO(jcanizales): This is conflating trailing metadata with Status details. Fix it once there's
// a decision on how to codify Status.
#include <grpc/grpc.h>
typedef struct grpc_status {
grpc_status_code status;
char *details;
grpc_metadata_array metadata;
} grpc_status;
@interface NSError (GRPC)
// Returns nil if the status is OK. Otherwise, a NSError whose code is one of
// GRPCErrorCode and whose domain is kGRPCErrorDomain.
+ (instancetype)grpc_errorFromStatus:(struct grpc_status *)status;
// Returns nil if the status code is OK. Otherwise, a NSError whose code is one of |GRPCErrorCode|
// and whose domain is |kGRPCErrorDomain|.
+ (instancetype)grpc_errorFromStatusCode:(grpc_status_code)statusCode details:(char *)details;
@end

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save