aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Jan Tattermusch <jtattermusch@google.com>2015-02-03 17:54:38 -0800
committerGravatar Jan Tattermusch <jtattermusch@google.com>2015-02-03 18:11:10 -0800
commita7608b081e30f6fef5fd36935f8c8591381d4bde (patch)
tree43c7d282a57a969cc5272422f431c270cd5ba3be
parentea6f6d99f46cc0445840408da8d6096405fc209c (diff)
Prototype of gRPC C# library (Core and API examples)
-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
50 files changed, 4600 insertions, 0 deletions
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