aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Craig Tiller <ctiller@google.com>2015-02-05 11:42:58 -0800
committerGravatar Craig Tiller <ctiller@google.com>2015-02-05 11:42:58 -0800
commit86d9159356d5d23981f122c3bd4ad912203666ca (patch)
tree1cf0b4622ada6275370277a748438102e44188f4
parent24fc2c4c5ddc56a678f17170ac4eec4ec570c780 (diff)
parent6b9afb153a82c921c7e80365a4e129c462c0ebad (diff)
Merge github.com:google/grpc into async-api-new
-rw-r--r--Makefile2
-rw-r--r--build.json2
-rw-r--r--include/grpc/support/time.h29
-rw-r--r--include/grpc/support/time_posix.h43
-rw-r--r--include/grpc/support/time_win32.h46
-rw-r--r--src/core/channel/client_channel.c5
-rw-r--r--src/core/iomgr/alarm.c4
-rw-r--r--src/core/iomgr/alarm_internal.h9
-rw-r--r--src/core/iomgr/resolve_address.c2
-rw-r--r--src/core/iomgr/socket_utils_linux.c2
-rw-r--r--src/core/iomgr/socket_utils_posix.c1
-rw-r--r--src/core/iomgr/tcp_server_posix.c6
-rw-r--r--src/core/support/log_linux.c6
-rw-r--r--src/core/support/log_posix.c7
-rw-r--r--src/core/support/string_posix.c3
-rw-r--r--src/core/support/sync_posix.c11
-rw-r--r--src/core/support/time.c16
-rw-r--r--src/core/support/time_posix.c25
-rw-r--r--src/core/surface/byte_buffer_queue.c12
-rw-r--r--src/core/surface/call.c27
-rw-r--r--src/core/surface/channel.c11
-rw-r--r--src/core/surface/channel.h2
-rw-r--r--src/core/surface/client.c3
-rw-r--r--src/core/surface/lame_client.c3
-rw-r--r--src/core/surface/server.c2
-rw-r--r--src/core/transport/chttp2_transport.c34
-rw-r--r--src/core/transport/metadata.c2
-rw-r--r--src/csharp/Grpc.sln38
-rw-r--r--src/csharp/GrpcApi/.gitignore2
-rw-r--r--src/csharp/GrpcApi/DummyMathServiceClient.cs74
-rw-r--r--src/csharp/GrpcApi/Examples.cs97
-rw-r--r--src/csharp/GrpcApi/GrpcApi.csproj64
-rw-r--r--src/csharp/GrpcApi/IMathServiceClient.cs26
-rw-r--r--src/csharp/GrpcApi/Math.cs1531
-rw-r--r--src/csharp/GrpcApi/MathServiceClientStub.cs75
-rw-r--r--src/csharp/GrpcApi/Messages.cs35
-rw-r--r--src/csharp/GrpcApi/Properties/AssemblyInfo.cs22
-rw-r--r--src/csharp/GrpcApi/RecordingObserver.cs32
-rwxr-xr-xsrc/csharp/GrpcApi/math.proto50
-rw-r--r--src/csharp/GrpcCore/.gitignore1
-rw-r--r--src/csharp/GrpcCore/Call.cs69
-rw-r--r--src/csharp/GrpcCore/Calls.cs85
-rw-r--r--src/csharp/GrpcCore/Channel.cs59
-rw-r--r--src/csharp/GrpcCore/ClientStreamingAsyncResult.cs37
-rw-r--r--src/csharp/GrpcCore/GrpcCore.csproj62
-rw-r--r--src/csharp/GrpcCore/GrpcEnvironment.cs91
-rw-r--r--src/csharp/GrpcCore/Internal/AsyncCall.cs485
-rw-r--r--src/csharp/GrpcCore/Internal/CallSafeHandle.cs182
-rw-r--r--src/csharp/GrpcCore/Internal/ChannelSafeHandle.cs34
-rw-r--r--src/csharp/GrpcCore/Internal/CompletionQueueSafeHandle.cs66
-rw-r--r--src/csharp/GrpcCore/Internal/Enums.cs75
-rw-r--r--src/csharp/GrpcCore/Internal/Event.cs191
-rw-r--r--src/csharp/GrpcCore/Internal/GrpcThreadPool.cs129
-rw-r--r--src/csharp/GrpcCore/Internal/SafeHandleZeroIsInvalid.cs28
-rw-r--r--src/csharp/GrpcCore/Internal/ServerSafeHandle.cs76
-rw-r--r--src/csharp/GrpcCore/Internal/StreamingInputObserver.cs33
-rw-r--r--src/csharp/GrpcCore/Internal/Timespec.cs67
-rw-r--r--src/csharp/GrpcCore/Properties/AssemblyInfo.cs24
-rw-r--r--src/csharp/GrpcCore/RpcException.cs27
-rw-r--r--src/csharp/GrpcCore/Server.cs141
-rw-r--r--src/csharp/GrpcCore/Status.cs36
-rw-r--r--src/csharp/GrpcCore/StatusCode.cs150
-rw-r--r--src/csharp/GrpcCoreTests/.gitignore2
-rw-r--r--src/csharp/GrpcCoreTests/ClientServerTest.cs48
-rw-r--r--src/csharp/GrpcCoreTests/GrpcCoreTests.csproj53
-rw-r--r--src/csharp/GrpcCoreTests/GrpcEnvironmentTest.cs18
-rw-r--r--src/csharp/GrpcCoreTests/Properties/AssemblyInfo.cs22
-rw-r--r--src/csharp/GrpcCoreTests/ServerTest.cs21
-rw-r--r--src/csharp/GrpcCoreTests/TestResult.xml41
-rw-r--r--src/csharp/GrpcCoreTests/TimespecTest.cs43
-rw-r--r--src/csharp/GrpcCoreTests/Utils.cs51
-rw-r--r--src/csharp/GrpcDemo/.gitignore1
-rw-r--r--src/csharp/GrpcDemo/GrpcDemo.csproj52
-rw-r--r--src/csharp/GrpcDemo/Program.cs28
-rw-r--r--src/csharp/GrpcDemo/Properties/AssemblyInfo.cs22
-rwxr-xr-xsrc/csharp/README.md4
-rwxr-xr-xsrc/csharp/lib/Google.ProtocolBuffers.dllbin0 -> 380416 bytes
-rw-r--r--src/node/README.md72
-rw-r--r--src/node/ext/timeval.cc5
-rw-r--r--test/core/echo/echo_test.c3
-rw-r--r--test/core/fling/fling_stream_test.c3
-rw-r--r--test/core/fling/fling_test.c3
-rw-r--r--test/core/json/json_test.c3
-rwxr-xr-xtools/gce_setup/builder.sh29
-rwxr-xr-xtools/gce_setup/cloud_prod_runner.sh4
-rwxr-xr-xtools/gce_setup/grpc_docker.sh22
-rwxr-xr-xtools/gce_setup/interop_test_runner.sh42
-rw-r--r--tools/gce_setup/post.html12
-rw-r--r--tools/gce_setup/pre.html14
-rwxr-xr-xtools/run_tests/run_tests.py5
-rw-r--r--tools/tsan_suppressions.txt2
-rw-r--r--vsprojects/vs2013/gpr.vcxproj2
-rw-r--r--vsprojects/vs2013/gpr.vcxproj.filters6
93 files changed, 4904 insertions, 238 deletions
diff --git a/Makefile b/Makefile
index 06a9467e70..527b67ea94 100644
--- a/Makefile
+++ b/Makefile
@@ -1670,8 +1670,6 @@ PUBLIC_HEADERS_C += \
include/grpc/support/sync_win32.h \
include/grpc/support/thd.h \
include/grpc/support/time.h \
- include/grpc/support/time_posix.h \
- include/grpc/support/time_win32.h \
include/grpc/support/useful.h \
LIBGPR_OBJS = $(addprefix objs/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBGPR_SRC))))
diff --git a/build.json b/build.json
index 9248d47b29..2c903bc992 100644
--- a/build.json
+++ b/build.json
@@ -225,8 +225,6 @@
"include/grpc/support/sync_win32.h",
"include/grpc/support/thd.h",
"include/grpc/support/time.h",
- "include/grpc/support/time_posix.h",
- "include/grpc/support/time_win32.h",
"include/grpc/support/useful.h"
],
"headers": [
diff --git a/include/grpc/support/time.h b/include/grpc/support/time.h
index 6327a2cffb..9fb1d0bc97 100644
--- a/include/grpc/support/time.h
+++ b/include/grpc/support/time.h
@@ -34,31 +34,22 @@
#ifndef __GRPC_SUPPORT_TIME_H__
#define __GRPC_SUPPORT_TIME_H__
/* Time support.
- We use gpr_timespec, which is typedefed to struct timespec on platforms which
- have it. On some machines, absolute times may be in local time. */
-
-/* Platform specific header declares gpr_timespec.
- gpr_timespec contains:
- time_t tv_sec; // seconds since start of 1970
- int tv_nsec; // nanoseconds; always in 0..999999999; never negative.
- */
+ We use gpr_timespec, which is analogous to struct timespec. On some
+ machines, absolute times may be in local time. */
#include <grpc/support/port_platform.h>
-
-#if defined(GPR_POSIX_TIME)
-#include <grpc/support/time_posix.h>
-#elif defined(GPR_WIN32)
-#include <grpc/support/time_win32.h>
-#else
-#error could not determine platform for time
-#endif
-
#include <stddef.h>
+#include <time.h>
#ifdef __cplusplus
extern "C" {
#endif
+typedef struct gpr_timespec {
+ time_t tv_sec;
+ int tv_nsec;
+} gpr_timespec;
+
/* Time constants. */
extern const gpr_timespec gpr_time_0; /* The zero time interval. */
extern const gpr_timespec gpr_inf_future; /* The far future */
@@ -103,10 +94,6 @@ int gpr_time_similar(gpr_timespec a, gpr_timespec b, gpr_timespec threshold);
/* Sleep until at least 'until' - an absolute timeout */
void gpr_sleep_until(gpr_timespec until);
-struct timeval gpr_timeval_from_timespec(gpr_timespec t);
-
-gpr_timespec gpr_timespec_from_timeval(struct timeval t);
-
double gpr_timespec_to_micros(gpr_timespec t);
#ifdef __cplusplus
diff --git a/include/grpc/support/time_posix.h b/include/grpc/support/time_posix.h
deleted file mode 100644
index 9ff6f7f493..0000000000
--- a/include/grpc/support/time_posix.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- *
- * Copyright 2014, 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.
- *
- */
-
-#ifndef __GRPC_SUPPORT_TIME_POSIX_H__
-#define __GRPC_SUPPORT_TIME_POSIX_H__
-/* Posix variant of gpr_time_platform.h */
-
-#include <sys/time.h>
-#include <time.h>
-
-typedef struct timespec gpr_timespec;
-
-#endif /* __GRPC_SUPPORT_TIME_POSIX_H__ */
diff --git a/include/grpc/support/time_win32.h b/include/grpc/support/time_win32.h
deleted file mode 100644
index e62ad64b8f..0000000000
--- a/include/grpc/support/time_win32.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- *
- * Copyright 2014, 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.
- *
- */
-
-#ifndef __GRPC_SUPPORT_TIME_WIN32_H__
-#define __GRPC_SUPPORT_TIME_WIN32_H__
-/* Win32 variant of gpr_time_platform.h */
-
-#include <Winsock.h>
-#include <time.h>
-
-typedef struct gpr_timespec {
- time_t tv_sec;
- long tv_nsec;
-} gpr_timespec;
-
-#endif /* __GRPC_SUPPORT_TIME_WIN32_H__ */
diff --git a/src/core/channel/client_channel.c b/src/core/channel/client_channel.c
index bcb024f2ac..507b91b8a6 100644
--- a/src/core/channel/client_channel.c
+++ b/src/core/channel/client_channel.c
@@ -298,6 +298,7 @@ static void channel_op(grpc_channel_element *elem,
grpc_channel_element *from_elem, grpc_channel_op *op) {
channel_data *chand = elem->channel_data;
grpc_child_channel *child_channel;
+ grpc_channel_op rop;
GPR_ASSERT(elem->filter == &grpc_client_channel_filter);
switch (op->type) {
@@ -323,6 +324,10 @@ static void channel_op(grpc_channel_element *elem,
if (child_channel) {
grpc_child_channel_destroy(child_channel, 1);
}
+ /* fake a transport closed to satisfy the refcounting in client */
+ rop.type = GRPC_TRANSPORT_CLOSED;
+ rop.dir = GRPC_CALL_UP;
+ grpc_channel_next_op(elem, &rop);
break;
case GRPC_TRANSPORT_GOAWAY:
/* receiving goaway: if it's from our active child, drop the active child;
diff --git a/src/core/iomgr/alarm.c b/src/core/iomgr/alarm.c
index 5b80368e3a..7884b21a1e 100644
--- a/src/core/iomgr/alarm.c
+++ b/src/core/iomgr/alarm.c
@@ -335,10 +335,6 @@ static int run_some_expired_alarms(gpr_mu *drop_mu, gpr_timespec now,
gpr_mu_unlock(&g_mu);
gpr_mu_unlock(&g_checker_mu);
- } else if (next) {
- gpr_mu_lock(&g_mu);
- *next = gpr_time_min(*next, g_shard_queue[0]->min_deadline);
- gpr_mu_unlock(&g_mu);
}
if (n && drop_mu) {
diff --git a/src/core/iomgr/alarm_internal.h b/src/core/iomgr/alarm_internal.h
index 5c6b869302..8503292fd1 100644
--- a/src/core/iomgr/alarm_internal.h
+++ b/src/core/iomgr/alarm_internal.h
@@ -39,6 +39,15 @@
/* iomgr internal api for dealing with alarms */
+/* Check for alarms to be run, and run them.
+ Return non zero if alarm callbacks were executed.
+ Drops drop_mu if it is non-null before executing callbacks.
+ If next is non-null, TRY to update *next with the next running alarm
+ IF that alarm occurs before *next current value.
+ *next is never guaranteed to be updated on any given execution; however,
+ with high probability at least one thread in the system will see an update
+ at any time slice. */
+
int grpc_alarm_check(gpr_mu *drop_mu, gpr_timespec now, gpr_timespec *next);
void grpc_alarm_list_init(gpr_timespec now);
diff --git a/src/core/iomgr/resolve_address.c b/src/core/iomgr/resolve_address.c
index 01681168ce..575f884d91 100644
--- a/src/core/iomgr/resolve_address.c
+++ b/src/core/iomgr/resolve_address.c
@@ -31,7 +31,9 @@
*
*/
+#ifndef _POSIX_SOURCE
#define _POSIX_SOURCE
+#endif
#include "src/core/iomgr/sockaddr.h"
#include "src/core/iomgr/resolve_address.h"
diff --git a/src/core/iomgr/socket_utils_linux.c b/src/core/iomgr/socket_utils_linux.c
index f971cb33bc..7ef58940c2 100644
--- a/src/core/iomgr/socket_utils_linux.c
+++ b/src/core/iomgr/socket_utils_linux.c
@@ -31,7 +31,9 @@
*
*/
+#ifndef _GNU_SOURCE
#define _GNU_SOURCE
+#endif
#include <grpc/support/port_platform.h>
#ifdef GPR_LINUX
diff --git a/src/core/iomgr/socket_utils_posix.c b/src/core/iomgr/socket_utils_posix.c
index 06c5033d45..9184b2a47c 100644
--- a/src/core/iomgr/socket_utils_posix.c
+++ b/src/core/iomgr/socket_utils_posix.c
@@ -35,7 +35,6 @@
#ifdef GPR_POSIX_SOCKETUTILS
-#define _BSD_SOURCE
#include "src/core/iomgr/socket_utils_posix.h"
#include <fcntl.h>
diff --git a/src/core/iomgr/tcp_server_posix.c b/src/core/iomgr/tcp_server_posix.c
index d169d23271..091f0aab1a 100644
--- a/src/core/iomgr/tcp_server_posix.c
+++ b/src/core/iomgr/tcp_server_posix.c
@@ -31,11 +31,15 @@
*
*/
+/* FIXME: "posix" files shouldn't be depending on _GNU_SOURCE */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
#include <grpc/support/port_platform.h>
#ifdef GPR_POSIX_SOCKET
-#define _GNU_SOURCE
#include "src/core/iomgr/tcp_server.h"
#include <limits.h>
diff --git a/src/core/support/log_linux.c b/src/core/support/log_linux.c
index a0307e1a9a..a64faa98bd 100644
--- a/src/core/support/log_linux.c
+++ b/src/core/support/log_linux.c
@@ -31,8 +31,14 @@
*
*/
+#ifndef _POSIX_SOURCE
#define _POSIX_SOURCE
+#endif
+
+#ifndef _GNU_SOURCE
#define _GNU_SOURCE
+#endif
+
#include <grpc/support/port_platform.h>
#ifdef GPR_LINUX
diff --git a/src/core/support/log_posix.c b/src/core/support/log_posix.c
index ab2d2e5a74..05f45de130 100644
--- a/src/core/support/log_posix.c
+++ b/src/core/support/log_posix.c
@@ -31,11 +31,16 @@
*
*/
-#ifndef _POSIX_C_SOURCE
+#if !defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 200112L
+#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200112L
#endif
+/* FIXME: "posix" files probably shouldn't depend on _GNU_SOURCE */
+#ifndef _GNU_SOURCE
#define _GNU_SOURCE
+#endif
+
#include <grpc/support/port_platform.h>
#if defined(GPR_POSIX_LOG)
diff --git a/src/core/support/string_posix.c b/src/core/support/string_posix.c
index 57832810ad..a6bb8058e6 100644
--- a/src/core/support/string_posix.c
+++ b/src/core/support/string_posix.c
@@ -33,7 +33,8 @@
/* Posix code for gpr snprintf support. */
-#ifndef _POSIX_C_SOURCE
+#if !defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 200112L
+#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200112L
#endif
diff --git a/src/core/support/sync_posix.c b/src/core/support/sync_posix.c
index 7f0e4a95a4..a28a4c6bf4 100644
--- a/src/core/support/sync_posix.c
+++ b/src/core/support/sync_posix.c
@@ -33,11 +33,17 @@
/* Posix gpr synchroization support code. */
+#if !defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 199309L
+#undef _POSIX_C_SOURCE
+#define _POSIX_C_SOURCE 199309L
+#endif
+
#include <grpc/support/port_platform.h>
#ifdef GPR_POSIX_SYNC
#include <errno.h>
+#include <time.h>
#include <grpc/support/log.h>
#include <grpc/support/sync.h>
#include <grpc/support/time.h>
@@ -67,7 +73,10 @@ int gpr_cv_wait(gpr_cv *cv, gpr_mu *mu, gpr_timespec abs_deadline) {
if (gpr_time_cmp(abs_deadline, gpr_inf_future) == 0) {
err = pthread_cond_wait(cv, mu);
} else {
- err = pthread_cond_timedwait(cv, mu, &abs_deadline);
+ struct timespec abs_deadline_ts;
+ abs_deadline_ts.tv_sec = abs_deadline.tv_sec;
+ abs_deadline_ts.tv_nsec = abs_deadline.tv_nsec;
+ err = pthread_cond_timedwait(cv, mu, &abs_deadline_ts);
}
GPR_ASSERT(err == 0 || err == ETIMEDOUT || err == EAGAIN);
return err == ETIMEDOUT;
diff --git a/src/core/support/time.c b/src/core/support/time.c
index 97243318fd..268a43c677 100644
--- a/src/core/support/time.c
+++ b/src/core/support/time.c
@@ -234,22 +234,6 @@ int gpr_time_similar(gpr_timespec a, gpr_timespec b, gpr_timespec threshold) {
}
}
-struct timeval gpr_timeval_from_timespec(gpr_timespec t) {
- /* TODO(klempner): Consider whether this should round up, since it is likely
- to be used for delays */
- struct timeval tv;
- tv.tv_sec = t.tv_sec;
- tv.tv_usec = t.tv_nsec / 1000;
- return tv;
-}
-
-gpr_timespec gpr_timespec_from_timeval(struct timeval t) {
- gpr_timespec ts;
- ts.tv_sec = t.tv_sec;
- ts.tv_nsec = t.tv_usec * 1000;
- return ts;
-}
-
gpr_int32 gpr_time_to_millis(gpr_timespec t) {
if (t.tv_sec >= 2147483) {
if (t.tv_sec == 2147483 && t.tv_nsec < 648 * GPR_NS_PER_MS) {
diff --git a/src/core/support/time_posix.c b/src/core/support/time_posix.c
index 9e11f8a865..7f0f028183 100644
--- a/src/core/support/time_posix.c
+++ b/src/core/support/time_posix.c
@@ -34,7 +34,8 @@
/* Posix code for gpr time support. */
/* So we get nanosleep and clock_* */
-#ifndef _POSIX_C_SOURCE
+#if !defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 199309L
+#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 199309L
#endif
@@ -47,11 +48,25 @@
#include <unistd.h>
#include <grpc/support/time.h>
+static struct timespec timespec_from_gpr(gpr_timespec gts) {
+ struct timespec rv;
+ rv.tv_sec = gts.tv_sec;
+ rv.tv_nsec = gts.tv_nsec;
+ return rv;
+}
+
#if _POSIX_TIMERS > 0
+static gpr_timespec gpr_from_timespec(struct timespec ts) {
+ gpr_timespec rv;
+ rv.tv_sec = ts.tv_sec;
+ rv.tv_nsec = ts.tv_nsec;
+ return rv;
+}
+
gpr_timespec gpr_now(void) {
- gpr_timespec now;
+ struct timespec now;
clock_gettime(CLOCK_REALTIME, &now);
- return now;
+ return gpr_from_timespec(now);
}
#else
/* For some reason Apple's OSes haven't implemented clock_gettime. */
@@ -69,6 +84,7 @@ gpr_timespec gpr_now(void) {
void gpr_sleep_until(gpr_timespec until) {
gpr_timespec now;
gpr_timespec delta;
+ struct timespec delta_ts;
for (;;) {
/* We could simplify by using clock_nanosleep instead, but it might be
@@ -79,7 +95,8 @@ void gpr_sleep_until(gpr_timespec until) {
}
delta = gpr_time_sub(until, now);
- if (nanosleep(&delta, NULL) == 0) {
+ delta_ts = timespec_from_gpr(delta);
+ if (nanosleep(&delta_ts, NULL) == 0) {
break;
}
}
diff --git a/src/core/surface/byte_buffer_queue.c b/src/core/surface/byte_buffer_queue.c
index dc280a60c5..9709a665ba 100644
--- a/src/core/surface/byte_buffer_queue.c
+++ b/src/core/surface/byte_buffer_queue.c
@@ -35,7 +35,13 @@
#include <grpc/support/alloc.h>
#include <grpc/support/useful.h>
-static void bba_destroy(grpc_bbq_array *array) { gpr_free(array->data); }
+static void bba_destroy(grpc_bbq_array *array, size_t start_pos) {
+ size_t i;
+ for (i = start_pos; i < array->count; i++) {
+ grpc_byte_buffer_destroy(array->data[i]);
+ }
+ gpr_free(array->data);
+}
/* Append an operation to an array, expanding as needed */
static void bba_push(grpc_bbq_array *a, grpc_byte_buffer *buffer) {
@@ -47,8 +53,8 @@ static void bba_push(grpc_bbq_array *a, grpc_byte_buffer *buffer) {
}
void grpc_bbq_destroy(grpc_byte_buffer_queue *q) {
- bba_destroy(&q->filling);
- bba_destroy(&q->draining);
+ bba_destroy(&q->filling, 0);
+ bba_destroy(&q->draining, q->drain_pos);
}
int grpc_bbq_empty(grpc_byte_buffer_queue *q) {
diff --git a/src/core/surface/call.c b/src/core/surface/call.c
index e0bfec0018..1657edb3de 100644
--- a/src/core/surface/call.c
+++ b/src/core/surface/call.c
@@ -146,10 +146,10 @@ struct grpc_call {
/* Active ioreqs.
request_set and request_data contain one element per active ioreq
operation.
-
+
request_set[op] is an integer specifying a set of operations to which
the request belongs:
- - if it is < GRPC_IOREQ_OP_COUNT, then this operation is pending
+ - if it is < GRPC_IOREQ_OP_COUNT, then this operation is pending
completion, and the integer represents to which group of operations
the ioreq belongs. Each group is represented by one master, and the
integer in request_set is an index into masters to find the master
@@ -158,16 +158,17 @@ struct grpc_call {
started
- finally, if request_set[op] is REQSET_DONE, then the operation is
complete and unavailable to be started again
-
+
request_data[op] is the request data as supplied by the initiator of
a request, and is valid iff request_set[op] <= GRPC_IOREQ_OP_COUNT.
The set fields are as per the request type specified by op.
- Finally, one element of masters[op] is set per active _group_ of ioreq
+ Finally, one element of masters is set per active _set_ of ioreq
operations. It describes work left outstanding, result status, and
what work to perform upon operation completion. As one ioreq of each
op type can be active at once, by convention we choose the first element
- of a the group to be the master. This allows constant time allocation
+ of the group to be the master -- ie the master of in-progress operation
+ op is masters[request_set[op]]. This allows constant time allocation
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];
@@ -199,7 +200,7 @@ struct grpc_call {
/* Call refcount - to keep the call alive during asynchronous operations */
gpr_refcount internal_refcount;
- /* Data that the legacy api needs to track. To be deleted at some point
+ /* Data that the legacy api needs to track. To be deleted at some point
soon */
legacy_state *legacy_state;
};
@@ -278,6 +279,7 @@ static void destroy_call(void *call, int ignored_success) {
if (c->legacy_state) {
destroy_legacy_state(c->legacy_state);
}
+ grpc_bbq_destroy(&c->incoming_queue);
gpr_free(c);
}
@@ -338,8 +340,10 @@ static void unlock(grpc_call *call) {
send_action sa = SEND_NOTHING;
completed_request completed_requests[GRPC_IOREQ_OP_COUNT];
int num_completed_requests = call->num_completed_requests;
- int need_more_data = call->need_more_data &&
- !is_op_live(call, GRPC_IOREQ_SEND_INITIAL_METADATA);
+ int need_more_data =
+ call->need_more_data &&
+ !call->sending &&
+ call->write_state >= WRITE_STATE_STARTED;
int i;
if (need_more_data) {
@@ -1065,6 +1069,8 @@ struct legacy_state {
char *details;
grpc_status_code status;
+ char *send_details;
+
size_t msg_in_read_idx;
grpc_byte_buffer *msg_in;
@@ -1090,6 +1096,8 @@ static void destroy_legacy_state(legacy_state *ls) {
}
gpr_free(ls->initial_md_in.metadata);
gpr_free(ls->trailing_md_in.metadata);
+ gpr_free(ls->details);
+ gpr_free(ls->send_details);
gpr_free(ls);
}
@@ -1339,8 +1347,7 @@ grpc_call_error grpc_call_start_write_status_old(grpc_call *call,
reqs[0].data.send_metadata.metadata = ls->md_out[ls->md_out_buffer];
reqs[1].op = GRPC_IOREQ_SEND_STATUS;
reqs[1].data.send_status.code = status;
- /* MEMLEAK */
- reqs[1].data.send_status.details = gpr_strdup(details);
+ reqs[1].data.send_status.details = ls->send_details = gpr_strdup(details);
reqs[2].op = GRPC_IOREQ_SEND_CLOSE;
err = start_ioreq(call, reqs, 3, finish_finish, tag);
unlock(call);
diff --git a/src/core/surface/channel.c b/src/core/surface/channel.c
index 6d47787b7c..514073ce0b 100644
--- a/src/core/surface/channel.c
+++ b/src/core/surface/channel.c
@@ -52,6 +52,9 @@ struct grpc_channel {
};
#define CHANNEL_STACK_FROM_CHANNEL(c) ((grpc_channel_stack *)((c)+1))
+#define CHANNEL_FROM_CHANNEL_STACK(channel_stack) (((grpc_channel *)(channel_stack)) - 1)
+#define CHANNEL_FROM_TOP_ELEM(top_elem) \
+ CHANNEL_FROM_CHANNEL_STACK(grpc_channel_stack_from_top_element(top_elem))
grpc_channel *grpc_channel_create_from_filters(
const grpc_channel_filter **filters, size_t num_filters,
@@ -60,8 +63,8 @@ grpc_channel *grpc_channel_create_from_filters(
sizeof(grpc_channel) + grpc_channel_stack_size(filters, num_filters);
grpc_channel *channel = gpr_malloc(size);
channel->is_client = is_client;
- /* decremented by grpc_channel_destroy */
- gpr_ref_init(&channel->refs, 1);
+ /* decremented by grpc_channel_destroy, and grpc_client_channel_closed if is_client */
+ gpr_ref_init(&channel->refs, 1 + is_client);
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");
@@ -166,6 +169,10 @@ void grpc_channel_destroy(grpc_channel *channel) {
grpc_channel_internal_unref(channel);
}
+void grpc_client_channel_closed(grpc_channel_element *elem) {
+ grpc_channel_internal_unref(CHANNEL_FROM_TOP_ELEM(elem));
+}
+
grpc_channel_stack *grpc_channel_get_channel_stack(grpc_channel *channel) {
return CHANNEL_STACK_FROM_CHANNEL(channel);
}
diff --git a/src/core/surface/channel.h b/src/core/surface/channel.h
index b3ea2ede40..ff9bbc237e 100644
--- a/src/core/surface/channel.h
+++ b/src/core/surface/channel.h
@@ -45,6 +45,8 @@ grpc_mdctx *grpc_channel_get_metadata_context(grpc_channel *channel);
grpc_mdstr *grpc_channel_get_status_string(grpc_channel *channel);
grpc_mdstr *grpc_channel_get_message_string(grpc_channel *channel);
+void grpc_client_channel_closed(grpc_channel_element *elem);
+
void grpc_channel_internal_ref(grpc_channel *channel);
void grpc_channel_internal_unref(grpc_channel *channel);
diff --git a/src/core/surface/client.c b/src/core/surface/client.c
index fa63e855cc..64ee9d51e8 100644
--- a/src/core/surface/client.c
+++ b/src/core/surface/client.c
@@ -34,6 +34,7 @@
#include "src/core/surface/client.h"
#include "src/core/surface/call.h"
+#include "src/core/surface/channel.h"
#include "src/core/support/string.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
@@ -87,7 +88,7 @@ static void channel_op(grpc_channel_element *elem,
gpr_log(GPR_ERROR, "Client cannot accept new calls");
break;
case GRPC_TRANSPORT_CLOSED:
- gpr_log(GPR_ERROR, "Transport closed");
+ grpc_client_channel_closed(elem);
break;
case GRPC_TRANSPORT_GOAWAY:
gpr_slice_unref(op->data.goaway.message);
diff --git a/src/core/surface/lame_client.c b/src/core/surface/lame_client.c
index 2f5eff5584..411dbabfd3 100644
--- a/src/core/surface/lame_client.c
+++ b/src/core/surface/lame_client.c
@@ -76,6 +76,9 @@ static void channel_op(grpc_channel_element *elem,
case GRPC_CHANNEL_GOAWAY:
gpr_slice_unref(op->data.goaway.message);
break;
+ case GRPC_CHANNEL_DISCONNECT:
+ grpc_client_channel_closed(elem);
+ break;
default:
break;
}
diff --git a/src/core/surface/server.c b/src/core/surface/server.c
index d7e1dcd800..c0c524ad8d 100644
--- a/src/core/surface/server.c
+++ b/src/core/surface/server.c
@@ -265,7 +265,6 @@ static void stream_closed(grpc_call_element *elem) {
gpr_mu_lock(&chand->server->mu);
switch (calld->state) {
case ACTIVATED:
- grpc_call_stream_closed(elem);
break;
case PENDING:
call_list_remove(chand->server, calld, PENDING_START);
@@ -278,6 +277,7 @@ static void stream_closed(grpc_call_element *elem) {
break;
}
gpr_mu_unlock(&chand->server->mu);
+ grpc_call_stream_closed(elem);
}
static void read_closed(grpc_call_element *elem) {
diff --git a/src/core/transport/chttp2_transport.c b/src/core/transport/chttp2_transport.c
index f560417617..ea579cf4a5 100644
--- a/src/core/transport/chttp2_transport.c
+++ b/src/core/transport/chttp2_transport.c
@@ -184,7 +184,6 @@ struct transport {
gpr_uint8 is_client;
gpr_mu mu;
- gpr_cv cv;
/* basic state management - what are we doing at the moment? */
gpr_uint8 reading;
@@ -328,6 +327,9 @@ static void maybe_start_some_streams(transport *t);
static void become_skip_parser(transport *t);
+static void recv_data(void *tp, gpr_slice *slices, size_t nslices,
+ grpc_endpoint_cb_status error);
+
/*
* CONSTRUCTION/DESTRUCTION/REFCOUNTING
*/
@@ -382,8 +384,8 @@ static void ref_transport(transport *t) { gpr_ref(&t->refs); }
static void init_transport(transport *t, grpc_transport_setup_callback setup,
void *arg, const grpc_channel_args *channel_args,
- grpc_endpoint *ep, grpc_mdctx *mdctx,
- int is_client) {
+ grpc_endpoint *ep, gpr_slice *slices, size_t nslices,
+ grpc_mdctx *mdctx, int is_client) {
size_t i;
int j;
grpc_transport_setup_result sr;
@@ -395,7 +397,6 @@ static void init_transport(transport *t, grpc_transport_setup_callback setup,
/* one ref is for destroy, the other for when ep becomes NULL */
gpr_ref_init(&t->refs, 2);
gpr_mu_init(&t->mu);
- gpr_cv_init(&t->cv);
t->metadata_context = mdctx;
t->str_grpc_timeout =
grpc_mdstr_from_string(t->metadata_context, "grpc-timeout");
@@ -422,6 +423,7 @@ static void init_transport(transport *t, grpc_transport_setup_callback setup,
gpr_slice_buffer_init(&t->outbuf);
gpr_slice_buffer_init(&t->qbuf);
grpc_sopb_init(&t->nuke_later_sopb);
+ grpc_chttp2_hpack_parser_init(&t->hpack_parser, t->metadata_context);
if (is_client) {
gpr_slice_buffer_add(&t->qbuf,
gpr_slice_from_copied_string(CLIENT_CONNECT_STRING));
@@ -476,14 +478,15 @@ static void init_transport(transport *t, grpc_transport_setup_callback setup,
ref_transport(t);
gpr_mu_unlock(&t->mu);
+ ref_transport(t);
+ recv_data(t, slices, nslices, GRPC_ENDPOINT_CB_OK);
+
sr = setup(arg, &t->base, t->metadata_context);
lock(t);
t->cb = sr.callbacks;
t->cb_user_data = sr.user_data;
- grpc_chttp2_hpack_parser_init(&t->hpack_parser, t->metadata_context);
t->calling_back = 0;
- gpr_cv_broadcast(&t->cv);
unlock(t);
unref_transport(t);
}
@@ -492,9 +495,6 @@ static void destroy_transport(grpc_transport *gt) {
transport *t = (transport *)gt;
gpr_mu_lock(&t->mu);
- while (t->calling_back) {
- gpr_cv_wait(&t->cv, &t->mu, gpr_inf_future);
- }
t->cb = NULL;
gpr_mu_unlock(&t->mu);
@@ -573,13 +573,6 @@ static void destroy_stream(grpc_transport *gt, grpc_stream *gs) {
gpr_mu_lock(&t->mu);
- /* await pending callbacks
- TODO(ctiller): this could be optimized to check if this stream is getting
- callbacks */
- while (t->calling_back) {
- gpr_cv_wait(&t->cv, &t->mu, gpr_inf_future);
- }
-
/* stop parsing if we're currently parsing this stream */
if (t->deframe_state == DTS_FRAME && t->incoming_stream_id == s->id &&
s->id != 0) {
@@ -591,7 +584,6 @@ static void destroy_stream(grpc_transport *gt, grpc_stream *gs) {
}
remove_from_stream_map(t, s);
- gpr_cv_broadcast(&t->cv);
gpr_mu_unlock(&t->mu);
grpc_sopb_destroy(&s->outgoing_sopb);
@@ -761,7 +753,6 @@ static void unlock(transport *t) {
if (perform_callbacks || call_closed || num_goaways) {
lock(t);
t->calling_back = 0;
- gpr_cv_broadcast(&t->cv);
unlock(t);
unref_transport(t);
}
@@ -892,7 +883,6 @@ static void finish_write_common(transport *t, int success) {
if (!t->reading) {
grpc_endpoint_destroy(t->ep);
t->ep = NULL;
- gpr_cv_broadcast(&t->cv);
unref_transport(t); /* safe because we'll still have the ref for write */
}
unlock(t);
@@ -1673,7 +1663,6 @@ static void recv_data(void *tp, gpr_slice *slices, size_t nslices,
if (!t->writing && t->ep) {
grpc_endpoint_destroy(t->ep);
t->ep = NULL;
- gpr_cv_broadcast(&t->cv);
unref_transport(t); /* safe as we still have a ref for read */
}
unlock(t);
@@ -1769,7 +1758,6 @@ void grpc_create_chttp2_transport(grpc_transport_setup_callback setup,
size_t nslices, grpc_mdctx *mdctx,
int is_client) {
transport *t = gpr_malloc(sizeof(transport));
- init_transport(t, setup, arg, channel_args, ep, mdctx, is_client);
- ref_transport(t);
- recv_data(t, slices, nslices, GRPC_ENDPOINT_CB_OK);
+ init_transport(t, setup, arg, channel_args, ep, slices, nslices, mdctx,
+ is_client);
}
diff --git a/src/core/transport/metadata.c b/src/core/transport/metadata.c
index 74bbb02134..85705e8762 100644
--- a/src/core/transport/metadata.c
+++ b/src/core/transport/metadata.c
@@ -117,7 +117,7 @@ static void unlock(grpc_mdctx *ctx) {
if (ctx->orphaned) {
/* 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);
}
diff --git a/src/csharp/Grpc.sln b/src/csharp/Grpc.sln
new file mode 100644
index 0000000000..5890617acf
--- /dev/null
+++ b/src/csharp/Grpc.sln
@@ -0,0 +1,38 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GrpcDemo", "GrpcDemo\GrpcDemo.csproj", "{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GrpcApi", "GrpcApi\GrpcApi.csproj", "{7DC1433E-3225-42C7-B7EA-546D56E27A4B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GrpcCore", "GrpcCore\GrpcCore.csproj", "{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GrpcCoreTests", "GrpcCoreTests\GrpcCoreTests.csproj", "{86EC5CB4-4EA2-40A2-8057-86542A0353BB}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x86 = Debug|x86
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Debug|x86.ActiveCfg = Debug|x86
+ {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Debug|x86.Build.0 = Debug|x86
+ {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Release|x86.ActiveCfg = Release|x86
+ {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Release|x86.Build.0 = Release|x86
+ {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Debug|x86.Build.0 = Debug|Any CPU
+ {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Release|x86.ActiveCfg = Release|Any CPU
+ {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Release|x86.Build.0 = Release|Any CPU
+ {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Debug|x86.Build.0 = Debug|Any CPU
+ {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Release|x86.ActiveCfg = Release|Any CPU
+ {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Release|x86.Build.0 = Release|Any CPU
+ {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Debug|x86.Build.0 = Debug|Any CPU
+ {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Release|x86.ActiveCfg = Release|Any CPU
+ {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Release|x86.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(MonoDevelopProperties) = preSolution
+ StartupItem = GrpcDemo\GrpcDemo.csproj
+ EndGlobalSection
+EndGlobal
diff --git a/src/csharp/GrpcApi/.gitignore b/src/csharp/GrpcApi/.gitignore
new file mode 100644
index 0000000000..2cc8cca52d
--- /dev/null
+++ b/src/csharp/GrpcApi/.gitignore
@@ -0,0 +1,2 @@
+test-results
+bin
diff --git a/src/csharp/GrpcApi/DummyMathServiceClient.cs b/src/csharp/GrpcApi/DummyMathServiceClient.cs
new file mode 100644
index 0000000000..6799109be4
--- /dev/null
+++ b/src/csharp/GrpcApi/DummyMathServiceClient.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+using System.Reactive.Linq;
+
+namespace math
+{
+// /// <summary>
+// /// Dummy local implementation of math service.
+// /// </summary>
+// public class DummyMathServiceClient : IMathServiceClient
+// {
+// public DivReply Div(DivArgs args, CancellationToken token = default(CancellationToken))
+// {
+// // TODO: cancellation...
+// return DivInternal(args);
+// }
+//
+// public Task<DivReply> DivAsync(DivArgs args, CancellationToken token = default(CancellationToken))
+// {
+// return Task.Factory.StartNew(() => DivInternal(args), token);
+// }
+//
+// public IObservable<Num> Fib(FibArgs args, CancellationToken token = default(CancellationToken))
+// {
+// if (args.Limit > 0)
+// {
+// // TODO: cancellation
+// return FibInternal(args.Limit).ToObservable();
+// }
+//
+// throw new NotImplementedException("Not implemented yet");
+// }
+//
+// public Task<Num> Sum(IObservable<Num> inputs, CancellationToken token = default(CancellationToken))
+// {
+// // TODO: implement
+// inputs = null;
+// return Task.Factory.StartNew(() => Num.CreateBuilder().Build(), token);
+// }
+//
+// public IObservable<DivReply> DivMany(IObservable<DivArgs> inputs, CancellationToken token = default(CancellationToken))
+// {
+// // TODO: implement
+// inputs = null;
+// return new List<DivReply> { }.ToObservable ();
+// }
+//
+//
+// DivReply DivInternal(DivArgs args)
+// {
+// long quotient = args.Dividend / args.Divisor;
+// long remainder = args.Dividend % args.Divisor;
+// return new DivReply.Builder{ Quotient = quotient, Remainder = remainder }.Build();
+// }
+//
+// IEnumerable<Num> FibInternal(long n)
+// {
+// long a = 0;
+// yield return new Num.Builder{Num_=a}.Build();
+//
+// long b = 1;
+// for (long i = 0; i < n - 1; i++)
+// {
+// long temp = a;
+// a = b;
+// b = temp + b;
+// yield return new Num.Builder{Num_=a}.Build();
+// }
+// }
+// }
+}
+
diff --git a/src/csharp/GrpcApi/Examples.cs b/src/csharp/GrpcApi/Examples.cs
new file mode 100644
index 0000000000..d45b702708
--- /dev/null
+++ b/src/csharp/GrpcApi/Examples.cs
@@ -0,0 +1,97 @@
+using System;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+using System.Reactive.Linq;
+
+namespace math
+{
+ public class Examples
+ {
+ public static void DivExample(IMathServiceClient stub)
+ {
+ DivReply result = stub.Div(new DivArgs.Builder { Dividend = 10, Divisor = 3 }.Build());
+ Console.WriteLine("Div Result: " + result);
+ }
+
+ public static void DivAsyncExample(IMathServiceClient stub)
+ {
+ Task<DivReply> call = stub.DivAsync(new DivArgs.Builder { Dividend = 4, Divisor = 5 }.Build());
+ DivReply result = call.Result;
+ Console.WriteLine(result);
+ }
+
+ public static void DivAsyncWithCancellationExample(IMathServiceClient stub)
+ {
+ Task<DivReply> call = stub.DivAsync(new DivArgs.Builder { Dividend = 4, Divisor = 5 }.Build());
+ DivReply result = call.Result;
+ Console.WriteLine(result);
+ }
+
+ public static void FibExample(IMathServiceClient stub)
+ {
+ var recorder = new RecordingObserver<Num>();
+ stub.Fib(new FibArgs.Builder { Limit = 5 }.Build(), recorder);
+
+ List<Num> numbers = recorder.ToList().Result;
+ Console.WriteLine("Fib Result: " + string.Join("|", recorder.ToList().Result));
+ }
+
+ public static void SumExample(IMathServiceClient stub)
+ {
+ List<Num> numbers = new List<Num>{new Num.Builder { Num_ = 1 }.Build(),
+ new Num.Builder { Num_ = 2 }.Build(),
+ new Num.Builder { Num_ = 3 }.Build()};
+
+ var res = stub.Sum();
+ foreach (var num in numbers) {
+ res.Inputs.OnNext(num);
+ }
+ res.Inputs.OnCompleted();
+
+ Console.WriteLine("Sum Result: " + res.Task.Result);
+ }
+
+ public static void DivManyExample(IMathServiceClient stub)
+ {
+ List<DivArgs> divArgsList = new List<DivArgs>{
+ new DivArgs.Builder { Dividend = 10, Divisor = 3 }.Build(),
+ new DivArgs.Builder { Dividend = 100, Divisor = 21 }.Build(),
+ new DivArgs.Builder { Dividend = 7, Divisor = 2 }.Build()
+ };
+
+ var recorder = new RecordingObserver<DivReply>();
+
+ var inputs = stub.DivMany(recorder);
+ foreach (var input in divArgsList)
+ {
+ inputs.OnNext(input);
+ }
+ inputs.OnCompleted();
+
+ Console.WriteLine("DivMany Result: " + string.Join("|", recorder.ToList().Result));
+ }
+
+ public static void DependendRequestsExample(IMathServiceClient stub)
+ {
+ var numberList = new List<Num>
+ { new Num.Builder{ Num_ = 1 }.Build(),
+ new Num.Builder{ Num_ = 2 }.Build(), new Num.Builder{ Num_ = 3 }.Build()
+ };
+
+ numberList.ToObservable();
+
+ //IObserver<Num> numbers;
+ //Task<Num> call = stub.Sum(out numbers);
+ //foreach (var num in numberList)
+ //{
+ // numbers.OnNext(num);
+ //}
+ //numbers.OnCompleted();
+
+ //Num sum = call.Result;
+
+ //DivReply result = stub.Div(new DivArgs.Builder { Dividend = sum.Num_, Divisor = numberList.Count }.Build());
+ }
+ }
+}
+
diff --git a/src/csharp/GrpcApi/GrpcApi.csproj b/src/csharp/GrpcApi/GrpcApi.csproj
new file mode 100644
index 0000000000..d0377828b5
--- /dev/null
+++ b/src/csharp/GrpcApi/GrpcApi.csproj
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>10.0.0</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{7DC1433E-3225-42C7-B7EA-546D56E27A4B}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <RootNamespace>GrpcApi</RootNamespace>
+ <AssemblyName>GrpcApi</AssemblyName>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug</OutputPath>
+ <DefineConstants>DEBUG;</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <ConsolePause>false</ConsolePause>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>full</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release</OutputPath>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <ConsolePause>false</ConsolePause>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Reactive.Linq, Version=2.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="System.Data.Linq" />
+ <Reference Include="System.Reactive.Interfaces, Version=2.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="System.Reactive.Core, Version=2.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="Google.ProtocolBuffers">
+ <HintPath>..\lib\Google.ProtocolBuffers.dll</HintPath>
+ </Reference>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="Examples.cs" />
+ <Compile Include="IMathServiceClient.cs" />
+ <Compile Include="Math.cs" />
+ <Compile Include="DummyMathServiceClient.cs" />
+ <Compile Include="MathServiceClientStub.cs" />
+ <Compile Include="RecordingObserver.cs" />
+ </ItemGroup>
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+ <ItemGroup>
+ <ProjectReference Include="..\GrpcCore\GrpcCore.csproj">
+ <Project>{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}</Project>
+ <Name>GrpcCore</Name>
+ </ProjectReference>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/src/csharp/GrpcApi/IMathServiceClient.cs b/src/csharp/GrpcApi/IMathServiceClient.cs
new file mode 100644
index 0000000000..51385a328f
--- /dev/null
+++ b/src/csharp/GrpcApi/IMathServiceClient.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+using System.Reactive.Linq;
+using Google.GRPC.Core;
+
+namespace math
+{
+ /// <summary>
+ /// Hand-written stub for MathService defined in math.proto.
+ /// This code will be generated by gRPC codegen in the future.
+ /// </summary>
+ public interface IMathServiceClient
+ {
+ DivReply Div(DivArgs args, CancellationToken token = default(CancellationToken));
+
+ Task<DivReply> DivAsync(DivArgs args, CancellationToken token = default(CancellationToken));
+
+ Task Fib(FibArgs args, IObserver<Num> outputs, CancellationToken token = default(CancellationToken));
+
+ ClientStreamingAsyncResult<Num, Num> Sum(CancellationToken token = default(CancellationToken));
+
+ IObserver<DivArgs> DivMany(IObserver<DivReply> outputs, CancellationToken token = default(CancellationToken));
+ }
+} \ No newline at end of file
diff --git a/src/csharp/GrpcApi/Math.cs b/src/csharp/GrpcApi/Math.cs
new file mode 100644
index 0000000000..2d700337ac
--- /dev/null
+++ b/src/csharp/GrpcApi/Math.cs
@@ -0,0 +1,1531 @@
+// Generated by ProtoGen, Version=2.4.1.521, Culture=neutral, PublicKeyToken=17b3b1f090c3ea48. DO NOT EDIT!
+#pragma warning disable 1591, 0612, 3021
+#region Designer generated code
+
+using pb = global::Google.ProtocolBuffers;
+using pbc = global::Google.ProtocolBuffers.Collections;
+using pbd = global::Google.ProtocolBuffers.Descriptors;
+using scg = global::System.Collections.Generic;
+namespace math {
+
+ namespace Proto {
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ public static partial class Math {
+
+ #region Extension registration
+ public static void RegisterAllExtensions(pb::ExtensionRegistry registry) {
+ }
+ #endregion
+ #region Static variables
+ internal static pbd::MessageDescriptor internal__static_math_DivArgs__Descriptor;
+ internal static pb::FieldAccess.FieldAccessorTable<global::math.DivArgs, global::math.DivArgs.Builder> internal__static_math_DivArgs__FieldAccessorTable;
+ internal static pbd::MessageDescriptor internal__static_math_DivReply__Descriptor;
+ internal static pb::FieldAccess.FieldAccessorTable<global::math.DivReply, global::math.DivReply.Builder> internal__static_math_DivReply__FieldAccessorTable;
+ internal static pbd::MessageDescriptor internal__static_math_FibArgs__Descriptor;
+ internal static pb::FieldAccess.FieldAccessorTable<global::math.FibArgs, global::math.FibArgs.Builder> internal__static_math_FibArgs__FieldAccessorTable;
+ internal static pbd::MessageDescriptor internal__static_math_Num__Descriptor;
+ internal static pb::FieldAccess.FieldAccessorTable<global::math.Num, global::math.Num.Builder> internal__static_math_Num__FieldAccessorTable;
+ internal static pbd::MessageDescriptor internal__static_math_FibReply__Descriptor;
+ internal static pb::FieldAccess.FieldAccessorTable<global::math.FibReply, global::math.FibReply.Builder> internal__static_math_FibReply__FieldAccessorTable;
+ #endregion
+ #region Descriptor
+ public static pbd::FileDescriptor Descriptor {
+ get { return descriptor; }
+ }
+ private static pbd::FileDescriptor descriptor;
+
+ static Math() {
+ byte[] descriptorData = global::System.Convert.FromBase64String(
+ string.Concat(
+ "CgptYXRoLnByb3RvEgRtYXRoIiwKB0RpdkFyZ3MSEAoIZGl2aWRlbmQYASAB",
+ "KAMSDwoHZGl2aXNvchgCIAEoAyIvCghEaXZSZXBseRIQCghxdW90aWVudBgB",
+ "IAEoAxIRCglyZW1haW5kZXIYAiABKAMiGAoHRmliQXJncxINCgVsaW1pdBgB",
+ "IAEoAyISCgNOdW0SCwoDbnVtGAEgASgDIhkKCEZpYlJlcGx5Eg0KBWNvdW50",
+ "GAEgASgDMqQBCgRNYXRoEiYKA0RpdhINLm1hdGguRGl2QXJncxoOLm1hdGgu",
+ "RGl2UmVwbHkiABIuCgdEaXZNYW55Eg0ubWF0aC5EaXZBcmdzGg4ubWF0aC5E",
+ "aXZSZXBseSIAKAEwARIjCgNGaWISDS5tYXRoLkZpYkFyZ3MaCS5tYXRoLk51",
+ "bSIAMAESHwoDU3VtEgkubWF0aC5OdW0aCS5tYXRoLk51bSIAKAE="));
+ pbd::FileDescriptor.InternalDescriptorAssigner assigner = delegate(pbd::FileDescriptor root) {
+ descriptor = root;
+ internal__static_math_DivArgs__Descriptor = Descriptor.MessageTypes[0];
+ internal__static_math_DivArgs__FieldAccessorTable =
+ new pb::FieldAccess.FieldAccessorTable<global::math.DivArgs, global::math.DivArgs.Builder>(internal__static_math_DivArgs__Descriptor,
+ new string[] { "Dividend", "Divisor", });
+ internal__static_math_DivReply__Descriptor = Descriptor.MessageTypes[1];
+ internal__static_math_DivReply__FieldAccessorTable =
+ new pb::FieldAccess.FieldAccessorTable<global::math.DivReply, global::math.DivReply.Builder>(internal__static_math_DivReply__Descriptor,
+ new string[] { "Quotient", "Remainder", });
+ internal__static_math_FibArgs__Descriptor = Descriptor.MessageTypes[2];
+ internal__static_math_FibArgs__FieldAccessorTable =
+ new pb::FieldAccess.FieldAccessorTable<global::math.FibArgs, global::math.FibArgs.Builder>(internal__static_math_FibArgs__Descriptor,
+ new string[] { "Limit", });
+ internal__static_math_Num__Descriptor = Descriptor.MessageTypes[3];
+ internal__static_math_Num__FieldAccessorTable =
+ new pb::FieldAccess.FieldAccessorTable<global::math.Num, global::math.Num.Builder>(internal__static_math_Num__Descriptor,
+ new string[] { "Num_", });
+ internal__static_math_FibReply__Descriptor = Descriptor.MessageTypes[4];
+ internal__static_math_FibReply__FieldAccessorTable =
+ new pb::FieldAccess.FieldAccessorTable<global::math.FibReply, global::math.FibReply.Builder>(internal__static_math_FibReply__Descriptor,
+ new string[] { "Count", });
+ pb::ExtensionRegistry registry = pb::ExtensionRegistry.CreateInstance();
+ RegisterAllExtensions(registry);
+ return registry;
+ };
+ pbd::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData,
+ new pbd::FileDescriptor[] {
+ }, assigner);
+ }
+ #endregion
+
+ }
+ }
+ #region Messages
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ public sealed partial class DivArgs : pb::GeneratedMessage<DivArgs, DivArgs.Builder> {
+ private DivArgs() { }
+ private static readonly DivArgs defaultInstance = new DivArgs().MakeReadOnly();
+ private static readonly string[] _divArgsFieldNames = new string[] { "dividend", "divisor" };
+ private static readonly uint[] _divArgsFieldTags = new uint[] { 8, 16 };
+ public static DivArgs DefaultInstance {
+ get { return defaultInstance; }
+ }
+
+ public override DivArgs DefaultInstanceForType {
+ get { return DefaultInstance; }
+ }
+
+ protected override DivArgs ThisMessage {
+ get { return this; }
+ }
+
+ public static pbd::MessageDescriptor Descriptor {
+ get { return global::math.Proto.Math.internal__static_math_DivArgs__Descriptor; }
+ }
+
+ protected override pb::FieldAccess.FieldAccessorTable<DivArgs, DivArgs.Builder> InternalFieldAccessors {
+ get { return global::math.Proto.Math.internal__static_math_DivArgs__FieldAccessorTable; }
+ }
+
+ public const int DividendFieldNumber = 1;
+ private bool hasDividend;
+ private long dividend_;
+ public bool HasDividend {
+ get { return hasDividend; }
+ }
+ public long Dividend {
+ get { return dividend_; }
+ }
+
+ public const int DivisorFieldNumber = 2;
+ private bool hasDivisor;
+ private long divisor_;
+ public bool HasDivisor {
+ get { return hasDivisor; }
+ }
+ public long Divisor {
+ get { return divisor_; }
+ }
+
+ public override bool IsInitialized {
+ get {
+ return true;
+ }
+ }
+
+ public override void WriteTo(pb::ICodedOutputStream output) {
+ int size = SerializedSize;
+ string[] field_names = _divArgsFieldNames;
+ if (hasDividend) {
+ output.WriteInt64(1, field_names[0], Dividend);
+ }
+ if (hasDivisor) {
+ output.WriteInt64(2, field_names[1], Divisor);
+ }
+ UnknownFields.WriteTo(output);
+ }
+
+ private int memoizedSerializedSize = -1;
+ public override int SerializedSize {
+ get {
+ int size = memoizedSerializedSize;
+ if (size != -1) return size;
+
+ size = 0;
+ if (hasDividend) {
+ size += pb::CodedOutputStream.ComputeInt64Size(1, Dividend);
+ }
+ if (hasDivisor) {
+ size += pb::CodedOutputStream.ComputeInt64Size(2, Divisor);
+ }
+ size += UnknownFields.SerializedSize;
+ memoizedSerializedSize = size;
+ return size;
+ }
+ }
+
+ public static DivArgs ParseFrom(pb::ByteString data) {
+ return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();
+ }
+ public static DivArgs ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) {
+ return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();
+ }
+ public static DivArgs ParseFrom(byte[] data) {
+ return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();
+ }
+ public static DivArgs ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) {
+ return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();
+ }
+ public static DivArgs ParseFrom(global::System.IO.Stream input) {
+ return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();
+ }
+ public static DivArgs ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {
+ return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();
+ }
+ public static DivArgs ParseDelimitedFrom(global::System.IO.Stream input) {
+ return CreateBuilder().MergeDelimitedFrom(input).BuildParsed();
+ }
+ public static DivArgs ParseDelimitedFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {
+ return CreateBuilder().MergeDelimitedFrom(input, extensionRegistry).BuildParsed();
+ }
+ public static DivArgs ParseFrom(pb::ICodedInputStream input) {
+ return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();
+ }
+ public static DivArgs ParseFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) {
+ return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();
+ }
+ private DivArgs MakeReadOnly() {
+ return this;
+ }
+
+ public static Builder CreateBuilder() { return new Builder(); }
+ public override Builder ToBuilder() { return CreateBuilder(this); }
+ public override Builder CreateBuilderForType() { return new Builder(); }
+ public static Builder CreateBuilder(DivArgs prototype) {
+ return new Builder(prototype);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ public sealed partial class Builder : pb::GeneratedBuilder<DivArgs, Builder> {
+ protected override Builder ThisBuilder {
+ get { return this; }
+ }
+ public Builder() {
+ result = DefaultInstance;
+ resultIsReadOnly = true;
+ }
+ internal Builder(DivArgs cloneFrom) {
+ result = cloneFrom;
+ resultIsReadOnly = true;
+ }
+
+ private bool resultIsReadOnly;
+ private DivArgs result;
+
+ private DivArgs PrepareBuilder() {
+ if (resultIsReadOnly) {
+ DivArgs original = result;
+ result = new DivArgs();
+ resultIsReadOnly = false;
+ MergeFrom(original);
+ }
+ return result;
+ }
+
+ public override bool IsInitialized {
+ get { return result.IsInitialized; }
+ }
+
+ protected override DivArgs MessageBeingBuilt {
+ get { return PrepareBuilder(); }
+ }
+
+ public override Builder Clear() {
+ result = DefaultInstance;
+ resultIsReadOnly = true;
+ return this;
+ }
+
+ public override Builder Clone() {
+ if (resultIsReadOnly) {
+ return new Builder(result);
+ } else {
+ return new Builder().MergeFrom(result);
+ }
+ }
+
+ public override pbd::MessageDescriptor DescriptorForType {
+ get { return global::math.DivArgs.Descriptor; }
+ }
+
+ public override DivArgs DefaultInstanceForType {
+ get { return global::math.DivArgs.DefaultInstance; }
+ }
+
+ public override DivArgs BuildPartial() {
+ if (resultIsReadOnly) {
+ return result;
+ }
+ resultIsReadOnly = true;
+ return result.MakeReadOnly();
+ }
+
+ public override Builder MergeFrom(pb::IMessage other) {
+ if (other is DivArgs) {
+ return MergeFrom((DivArgs) other);
+ } else {
+ base.MergeFrom(other);
+ return this;
+ }
+ }
+
+ public override Builder MergeFrom(DivArgs other) {
+ if (other == global::math.DivArgs.DefaultInstance) return this;
+ PrepareBuilder();
+ if (other.HasDividend) {
+ Dividend = other.Dividend;
+ }
+ if (other.HasDivisor) {
+ Divisor = other.Divisor;
+ }
+ this.MergeUnknownFields(other.UnknownFields);
+ return this;
+ }
+
+ public override Builder MergeFrom(pb::ICodedInputStream input) {
+ return MergeFrom(input, pb::ExtensionRegistry.Empty);
+ }
+
+ public override Builder MergeFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) {
+ PrepareBuilder();
+ pb::UnknownFieldSet.Builder unknownFields = null;
+ uint tag;
+ string field_name;
+ while (input.ReadTag(out tag, out field_name)) {
+ if(tag == 0 && field_name != null) {
+ int field_ordinal = global::System.Array.BinarySearch(_divArgsFieldNames, field_name, global::System.StringComparer.Ordinal);
+ if(field_ordinal >= 0)
+ tag = _divArgsFieldTags[field_ordinal];
+ else {
+ if (unknownFields == null) {
+ unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);
+ }
+ ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name);
+ continue;
+ }
+ }
+ switch (tag) {
+ case 0: {
+ throw pb::InvalidProtocolBufferException.InvalidTag();
+ }
+ default: {
+ if (pb::WireFormat.IsEndGroupTag(tag)) {
+ if (unknownFields != null) {
+ this.UnknownFields = unknownFields.Build();
+ }
+ return this;
+ }
+ if (unknownFields == null) {
+ unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);
+ }
+ ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name);
+ break;
+ }
+ case 8: {
+ result.hasDividend = input.ReadInt64(ref result.dividend_);
+ break;
+ }
+ case 16: {
+ result.hasDivisor = input.ReadInt64(ref result.divisor_);
+ break;
+ }
+ }
+ }
+
+ if (unknownFields != null) {
+ this.UnknownFields = unknownFields.Build();
+ }
+ return this;
+ }
+
+
+ public bool HasDividend {
+ get { return result.hasDividend; }
+ }
+ public long Dividend {
+ get { return result.Dividend; }
+ set { SetDividend(value); }
+ }
+ public Builder SetDividend(long value) {
+ PrepareBuilder();
+ result.hasDividend = true;
+ result.dividend_ = value;
+ return this;
+ }
+ public Builder ClearDividend() {
+ PrepareBuilder();
+ result.hasDividend = false;
+ result.dividend_ = 0L;
+ return this;
+ }
+
+ public bool HasDivisor {
+ get { return result.hasDivisor; }
+ }
+ public long Divisor {
+ get { return result.Divisor; }
+ set { SetDivisor(value); }
+ }
+ public Builder SetDivisor(long value) {
+ PrepareBuilder();
+ result.hasDivisor = true;
+ result.divisor_ = value;
+ return this;
+ }
+ public Builder ClearDivisor() {
+ PrepareBuilder();
+ result.hasDivisor = false;
+ result.divisor_ = 0L;
+ return this;
+ }
+ }
+ static DivArgs() {
+ object.ReferenceEquals(global::math.Proto.Math.Descriptor, null);
+ }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ public sealed partial class DivReply : pb::GeneratedMessage<DivReply, DivReply.Builder> {
+ private DivReply() { }
+ private static readonly DivReply defaultInstance = new DivReply().MakeReadOnly();
+ private static readonly string[] _divReplyFieldNames = new string[] { "quotient", "remainder" };
+ private static readonly uint[] _divReplyFieldTags = new uint[] { 8, 16 };
+ public static DivReply DefaultInstance {
+ get { return defaultInstance; }
+ }
+
+ public override DivReply DefaultInstanceForType {
+ get { return DefaultInstance; }
+ }
+
+ protected override DivReply ThisMessage {
+ get { return this; }
+ }
+
+ public static pbd::MessageDescriptor Descriptor {
+ get { return global::math.Proto.Math.internal__static_math_DivReply__Descriptor; }
+ }
+
+ protected override pb::FieldAccess.FieldAccessorTable<DivReply, DivReply.Builder> InternalFieldAccessors {
+ get { return global::math.Proto.Math.internal__static_math_DivReply__FieldAccessorTable; }
+ }
+
+ public const int QuotientFieldNumber = 1;
+ private bool hasQuotient;
+ private long quotient_;
+ public bool HasQuotient {
+ get { return hasQuotient; }
+ }
+ public long Quotient {
+ get { return quotient_; }
+ }
+
+ public const int RemainderFieldNumber = 2;
+ private bool hasRemainder;
+ private long remainder_;
+ public bool HasRemainder {
+ get { return hasRemainder; }
+ }
+ public long Remainder {
+ get { return remainder_; }
+ }
+
+ public override bool IsInitialized {
+ get {
+ return true;
+ }
+ }
+
+ public override void WriteTo(pb::ICodedOutputStream output) {
+ int size = SerializedSize;
+ string[] field_names = _divReplyFieldNames;
+ if (hasQuotient) {
+ output.WriteInt64(1, field_names[0], Quotient);
+ }
+ if (hasRemainder) {
+ output.WriteInt64(2, field_names[1], Remainder);
+ }
+ UnknownFields.WriteTo(output);
+ }
+
+ private int memoizedSerializedSize = -1;
+ public override int SerializedSize {
+ get {
+ int size = memoizedSerializedSize;
+ if (size != -1) return size;
+
+ size = 0;
+ if (hasQuotient) {
+ size += pb::CodedOutputStream.ComputeInt64Size(1, Quotient);
+ }
+ if (hasRemainder) {
+ size += pb::CodedOutputStream.ComputeInt64Size(2, Remainder);
+ }
+ size += UnknownFields.SerializedSize;
+ memoizedSerializedSize = size;
+ return size;
+ }
+ }
+
+ public static DivReply ParseFrom(pb::ByteString data) {
+ return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();
+ }
+ public static DivReply ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) {
+ return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();
+ }
+ public static DivReply ParseFrom(byte[] data) {
+ return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();
+ }
+ public static DivReply ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) {
+ return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();
+ }
+ public static DivReply ParseFrom(global::System.IO.Stream input) {
+ return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();
+ }
+ public static DivReply ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {
+ return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();
+ }
+ public static DivReply ParseDelimitedFrom(global::System.IO.Stream input) {
+ return CreateBuilder().MergeDelimitedFrom(input).BuildParsed();
+ }
+ public static DivReply ParseDelimitedFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {
+ return CreateBuilder().MergeDelimitedFrom(input, extensionRegistry).BuildParsed();
+ }
+ public static DivReply ParseFrom(pb::ICodedInputStream input) {
+ return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();
+ }
+ public static DivReply ParseFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) {
+ return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();
+ }
+ private DivReply MakeReadOnly() {
+ return this;
+ }
+
+ public static Builder CreateBuilder() { return new Builder(); }
+ public override Builder ToBuilder() { return CreateBuilder(this); }
+ public override Builder CreateBuilderForType() { return new Builder(); }
+ public static Builder CreateBuilder(DivReply prototype) {
+ return new Builder(prototype);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ public sealed partial class Builder : pb::GeneratedBuilder<DivReply, Builder> {
+ protected override Builder ThisBuilder {
+ get { return this; }
+ }
+ public Builder() {
+ result = DefaultInstance;
+ resultIsReadOnly = true;
+ }
+ internal Builder(DivReply cloneFrom) {
+ result = cloneFrom;
+ resultIsReadOnly = true;
+ }
+
+ private bool resultIsReadOnly;
+ private DivReply result;
+
+ private DivReply PrepareBuilder() {
+ if (resultIsReadOnly) {
+ DivReply original = result;
+ result = new DivReply();
+ resultIsReadOnly = false;
+ MergeFrom(original);
+ }
+ return result;
+ }
+
+ public override bool IsInitialized {
+ get { return result.IsInitialized; }
+ }
+
+ protected override DivReply MessageBeingBuilt {
+ get { return PrepareBuilder(); }
+ }
+
+ public override Builder Clear() {
+ result = DefaultInstance;
+ resultIsReadOnly = true;
+ return this;
+ }
+
+ public override Builder Clone() {
+ if (resultIsReadOnly) {
+ return new Builder(result);
+ } else {
+ return new Builder().MergeFrom(result);
+ }
+ }
+
+ public override pbd::MessageDescriptor DescriptorForType {
+ get { return global::math.DivReply.Descriptor; }
+ }
+
+ public override DivReply DefaultInstanceForType {
+ get { return global::math.DivReply.DefaultInstance; }
+ }
+
+ public override DivReply BuildPartial() {
+ if (resultIsReadOnly) {
+ return result;
+ }
+ resultIsReadOnly = true;
+ return result.MakeReadOnly();
+ }
+
+ public override Builder MergeFrom(pb::IMessage other) {
+ if (other is DivReply) {
+ return MergeFrom((DivReply) other);
+ } else {
+ base.MergeFrom(other);
+ return this;
+ }
+ }
+
+ public override Builder MergeFrom(DivReply other) {
+ if (other == global::math.DivReply.DefaultInstance) return this;
+ PrepareBuilder();
+ if (other.HasQuotient) {
+ Quotient = other.Quotient;
+ }
+ if (other.HasRemainder) {
+ Remainder = other.Remainder;
+ }
+ this.MergeUnknownFields(other.UnknownFields);
+ return this;
+ }
+
+ public override Builder MergeFrom(pb::ICodedInputStream input) {
+ return MergeFrom(input, pb::ExtensionRegistry.Empty);
+ }
+
+ public override Builder MergeFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) {
+ PrepareBuilder();
+ pb::UnknownFieldSet.Builder unknownFields = null;
+ uint tag;
+ string field_name;
+ while (input.ReadTag(out tag, out field_name)) {
+ if(tag == 0 && field_name != null) {
+ int field_ordinal = global::System.Array.BinarySearch(_divReplyFieldNames, field_name, global::System.StringComparer.Ordinal);
+ if(field_ordinal >= 0)
+ tag = _divReplyFieldTags[field_ordinal];
+ else {
+ if (unknownFields == null) {
+ unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);
+ }
+ ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name);
+ continue;
+ }
+ }
+ switch (tag) {
+ case 0: {
+ throw pb::InvalidProtocolBufferException.InvalidTag();
+ }
+ default: {
+ if (pb::WireFormat.IsEndGroupTag(tag)) {
+ if (unknownFields != null) {
+ this.UnknownFields = unknownFields.Build();
+ }
+ return this;
+ }
+ if (unknownFields == null) {
+ unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);
+ }
+ ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name);
+ break;
+ }
+ case 8: {
+ result.hasQuotient = input.ReadInt64(ref result.quotient_);
+ break;
+ }
+ case 16: {
+ result.hasRemainder = input.ReadInt64(ref result.remainder_);
+ break;
+ }
+ }
+ }
+
+ if (unknownFields != null) {
+ this.UnknownFields = unknownFields.Build();
+ }
+ return this;
+ }
+
+
+ public bool HasQuotient {
+ get { return result.hasQuotient; }
+ }
+ public long Quotient {
+ get { return result.Quotient; }
+ set { SetQuotient(value); }
+ }
+ public Builder SetQuotient(long value) {
+ PrepareBuilder();
+ result.hasQuotient = true;
+ result.quotient_ = value;
+ return this;
+ }
+ public Builder ClearQuotient() {
+ PrepareBuilder();
+ result.hasQuotient = false;
+ result.quotient_ = 0L;
+ return this;
+ }
+
+ public bool HasRemainder {
+ get { return result.hasRemainder; }
+ }
+ public long Remainder {
+ get { return result.Remainder; }
+ set { SetRemainder(value); }
+ }
+ public Builder SetRemainder(long value) {
+ PrepareBuilder();
+ result.hasRemainder = true;
+ result.remainder_ = value;
+ return this;
+ }
+ public Builder ClearRemainder() {
+ PrepareBuilder();
+ result.hasRemainder = false;
+ result.remainder_ = 0L;
+ return this;
+ }
+ }
+ static DivReply() {
+ object.ReferenceEquals(global::math.Proto.Math.Descriptor, null);
+ }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ public sealed partial class FibArgs : pb::GeneratedMessage<FibArgs, FibArgs.Builder> {
+ private FibArgs() { }
+ private static readonly FibArgs defaultInstance = new FibArgs().MakeReadOnly();
+ private static readonly string[] _fibArgsFieldNames = new string[] { "limit" };
+ private static readonly uint[] _fibArgsFieldTags = new uint[] { 8 };
+ public static FibArgs DefaultInstance {
+ get { return defaultInstance; }
+ }
+
+ public override FibArgs DefaultInstanceForType {
+ get { return DefaultInstance; }
+ }
+
+ protected override FibArgs ThisMessage {
+ get { return this; }
+ }
+
+ public static pbd::MessageDescriptor Descriptor {
+ get { return global::math.Proto.Math.internal__static_math_FibArgs__Descriptor; }
+ }
+
+ protected override pb::FieldAccess.FieldAccessorTable<FibArgs, FibArgs.Builder> InternalFieldAccessors {
+ get { return global::math.Proto.Math.internal__static_math_FibArgs__FieldAccessorTable; }
+ }
+
+ public const int LimitFieldNumber = 1;
+ private bool hasLimit;
+ private long limit_;
+ public bool HasLimit {
+ get { return hasLimit; }
+ }
+ public long Limit {
+ get { return limit_; }
+ }
+
+ public override bool IsInitialized {
+ get {
+ return true;
+ }
+ }
+
+ public override void WriteTo(pb::ICodedOutputStream output) {
+ int size = SerializedSize;
+ string[] field_names = _fibArgsFieldNames;
+ if (hasLimit) {
+ output.WriteInt64(1, field_names[0], Limit);
+ }
+ UnknownFields.WriteTo(output);
+ }
+
+ private int memoizedSerializedSize = -1;
+ public override int SerializedSize {
+ get {
+ int size = memoizedSerializedSize;
+ if (size != -1) return size;
+
+ size = 0;
+ if (hasLimit) {
+ size += pb::CodedOutputStream.ComputeInt64Size(1, Limit);
+ }
+ size += UnknownFields.SerializedSize;
+ memoizedSerializedSize = size;
+ return size;
+ }
+ }
+
+ public static FibArgs ParseFrom(pb::ByteString data) {
+ return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();
+ }
+ public static FibArgs ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) {
+ return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();
+ }
+ public static FibArgs ParseFrom(byte[] data) {
+ return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();
+ }
+ public static FibArgs ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) {
+ return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();
+ }
+ public static FibArgs ParseFrom(global::System.IO.Stream input) {
+ return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();
+ }
+ public static FibArgs ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {
+ return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();
+ }
+ public static FibArgs ParseDelimitedFrom(global::System.IO.Stream input) {
+ return CreateBuilder().MergeDelimitedFrom(input).BuildParsed();
+ }
+ public static FibArgs ParseDelimitedFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {
+ return CreateBuilder().MergeDelimitedFrom(input, extensionRegistry).BuildParsed();
+ }
+ public static FibArgs ParseFrom(pb::ICodedInputStream input) {
+ return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();
+ }
+ public static FibArgs ParseFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) {
+ return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();
+ }
+ private FibArgs MakeReadOnly() {
+ return this;
+ }
+
+ public static Builder CreateBuilder() { return new Builder(); }
+ public override Builder ToBuilder() { return CreateBuilder(this); }
+ public override Builder CreateBuilderForType() { return new Builder(); }
+ public static Builder CreateBuilder(FibArgs prototype) {
+ return new Builder(prototype);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ public sealed partial class Builder : pb::GeneratedBuilder<FibArgs, Builder> {
+ protected override Builder ThisBuilder {
+ get { return this; }
+ }
+ public Builder() {
+ result = DefaultInstance;
+ resultIsReadOnly = true;
+ }
+ internal Builder(FibArgs cloneFrom) {
+ result = cloneFrom;
+ resultIsReadOnly = true;
+ }
+
+ private bool resultIsReadOnly;
+ private FibArgs result;
+
+ private FibArgs PrepareBuilder() {
+ if (resultIsReadOnly) {
+ FibArgs original = result;
+ result = new FibArgs();
+ resultIsReadOnly = false;
+ MergeFrom(original);
+ }
+ return result;
+ }
+
+ public override bool IsInitialized {
+ get { return result.IsInitialized; }
+ }
+
+ protected override FibArgs MessageBeingBuilt {
+ get { return PrepareBuilder(); }
+ }
+
+ public override Builder Clear() {
+ result = DefaultInstance;
+ resultIsReadOnly = true;
+ return this;
+ }
+
+ public override Builder Clone() {
+ if (resultIsReadOnly) {
+ return new Builder(result);
+ } else {
+ return new Builder().MergeFrom(result);
+ }
+ }
+
+ public override pbd::MessageDescriptor DescriptorForType {
+ get { return global::math.FibArgs.Descriptor; }
+ }
+
+ public override FibArgs DefaultInstanceForType {
+ get { return global::math.FibArgs.DefaultInstance; }
+ }
+
+ public override FibArgs BuildPartial() {
+ if (resultIsReadOnly) {
+ return result;
+ }
+ resultIsReadOnly = true;
+ return result.MakeReadOnly();
+ }
+
+ public override Builder MergeFrom(pb::IMessage other) {
+ if (other is FibArgs) {
+ return MergeFrom((FibArgs) other);
+ } else {
+ base.MergeFrom(other);
+ return this;
+ }
+ }
+
+ public override Builder MergeFrom(FibArgs other) {
+ if (other == global::math.FibArgs.DefaultInstance) return this;
+ PrepareBuilder();
+ if (other.HasLimit) {
+ Limit = other.Limit;
+ }
+ this.MergeUnknownFields(other.UnknownFields);
+ return this;
+ }
+
+ public override Builder MergeFrom(pb::ICodedInputStream input) {
+ return MergeFrom(input, pb::ExtensionRegistry.Empty);
+ }
+
+ public override Builder MergeFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) {
+ PrepareBuilder();
+ pb::UnknownFieldSet.Builder unknownFields = null;
+ uint tag;
+ string field_name;
+ while (input.ReadTag(out tag, out field_name)) {
+ if(tag == 0 && field_name != null) {
+ int field_ordinal = global::System.Array.BinarySearch(_fibArgsFieldNames, field_name, global::System.StringComparer.Ordinal);
+ if(field_ordinal >= 0)
+ tag = _fibArgsFieldTags[field_ordinal];
+ else {
+ if (unknownFields == null) {
+ unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);
+ }
+ ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name);
+ continue;
+ }
+ }
+ switch (tag) {
+ case 0: {
+ throw pb::InvalidProtocolBufferException.InvalidTag();
+ }
+ default: {
+ if (pb::WireFormat.IsEndGroupTag(tag)) {
+ if (unknownFields != null) {
+ this.UnknownFields = unknownFields.Build();
+ }
+ return this;
+ }
+ if (unknownFields == null) {
+ unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);
+ }
+ ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name);
+ break;
+ }
+ case 8: {
+ result.hasLimit = input.ReadInt64(ref result.limit_);
+ break;
+ }
+ }
+ }
+
+ if (unknownFields != null) {
+ this.UnknownFields = unknownFields.Build();
+ }
+ return this;
+ }
+
+
+ public bool HasLimit {
+ get { return result.hasLimit; }
+ }
+ public long Limit {
+ get { return result.Limit; }
+ set { SetLimit(value); }
+ }
+ public Builder SetLimit(long value) {
+ PrepareBuilder();
+ result.hasLimit = true;
+ result.limit_ = value;
+ return this;
+ }
+ public Builder ClearLimit() {
+ PrepareBuilder();
+ result.hasLimit = false;
+ result.limit_ = 0L;
+ return this;
+ }
+ }
+ static FibArgs() {
+ object.ReferenceEquals(global::math.Proto.Math.Descriptor, null);
+ }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ public sealed partial class Num : pb::GeneratedMessage<Num, Num.Builder> {
+ private Num() { }
+ private static readonly Num defaultInstance = new Num().MakeReadOnly();
+ private static readonly string[] _numFieldNames = new string[] { "num" };
+ private static readonly uint[] _numFieldTags = new uint[] { 8 };
+ public static Num DefaultInstance {
+ get { return defaultInstance; }
+ }
+
+ public override Num DefaultInstanceForType {
+ get { return DefaultInstance; }
+ }
+
+ protected override Num ThisMessage {
+ get { return this; }
+ }
+
+ public static pbd::MessageDescriptor Descriptor {
+ get { return global::math.Proto.Math.internal__static_math_Num__Descriptor; }
+ }
+
+ protected override pb::FieldAccess.FieldAccessorTable<Num, Num.Builder> InternalFieldAccessors {
+ get { return global::math.Proto.Math.internal__static_math_Num__FieldAccessorTable; }
+ }
+
+ public const int Num_FieldNumber = 1;
+ private bool hasNum_;
+ private long num_;
+ public bool HasNum_ {
+ get { return hasNum_; }
+ }
+ public long Num_ {
+ get { return num_; }
+ }
+
+ public override bool IsInitialized {
+ get {
+ return true;
+ }
+ }
+
+ public override void WriteTo(pb::ICodedOutputStream output) {
+ int size = SerializedSize;
+ string[] field_names = _numFieldNames;
+ if (hasNum_) {
+ output.WriteInt64(1, field_names[0], Num_);
+ }
+ UnknownFields.WriteTo(output);
+ }
+
+ private int memoizedSerializedSize = -1;
+ public override int SerializedSize {
+ get {
+ int size = memoizedSerializedSize;
+ if (size != -1) return size;
+
+ size = 0;
+ if (hasNum_) {
+ size += pb::CodedOutputStream.ComputeInt64Size(1, Num_);
+ }
+ size += UnknownFields.SerializedSize;
+ memoizedSerializedSize = size;
+ return size;
+ }
+ }
+
+ public static Num ParseFrom(pb::ByteString data) {
+ return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();
+ }
+ public static Num ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) {
+ return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();
+ }
+ public static Num ParseFrom(byte[] data) {
+ return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();
+ }
+ public static Num ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) {
+ return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();
+ }
+ public static Num ParseFrom(global::System.IO.Stream input) {
+ return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();
+ }
+ public static Num ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {
+ return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();
+ }
+ public static Num ParseDelimitedFrom(global::System.IO.Stream input) {
+ return CreateBuilder().MergeDelimitedFrom(input).BuildParsed();
+ }
+ public static Num ParseDelimitedFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {
+ return CreateBuilder().MergeDelimitedFrom(input, extensionRegistry).BuildParsed();
+ }
+ public static Num ParseFrom(pb::ICodedInputStream input) {
+ return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();
+ }
+ public static Num ParseFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) {
+ return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();
+ }
+ private Num MakeReadOnly() {
+ return this;
+ }
+
+ public static Builder CreateBuilder() { return new Builder(); }
+ public override Builder ToBuilder() { return CreateBuilder(this); }
+ public override Builder CreateBuilderForType() { return new Builder(); }
+ public static Builder CreateBuilder(Num prototype) {
+ return new Builder(prototype);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ public sealed partial class Builder : pb::GeneratedBuilder<Num, Builder> {
+ protected override Builder ThisBuilder {
+ get { return this; }
+ }
+ public Builder() {
+ result = DefaultInstance;
+ resultIsReadOnly = true;
+ }
+ internal Builder(Num cloneFrom) {
+ result = cloneFrom;
+ resultIsReadOnly = true;
+ }
+
+ private bool resultIsReadOnly;
+ private Num result;
+
+ private Num PrepareBuilder() {
+ if (resultIsReadOnly) {
+ Num original = result;
+ result = new Num();
+ resultIsReadOnly = false;
+ MergeFrom(original);
+ }
+ return result;
+ }
+
+ public override bool IsInitialized {
+ get { return result.IsInitialized; }
+ }
+
+ protected override Num MessageBeingBuilt {
+ get { return PrepareBuilder(); }
+ }
+
+ public override Builder Clear() {
+ result = DefaultInstance;
+ resultIsReadOnly = true;
+ return this;
+ }
+
+ public override Builder Clone() {
+ if (resultIsReadOnly) {
+ return new Builder(result);
+ } else {
+ return new Builder().MergeFrom(result);
+ }
+ }
+
+ public override pbd::MessageDescriptor DescriptorForType {
+ get { return global::math.Num.Descriptor; }
+ }
+
+ public override Num DefaultInstanceForType {
+ get { return global::math.Num.DefaultInstance; }
+ }
+
+ public override Num BuildPartial() {
+ if (resultIsReadOnly) {
+ return result;
+ }
+ resultIsReadOnly = true;
+ return result.MakeReadOnly();
+ }
+
+ public override Builder MergeFrom(pb::IMessage other) {
+ if (other is Num) {
+ return MergeFrom((Num) other);
+ } else {
+ base.MergeFrom(other);
+ return this;
+ }
+ }
+
+ public override Builder MergeFrom(Num other) {
+ if (other == global::math.Num.DefaultInstance) return this;
+ PrepareBuilder();
+ if (other.HasNum_) {
+ Num_ = other.Num_;
+ }
+ this.MergeUnknownFields(other.UnknownFields);
+ return this;
+ }
+
+ public override Builder MergeFrom(pb::ICodedInputStream input) {
+ return MergeFrom(input, pb::ExtensionRegistry.Empty);
+ }
+
+ public override Builder MergeFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) {
+ PrepareBuilder();
+ pb::UnknownFieldSet.Builder unknownFields = null;
+ uint tag;
+ string field_name;
+ while (input.ReadTag(out tag, out field_name)) {
+ if(tag == 0 && field_name != null) {
+ int field_ordinal = global::System.Array.BinarySearch(_numFieldNames, field_name, global::System.StringComparer.Ordinal);
+ if(field_ordinal >= 0)
+ tag = _numFieldTags[field_ordinal];
+ else {
+ if (unknownFields == null) {
+ unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);
+ }
+ ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name);
+ continue;
+ }
+ }
+ switch (tag) {
+ case 0: {
+ throw pb::InvalidProtocolBufferException.InvalidTag();
+ }
+ default: {
+ if (pb::WireFormat.IsEndGroupTag(tag)) {
+ if (unknownFields != null) {
+ this.UnknownFields = unknownFields.Build();
+ }
+ return this;
+ }
+ if (unknownFields == null) {
+ unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);
+ }
+ ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name);
+ break;
+ }
+ case 8: {
+ result.hasNum_ = input.ReadInt64(ref result.num_);
+ break;
+ }
+ }
+ }
+
+ if (unknownFields != null) {
+ this.UnknownFields = unknownFields.Build();
+ }
+ return this;
+ }
+
+
+ public bool HasNum_ {
+ get { return result.hasNum_; }
+ }
+ public long Num_ {
+ get { return result.Num_; }
+ set { SetNum_(value); }
+ }
+ public Builder SetNum_(long value) {
+ PrepareBuilder();
+ result.hasNum_ = true;
+ result.num_ = value;
+ return this;
+ }
+ public Builder ClearNum_() {
+ PrepareBuilder();
+ result.hasNum_ = false;
+ result.num_ = 0L;
+ return this;
+ }
+ }
+ static Num() {
+ object.ReferenceEquals(global::math.Proto.Math.Descriptor, null);
+ }
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ public sealed partial class FibReply : pb::GeneratedMessage<FibReply, FibReply.Builder> {
+ private FibReply() { }
+ private static readonly FibReply defaultInstance = new FibReply().MakeReadOnly();
+ private static readonly string[] _fibReplyFieldNames = new string[] { "count" };
+ private static readonly uint[] _fibReplyFieldTags = new uint[] { 8 };
+ public static FibReply DefaultInstance {
+ get { return defaultInstance; }
+ }
+
+ public override FibReply DefaultInstanceForType {
+ get { return DefaultInstance; }
+ }
+
+ protected override FibReply ThisMessage {
+ get { return this; }
+ }
+
+ public static pbd::MessageDescriptor Descriptor {
+ get { return global::math.Proto.Math.internal__static_math_FibReply__Descriptor; }
+ }
+
+ protected override pb::FieldAccess.FieldAccessorTable<FibReply, FibReply.Builder> InternalFieldAccessors {
+ get { return global::math.Proto.Math.internal__static_math_FibReply__FieldAccessorTable; }
+ }
+
+ public const int CountFieldNumber = 1;
+ private bool hasCount;
+ private long count_;
+ public bool HasCount {
+ get { return hasCount; }
+ }
+ public long Count {
+ get { return count_; }
+ }
+
+ public override bool IsInitialized {
+ get {
+ return true;
+ }
+ }
+
+ public override void WriteTo(pb::ICodedOutputStream output) {
+ int size = SerializedSize;
+ string[] field_names = _fibReplyFieldNames;
+ if (hasCount) {
+ output.WriteInt64(1, field_names[0], Count);
+ }
+ UnknownFields.WriteTo(output);
+ }
+
+ private int memoizedSerializedSize = -1;
+ public override int SerializedSize {
+ get {
+ int size = memoizedSerializedSize;
+ if (size != -1) return size;
+
+ size = 0;
+ if (hasCount) {
+ size += pb::CodedOutputStream.ComputeInt64Size(1, Count);
+ }
+ size += UnknownFields.SerializedSize;
+ memoizedSerializedSize = size;
+ return size;
+ }
+ }
+
+ public static FibReply ParseFrom(pb::ByteString data) {
+ return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();
+ }
+ public static FibReply ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) {
+ return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();
+ }
+ public static FibReply ParseFrom(byte[] data) {
+ return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();
+ }
+ public static FibReply ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) {
+ return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();
+ }
+ public static FibReply ParseFrom(global::System.IO.Stream input) {
+ return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();
+ }
+ public static FibReply ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {
+ return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();
+ }
+ public static FibReply ParseDelimitedFrom(global::System.IO.Stream input) {
+ return CreateBuilder().MergeDelimitedFrom(input).BuildParsed();
+ }
+ public static FibReply ParseDelimitedFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {
+ return CreateBuilder().MergeDelimitedFrom(input, extensionRegistry).BuildParsed();
+ }
+ public static FibReply ParseFrom(pb::ICodedInputStream input) {
+ return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();
+ }
+ public static FibReply ParseFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) {
+ return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();
+ }
+ private FibReply MakeReadOnly() {
+ return this;
+ }
+
+ public static Builder CreateBuilder() { return new Builder(); }
+ public override Builder ToBuilder() { return CreateBuilder(this); }
+ public override Builder CreateBuilderForType() { return new Builder(); }
+ public static Builder CreateBuilder(FibReply prototype) {
+ return new Builder(prototype);
+ }
+
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ public sealed partial class Builder : pb::GeneratedBuilder<FibReply, Builder> {
+ protected override Builder ThisBuilder {
+ get { return this; }
+ }
+ public Builder() {
+ result = DefaultInstance;
+ resultIsReadOnly = true;
+ }
+ internal Builder(FibReply cloneFrom) {
+ result = cloneFrom;
+ resultIsReadOnly = true;
+ }
+
+ private bool resultIsReadOnly;
+ private FibReply result;
+
+ private FibReply PrepareBuilder() {
+ if (resultIsReadOnly) {
+ FibReply original = result;
+ result = new FibReply();
+ resultIsReadOnly = false;
+ MergeFrom(original);
+ }
+ return result;
+ }
+
+ public override bool IsInitialized {
+ get { return result.IsInitialized; }
+ }
+
+ protected override FibReply MessageBeingBuilt {
+ get { return PrepareBuilder(); }
+ }
+
+ public override Builder Clear() {
+ result = DefaultInstance;
+ resultIsReadOnly = true;
+ return this;
+ }
+
+ public override Builder Clone() {
+ if (resultIsReadOnly) {
+ return new Builder(result);
+ } else {
+ return new Builder().MergeFrom(result);
+ }
+ }
+
+ public override pbd::MessageDescriptor DescriptorForType {
+ get { return global::math.FibReply.Descriptor; }
+ }
+
+ public override FibReply DefaultInstanceForType {
+ get { return global::math.FibReply.DefaultInstance; }
+ }
+
+ public override FibReply BuildPartial() {
+ if (resultIsReadOnly) {
+ return result;
+ }
+ resultIsReadOnly = true;
+ return result.MakeReadOnly();
+ }
+
+ public override Builder MergeFrom(pb::IMessage other) {
+ if (other is FibReply) {
+ return MergeFrom((FibReply) other);
+ } else {
+ base.MergeFrom(other);
+ return this;
+ }
+ }
+
+ public override Builder MergeFrom(FibReply other) {
+ if (other == global::math.FibReply.DefaultInstance) return this;
+ PrepareBuilder();
+ if (other.HasCount) {
+ Count = other.Count;
+ }
+ this.MergeUnknownFields(other.UnknownFields);
+ return this;
+ }
+
+ public override Builder MergeFrom(pb::ICodedInputStream input) {
+ return MergeFrom(input, pb::ExtensionRegistry.Empty);
+ }
+
+ public override Builder MergeFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) {
+ PrepareBuilder();
+ pb::UnknownFieldSet.Builder unknownFields = null;
+ uint tag;
+ string field_name;
+ while (input.ReadTag(out tag, out field_name)) {
+ if(tag == 0 && field_name != null) {
+ int field_ordinal = global::System.Array.BinarySearch(_fibReplyFieldNames, field_name, global::System.StringComparer.Ordinal);
+ if(field_ordinal >= 0)
+ tag = _fibReplyFieldTags[field_ordinal];
+ else {
+ if (unknownFields == null) {
+ unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);
+ }
+ ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name);
+ continue;
+ }
+ }
+ switch (tag) {
+ case 0: {
+ throw pb::InvalidProtocolBufferException.InvalidTag();
+ }
+ default: {
+ if (pb::WireFormat.IsEndGroupTag(tag)) {
+ if (unknownFields != null) {
+ this.UnknownFields = unknownFields.Build();
+ }
+ return this;
+ }
+ if (unknownFields == null) {
+ unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);
+ }
+ ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name);
+ break;
+ }
+ case 8: {
+ result.hasCount = input.ReadInt64(ref result.count_);
+ break;
+ }
+ }
+ }
+
+ if (unknownFields != null) {
+ this.UnknownFields = unknownFields.Build();
+ }
+ return this;
+ }
+
+
+ public bool HasCount {
+ get { return result.hasCount; }
+ }
+ public long Count {
+ get { return result.Count; }
+ set { SetCount(value); }
+ }
+ public Builder SetCount(long value) {
+ PrepareBuilder();
+ result.hasCount = true;
+ result.count_ = value;
+ return this;
+ }
+ public Builder ClearCount() {
+ PrepareBuilder();
+ result.hasCount = false;
+ result.count_ = 0L;
+ return this;
+ }
+ }
+ static FibReply() {
+ object.ReferenceEquals(global::math.Proto.Math.Descriptor, null);
+ }
+ }
+
+ #endregion
+
+ #region Services
+ /*
+ * Service generation is now disabled by default, use the following option to enable:
+ * option (google.protobuf.csharp_file_options).service_generator_type = GENERIC;
+ */
+ #endregion
+
+}
+
+#endregion Designer generated code
diff --git a/src/csharp/GrpcApi/MathServiceClientStub.cs b/src/csharp/GrpcApi/MathServiceClientStub.cs
new file mode 100644
index 0000000000..493c186b8e
--- /dev/null
+++ b/src/csharp/GrpcApi/MathServiceClientStub.cs
@@ -0,0 +1,75 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+using System.Reactive.Linq;
+using Google.GRPC.Core;
+
+namespace math
+{
+ /// <summary>
+ /// Implementation of math service stub (this is handwritten version of code
+ /// that will normally be generated).
+ /// </summary>
+ public class MathServiceClientStub : IMathServiceClient
+ {
+ readonly Channel channel;
+ readonly TimeSpan methodTimeout;
+
+ public MathServiceClientStub(Channel channel, TimeSpan methodTimeout)
+ {
+ this.channel = channel;
+ this.methodTimeout = methodTimeout;
+ }
+
+ public DivReply Div(DivArgs args, CancellationToken token = default(CancellationToken))
+ {
+ var call = new Google.GRPC.Core.Call<DivArgs, DivReply>("/math.Math/Div", Serialize_DivArgs, Deserialize_DivReply, methodTimeout, channel);
+ return Calls.BlockingUnaryCall(call, args, token);
+ }
+
+ public Task<DivReply> DivAsync(DivArgs args, CancellationToken token = default(CancellationToken))
+ {
+ var call = new Google.GRPC.Core.Call<DivArgs, DivReply>("/math.Math/Div", Serialize_DivArgs, Deserialize_DivReply, methodTimeout, channel);
+ return Calls.AsyncUnaryCall(call, args, token);
+ }
+
+ public Task Fib(FibArgs args, IObserver<Num> outputs, CancellationToken token = default(CancellationToken))
+ {
+ var call = new Google.GRPC.Core.Call<FibArgs, Num>("/math.Math/Fib", Serialize_FibArgs, Deserialize_Num, methodTimeout, channel);
+ return Calls.AsyncServerStreamingCall(call, args, outputs, token);
+ }
+
+ public ClientStreamingAsyncResult<Num, Num> Sum(CancellationToken token = default(CancellationToken))
+ {
+ var call = new Google.GRPC.Core.Call<Num, Num>("/math.Math/Sum", Serialize_Num, Deserialize_Num, methodTimeout, channel);
+ return Calls.AsyncClientStreamingCall(call, token);
+ }
+
+ public IObserver<DivArgs> DivMany(IObserver<DivReply> outputs, CancellationToken token = default(CancellationToken))
+ {
+ var call = new Google.GRPC.Core.Call<DivArgs, DivReply>("/math.Math/DivMany", Serialize_DivArgs, Deserialize_DivReply, methodTimeout, channel);
+ return Calls.DuplexStreamingCall(call, outputs, token);
+ }
+
+ private static byte[] Serialize_DivArgs(DivArgs arg) {
+ return arg.ToByteArray();
+ }
+
+ private static byte[] Serialize_FibArgs(FibArgs arg) {
+ return arg.ToByteArray();
+ }
+
+ private static byte[] Serialize_Num(Num arg) {
+ return arg.ToByteArray();
+ }
+
+ private static DivReply Deserialize_DivReply(byte[] payload) {
+ return DivReply.CreateBuilder().MergeFrom(payload).Build();
+ }
+
+ private static Num Deserialize_Num(byte[] payload) {
+ return Num.CreateBuilder().MergeFrom(payload).Build();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/csharp/GrpcApi/Messages.cs b/src/csharp/GrpcApi/Messages.cs
new file mode 100644
index 0000000000..b08816bdb7
--- /dev/null
+++ b/src/csharp/GrpcApi/Messages.cs
@@ -0,0 +1,35 @@
+//using System;
+
+//namespace Google.GRPC.Examples.Math
+//{
+// // Messages in this file are placeholders for actual protobuf message classes
+// // that will be generated from math.proto file.
+//
+// public class DivArgs
+// {
+// public long Dividend{ get; set; }
+// public long Divisor { get; set; }
+// }
+//
+// public class DivReply
+// {
+// public long Quotient { get; set; }
+// public long Remainder { get; set; }
+// }
+//
+// public class FibArgs
+// {
+// public long Limit { get; set; }
+// }
+//
+// public class Number
+// {
+// public long Num { get; set; }
+// }
+//
+// public class FibReply
+// {
+// public long Count { get; set; }
+// }
+//}
+
diff --git a/src/csharp/GrpcApi/Properties/AssemblyInfo.cs b/src/csharp/GrpcApi/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..725f12c486
--- /dev/null
+++ b/src/csharp/GrpcApi/Properties/AssemblyInfo.cs
@@ -0,0 +1,22 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+// Information about this assembly is defined by the following attributes.
+// Change them to the values specific to your project.
+[assembly: AssemblyTitle ("GrpcApi")]
+[assembly: AssemblyDescription ("")]
+[assembly: AssemblyConfiguration ("")]
+[assembly: AssemblyCompany ("")]
+[assembly: AssemblyProduct ("")]
+[assembly: AssemblyCopyright ("jtattermusch")]
+[assembly: AssemblyTrademark ("")]
+[assembly: AssemblyCulture ("")]
+// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
+// The form "{Major}.{Minor}.*" will automatically update the build and revision,
+// and "{Major}.{Minor}.{Build}.*" will update just the revision.
+[assembly: AssemblyVersion ("1.0.*")]
+// The following attributes are used to specify the signing key for the assembly,
+// if desired. See the Mono documentation for more information about signing.
+//[assembly: AssemblyDelaySign(false)]
+//[assembly: AssemblyKeyFile("")]
+
diff --git a/src/csharp/GrpcApi/RecordingObserver.cs b/src/csharp/GrpcApi/RecordingObserver.cs
new file mode 100644
index 0000000000..8ba3787905
--- /dev/null
+++ b/src/csharp/GrpcApi/RecordingObserver.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+
+namespace math
+{
+ public class RecordingObserver<T> : IObserver<T>
+ {
+ TaskCompletionSource<List<T>> tcs = new TaskCompletionSource<List<T>>();
+ List<T> data = new List<T>();
+
+ public void OnCompleted()
+ {
+ tcs.SetResult(data);
+ }
+
+ public void OnError(Exception error)
+ {
+ tcs.SetException(error);
+ }
+
+ public void OnNext(T value)
+ {
+ data.Add(value);
+ }
+
+ public Task<List<T>> ToList() {
+ return tcs.Task;
+ }
+ }
+}
+
diff --git a/src/csharp/GrpcApi/math.proto b/src/csharp/GrpcApi/math.proto
new file mode 100755
index 0000000000..e98b99e002
--- /dev/null
+++ b/src/csharp/GrpcApi/math.proto
@@ -0,0 +1,50 @@
+syntax = "proto2";
+
+package math;
+
+message DivArgs {
+ optional int64 dividend = 1;
+ optional int64 divisor = 2;
+}
+
+message DivReply {
+ optional int64 quotient = 1;
+ optional int64 remainder = 2;
+}
+
+message FibArgs {
+ optional int64 limit = 1;
+}
+
+message Num {
+ optional int64 num = 1;
+}
+
+message FibReply {
+ optional int64 count = 1;
+}
+
+service Math {
+ // Div divides args.dividend by args.divisor and returns the quotient and
+ // remainder.
+ rpc Div (DivArgs) returns (DivReply) {
+ }
+
+ // DivMany accepts an arbitrary number of division args from the client stream
+ // and sends back the results in the reply stream. The stream continues until
+ // the client closes its end; the server does the same after sending all the
+ // replies. The stream ends immediately if either end aborts.
+ rpc DivMany (stream DivArgs) returns (stream DivReply) {
+ }
+
+ // Fib generates numbers in the Fibonacci sequence. If args.limit > 0, Fib
+ // generates up to limit numbers; otherwise it continues until the call is
+ // canceled. Unlike Fib above, Fib has no final FibReply.
+ rpc Fib (FibArgs) returns (stream Num) {
+ }
+
+ // Sum sums a stream of numbers, returning the final result once the stream
+ // is closed.
+ rpc Sum (stream Num) returns (Num) {
+ }
+}
diff --git a/src/csharp/GrpcCore/.gitignore b/src/csharp/GrpcCore/.gitignore
new file mode 100644
index 0000000000..ba077a4031
--- /dev/null
+++ b/src/csharp/GrpcCore/.gitignore
@@ -0,0 +1 @@
+bin
diff --git a/src/csharp/GrpcCore/Call.cs b/src/csharp/GrpcCore/Call.cs
new file mode 100644
index 0000000000..bf257e5d59
--- /dev/null
+++ b/src/csharp/GrpcCore/Call.cs
@@ -0,0 +1,69 @@
+using System;
+using Google.GRPC.Core.Internal;
+
+namespace Google.GRPC.Core
+{
+ public class Call<TRequest, TResponse>
+ {
+ readonly string methodName;
+ readonly Func<TRequest, byte[]> requestSerializer;
+ readonly Func<byte[], TResponse> responseDeserializer;
+ readonly TimeSpan timeout;
+ readonly Channel channel;
+
+ // TODO: channel param should be removed in the future.
+ public Call(string methodName,
+ Func<TRequest, byte[]> requestSerializer,
+ Func<byte[], TResponse> responseDeserializer,
+ TimeSpan timeout,
+ Channel channel) {
+ this.methodName = methodName;
+ this.requestSerializer = requestSerializer;
+ this.responseDeserializer = responseDeserializer;
+ this.timeout = timeout;
+ this.channel = channel;
+ }
+
+
+ public Channel Channel
+ {
+ get
+ {
+ return this.channel;
+ }
+ }
+
+ public TimeSpan Timeout
+ {
+ get
+ {
+ return this.timeout;
+ }
+ }
+
+ public string MethodName
+ {
+ get
+ {
+ return this.methodName;
+ }
+ }
+
+ public Func<TRequest, byte[]> RequestSerializer
+ {
+ get
+ {
+ return this.requestSerializer;
+ }
+ }
+
+ public Func<byte[], TResponse> ResponseDeserializer
+ {
+ get
+ {
+ return this.responseDeserializer;
+ }
+ }
+ }
+}
+
diff --git a/src/csharp/GrpcCore/Calls.cs b/src/csharp/GrpcCore/Calls.cs
new file mode 100644
index 0000000000..c3e51cb478
--- /dev/null
+++ b/src/csharp/GrpcCore/Calls.cs
@@ -0,0 +1,85 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Google.GRPC.Core.Internal;
+
+namespace Google.GRPC.Core
+{
+ // NOTE: this class is work-in-progress
+
+ /// <summary>
+ /// Helper methods for generated stubs to make RPC calls.
+ /// </summary>
+ public static class Calls
+ {
+ public static TResponse BlockingUnaryCall<TRequest, TResponse>(Call<TRequest, TResponse> call, TRequest req, CancellationToken token)
+ {
+ //TODO: implement this in real synchronous style once new GRPC C core API is available.
+ return AsyncUnaryCall(call, req, token).Result;
+ }
+
+ public static async Task<TResponse> AsyncUnaryCall<TRequest, TResponse>(Call<TRequest, TResponse> call, TRequest req, CancellationToken token)
+ {
+ var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestSerializer, call.ResponseDeserializer);
+ asyncCall.Initialize(call.Channel, call.MethodName);
+ asyncCall.Start(false, GetCompletionQueue());
+
+ await asyncCall.WriteAsync(req);
+ await asyncCall.WritesCompletedAsync();
+
+ TResponse response = await asyncCall.ReadAsync();
+
+ Status status = await asyncCall.Finished;
+
+ if (status.StatusCode != StatusCode.GRPC_STATUS_OK)
+ {
+ throw new RpcException(status);
+ }
+ return response;
+ }
+
+ public static async Task AsyncServerStreamingCall<TRequest, TResponse>(Call<TRequest, TResponse> call, TRequest req, IObserver<TResponse> outputs, CancellationToken token)
+ {
+ var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestSerializer, call.ResponseDeserializer);
+ asyncCall.Initialize(call.Channel, call.MethodName);
+ asyncCall.Start(false, GetCompletionQueue());
+
+ asyncCall.StartReadingToStream(outputs);
+
+ await asyncCall.WriteAsync(req);
+ await asyncCall.WritesCompletedAsync();
+ }
+
+ public static ClientStreamingAsyncResult<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(Call<TRequest, TResponse> call, CancellationToken token)
+ {
+ var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestSerializer, call.ResponseDeserializer);
+ asyncCall.Initialize(call.Channel, call.MethodName);
+ asyncCall.Start(false, GetCompletionQueue());
+
+ var task = asyncCall.ReadAsync();
+ var inputs = new StreamingInputObserver<TRequest, TResponse>(asyncCall);
+ return new ClientStreamingAsyncResult<TRequest, TResponse>(task, inputs);
+ }
+
+ public static TResponse BlockingClientStreamingCall<TRequest, TResponse>(Call<TRequest, TResponse> call, IObservable<TRequest> inputs, CancellationToken token)
+ {
+ throw new NotImplementedException();
+ }
+
+ public static IObserver<TRequest> DuplexStreamingCall<TRequest, TResponse>(Call<TRequest, TResponse> call, IObserver<TResponse> outputs, CancellationToken token)
+ {
+ var asyncCall = new AsyncCall<TRequest, TResponse>(call.RequestSerializer, call.ResponseDeserializer);
+ asyncCall.Initialize(call.Channel, call.MethodName);
+ asyncCall.Start(false, GetCompletionQueue());
+
+ asyncCall.StartReadingToStream(outputs);
+ var inputs = new StreamingInputObserver<TRequest, TResponse>(asyncCall);
+ return inputs;
+ }
+
+ private static CompletionQueueSafeHandle GetCompletionQueue() {
+ return GrpcEnvironment.ThreadPool.CompletionQueue;
+ }
+ }
+}
+
diff --git a/src/csharp/GrpcCore/Channel.cs b/src/csharp/GrpcCore/Channel.cs
new file mode 100644
index 0000000000..b0d8beeb7b
--- /dev/null
+++ b/src/csharp/GrpcCore/Channel.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+using Google.GRPC.Core.Internal;
+
+namespace Google.GRPC.Core
+{
+ public class Channel : IDisposable
+ {
+ /// <summary>
+ /// Make sure GPRC environment is initialized before any channels get used.
+ /// </summary>
+ static Channel() {
+ GrpcEnvironment.EnsureInitialized();
+ }
+
+ readonly ChannelSafeHandle handle;
+ readonly String target;
+
+ // TODO: add way how to create grpc_secure_channel....
+ // TODO: add support for channel args...
+ public Channel(string target)
+ {
+ this.handle = ChannelSafeHandle.Create(target, IntPtr.Zero);
+ this.target = target;
+ }
+
+ internal ChannelSafeHandle Handle
+ {
+ get
+ {
+ return this.handle;
+ }
+ }
+
+ public string Target
+ {
+ get
+ {
+ return this.target;
+ }
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (handle != null && !handle.IsInvalid)
+ {
+ handle.Dispose();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/csharp/GrpcCore/ClientStreamingAsyncResult.cs b/src/csharp/GrpcCore/ClientStreamingAsyncResult.cs
new file mode 100644
index 0000000000..9e7312c1fa
--- /dev/null
+++ b/src/csharp/GrpcCore/ClientStreamingAsyncResult.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Threading.Tasks;
+
+namespace Google.GRPC.Core
+{
+ /// <summary>
+ /// Return type for client streaming async method.
+ /// </summary>
+ public struct ClientStreamingAsyncResult<TRequest, TResponse>
+ {
+ readonly Task<TResponse> task;
+ readonly IObserver<TRequest> inputs;
+
+ public ClientStreamingAsyncResult(Task<TResponse> task, IObserver<TRequest> inputs)
+ {
+ this.task = task;
+ this.inputs = inputs;
+ }
+
+ public Task<TResponse> Task
+ {
+ get
+ {
+ return this.task;
+ }
+ }
+
+ public IObserver<TRequest> Inputs
+ {
+ get
+ {
+ return this.inputs;
+ }
+ }
+ }
+}
+
diff --git a/src/csharp/GrpcCore/GrpcCore.csproj b/src/csharp/GrpcCore/GrpcCore.csproj
new file mode 100644
index 0000000000..f0c84e78ea
--- /dev/null
+++ b/src/csharp/GrpcCore/GrpcCore.csproj
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>10.0.0</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <RootNamespace>GrpcCore</RootNamespace>
+ <AssemblyName>GrpcCore</AssemblyName>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug</OutputPath>
+ <DefineConstants>DEBUG;</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <ConsolePause>false</ConsolePause>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>full</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release</OutputPath>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <ConsolePause>false</ConsolePause>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="RpcException.cs" />
+ <Compile Include="Calls.cs" />
+ <Compile Include="Call.cs" />
+ <Compile Include="ClientStreamingAsyncResult.cs" />
+ <Compile Include="GrpcEnvironment.cs" />
+ <Compile Include="Status.cs" />
+ <Compile Include="StatusCode.cs" />
+ <Compile Include="Server.cs" />
+ <Compile Include="Channel.cs" />
+ <Compile Include="Internal\CallSafeHandle.cs" />
+ <Compile Include="Internal\ChannelSafeHandle.cs" />
+ <Compile Include="Internal\CompletionQueueSafeHandle.cs" />
+ <Compile Include="Internal\Enums.cs" />
+ <Compile Include="Internal\Event.cs" />
+ <Compile Include="Internal\SafeHandleZeroIsInvalid.cs" />
+ <Compile Include="Internal\Timespec.cs" />
+ <Compile Include="Internal\GrpcThreadPool.cs" />
+ <Compile Include="Internal\AsyncCall.cs" />
+ <Compile Include="Internal\ServerSafeHandle.cs" />
+ <Compile Include="Internal\StreamingInputObserver.cs" />
+ </ItemGroup>
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+ <ItemGroup>
+ <Folder Include="Internal\" />
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/src/csharp/GrpcCore/GrpcEnvironment.cs b/src/csharp/GrpcCore/GrpcEnvironment.cs
new file mode 100644
index 0000000000..7a644f4961
--- /dev/null
+++ b/src/csharp/GrpcCore/GrpcEnvironment.cs
@@ -0,0 +1,91 @@
+using System;
+using Google.GRPC.Core.Internal;
+using System.Runtime.InteropServices;
+
+namespace Google.GRPC.Core
+{
+ /// <summary>
+ /// Encapsulates initialization and shutdown of GRPC C core library.
+ /// You should not need to initialize it manually, as static constructors
+ /// should load the library when needed.
+ /// </summary>
+ public static class GrpcEnvironment
+ {
+ const int THREAD_POOL_SIZE = 1;
+
+ [DllImport("libgrpc.so")]
+ static extern void grpc_init();
+
+ [DllImport("libgrpc.so")]
+ static extern void grpc_shutdown();
+
+ static object staticLock = new object();
+ static bool initCalled = false;
+ static bool shutdownCalled = false;
+
+ static GrpcThreadPool threadPool = new GrpcThreadPool(THREAD_POOL_SIZE);
+
+ /// <summary>
+ /// Makes sure GRPC environment is initialized.
+ /// </summary>
+ public static void EnsureInitialized() {
+ lock(staticLock)
+ {
+ if (!initCalled)
+ {
+ initCalled = true;
+ GrpcInit();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Shuts down the GRPC environment if it was initialized before.
+ /// Repeated invocations have no effect.
+ /// </summary>
+ public static void Shutdown()
+ {
+ lock(staticLock)
+ {
+ if (initCalled && !shutdownCalled)
+ {
+ shutdownCalled = true;
+ GrpcShutdown();
+ }
+ }
+
+ }
+
+ /// <summary>
+ /// Initializes GRPC C Core library.
+ /// </summary>
+ private static void GrpcInit()
+ {
+ grpc_init();
+ threadPool.Start();
+ // TODO: use proper logging here
+ Console.WriteLine("GRPC initialized.");
+ }
+
+ /// <summary>
+ /// Shutdown GRPC C Core library.
+ /// </summary>
+ private static void GrpcShutdown()
+ {
+ threadPool.Stop();
+ grpc_shutdown();
+
+ // TODO: use proper logging here
+ Console.WriteLine("GRPC shutdown.");
+ }
+
+ internal static GrpcThreadPool ThreadPool
+ {
+ get
+ {
+ return threadPool;
+ }
+ }
+ }
+}
+
diff --git a/src/csharp/GrpcCore/Internal/AsyncCall.cs b/src/csharp/GrpcCore/Internal/AsyncCall.cs
new file mode 100644
index 0000000000..e83ca0eaa9
--- /dev/null
+++ b/src/csharp/GrpcCore/Internal/AsyncCall.cs
@@ -0,0 +1,485 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Diagnostics;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Runtime.CompilerServices;
+using Google.GRPC.Core.Internal;
+
+namespace Google.GRPC.Core.Internal
+{
+ /// <summary>
+ /// Listener for call events that can be delivered from a completion queue.
+ /// </summary>
+ internal interface ICallEventListener {
+
+ void OnClientMetadata();
+
+ void OnRead(byte[] payload);
+
+ void OnWriteAccepted(GRPCOpError error);
+
+ void OnFinishAccepted(GRPCOpError error);
+
+ // ignore the status on server
+ void OnFinished(Status status);
+ }
+
+ /// <summary>
+ /// Handle native call lifecycle and provides convenience methods.
+ /// </summary>
+ internal class AsyncCall<TWrite, TRead>: ICallEventListener, IDisposable
+ {
+ readonly Func<TWrite, byte[]> serializer;
+ readonly Func<byte[], TRead> deserializer;
+
+ // TODO: make sure the delegate doesn't get garbage collected while
+ // native callbacks are in the completion queue.
+ readonly EventCallbackDelegate callbackHandler;
+
+ object myLock = new object();
+ bool disposed;
+ CallSafeHandle call;
+
+ bool started;
+ bool errorOccured;
+
+ bool cancelRequested;
+ bool halfcloseRequested;
+ bool halfclosed;
+ bool doneWithReading;
+ Nullable<Status> finishedStatus;
+
+ TaskCompletionSource<object> writeTcs;
+ TaskCompletionSource<TRead> readTcs;
+ TaskCompletionSource<object> halfcloseTcs = new TaskCompletionSource<object>();
+ TaskCompletionSource<Status> finishedTcs = new TaskCompletionSource<Status>();
+
+ IObserver<TRead> readObserver;
+
+ public AsyncCall(Func<TWrite, byte[]> serializer, Func<byte[], TRead> deserializer)
+ {
+ this.serializer = serializer;
+ this.deserializer = deserializer;
+ this.callbackHandler = HandleEvent;
+ }
+
+ public Task WriteAsync(TWrite msg)
+ {
+ return StartWrite(msg, false).Task;
+ }
+
+ public Task WritesCompletedAsync()
+ {
+ WritesDone();
+ return halfcloseTcs.Task;
+ }
+
+ public Task WriteStatusAsync(Status status)
+ {
+ WriteStatus(status);
+ return halfcloseTcs.Task;
+ }
+
+ public Task<TRead> ReadAsync()
+ {
+ return StartRead().Task;
+ }
+
+ public Task<Status> Finished
+ {
+ get
+ {
+ return finishedTcs.Task;
+ }
+ }
+
+ /// <summary>
+ /// Initiates reading to given observer.
+ /// </summary>
+ public void StartReadingToStream(IObserver<TRead> readObserver) {
+ lock (myLock)
+ {
+ CheckStarted();
+ if (this.readObserver != null)
+ {
+ throw new InvalidOperationException("Already registered an observer.");
+ }
+ this.readObserver = readObserver;
+ StartRead();
+ }
+ }
+
+ public void Initialize(Channel channel, String methodName) {
+ lock (myLock)
+ {
+ this.call = CallSafeHandle.Create(channel.Handle, methodName, channel.Target, Timespec.InfFuture);
+ }
+ }
+
+ public void InitializeServer(CallSafeHandle call)
+ {
+ lock(myLock)
+ {
+ this.call = call;
+ }
+ }
+
+ // Client only
+ public void Start(bool buffered, CompletionQueueSafeHandle cq)
+ {
+ lock (myLock)
+ {
+ if (started)
+ {
+ throw new InvalidOperationException("Already started.");
+ }
+
+ call.Invoke(cq, buffered, callbackHandler, callbackHandler);
+ started = true;
+ }
+ }
+
+ // Server only
+ public void Accept(CompletionQueueSafeHandle cq)
+ {
+ lock (myLock)
+ {
+ if (started)
+ {
+ throw new InvalidOperationException("Already started.");
+ }
+
+ call.ServerAccept(cq, callbackHandler);
+ call.ServerEndInitialMetadata(0);
+ started = true;
+ }
+ }
+
+ public TaskCompletionSource<object> StartWrite(TWrite msg, bool buffered)
+ {
+ lock (myLock)
+ {
+ CheckStarted();
+ CheckNotFinished();
+ CheckNoError();
+ CheckCancelNotRequested();
+
+ if (halfcloseRequested || halfclosed)
+ {
+ throw new InvalidOperationException("Already halfclosed.");
+ }
+
+ if (writeTcs != null)
+ {
+ throw new InvalidOperationException("Only one write can be pending at a time");
+ }
+
+ // TODO: wrap serialization...
+ byte[] payload = serializer(msg);
+
+ call.StartWrite(payload, buffered, callbackHandler);
+ writeTcs = new TaskCompletionSource<object>();
+ return writeTcs;
+ }
+ }
+
+ // client only
+ public void WritesDone()
+ {
+ lock (myLock)
+ {
+ CheckStarted();
+ CheckNotFinished();
+ CheckNoError();
+ CheckCancelNotRequested();
+
+ if (halfcloseRequested || halfclosed)
+ {
+ throw new InvalidOperationException("Already halfclosed.");
+ }
+
+ call.WritesDone(callbackHandler);
+ halfcloseRequested = true;
+ }
+ }
+
+ // server only
+ public void WriteStatus(Status status)
+ {
+ lock (myLock)
+ {
+ CheckStarted();
+ CheckNotFinished();
+ CheckNoError();
+ CheckCancelNotRequested();
+
+ if (halfcloseRequested || halfclosed)
+ {
+ throw new InvalidOperationException("Already halfclosed.");
+ }
+
+ call.StartWriteStatus(status, callbackHandler);
+ halfcloseRequested = true;
+ }
+ }
+
+ public TaskCompletionSource<TRead> StartRead()
+ {
+ lock (myLock)
+ {
+ CheckStarted();
+ CheckNotFinished();
+ CheckNoError();
+
+ // TODO: add check for not cancelled?
+
+ if (doneWithReading)
+ {
+ throw new InvalidOperationException("Already read the last message.");
+ }
+
+ if (readTcs != null)
+ {
+ throw new InvalidOperationException("Only one read can be pending at a time");
+ }
+
+ call.StartRead(callbackHandler);
+
+ readTcs = new TaskCompletionSource<TRead>();
+ return readTcs;
+ }
+ }
+
+ public void Cancel()
+ {
+ lock (myLock)
+ {
+ CheckStarted();
+ CheckNotFinished();
+
+ cancelRequested = true;
+ }
+ // grpc_call_cancel is threadsafe
+ call.Cancel();
+ }
+
+ public void CancelWithStatus(Status status)
+ {
+ lock (myLock)
+ {
+ CheckStarted();
+ CheckNotFinished();
+
+ cancelRequested = true;
+ }
+ // grpc_call_cancel_with_status is threadsafe
+ call.CancelWithStatus(status);
+ }
+
+ public void OnClientMetadata()
+ {
+ // TODO: implement....
+ }
+
+ public void OnRead(byte[] payload)
+ {
+ TaskCompletionSource<TRead> oldTcs = null;
+ IObserver<TRead> observer = null;
+ lock (myLock)
+ {
+ oldTcs = readTcs;
+ readTcs = null;
+ if (payload == null)
+ {
+ doneWithReading = true;
+ }
+ observer = readObserver;
+ }
+
+ // TODO: wrap deserialization...
+ TRead msg = payload != null ? deserializer(payload) : default(TRead);
+
+ oldTcs.SetResult(msg);
+
+ // TODO: make sure we deliver reads in the right order.
+
+ if (observer != null)
+ {
+ if (payload != null)
+ {
+ // TODO: wrap to handle exceptions
+ observer.OnNext(msg);
+
+ // start a new read
+ StartRead();
+ }
+ else
+ {
+ // TODO: wrap to handle exceptions;
+ observer.OnCompleted();
+ }
+
+ }
+ }
+
+ public void OnWriteAccepted(GRPCOpError error)
+ {
+ TaskCompletionSource<object> oldTcs = null;
+ lock (myLock)
+ {
+ UpdateErrorOccured(error);
+ oldTcs = writeTcs;
+ writeTcs = null;
+ }
+
+ if (errorOccured)
+ {
+ // TODO: use the right type of exception...
+ oldTcs.SetException(new Exception("Write failed"));
+ }
+ else
+ {
+ // TODO: where does the continuation run?
+ oldTcs.SetResult(null);
+ }
+ }
+
+ public void OnFinishAccepted(GRPCOpError error)
+ {
+ lock (myLock)
+ {
+ UpdateErrorOccured(error);
+ halfclosed = true;
+ }
+
+ if (errorOccured)
+ {
+ halfcloseTcs.SetException(new Exception("Halfclose failed"));
+
+ }
+ else
+ {
+ halfcloseTcs.SetResult(null);
+ }
+
+ }
+
+ public void OnFinished(Status status)
+ {
+ lock (myLock)
+ {
+ finishedStatus = status;
+
+ DisposeResourcesIfNeeded();
+ }
+ finishedTcs.SetResult(status);
+
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!disposed)
+ {
+ if (disposing)
+ {
+ if (call != null)
+ {
+ call.Dispose();
+ }
+ }
+ disposed = true;
+ }
+ }
+
+ private void UpdateErrorOccured(GRPCOpError error)
+ {
+ if (error == GRPCOpError.GRPC_OP_ERROR)
+ {
+ errorOccured = true;
+ }
+ }
+
+ private void CheckStarted()
+ {
+ if (!started)
+ {
+ throw new InvalidOperationException("Call not started");
+ }
+ }
+
+ private void CheckNoError()
+ {
+ if (errorOccured)
+ {
+ throw new InvalidOperationException("Error occured when processing call.");
+ }
+ }
+
+ private void CheckNotFinished()
+ {
+ if (finishedStatus.HasValue)
+ {
+ throw new InvalidOperationException("Already finished.");
+ }
+ }
+
+ private void CheckCancelNotRequested()
+ {
+ if (cancelRequested)
+ {
+ throw new InvalidOperationException("Cancel has been requested.");
+ }
+ }
+
+ private void DisposeResourcesIfNeeded()
+ {
+ if (call != null && started && finishedStatus.HasValue)
+ {
+ // TODO: should we also wait for all the pending events to finish?
+
+ call.Dispose();
+ }
+ }
+
+ private void HandleEvent(IntPtr eventPtr) {
+ try {
+ var ev = new EventSafeHandleNotOwned(eventPtr);
+ switch (ev.GetCompletionType())
+ {
+ case GRPCCompletionType.GRPC_CLIENT_METADATA_READ:
+ OnClientMetadata();
+ break;
+
+ case GRPCCompletionType.GRPC_READ:
+ byte[] payload = ev.GetReadData();
+ OnRead(payload);
+ break;
+
+ case GRPCCompletionType.GRPC_WRITE_ACCEPTED:
+ OnWriteAccepted(ev.GetWriteAccepted());
+ break;
+
+ case GRPCCompletionType.GRPC_FINISH_ACCEPTED:
+ OnFinishAccepted(ev.GetFinishAccepted());
+ break;
+
+ case GRPCCompletionType.GRPC_FINISHED:
+ OnFinished(ev.GetFinished());
+ break;
+
+ default:
+ throw new ArgumentException("Unexpected completion type");
+ }
+ } catch(Exception e) {
+ Console.WriteLine("Caught exception in a native handler: " + e);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/csharp/GrpcCore/Internal/CallSafeHandle.cs b/src/csharp/GrpcCore/Internal/CallSafeHandle.cs
new file mode 100644
index 0000000000..6c9c58a4c3
--- /dev/null
+++ b/src/csharp/GrpcCore/Internal/CallSafeHandle.cs
@@ -0,0 +1,182 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Diagnostics;
+using Google.GRPC.Core;
+
+namespace Google.GRPC.Core.Internal
+{
+ // TODO: we need to make sure that the delegates are not collected before invoked.
+ internal delegate void EventCallbackDelegate(IntPtr eventPtr);
+
+ /// <summary>
+ /// grpc_call from <grpc/grpc.h>
+ /// </summary>
+ internal class CallSafeHandle : SafeHandleZeroIsInvalid
+ {
+ const UInt32 GRPC_WRITE_BUFFER_HINT = 1;
+
+ [DllImport("libgrpc.so")]
+ static extern CallSafeHandle grpc_channel_create_call_old(ChannelSafeHandle channel, string method, string host, Timespec deadline);
+
+ [DllImport("libgrpc.so")]
+ static extern GRPCCallError grpc_call_add_metadata(CallSafeHandle call, IntPtr metadata, UInt32 flags);
+
+ [DllImport("libgrpc.so")]
+ static extern GRPCCallError grpc_call_invoke_old(CallSafeHandle call, CompletionQueueSafeHandle cq, IntPtr metadataReadTag, IntPtr finishedTag, UInt32 flags);
+
+ [DllImport("libgrpc.so", EntryPoint = "grpc_call_invoke_old")]
+ static extern GRPCCallError grpc_call_invoke_old_CALLBACK(CallSafeHandle call, CompletionQueueSafeHandle cq,
+ [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate metadataReadCallback,
+ [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate finishedCallback,
+ UInt32 flags);
+
+ [DllImport("libgrpc.so")]
+ static extern GRPCCallError grpc_call_server_accept_old(CallSafeHandle call, CompletionQueueSafeHandle completionQueue, IntPtr finishedTag);
+
+ [DllImport("libgrpc.so", EntryPoint = "grpc_call_server_accept_old")]
+ static extern GRPCCallError grpc_call_server_accept_old_CALLBACK(CallSafeHandle call, CompletionQueueSafeHandle completionQueue, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate finishedCallback);
+
+ [DllImport("libgrpc.so")]
+ static extern GRPCCallError grpc_call_server_end_initial_metadata_old(CallSafeHandle call, UInt32 flags);
+
+ [DllImport("libgrpc.so")]
+ static extern GRPCCallError grpc_call_cancel(CallSafeHandle call);
+
+ [DllImport("libgrpc.so")]
+ static extern GRPCCallError grpc_call_cancel_with_status(CallSafeHandle call, StatusCode status, string description);
+
+ [DllImport("libgrpc.so")]
+ static extern GRPCCallError grpc_call_start_write_status_old(CallSafeHandle call, StatusCode statusCode, string statusMessage, IntPtr tag);
+
+ [DllImport("libgrpc.so", EntryPoint = "grpc_call_start_write_status_old")]
+ static extern GRPCCallError grpc_call_start_write_status_old_CALLBACK(CallSafeHandle call, StatusCode statusCode, string statusMessage, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback);
+
+ [DllImport("libgrpc.so")]
+ static extern GRPCCallError grpc_call_writes_done_old(CallSafeHandle call, IntPtr tag);
+
+ [DllImport("libgrpc.so", EntryPoint = "grpc_call_writes_done_old")]
+ static extern GRPCCallError grpc_call_writes_done_old_CALLBACK(CallSafeHandle call, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback);
+
+ [DllImport("libgrpc.so")]
+ static extern GRPCCallError grpc_call_start_read_old(CallSafeHandle call, IntPtr tag);
+
+ [DllImport("libgrpc.so", EntryPoint = "grpc_call_start_read_old")]
+ static extern GRPCCallError grpc_call_start_read_old_CALLBACK(CallSafeHandle call, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback);
+
+ [DllImport("libgrpc_csharp_ext.so")]
+ static extern void grpc_call_start_write_from_copied_buffer(CallSafeHandle call,
+ byte[] buffer, UIntPtr length,
+ IntPtr tag, UInt32 flags);
+
+ [DllImport("libgrpc_csharp_ext.so", EntryPoint = "grpc_call_start_write_from_copied_buffer")]
+ static extern void grpc_call_start_write_from_copied_buffer_CALLBACK(CallSafeHandle call,
+ byte[] buffer, UIntPtr length,
+ [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback,
+ UInt32 flags);
+
+ [DllImport("libgrpc.so")]
+ static extern void grpc_call_destroy(IntPtr call);
+
+ private CallSafeHandle()
+ {
+ }
+
+ /// <summary>
+ /// Creates a client call.
+ /// </summary>
+ public static CallSafeHandle Create(ChannelSafeHandle channel, string method, string host, Timespec deadline)
+ {
+ return grpc_channel_create_call_old(channel, method, host, deadline);
+ }
+
+ public void Invoke(CompletionQueueSafeHandle cq, IntPtr metadataReadTag, IntPtr finishedTag, bool buffered)
+ {
+ AssertCallOk(grpc_call_invoke_old(this, cq, metadataReadTag, finishedTag, GetFlags(buffered)));
+ }
+
+ public void Invoke(CompletionQueueSafeHandle cq, bool buffered, EventCallbackDelegate metadataReadCallback, EventCallbackDelegate finishedCallback)
+ {
+ AssertCallOk(grpc_call_invoke_old_CALLBACK(this, cq, metadataReadCallback, finishedCallback, GetFlags(buffered)));
+ }
+
+ public void ServerAccept(CompletionQueueSafeHandle cq, IntPtr finishedTag)
+ {
+ AssertCallOk(grpc_call_server_accept_old(this, cq, finishedTag));
+ }
+
+ public void ServerAccept(CompletionQueueSafeHandle cq, EventCallbackDelegate callback)
+ {
+ AssertCallOk(grpc_call_server_accept_old_CALLBACK(this, cq, callback));
+ }
+
+ public void ServerEndInitialMetadata(UInt32 flags)
+ {
+ AssertCallOk(grpc_call_server_end_initial_metadata_old(this, flags));
+ }
+
+ public void StartWrite(byte[] payload, IntPtr tag, bool buffered)
+ {
+ grpc_call_start_write_from_copied_buffer(this, payload, new UIntPtr((ulong) payload.Length), tag, GetFlags(buffered));
+ }
+
+ public void StartWrite(byte[] payload, bool buffered, EventCallbackDelegate callback)
+ {
+ grpc_call_start_write_from_copied_buffer_CALLBACK(this, payload, new UIntPtr((ulong) payload.Length), callback, GetFlags(buffered));
+ }
+
+ public void StartWriteStatus(Status status, IntPtr tag)
+ {
+ AssertCallOk(grpc_call_start_write_status_old(this, status.StatusCode, status.Detail, tag));
+ }
+
+ public void StartWriteStatus(Status status, EventCallbackDelegate callback)
+ {
+ AssertCallOk(grpc_call_start_write_status_old_CALLBACK(this, status.StatusCode, status.Detail, callback));
+ }
+
+ public void WritesDone(IntPtr tag)
+ {
+ AssertCallOk(grpc_call_writes_done_old(this, tag));
+ }
+
+ public void WritesDone(EventCallbackDelegate callback)
+ {
+ AssertCallOk(grpc_call_writes_done_old_CALLBACK(this, callback));
+ }
+
+ public void StartRead(IntPtr tag)
+ {
+ AssertCallOk(grpc_call_start_read_old(this, tag));
+ }
+
+ public void StartRead(EventCallbackDelegate callback)
+ {
+ AssertCallOk(grpc_call_start_read_old_CALLBACK(this, callback));
+ }
+
+ public void Cancel()
+ {
+ AssertCallOk(grpc_call_cancel(this));
+ }
+
+ public void CancelWithStatus(Status status)
+ {
+ AssertCallOk(grpc_call_cancel_with_status(this, status.StatusCode, status.Detail));
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ grpc_call_destroy(handle);
+ return true;
+ }
+
+ private static void AssertCallOk(GRPCCallError callError)
+ {
+ Trace.Assert(callError == GRPCCallError.GRPC_CALL_OK, "Status not GRPC_CALL_OK");
+ }
+
+ private static UInt32 GetFlags(bool buffered) {
+ return buffered ? 0 : GRPC_WRITE_BUFFER_HINT;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/csharp/GrpcCore/Internal/ChannelSafeHandle.cs b/src/csharp/GrpcCore/Internal/ChannelSafeHandle.cs
new file mode 100644
index 0000000000..3a09d8b1b6
--- /dev/null
+++ b/src/csharp/GrpcCore/Internal/ChannelSafeHandle.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Google.GRPC.Core.Internal
+{
+ /// <summary>
+ /// grpc_channel from <grpc/grpc.h>
+ /// </summary>
+ internal class ChannelSafeHandle : SafeHandleZeroIsInvalid
+ {
+ [DllImport("libgrpc.so")]
+ static extern ChannelSafeHandle grpc_channel_create(string target, IntPtr channelArgs);
+
+ [DllImport("libgrpc.so")]
+ static extern void grpc_channel_destroy(IntPtr channel);
+
+ private ChannelSafeHandle()
+ {
+ }
+
+ public static ChannelSafeHandle Create(string target, IntPtr channelArgs)
+ {
+ return grpc_channel_create(target, channelArgs);
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ grpc_channel_destroy(handle);
+ return true;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/csharp/GrpcCore/Internal/CompletionQueueSafeHandle.cs b/src/csharp/GrpcCore/Internal/CompletionQueueSafeHandle.cs
new file mode 100644
index 0000000000..73dd3edde3
--- /dev/null
+++ b/src/csharp/GrpcCore/Internal/CompletionQueueSafeHandle.cs
@@ -0,0 +1,66 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+
+namespace Google.GRPC.Core.Internal
+{
+ /// <summary>
+ /// grpc_completion_queue from <grpc/grpc.h>
+ /// </summary>
+ internal class CompletionQueueSafeHandle : SafeHandleZeroIsInvalid
+ {
+ [DllImport("libgrpc.so")]
+ static extern CompletionQueueSafeHandle grpc_completion_queue_create();
+
+ [DllImport("libgrpc.so")]
+ static extern EventSafeHandle grpc_completion_queue_pluck(CompletionQueueSafeHandle cq, IntPtr tag, Timespec deadline);
+
+ [DllImport("libgrpc.so")]
+ static extern EventSafeHandle grpc_completion_queue_next(CompletionQueueSafeHandle cq, Timespec deadline);
+
+ [DllImport("libgrpc.so")]
+ static extern void grpc_completion_queue_shutdown(CompletionQueueSafeHandle cq);
+
+ [DllImport("libgrpc_csharp_ext.so")]
+ static extern GRPCCompletionType grpc_completion_queue_next_with_callback(CompletionQueueSafeHandle cq);
+
+ [DllImport("libgrpc.so")]
+ static extern void grpc_completion_queue_destroy(IntPtr cq);
+
+ private CompletionQueueSafeHandle()
+ {
+ }
+
+ public static CompletionQueueSafeHandle Create()
+ {
+ return grpc_completion_queue_create();
+ }
+
+ public EventSafeHandle Next(Timespec deadline)
+ {
+ return grpc_completion_queue_next(this, deadline);
+ }
+
+ public GRPCCompletionType NextWithCallback()
+ {
+ return grpc_completion_queue_next_with_callback(this);
+ }
+
+ public EventSafeHandle Pluck(IntPtr tag, Timespec deadline)
+ {
+ return grpc_completion_queue_pluck(this, tag, deadline);
+ }
+
+ public void Shutdown()
+ {
+ grpc_completion_queue_shutdown(this);
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ grpc_completion_queue_destroy(handle);
+ return true;
+ }
+ }
+}
+
diff --git a/src/csharp/GrpcCore/Internal/Enums.cs b/src/csharp/GrpcCore/Internal/Enums.cs
new file mode 100644
index 0000000000..46e3bca6eb
--- /dev/null
+++ b/src/csharp/GrpcCore/Internal/Enums.cs
@@ -0,0 +1,75 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Google.GRPC.Core.Internal
+{
+ /// <summary>
+ /// from grpc/grpc.h
+ /// </summary>
+ internal enum GRPCCallError
+ {
+ /* everything went ok */
+ GRPC_CALL_OK = 0,
+ /* something failed, we don't know what */
+ GRPC_CALL_ERROR,
+ /* this method is not available on the server */
+ GRPC_CALL_ERROR_NOT_ON_SERVER,
+ /* this method is not available on the client */
+ GRPC_CALL_ERROR_NOT_ON_CLIENT,
+ /* this method must be called before server_accept */
+ GRPC_CALL_ERROR_ALREADY_ACCEPTED,
+ /* this method must be called before invoke */
+ GRPC_CALL_ERROR_ALREADY_INVOKED,
+ /* this method must be called after invoke */
+ GRPC_CALL_ERROR_NOT_INVOKED,
+ /* this call is already finished
+ (writes_done or write_status has already been called) */
+ GRPC_CALL_ERROR_ALREADY_FINISHED,
+ /* there is already an outstanding read/write operation on the call */
+ GRPC_CALL_ERROR_TOO_MANY_OPERATIONS,
+ /* the flags value was illegal for this call */
+ GRPC_CALL_ERROR_INVALID_FLAGS
+ }
+
+ /// <summary>
+ /// grpc_completion_type from grpc/grpc.h
+ /// </summary>
+ internal enum GRPCCompletionType
+ {
+ GRPC_QUEUE_SHUTDOWN,
+ /* Shutting down */
+ GRPC_READ,
+ /* A read has completed */
+ GRPC_INVOKE_ACCEPTED,
+ /* An invoke call has been accepted by flow
+ control */
+ GRPC_WRITE_ACCEPTED,
+ /* A write has been accepted by
+ flow control */
+ GRPC_FINISH_ACCEPTED,
+ /* writes_done or write_status has been accepted */
+ GRPC_CLIENT_METADATA_READ,
+ /* The metadata array sent by server received at
+ client */
+ GRPC_FINISHED,
+ /* An RPC has finished. The event contains status.
+ On the server this will be OK or Cancelled. */
+ GRPC_SERVER_RPC_NEW,
+ /* A new RPC has arrived at the server */
+ GRPC_COMPLETION_DO_NOT_USE
+ /* must be last, forces users to include
+ a default: case */
+ }
+
+ /// <summary>
+ /// grpc_op_error from grpc/grpc.h
+ /// </summary>
+ internal enum GRPCOpError
+ {
+ /* everything went ok */
+ GRPC_OP_OK = 0,
+ /* something failed, we don't know what */
+ GRPC_OP_ERROR
+ }
+}
+
diff --git a/src/csharp/GrpcCore/Internal/Event.cs b/src/csharp/GrpcCore/Internal/Event.cs
new file mode 100644
index 0000000000..7056005ba6
--- /dev/null
+++ b/src/csharp/GrpcCore/Internal/Event.cs
@@ -0,0 +1,191 @@
+using System;
+using System.Runtime.InteropServices;
+using Google.GRPC.Core;
+
+namespace Google.GRPC.Core.Internal
+{
+ /// <summary>
+ /// grpc_event from grpc/grpc.h
+ /// </summary>
+ internal class EventSafeHandle : SafeHandleZeroIsInvalid
+ {
+ [DllImport("libgrpc.so")]
+ static extern void grpc_event_finish(IntPtr ev);
+
+ [DllImport("libgrpc_csharp_ext.so")]
+ static extern GRPCCompletionType grpc_event_type(EventSafeHandle ev);
+
+ [DllImport("libgrpc_csharp_ext.so")]
+ static extern CallSafeHandle grpc_event_call(EventSafeHandle ev);
+
+ [DllImport("libgrpc_csharp_ext.so")]
+ static extern GRPCOpError grpc_event_write_accepted(EventSafeHandle ev);
+
+ [DllImport("libgrpc_csharp_ext.so")]
+ static extern GRPCOpError grpc_event_finish_accepted(EventSafeHandle ev);
+
+ [DllImport("libgrpc_csharp_ext.so")]
+ static extern StatusCode grpc_event_finished_status(EventSafeHandle ev);
+
+ [DllImport("libgrpc_csharp_ext.so")]
+ static extern IntPtr grpc_event_finished_details(EventSafeHandle ev); // returns const char*
+
+ [DllImport("libgrpc_csharp_ext.so")]
+ static extern IntPtr grpc_event_read_length(EventSafeHandle ev);
+
+ [DllImport("libgrpc_csharp_ext.so")]
+ static extern void grpc_event_read_copy_to_buffer(EventSafeHandle ev, byte[] buffer, UIntPtr bufferLen);
+
+ [DllImport("libgrpc_csharp_ext.so")]
+ static extern IntPtr grpc_event_server_rpc_new_method(EventSafeHandle ev); // returns const char*
+
+ public GRPCCompletionType GetCompletionType()
+ {
+ return grpc_event_type(this);
+ }
+
+ public GRPCOpError GetWriteAccepted()
+ {
+ return grpc_event_write_accepted(this);
+ }
+
+ public GRPCOpError GetFinishAccepted()
+ {
+ return grpc_event_finish_accepted(this);
+ }
+
+ public Status GetFinished()
+ {
+ // TODO: can the native method return string directly?
+ string details = Marshal.PtrToStringAnsi(grpc_event_finished_details(this));
+ return new Status(grpc_event_finished_status(this), details);
+ }
+
+ public byte[] GetReadData()
+ {
+ IntPtr len = grpc_event_read_length(this);
+ if (len == new IntPtr(-1))
+ {
+ return null;
+ }
+ byte[] data = new byte[(int) len];
+ grpc_event_read_copy_to_buffer(this, data, new UIntPtr((ulong)data.Length));
+ return data;
+ }
+
+ public CallSafeHandle GetCall() {
+ return grpc_event_call(this);
+ }
+
+ public string GetServerRpcNewMethod() {
+ // TODO: can the native method return string directly?
+ return Marshal.PtrToStringAnsi(grpc_event_server_rpc_new_method(this));
+ }
+
+ //TODO: client_metadata_read event type
+
+ protected override bool ReleaseHandle()
+ {
+ grpc_event_finish(handle);
+ return true;
+ }
+ }
+
+ // TODO: this is basically c&p of EventSafeHandle. Unify!
+ /// <summary>
+ /// Not owned version of
+ /// grpc_event from grpc/grpc.h
+ /// </summary>
+ internal class EventSafeHandleNotOwned : SafeHandleZeroIsInvalid
+ {
+ [DllImport("libgrpc.so")]
+ static extern void grpc_event_finish(IntPtr ev);
+
+ [DllImport("libgrpc_csharp_ext.so")]
+ static extern GRPCCompletionType grpc_event_type(EventSafeHandleNotOwned ev);
+
+ [DllImport("libgrpc_csharp_ext.so")]
+ static extern CallSafeHandle grpc_event_call(EventSafeHandleNotOwned ev);
+
+ [DllImport("libgrpc_csharp_ext.so")]
+ static extern GRPCOpError grpc_event_write_accepted(EventSafeHandleNotOwned ev);
+
+ [DllImport("libgrpc_csharp_ext.so")]
+ static extern GRPCOpError grpc_event_finish_accepted(EventSafeHandleNotOwned ev);
+
+ [DllImport("libgrpc_csharp_ext.so")]
+ static extern StatusCode grpc_event_finished_status(EventSafeHandleNotOwned ev);
+
+ [DllImport("libgrpc_csharp_ext.so")]
+ static extern IntPtr grpc_event_finished_details(EventSafeHandleNotOwned ev); // returns const char*
+
+ [DllImport("libgrpc_csharp_ext.so")]
+ static extern IntPtr grpc_event_read_length(EventSafeHandleNotOwned ev);
+
+ [DllImport("libgrpc_csharp_ext.so")]
+ static extern void grpc_event_read_copy_to_buffer(EventSafeHandleNotOwned ev, byte[] buffer, UIntPtr bufferLen);
+
+ [DllImport("libgrpc_csharp_ext.so")]
+ static extern IntPtr grpc_event_server_rpc_new_method(EventSafeHandleNotOwned ev); // returns const char*
+
+ public EventSafeHandleNotOwned() : base(false)
+ {
+ }
+
+ public EventSafeHandleNotOwned(IntPtr handle) : base(false)
+ {
+ SetHandle(handle);
+ }
+
+ public GRPCCompletionType GetCompletionType()
+ {
+ return grpc_event_type(this);
+ }
+
+ public GRPCOpError GetWriteAccepted()
+ {
+ return grpc_event_write_accepted(this);
+ }
+
+ public GRPCOpError GetFinishAccepted()
+ {
+ return grpc_event_finish_accepted(this);
+ }
+
+ public Status GetFinished()
+ {
+ // TODO: can the native method return string directly?
+ string details = Marshal.PtrToStringAnsi(grpc_event_finished_details(this));
+ return new Status(grpc_event_finished_status(this), details);
+ }
+
+ public byte[] GetReadData()
+ {
+ IntPtr len = grpc_event_read_length(this);
+ if (len == new IntPtr(-1))
+ {
+ return null;
+ }
+ byte[] data = new byte[(int) len];
+ grpc_event_read_copy_to_buffer(this, data, new UIntPtr((ulong)data.Length));
+ return data;
+ }
+
+ public CallSafeHandle GetCall() {
+ return grpc_event_call(this);
+ }
+
+ public string GetServerRpcNewMethod() {
+ // TODO: can the native method return string directly?
+ return Marshal.PtrToStringAnsi(grpc_event_server_rpc_new_method(this));
+ }
+
+ //TODO: client_metadata_read event type
+
+ protected override bool ReleaseHandle()
+ {
+ grpc_event_finish(handle);
+ return true;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/csharp/GrpcCore/Internal/GrpcThreadPool.cs b/src/csharp/GrpcCore/Internal/GrpcThreadPool.cs
new file mode 100644
index 0000000000..1139e54a1d
--- /dev/null
+++ b/src/csharp/GrpcCore/Internal/GrpcThreadPool.cs
@@ -0,0 +1,129 @@
+using System;
+using Google.GRPC.Core.Internal;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Collections.Generic;
+
+namespace Google.GRPC.Core.Internal
+{
+ /// <summary>
+ /// Pool of threads polling on the same completion queue.
+ /// </summary>
+ internal class GrpcThreadPool
+ {
+ readonly object myLock = new object();
+ readonly List<Thread> threads = new List<Thread>();
+ readonly int poolSize;
+ readonly Action<EventSafeHandle> eventHandler;
+
+ CompletionQueueSafeHandle cq;
+
+ public GrpcThreadPool(int poolSize) {
+ this.poolSize = poolSize;
+ }
+
+ internal GrpcThreadPool(int poolSize, Action<EventSafeHandle> eventHandler) {
+ this.poolSize = poolSize;
+ this.eventHandler = eventHandler;
+ }
+
+ public void Start() {
+
+ lock (myLock)
+ {
+ if (cq != null)
+ {
+ throw new InvalidOperationException("Already started.");
+ }
+
+ cq = CompletionQueueSafeHandle.Create();
+
+ for (int i = 0; i < poolSize; i++)
+ {
+ threads.Add(CreateAndStartThread(i));
+ }
+ }
+ }
+
+ public void Stop() {
+
+ lock (myLock)
+ {
+ cq.Shutdown();
+
+ Console.WriteLine("Waiting for GPRC threads to finish.");
+ foreach (var thread in threads)
+ {
+ thread.Join();
+ }
+
+ cq.Dispose();
+
+ }
+ }
+
+ internal CompletionQueueSafeHandle CompletionQueue
+ {
+ get
+ {
+ return cq;
+ }
+ }
+
+ private Thread CreateAndStartThread(int i) {
+ Action body;
+ if (eventHandler != null)
+ {
+ body = ThreadBodyWithHandler;
+ }
+ else
+ {
+ body = ThreadBodyNoHandler;
+ }
+ var thread = new Thread(new ThreadStart(body));
+ thread.IsBackground = false;
+ thread.Start();
+ if (eventHandler != null)
+ {
+ thread.Name = "grpc_server_newrpc " + i;
+ }
+ else
+ {
+ thread.Name = "grpc " + i;
+ }
+ return thread;
+ }
+
+ /// <summary>
+ /// Body of the polling thread.
+ /// </summary>
+ private void ThreadBodyNoHandler()
+ {
+ GRPCCompletionType completionType;
+ do
+ {
+ completionType = cq.NextWithCallback();
+ } while(completionType != GRPCCompletionType.GRPC_QUEUE_SHUTDOWN);
+ Console.WriteLine("Completion queue has shutdown successfully, thread " + Thread.CurrentThread.Name + " exiting.");
+ }
+
+ /// <summary>
+ /// Body of the polling thread.
+ /// </summary>
+ private void ThreadBodyWithHandler()
+ {
+ GRPCCompletionType completionType;
+ do
+ {
+ using (EventSafeHandle ev = cq.Next(Timespec.InfFuture)) {
+ completionType = ev.GetCompletionType();
+ eventHandler(ev);
+ }
+ } while(completionType != GRPCCompletionType.GRPC_QUEUE_SHUTDOWN);
+ Console.WriteLine("Completion queue has shutdown successfully, thread " + Thread.CurrentThread.Name + " exiting.");
+ }
+ }
+
+}
+
diff --git a/src/csharp/GrpcCore/Internal/SafeHandleZeroIsInvalid.cs b/src/csharp/GrpcCore/Internal/SafeHandleZeroIsInvalid.cs
new file mode 100644
index 0000000000..5a1252b881
--- /dev/null
+++ b/src/csharp/GrpcCore/Internal/SafeHandleZeroIsInvalid.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Google.GRPC.Core.Internal
+{
+ /// <summary>
+ /// Safe handle to wrap native objects.
+ /// </summary>
+ internal abstract class SafeHandleZeroIsInvalid : SafeHandle
+ {
+ public SafeHandleZeroIsInvalid() : base(IntPtr.Zero, true)
+ {
+ }
+
+ public SafeHandleZeroIsInvalid(bool ownsHandle) : base(IntPtr.Zero, ownsHandle)
+ {
+ }
+
+ public override bool IsInvalid
+ {
+ get
+ {
+ return handle == IntPtr.Zero;
+ }
+ }
+ }
+}
+
diff --git a/src/csharp/GrpcCore/Internal/ServerSafeHandle.cs b/src/csharp/GrpcCore/Internal/ServerSafeHandle.cs
new file mode 100644
index 0000000000..0d38bce63e
--- /dev/null
+++ b/src/csharp/GrpcCore/Internal/ServerSafeHandle.cs
@@ -0,0 +1,76 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Diagnostics;
+using System.Collections.Concurrent;
+
+namespace Google.GRPC.Core.Internal
+{
+ /// <summary>
+ /// grpc_server from grpc/grpc.h
+ /// </summary>
+ internal sealed class ServerSafeHandle : SafeHandleZeroIsInvalid
+ {
+ [DllImport("libgrpc.so", EntryPoint = "grpc_server_request_call_old")]
+ static extern GRPCCallError grpc_server_request_call_old_CALLBACK(ServerSafeHandle server, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback);
+
+ [DllImport("libgrpc.so")]
+ static extern ServerSafeHandle grpc_server_create(CompletionQueueSafeHandle cq, IntPtr args);
+
+ // TODO: check int representation size
+ [DllImport("libgrpc.so")]
+ static extern int grpc_server_add_http2_port(ServerSafeHandle server, string addr);
+
+ // TODO: check int representation size
+ [DllImport("libgrpc.so")]
+ static extern int grpc_server_add_secure_http2_port(ServerSafeHandle server, string addr);
+
+ [DllImport("libgrpc.so")]
+ static extern void grpc_server_start(ServerSafeHandle server);
+
+ [DllImport("libgrpc.so")]
+ static extern void grpc_server_shutdown(ServerSafeHandle server);
+
+ [DllImport("libgrpc.so")]
+ static extern void grpc_server_shutdown_and_notify(ServerSafeHandle server, IntPtr tag);
+
+ [DllImport("libgrpc.so")]
+ static extern void grpc_server_destroy(IntPtr server);
+
+ private ServerSafeHandle()
+ {
+ }
+
+ public static ServerSafeHandle NewServer(CompletionQueueSafeHandle cq, IntPtr args)
+ {
+ // TODO: also grpc_secure_server_create...
+ return grpc_server_create(cq, args);
+ }
+
+ public int AddPort(string addr)
+ {
+ // TODO: also grpc_server_add_secure_http2_port...
+ return grpc_server_add_http2_port(this, addr);
+ }
+
+ public void Start()
+ {
+ grpc_server_start(this);
+ }
+
+ public void Shutdown()
+ {
+ grpc_server_shutdown(this);
+ }
+
+ public GRPCCallError RequestCall(EventCallbackDelegate callback)
+ {
+ return grpc_server_request_call_old_CALLBACK(this, callback);
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ grpc_server_destroy(handle);
+ return true;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/csharp/GrpcCore/Internal/StreamingInputObserver.cs b/src/csharp/GrpcCore/Internal/StreamingInputObserver.cs
new file mode 100644
index 0000000000..d483e53a2d
--- /dev/null
+++ b/src/csharp/GrpcCore/Internal/StreamingInputObserver.cs
@@ -0,0 +1,33 @@
+using System;
+using Google.GRPC.Core.Internal;
+
+namespace Google.GRPC.Core
+{
+ internal class StreamingInputObserver<TWrite, TRead> : IObserver<TWrite>
+ {
+ readonly AsyncCall<TWrite, TRead> call;
+
+ public StreamingInputObserver(AsyncCall<TWrite, TRead> call)
+ {
+ this.call = call;
+ }
+
+ public void OnCompleted()
+ {
+ // TODO: how bad is the Wait here?
+ call.WritesCompletedAsync().Wait();
+ }
+
+ public void OnError(Exception error)
+ {
+ throw new InvalidOperationException("This should never be called.");
+ }
+
+ public void OnNext(TWrite value)
+ {
+ // TODO: how bad is the Wait here?
+ call.WriteAsync(value).Wait();
+ }
+ }
+}
+
diff --git a/src/csharp/GrpcCore/Internal/Timespec.cs b/src/csharp/GrpcCore/Internal/Timespec.cs
new file mode 100644
index 0000000000..8ffaf70bbf
--- /dev/null
+++ b/src/csharp/GrpcCore/Internal/Timespec.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Threading;
+
+namespace Google.GRPC.Core.Internal
+{
+ /// <summary>
+ /// gpr_timespec from grpc/support/time.h
+ /// </summary>
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct Timespec
+ {
+ const int nanosPerSecond = 1000 * 1000 * 1000;
+ const int nanosPerTick = 100;
+
+ [DllImport("libgpr.so")]
+ static extern Timespec gpr_now();
+
+ // TODO: this only works on 64bit linux, can we autoselect the right size of ints?
+ // perhaps using IntPtr would work.
+ public System.Int64 tv_sec;
+ public System.Int64 tv_nsec;
+
+ /// <summary>
+ /// Timespec a long time in the future.
+ /// </summary>
+ public static Timespec InfFuture
+ {
+ get
+ {
+ // TODO: set correct value based on the length of the struct
+ return new Timespec { tv_sec = Int32.MaxValue, tv_nsec = 0 };
+ }
+ }
+
+ public static Timespec Now
+ {
+ get
+ {
+ return gpr_now();
+ }
+ }
+
+ /// <summary>
+ /// Creates a GPR deadline from current instant and given timeout.
+ /// </summary>
+ /// <returns>The from timeout.</returns>
+ public static Timespec DeadlineFromTimeout(TimeSpan timeout) {
+ if (timeout == Timeout.InfiniteTimeSpan)
+ {
+ return Timespec.InfFuture;
+ }
+ return Timespec.Now.Add(timeout);
+ }
+
+ public Timespec Add(TimeSpan timeSpan) {
+ long nanos = tv_nsec + (timeSpan.Ticks % TimeSpan.TicksPerSecond) * nanosPerTick;
+ long overflow_sec = (nanos > nanosPerSecond) ? 1 : 0;
+
+ Timespec result;
+ result.tv_nsec = nanos % nanosPerSecond;
+ result.tv_sec = tv_sec + (timeSpan.Ticks / TimeSpan.TicksPerSecond) + overflow_sec;
+ return result;
+ }
+ }
+}
+
diff --git a/src/csharp/GrpcCore/Properties/AssemblyInfo.cs b/src/csharp/GrpcCore/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..74aba25767
--- /dev/null
+++ b/src/csharp/GrpcCore/Properties/AssemblyInfo.cs
@@ -0,0 +1,24 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+// Information about this assembly is defined by the following attributes.
+// Change them to the values specific to your project.
+[assembly: AssemblyTitle ("GrpcCore")]
+[assembly: AssemblyDescription ("")]
+[assembly: AssemblyConfiguration ("")]
+[assembly: AssemblyCompany ("")]
+[assembly: AssemblyProduct ("")]
+[assembly: AssemblyCopyright ("jtattermusch")]
+[assembly: AssemblyTrademark ("")]
+[assembly: AssemblyCulture ("")]
+// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
+// The form "{Major}.{Minor}.*" will automatically update the build and revision,
+// and "{Major}.{Minor}.{Build}.*" will update just the revision.
+[assembly: AssemblyVersion ("1.0.*")]
+// The following attributes are used to specify the signing key for the assembly,
+// if desired. See the Mono documentation for more information about signing.
+//[assembly: AssemblyDelaySign(false)]
+//[assembly: AssemblyKeyFile("")]
+
+[assembly: InternalsVisibleTo("GrpcCoreTests")]
+
diff --git a/src/csharp/GrpcCore/RpcException.cs b/src/csharp/GrpcCore/RpcException.cs
new file mode 100644
index 0000000000..8811c3a7c7
--- /dev/null
+++ b/src/csharp/GrpcCore/RpcException.cs
@@ -0,0 +1,27 @@
+using System;
+
+namespace Google.GRPC.Core
+{
+ public class RpcException : Exception
+ {
+ private readonly Status status;
+
+ public RpcException(Status status)
+ {
+ this.status = status;
+ }
+
+ public RpcException(Status status, string message) : base(message)
+ {
+ this.status = status;
+ }
+
+ public Status Status {
+ get
+ {
+ return status;
+ }
+ }
+ }
+}
+
diff --git a/src/csharp/GrpcCore/Server.cs b/src/csharp/GrpcCore/Server.cs
new file mode 100644
index 0000000000..68da1a8300
--- /dev/null
+++ b/src/csharp/GrpcCore/Server.cs
@@ -0,0 +1,141 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Diagnostics;
+using System.Collections.Concurrent;
+using Google.GRPC.Core.Internal;
+
+namespace Google.GRPC.Core
+{
+ /// <summary>
+ /// Server is implemented only to be able to do
+ /// in-process testing.
+ /// </summary>
+ public class Server
+ {
+ // TODO: make sure the delegate doesn't get garbage collected while
+ // native callbacks are in the completion queue.
+ readonly EventCallbackDelegate newRpcHandler;
+
+ readonly BlockingCollection<NewRpcInfo> newRpcQueue = new BlockingCollection<NewRpcInfo>();
+ readonly ServerSafeHandle handle;
+
+ static Server() {
+ GrpcEnvironment.EnsureInitialized();
+ }
+
+ public Server()
+ {
+ // TODO: what is the tag for server shutdown?
+ this.handle = ServerSafeHandle.NewServer(GetCompletionQueue(), IntPtr.Zero);
+ this.newRpcHandler = HandleNewRpc;
+ }
+
+ public int AddPort(string addr) {
+ return handle.AddPort(addr);
+ }
+
+ public void Start()
+ {
+ handle.Start();
+ }
+
+ public void RunRpc()
+ {
+ AllowOneRpc();
+
+ try {
+ var rpcInfo = newRpcQueue.Take();
+
+ Console.WriteLine("Server received RPC " + rpcInfo.Method);
+
+ AsyncCall<byte[], byte[]> asyncCall = new AsyncCall<byte[], byte[]>(
+ (payload) => payload, (payload) => payload);
+
+ asyncCall.InitializeServer(rpcInfo.Call);
+
+ asyncCall.Accept(GetCompletionQueue());
+
+ while(true) {
+ byte[] payload = asyncCall.ReadAsync().Result;
+ if (payload == null)
+ {
+ break;
+ }
+ }
+
+ asyncCall.WriteAsync(new byte[] { }).Wait();
+
+ // TODO: what should be the details?
+ asyncCall.WriteStatusAsync(new Status(StatusCode.GRPC_STATUS_OK, "")).Wait();
+
+ asyncCall.Finished.Wait();
+ } catch(Exception e) {
+ Console.WriteLine("Exception while handling RPC: " + e);
+ }
+ }
+
+ // TODO: implement disposal properly...
+ public void Shutdown() {
+ handle.Shutdown();
+
+
+ //handle.Dispose();
+ }
+
+ private void AllowOneRpc()
+ {
+ AssertCallOk(handle.RequestCall(newRpcHandler));
+ }
+
+ private void HandleNewRpc(IntPtr eventPtr)
+ {
+ try
+ {
+ var ev = new EventSafeHandleNotOwned(eventPtr);
+ newRpcQueue.Add(new NewRpcInfo(ev.GetCall(), ev.GetServerRpcNewMethod()));
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine("Caught exception in a native handler: " + e);
+ }
+ }
+
+ private static void AssertCallOk(GRPCCallError callError)
+ {
+ Trace.Assert(callError == GRPCCallError.GRPC_CALL_OK, "Status not GRPC_CALL_OK");
+ }
+
+ private static CompletionQueueSafeHandle GetCompletionQueue()
+ {
+ return GrpcEnvironment.ThreadPool.CompletionQueue;
+ }
+
+ private struct NewRpcInfo
+ {
+ private CallSafeHandle call;
+ private string method;
+
+ public NewRpcInfo(CallSafeHandle call, string method)
+ {
+ this.call = call;
+ this.method = method;
+ }
+
+ public CallSafeHandle Call
+ {
+ get
+ {
+ return this.call;
+ }
+ }
+
+ public string Method
+ {
+ get
+ {
+ return this.method;
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/csharp/GrpcCore/Status.cs b/src/csharp/GrpcCore/Status.cs
new file mode 100644
index 0000000000..f1212f8d67
--- /dev/null
+++ b/src/csharp/GrpcCore/Status.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Google.GRPC.Core
+{
+ /// <summary>
+ /// Represents RPC result.
+ /// </summary>
+ public struct Status
+ {
+ readonly StatusCode statusCode;
+ readonly string detail;
+
+ public Status(StatusCode statusCode, string detail)
+ {
+ this.statusCode = statusCode;
+ this.detail = detail;
+ }
+
+ public StatusCode StatusCode
+ {
+ get
+ {
+ return statusCode;
+ }
+ }
+
+ public string Detail
+ {
+ get
+ {
+ return detail;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/csharp/GrpcCore/StatusCode.cs b/src/csharp/GrpcCore/StatusCode.cs
new file mode 100644
index 0000000000..80fc8bd581
--- /dev/null
+++ b/src/csharp/GrpcCore/StatusCode.cs
@@ -0,0 +1,150 @@
+using System;
+
+namespace Google.GRPC.Core
+{
+ // TODO: element names should changed to comply with C# naming conventions.
+ /// <summary>
+ /// grpc_status_code from grpc/status.h
+ /// </summary>
+ public enum StatusCode
+ {
+ /* Not an error; returned on success
+
+ HTTP Mapping: 200 OK */
+ GRPC_STATUS_OK = 0,
+ /* The operation was cancelled (typically by the caller).
+
+ HTTP Mapping: 499 Client Closed Request */
+ GRPC_STATUS_CANCELLED = 1,
+ /* Unknown error. An example of where this error may be returned is
+ if a Status value received from another address space belongs to
+ an error-space that is not known in this address space. Also
+ errors raised by APIs that do not return enough error information
+ may be converted to this error.
+
+ HTTP Mapping: 500 Internal Server Error */
+ GRPC_STATUS_UNKNOWN = 2,
+ /* Client specified an invalid argument. Note that this differs
+ from FAILED_PRECONDITION. INVALID_ARGUMENT indicates arguments
+ that are problematic regardless of the state of the system
+ (e.g., a malformed file name).
+
+ HTTP Mapping: 400 Bad Request */
+ GRPC_STATUS_INVALID_ARGUMENT = 3,
+ /* Deadline expired before operation could complete. For operations
+ that change the state of the system, this error may be returned
+ even if the operation has completed successfully. For example, a
+ successful response from a server could have been delayed long
+ enough for the deadline to expire.
+
+ HTTP Mapping: 504 Gateway Timeout */
+ GRPC_STATUS_DEADLINE_EXCEEDED = 4,
+ /* Some requested entity (e.g., file or directory) was not found.
+
+ HTTP Mapping: 404 Not Found */
+ GRPC_STATUS_NOT_FOUND = 5,
+ /* Some entity that we attempted to create (e.g., file or directory)
+ already exists.
+
+ HTTP Mapping: 409 Conflict */
+ GRPC_STATUS_ALREADY_EXISTS = 6,
+ /* The caller does not have permission to execute the specified
+ operation. PERMISSION_DENIED must not be used for rejections
+ caused by exhausting some resource (use RESOURCE_EXHAUSTED
+ instead for those errors). PERMISSION_DENIED must not be
+ used if the caller can not be identified (use UNAUTHENTICATED
+ instead for those errors).
+
+ HTTP Mapping: 403 Forbidden */
+ GRPC_STATUS_PERMISSION_DENIED = 7,
+ /* The request does not have valid authentication credentials for the
+ operation.
+
+ HTTP Mapping: 401 Unauthorized */
+ GRPC_STATUS_UNAUTHENTICATED = 16,
+ /* Some resource has been exhausted, perhaps a per-user quota, or
+ perhaps the entire file system is out of space.
+
+ HTTP Mapping: 429 Too Many Requests */
+ GRPC_STATUS_RESOURCE_EXHAUSTED = 8,
+ /* Operation was rejected because the system is not in a state
+ required for the operation's execution. For example, directory
+ to be deleted may be non-empty, an rmdir operation is applied to
+ a non-directory, etc.
+
+ A litmus test that may help a service implementor in deciding
+ between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE:
+ (a) Use UNAVAILABLE if the client can retry just the failing call.
+ (b) Use ABORTED if the client should retry at a higher-level
+ (e.g., restarting a read-modify-write sequence).
+ (c) Use FAILED_PRECONDITION if the client should not retry until
+ the system state has been explicitly fixed. E.g., if an "rmdir"
+ fails because the directory is non-empty, FAILED_PRECONDITION
+ should be returned since the client should not retry unless
+ they have first fixed up the directory by deleting files from it.
+ (d) Use FAILED_PRECONDITION if the client performs conditional
+ REST Get/Update/Delete on a resource and the resource on the
+ server does not match the condition. E.g., conflicting
+ read-modify-write on the same resource.
+
+ HTTP Mapping: 400 Bad Request
+
+ NOTE: HTTP spec says 412 Precondition Failed should only be used if
+ the request contains Etag related headers. So if the server does see
+ Etag related headers in the request, it may choose to return 412
+ instead of 400 for this error code. */
+ GRPC_STATUS_FAILED_PRECONDITION = 9,
+ /* The operation was aborted, typically due to a concurrency issue
+ like sequencer check failures, transaction aborts, etc.
+
+ See litmus test above for deciding between FAILED_PRECONDITION,
+ ABORTED, and UNAVAILABLE.
+
+ HTTP Mapping: 409 Conflict */
+ GRPC_STATUS_ABORTED = 10,
+ /* Operation was attempted past the valid range. E.g., seeking or
+ reading past end of file.
+
+ Unlike INVALID_ARGUMENT, this error indicates a problem that may
+ be fixed if the system state changes. For example, a 32-bit file
+ system will generate INVALID_ARGUMENT if asked to read at an
+ offset that is not in the range [0,2^32-1], but it will generate
+ OUT_OF_RANGE if asked to read from an offset past the current
+ file size.
+
+ There is a fair bit of overlap between FAILED_PRECONDITION and
+ OUT_OF_RANGE. We recommend using OUT_OF_RANGE (the more specific
+ error) when it applies so that callers who are iterating through
+ a space can easily look for an OUT_OF_RANGE error to detect when
+ they are done.
+
+ HTTP Mapping: 400 Bad Request */
+ GRPC_STATUS_OUT_OF_RANGE = 11,
+ /* Operation is not implemented or not supported/enabled in this service.
+
+ HTTP Mapping: 501 Not Implemented */
+ GRPC_STATUS_UNIMPLEMENTED = 12,
+ /* Internal errors. Means some invariants expected by underlying
+ system has been broken. If you see one of these errors,
+ something is very broken.
+
+ HTTP Mapping: 500 Internal Server Error */
+ GRPC_STATUS_INTERNAL = 13,
+ /* The service is currently unavailable. This is a most likely a
+ transient condition and may be corrected by retrying with
+ a backoff.
+
+ See litmus test above for deciding between FAILED_PRECONDITION,
+ ABORTED, and UNAVAILABLE.
+
+ HTTP Mapping: 503 Service Unavailable */
+ GRPC_STATUS_UNAVAILABLE = 14,
+ /* Unrecoverable data loss or corruption.
+
+ HTTP Mapping: 500 Internal Server Error */
+ GRPC_STATUS_DATA_LOSS = 15,
+ /* Force users to include a default branch: */
+ GRPC_STATUS__DO_NOT_USE = -1
+ }
+}
+
diff --git a/src/csharp/GrpcCoreTests/.gitignore b/src/csharp/GrpcCoreTests/.gitignore
new file mode 100644
index 0000000000..2cc8cca52d
--- /dev/null
+++ b/src/csharp/GrpcCoreTests/.gitignore
@@ -0,0 +1,2 @@
+test-results
+bin
diff --git a/src/csharp/GrpcCoreTests/ClientServerTest.cs b/src/csharp/GrpcCoreTests/ClientServerTest.cs
new file mode 100644
index 0000000000..823ee94288
--- /dev/null
+++ b/src/csharp/GrpcCoreTests/ClientServerTest.cs
@@ -0,0 +1,48 @@
+using System;
+using NUnit.Framework;
+using Google.GRPC.Core.Internal;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Google.GRPC.Core.Tests
+{
+ public class ClientServerTest
+ {
+ string request = "REQUEST";
+ string serverAddr = "localhost:" + Utils.PickUnusedPort();
+
+ [Test]
+ public void EmptyCall()
+ {
+ Server server = new Server();
+ server.AddPort(serverAddr);
+ server.Start();
+
+ Task.Factory.StartNew(
+ () => {
+ server.RunRpc();
+ }
+ );
+
+ using (Channel channel = new Channel(serverAddr))
+ {
+ CreateCall(channel);
+ string response = Calls.BlockingUnaryCall(CreateCall(channel), request, default(CancellationToken));
+ Console.WriteLine("Received response: " + response);
+ }
+
+ server.Shutdown();
+
+ GrpcEnvironment.Shutdown();
+ }
+
+ private Call<string, string> CreateCall(Channel channel)
+ {
+ return new Call<string, string>("/tests.Test/EmptyCall",
+ (s) => System.Text.Encoding.ASCII.GetBytes(s),
+ (b) => System.Text.Encoding.ASCII.GetString(b),
+ Timeout.InfiniteTimeSpan, channel);
+ }
+ }
+}
+
diff --git a/src/csharp/GrpcCoreTests/GrpcCoreTests.csproj b/src/csharp/GrpcCoreTests/GrpcCoreTests.csproj
new file mode 100644
index 0000000000..3de0f585cd
--- /dev/null
+++ b/src/csharp/GrpcCoreTests/GrpcCoreTests.csproj
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <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>GrpcCoreTests</RootNamespace>
+ <AssemblyName>GrpcCoreTests</AssemblyName>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug</OutputPath>
+ <DefineConstants>DEBUG;</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <ConsolePause>false</ConsolePause>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>full</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release</OutputPath>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <ConsolePause>false</ConsolePause>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="nunit.framework, Version=2.6.0.0, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77">
+ <Private>False</Private>
+ </Reference>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="ClientServerTest.cs" />
+ <Compile Include="ServerTest.cs" />
+ <Compile Include="Utils.cs" />
+ <Compile Include="GrpcEnvironmentTest.cs" />
+ <Compile Include="TimespecTest.cs" />
+ </ItemGroup>
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+ <ItemGroup>
+ <ProjectReference Include="..\GrpcCore\GrpcCore.csproj">
+ <Project>{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}</Project>
+ <Name>GrpcCore</Name>
+ </ProjectReference>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/src/csharp/GrpcCoreTests/GrpcEnvironmentTest.cs b/src/csharp/GrpcCoreTests/GrpcEnvironmentTest.cs
new file mode 100644
index 0000000000..136878d76e
--- /dev/null
+++ b/src/csharp/GrpcCoreTests/GrpcEnvironmentTest.cs
@@ -0,0 +1,18 @@
+using System;
+using NUnit.Framework;
+using Google.GRPC.Core;
+using System.Threading;
+
+namespace Google.GRPC.Core.Tests
+{
+ public class GrpcEnvironmentTest
+ {
+ [Test]
+ public void InitializeAndShutdownGrpcEnvironment() {
+ GrpcEnvironment.EnsureInitialized();
+ Thread.Sleep(500);
+ Assert.IsNotNull(GrpcEnvironment.ThreadPool.CompletionQueue);
+ GrpcEnvironment.Shutdown();
+ }
+ }
+}
diff --git a/src/csharp/GrpcCoreTests/Properties/AssemblyInfo.cs b/src/csharp/GrpcCoreTests/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..565b1e2bd6
--- /dev/null
+++ b/src/csharp/GrpcCoreTests/Properties/AssemblyInfo.cs
@@ -0,0 +1,22 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+// Information about this assembly is defined by the following attributes.
+// Change them to the values specific to your project.
+[assembly: AssemblyTitle("GrpcCoreTests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("")]
+[assembly: AssemblyCopyright("jtattermusch")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
+// The form "{Major}.{Minor}.*" will automatically update the build and revision,
+// and "{Major}.{Minor}.{Build}.*" will update just the revision.
+[assembly: AssemblyVersion("1.0.*")]
+// The following attributes are used to specify the signing key for the assembly,
+// if desired. See the Mono documentation for more information about signing.
+//[assembly: AssemblyDelaySign(false)]
+//[assembly: AssemblyKeyFile("")]
+
diff --git a/src/csharp/GrpcCoreTests/ServerTest.cs b/src/csharp/GrpcCoreTests/ServerTest.cs
new file mode 100644
index 0000000000..b34101bbf5
--- /dev/null
+++ b/src/csharp/GrpcCoreTests/ServerTest.cs
@@ -0,0 +1,21 @@
+using System;
+using NUnit.Framework;
+using Google.GRPC.Core.Internal;
+
+namespace Google.GRPC.Core.Tests
+{
+ public class ServerTest
+ {
+ [Test]
+ public void StartAndShutdownServer() {
+
+ Server server = new Server();
+ server.AddPort("localhost:" + Utils.PickUnusedPort());
+ server.Start();
+ server.Shutdown();
+
+ GrpcEnvironment.Shutdown();
+ }
+
+ }
+}
diff --git a/src/csharp/GrpcCoreTests/TestResult.xml b/src/csharp/GrpcCoreTests/TestResult.xml
new file mode 100644
index 0000000000..a5a6abd7b9
--- /dev/null
+++ b/src/csharp/GrpcCoreTests/TestResult.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!--This file represents the results of running a test suite-->
+<test-results name="/usr/local/google/home/jtattermusch/github/grpc/src/csharp/GrpcCoreTests/bin/Debug/GrpcCoreTests.dll" total="3" errors="0" failures="0" not-run="0" inconclusive="0" ignored="0" skipped="0" invalid="0" date="2015-01-29" time="19:40:47">
+ <environment nunit-version="2.6.0.0" clr-version="4.0.30319.17020" os-version="Unix 3.13.0.43" platform="Unix" cwd="/usr/local/google/home/jtattermusch/github/grpc/src/csharp/GrpcCoreTests" machine-name="jtattermusch.mtv.corp.google.com" user="jtattermusch" user-domain="jtattermusch.mtv.corp.google.com" />
+ <culture-info current-culture="en-US" current-uiculture="en-US" />
+ <test-suite type="Assembly" name="/usr/local/google/home/jtattermusch/github/grpc/src/csharp/GrpcCoreTests/bin/Debug/GrpcCoreTests.dll" executed="True" result="Success" success="True" time="0.172" asserts="0">
+ <results>
+ <test-suite type="Namespace" name="Google" executed="True" result="Success" success="True" time="0.166" asserts="0">
+ <results>
+ <test-suite type="Namespace" name="GRPC" executed="True" result="Success" success="True" time="0.166" asserts="0">
+ <results>
+ <test-suite type="Namespace" name="Core" executed="True" result="Success" success="True" time="0.166" asserts="0">
+ <results>
+ <test-suite type="Namespace" name="Tests" executed="True" result="Success" success="True" time="0.166" asserts="0">
+ <results>
+ <test-suite type="TestFixture" name="CallsTest" executed="True" result="Success" success="True" time="0.009" asserts="0">
+ <results>
+ <test-case name="Google.GRPC.Core.Tests.CallsTest.Test1" executed="True" result="Success" success="True" time="0.004" asserts="0" />
+ </results>
+ </test-suite>
+ <test-suite type="TestFixture" name="ClientServerTest" executed="True" result="Success" success="True" time="0.149" asserts="0">
+ <results>
+ <test-case name="Google.GRPC.Core.Tests.ClientServerTest.EmptyCall" executed="True" result="Success" success="True" time="0.111" asserts="0" />
+ </results>
+ </test-suite>
+ <test-suite type="TestFixture" name="ServerTest" executed="True" result="Success" success="True" time="0.001" asserts="0">
+ <results>
+ <test-case name="Google.GRPC.Core.Tests.ServerTest.StartAndShutdownServer" executed="True" result="Success" success="True" time="0.001" asserts="0" />
+ </results>
+ </test-suite>
+ </results>
+ </test-suite>
+ </results>
+ </test-suite>
+ </results>
+ </test-suite>
+ </results>
+ </test-suite>
+ </results>
+ </test-suite>
+</test-results> \ No newline at end of file
diff --git a/src/csharp/GrpcCoreTests/TimespecTest.cs b/src/csharp/GrpcCoreTests/TimespecTest.cs
new file mode 100644
index 0000000000..484bad7ca1
--- /dev/null
+++ b/src/csharp/GrpcCoreTests/TimespecTest.cs
@@ -0,0 +1,43 @@
+using System;
+using NUnit.Framework;
+using Google.GRPC.Core.Internal;
+
+namespace Google.GRPC.Core.Internal.Tests
+{
+ public class TimespecTest
+ {
+ [Test]
+ public void Now()
+ {
+ var timespec = Timespec.Now;
+ }
+
+ [Test]
+ public void Add()
+ {
+ var t = new Timespec { tv_sec = 12345, tv_nsec = 123456789 };
+ var result = t.Add(TimeSpan.FromTicks(TimeSpan.TicksPerSecond * 10));
+ Assert.AreEqual(result.tv_sec, 12355);
+ Assert.AreEqual(result.tv_nsec, 123456789);
+ }
+
+ [Test]
+ public void Add_Nanos()
+ {
+ var t = new Timespec { tv_sec = 12345, tv_nsec = 123456789 };
+ var result = t.Add(TimeSpan.FromTicks(10));
+ Assert.AreEqual(result.tv_sec, 12345);
+ Assert.AreEqual(result.tv_nsec, 123456789 + 1000);
+ }
+
+ [Test]
+ public void Add_NanosOverflow()
+ {
+ var t = new Timespec { tv_sec = 12345, tv_nsec = 999999999 };
+ var result = t.Add(TimeSpan.FromTicks(TimeSpan.TicksPerSecond * 10 + 10));
+ Assert.AreEqual(result.tv_sec, 12356);
+ Assert.AreEqual(result.tv_nsec, 999);
+ }
+ }
+}
+
diff --git a/src/csharp/GrpcCoreTests/Utils.cs b/src/csharp/GrpcCoreTests/Utils.cs
new file mode 100644
index 0000000000..b0c0a7b620
--- /dev/null
+++ b/src/csharp/GrpcCoreTests/Utils.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Net;
+using System.Net.Sockets;
+
+namespace Google.GRPC.Core.Tests
+{
+ /// <summary>
+ /// Testing utils.
+ /// </summary>
+ public class Utils
+ {
+ static Random random = new Random();
+ // TODO: cleanup this code a bit
+ public static int PickUnusedPort()
+ {
+ int port;
+ do
+ {
+ port = random.Next(2000, 50000);
+
+ } while(!IsPortAvailable(port));
+ return port;
+ }
+ // TODO: cleanup this code a bit
+ public static bool IsPortAvailable(int port)
+ {
+ bool available = true;
+
+ TcpListener server = null;
+ try
+ {
+ IPAddress ipAddress = Dns.GetHostEntry("localhost").AddressList[0];
+ server = new TcpListener(ipAddress, port);
+ server.Start();
+ }
+ catch (Exception ex)
+ {
+ available = false;
+ }
+ finally
+ {
+ if (server != null)
+ {
+ server.Stop();
+ }
+ }
+ return available;
+ }
+ }
+}
+
diff --git a/src/csharp/GrpcDemo/.gitignore b/src/csharp/GrpcDemo/.gitignore
new file mode 100644
index 0000000000..ba077a4031
--- /dev/null
+++ b/src/csharp/GrpcDemo/.gitignore
@@ -0,0 +1 @@
+bin
diff --git a/src/csharp/GrpcDemo/GrpcDemo.csproj b/src/csharp/GrpcDemo/GrpcDemo.csproj
new file mode 100644
index 0000000000..31ce7f133b
--- /dev/null
+++ b/src/csharp/GrpcDemo/GrpcDemo.csproj
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
+ <ProductVersion>10.0.0</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <RootNamespace>GrpcDemo</RootNamespace>
+ <AssemblyName>GrpcDemo</AssemblyName>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug</OutputPath>
+ <DefineConstants>DEBUG;</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <Externalconsole>true</Externalconsole>
+ <PlatformTarget>x86</PlatformTarget>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
+ <DebugType>full</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release</OutputPath>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <Externalconsole>true</Externalconsole>
+ <PlatformTarget>x86</PlatformTarget>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Program.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+ <ItemGroup>
+ <ProjectReference Include="..\GrpcApi\GrpcApi.csproj">
+ <Project>{7DC1433E-3225-42C7-B7EA-546D56E27A4B}</Project>
+ <Name>GrpcApi</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\GrpcCore\GrpcCore.csproj">
+ <Project>{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}</Project>
+ <Name>GrpcCore</Name>
+ </ProjectReference>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/src/csharp/GrpcDemo/Program.cs b/src/csharp/GrpcDemo/Program.cs
new file mode 100644
index 0000000000..258762dbb9
--- /dev/null
+++ b/src/csharp/GrpcDemo/Program.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Runtime.InteropServices;
+using Google.GRPC.Core;
+using System.Threading;
+using math;
+
+namespace Google.GRPC.Demo
+{
+ class MainClass
+ {
+ public static void Main (string[] args)
+ {
+ using (Channel channel = new Channel("127.0.0.1:23456"))
+ {
+ IMathServiceClient stub = new MathServiceClientStub(channel, Timeout.InfiniteTimeSpan);
+ Examples.DivExample(stub);
+
+ Examples.FibExample(stub);
+
+ Examples.SumExample(stub);
+
+ Examples.DivManyExample(stub);
+ }
+
+ GrpcEnvironment.Shutdown();
+ }
+ }
+}
diff --git a/src/csharp/GrpcDemo/Properties/AssemblyInfo.cs b/src/csharp/GrpcDemo/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..b8e1406da7
--- /dev/null
+++ b/src/csharp/GrpcDemo/Properties/AssemblyInfo.cs
@@ -0,0 +1,22 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+// Information about this assembly is defined by the following attributes.
+// Change them to the values specific to your project.
+[assembly: AssemblyTitle ("GrpcDemo")]
+[assembly: AssemblyDescription ("")]
+[assembly: AssemblyConfiguration ("")]
+[assembly: AssemblyCompany ("")]
+[assembly: AssemblyProduct ("")]
+[assembly: AssemblyCopyright ("jtattermusch")]
+[assembly: AssemblyTrademark ("")]
+[assembly: AssemblyCulture ("")]
+// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
+// The form "{Major}.{Minor}.*" will automatically update the build and revision,
+// and "{Major}.{Minor}.{Build}.*" will update just the revision.
+[assembly: AssemblyVersion ("1.0.*")]
+// The following attributes are used to specify the signing key for the assembly,
+// if desired. See the Mono documentation for more information about signing.
+//[assembly: AssemblyDelaySign(false)]
+//[assembly: AssemblyKeyFile("")]
+
diff --git a/src/csharp/README.md b/src/csharp/README.md
index 5b56303c14..75bfb26252 100755
--- a/src/csharp/README.md
+++ b/src/csharp/README.md
@@ -19,4 +19,8 @@ CONTENTS
- ext:
The extension library that wraps C API to be more digestible by C#.
+- GrpcCore:
+ The main gRPC C# library.
+- GrpcApi:
+ API examples for math.proto.
diff --git a/src/csharp/lib/Google.ProtocolBuffers.dll b/src/csharp/lib/Google.ProtocolBuffers.dll
new file mode 100755
index 0000000000..ce2f466b24
--- /dev/null
+++ b/src/csharp/lib/Google.ProtocolBuffers.dll
Binary files differ
diff --git a/src/node/README.md b/src/node/README.md
index 55329d8cb2..c342b7ca57 100644
--- a/src/node/README.md
+++ b/src/node/README.md
@@ -1,12 +1,70 @@
-# Node.js GRPC extension
+# Node.js gRPC Library
-The package is built with
+## Installation
- node-gyp configure
- node-gyp build
+First, clone this repository (NPM package coming soon). Then follow the instructions in the `INSTALL` file in the root of the repository to install the C core library that this package depends on.
-or, for brevity
+Then, simply run `npm install` in or referencing this directory.
- node-gyp configure build
+## Tests
-The tests can be run with `npm test` on a dev install. \ No newline at end of file
+To run the test suite, simply run `npm test` in the install location.
+
+## API
+
+This library internally uses [ProtoBuf.js](https://github.com/dcodeIO/ProtoBuf.js), and some structures it exports match those exported by that library
+
+If you require this module, you will get an object with the following members
+
+```javascript
+function load(filename)
+```
+
+Takes a filename of a [Protocol Buffer](https://developers.google.com/protocol-buffers/) file, and returns an object representing the structure of the protocol buffer in the following way:
+
+ - Namespaces become maps from the names of their direct members to those member objects
+ - Service definitions become client constructors for clients for that service. They also have a `service` member that can be used for constructing servers.
+ - Message definitions become Message constructors like those that ProtoBuf.js would create
+ - Enum definitions become Enum objects like those that ProtoBuf.js would create
+ - Anything else becomes the relevant reflection object that ProtoBuf.js would create
+
+
+```javascript
+function loadObject(reflectionObject)
+```
+
+Returns the same structure that `load` returns, but takes a reflection object from `ProtoBuf.js` instead of a file name.
+
+```javascript
+function buildServer(serviceArray)
+```
+
+Takes an array of service objects and returns a constructor for a server that handles requests to all of those services.
+
+
+```javascript
+status
+```
+
+An object mapping status names to status code numbers.
+
+
+```javascript
+callError
+```
+
+An object mapping call error names to codes. This is primarily useful for tracking down certain kinds of internal errors.
+
+
+```javascript
+Credentials
+```
+
+An object with factory methods for creating credential objects for clients.
+
+
+```javascript
+ServerCredentials
+```
+
+An object with factory methods fro creating credential objects for servers.
diff --git a/src/node/ext/timeval.cc b/src/node/ext/timeval.cc
index 687e33576b..20d52f0963 100644
--- a/src/node/ext/timeval.cc
+++ b/src/node/ext/timeval.cc
@@ -56,9 +56,8 @@ double TimespecToMilliseconds(gpr_timespec timespec) {
} else if (gpr_time_cmp(timespec, gpr_inf_past) == 0) {
return -std::numeric_limits<double>::infinity();
} else {
- struct timeval time = gpr_timeval_from_timespec(timespec);
- return (static_cast<double>(time.tv_sec) * 1000 +
- static_cast<double>(time.tv_usec) / 1000);
+ return (static_cast<double>(timespec.tv_sec) * 1000 +
+ static_cast<double>(timespec.tv_nsec) / 1000000);
}
}
diff --git a/test/core/echo/echo_test.c b/test/core/echo/echo_test.c
index 83b83ab7ff..5450dfbef5 100644
--- a/test/core/echo/echo_test.c
+++ b/test/core/echo/echo_test.c
@@ -31,7 +31,10 @@
*
*/
+#ifndef _POSIX_SOURCE
#define _POSIX_SOURCE
+#endif
+
#include <unistd.h>
#include <assert.h>
#include <stdio.h>
diff --git a/test/core/fling/fling_stream_test.c b/test/core/fling/fling_stream_test.c
index 7f52fb1bad..1db2f1a791 100644
--- a/test/core/fling/fling_stream_test.c
+++ b/test/core/fling/fling_stream_test.c
@@ -31,7 +31,10 @@
*
*/
+#ifndef _POSIX_SOURCE
#define _POSIX_SOURCE
+#endif
+
#include <unistd.h>
#include <assert.h>
#include <stdio.h>
diff --git a/test/core/fling/fling_test.c b/test/core/fling/fling_test.c
index b2272f20c8..4f41a21aaa 100644
--- a/test/core/fling/fling_test.c
+++ b/test/core/fling/fling_test.c
@@ -31,7 +31,10 @@
*
*/
+#ifndef _POSIX_SOURCE
#define _POSIX_SOURCE
+#endif
+
#include <unistd.h>
#include <assert.h>
#include <stdio.h>
diff --git a/test/core/json/json_test.c b/test/core/json/json_test.c
index 11659a5716..6d0227ad39 100644
--- a/test/core/json/json_test.c
+++ b/test/core/json/json_test.c
@@ -151,7 +151,7 @@ static void test_pairs() {
GPR_ASSERT(!json);
}
- free(scratchpad);
+ gpr_free(scratchpad);
}
}
@@ -166,6 +166,7 @@ static void test_atypical() {
grpc_json_destroy(json->child);
json->child = brother;
grpc_json_destroy(json);
+ gpr_free(scratchpad);
}
int main(int argc, char **argv) {
diff --git a/tools/gce_setup/builder.sh b/tools/gce_setup/builder.sh
new file mode 100755
index 0000000000..49b3c436fe
--- /dev/null
+++ b/tools/gce_setup/builder.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+main() {
+ # restart builder vm and wait for images to sync to it
+ source grpc_docker.sh
+ ./new_grpc_docker_builder.sh -igrpc-docker-builder-alt-2 -anone
+ cd ../../
+ sleep 3600
+
+ # build images for all languages
+ languages=(cxx java go ruby node)
+ for lan in "${languages[@]}"
+ do
+ grpc_update_image $lan
+ done
+
+ # restart client and server vm and wait for images to sync to them
+ cd tools/gce_setup
+ ./new_grpc_docker_builder.sh -igrpc-docker-testclients -anone
+ ./new_grpc_docker_builder.sh -igrpc-docker-server -anone
+ sleep 3600
+
+ # launch images for all languages on server
+ grpc_launch_servers grpc-docker-server
+
+}
+
+set -x
+main "$@"
diff --git a/tools/gce_setup/cloud_prod_runner.sh b/tools/gce_setup/cloud_prod_runner.sh
index 0c1163ad7d..200f859ede 100755
--- a/tools/gce_setup/cloud_prod_runner.sh
+++ b/tools/gce_setup/cloud_prod_runner.sh
@@ -2,8 +2,8 @@
main() {
source grpc_docker.sh
- test_cases=(large_unary empty_unary client_streaming server_streaming)
- clients=(cxx java go ruby)
+ test_cases=(large_unary empty_unary ping_pong client_streaming server_streaming)
+ clients=(cxx java go ruby node)
for test_case in "${test_cases[@]}"
do
for client in "${clients[@]}"
diff --git a/tools/gce_setup/grpc_docker.sh b/tools/gce_setup/grpc_docker.sh
index 2ac75f3cc5..2e02653864 100755
--- a/tools/gce_setup/grpc_docker.sh
+++ b/tools/gce_setup/grpc_docker.sh
@@ -762,7 +762,16 @@ grpc_interop_test() {
echo " $ssh_cmd"
echo "on $host"
[[ $dry_run == 1 ]] && return 0 # don't run the command on a dry run
- gcloud compute $project_opt ssh $zone_opt $host --command "$cmd"
+ gcloud compute $project_opt ssh $zone_opt $host --command "$cmd" &
+ PID=$!
+ sleep 10
+ echo "pid is $PID"
+ if ps -p $PID
+ then
+ kill $PID
+ return 1
+ fi
+
}
# Runs a test command on a docker instance.
@@ -808,7 +817,16 @@ grpc_cloud_prod_test() {
echo " $ssh_cmd"
echo "on $host"
[[ $dry_run == 1 ]] && return 0 # don't run the command on a dry run
- gcloud compute $project_opt ssh $zone_opt $host --command "$cmd"
+ gcloud compute $project_opt ssh $zone_opt $host --command "$cmd" &
+ PID=$!
+ sleep 10
+ echo "pid is $PID"
+ if ps -p $PID
+ then
+ kill $PID
+ return 1
+ fi
+
}
# Runs a test command on a docker instance.
diff --git a/tools/gce_setup/interop_test_runner.sh b/tools/gce_setup/interop_test_runner.sh
index 1244307912..456ad4b472 100755
--- a/tools/gce_setup/interop_test_runner.sh
+++ b/tools/gce_setup/interop_test_runner.sh
@@ -1,33 +1,8 @@
#!/bin/bash
thisfile=$(readlink -ne "${BASH_SOURCE[0]}")
-
-run_test() {
- local test_case=$1
- shift
- local client=$1
- shift
- local server=$1
- if grpc_interop_test $test_case grpc-docker-testclients $client grpc-docker-server $server
- then
- echo "$test_case $client $server passed" >> /tmp/interop_result.txt
- else
- echo "$test_case $client $server failed" >> /tmp/interop_result.txt
- fi
-}
-
-time_out() {
- local test_case=$1
- shift
- local client=$1
- shift
- local server=$1
- if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
- if ! timeout 20s bash -l -c "source $thisfile && run_test $test_case $client $server"
- then
- echo "$test_case $client $server timed out" >> /tmp/interop_result.txt
- fi
- fi
-}
+current_time=$(date "+%Y-%m-%d-%H-%M-%S")
+result_file_name=interop_result.$current_time.html
+echo $result_file_name
main() {
source grpc_docker.sh
@@ -40,13 +15,22 @@ main() {
do
for server in "${servers[@]}"
do
- time_out $test_case $client $server
+ if grpc_interop_test $test_case grpc-docker-testclients $client grpc-docker-server $server
+ then
+ echo " ['$test_case', '$client', '$server', true]," >> /tmp/interop_result.txt
+ else
+ echo " ['$test_case', '$client', '$server', false]," >> /tmp/interop_result.txt
+ fi
done
done
done
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
+ cat pre.html /tmp/interop_result.txt post.html > /tmp/interop_result.html
gsutil cp /tmp/interop_result.txt gs://stoked-keyword-656-output/interop_result.txt
+ gsutil cp /tmp/interop_result.html gs://stoked-keyword-656-output/interop_result.html
+ gsutil cp /tmp/interop_result.html gs://stoked-keyword-656-output/result_history/$result_file_name
rm /tmp/interop_result.txt
+ rm /tmp/interop_result.html
fi
}
diff --git a/tools/gce_setup/post.html b/tools/gce_setup/post.html
new file mode 100644
index 0000000000..57cbc8c369
--- /dev/null
+++ b/tools/gce_setup/post.html
@@ -0,0 +1,12 @@
+ ]);
+
+ var table = new google.visualization.Table(document.getElementById('table_div'));
+
+ table.draw(data, {showRowNumber: true});
+ }
+ </script>
+ </head>
+ <body>
+ <div id="table_div"></div>
+ </body>
+</html>
diff --git a/tools/gce_setup/pre.html b/tools/gce_setup/pre.html
new file mode 100644
index 0000000000..74ce5ce202
--- /dev/null
+++ b/tools/gce_setup/pre.html
@@ -0,0 +1,14 @@
+<html>
+ <head>
+ <script type="text/javascript" src="https://www.google.com/jsapi"></script>
+ <script type="text/javascript">
+ google.load("visualization", "1", {packages:["table"]});
+ google.setOnLoadCallback(drawTable);
+
+ function drawTable() {
+ var data = new google.visualization.DataTable();
+ data.addColumn('string', 'TestCase');
+ data.addColumn('string', 'Client');
+ data.addColumn('string', 'Server');
+ data.addColumn('boolean', 'Pass');
+ data.addRows([
diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py
index 280c3f05cb..cb54c0db82 100755
--- a/tools/run_tests/run_tests.py
+++ b/tools/run_tests/run_tests.py
@@ -108,10 +108,11 @@ class PythonLanguage(object):
_CONFIGS = {
'dbg': SimpleConfig('dbg'),
'opt': SimpleConfig('opt'),
- 'tsan': SimpleConfig('tsan'),
+ 'tsan': SimpleConfig('tsan', environ={
+ 'TSAN_OPTIONS': 'suppressions=tools/tsan_suppressions.txt'}),
'msan': SimpleConfig('msan'),
'asan': SimpleConfig('asan', environ={
- 'ASAN_OPTIONS': 'detect_leaks=1:color=always'}),
+ 'ASAN_OPTIONS': 'detect_leaks=1:color=always:suppressions=tools/tsan_suppressions.txt'}),
'gcov': SimpleConfig('gcov'),
'memcheck': ValgrindConfig('valgrind', 'memcheck'),
'helgrind': ValgrindConfig('dbg', 'helgrind')
diff --git a/tools/tsan_suppressions.txt b/tools/tsan_suppressions.txt
new file mode 100644
index 0000000000..23d57f9fd1
--- /dev/null
+++ b/tools/tsan_suppressions.txt
@@ -0,0 +1,2 @@
+# OPENSSL_cleanse does racy access to a global
+race:OPENSSL_cleanse
diff --git a/vsprojects/vs2013/gpr.vcxproj b/vsprojects/vs2013/gpr.vcxproj
index c77a61d782..0d429ab43d 100644
--- a/vsprojects/vs2013/gpr.vcxproj
+++ b/vsprojects/vs2013/gpr.vcxproj
@@ -92,8 +92,6 @@
<ClInclude Include="..\..\include\grpc\support\sync_win32.h" />
<ClInclude Include="..\..\include\grpc\support\thd.h" />
<ClInclude Include="..\..\include\grpc\support\time.h" />
- <ClInclude Include="..\..\include\grpc\support\time_posix.h" />
- <ClInclude Include="..\..\include\grpc\support\time_win32.h" />
<ClInclude Include="..\..\include\grpc\support\useful.h" />
</ItemGroup>
<ItemGroup>
diff --git a/vsprojects/vs2013/gpr.vcxproj.filters b/vsprojects/vs2013/gpr.vcxproj.filters
index e385efcfb2..a992558654 100644
--- a/vsprojects/vs2013/gpr.vcxproj.filters
+++ b/vsprojects/vs2013/gpr.vcxproj.filters
@@ -138,12 +138,6 @@
<ClInclude Include="..\..\include\grpc\support\time.h">
<Filter>include\grpc\support</Filter>
</ClInclude>
- <ClInclude Include="..\..\include\grpc\support\time_posix.h">
- <Filter>include\grpc\support</Filter>
- </ClInclude>
- <ClInclude Include="..\..\include\grpc\support\time_win32.h">
- <Filter>include\grpc\support</Filter>
- </ClInclude>
<ClInclude Include="..\..\include\grpc\support\useful.h">
<Filter>include\grpc\support</Filter>
</ClInclude>