Merge pull request #19792 from jtattermusch/csharp_ibufferwriter_serialization

C#: support serialization to IBufferWriter
pull/20176/head
Jan Tattermusch 6 years ago committed by GitHub
commit a1ad4800da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 21
      src/csharp/Grpc.Core.Api/SerializationContext.cs
  2. 2
      src/csharp/Grpc.Core.Tests/ContextualMarshallerTest.cs
  3. 207
      src/csharp/Grpc.Core.Tests/Internal/DefaultSerializationContextTest.cs
  4. 10
      src/csharp/Grpc.Core.Tests/Internal/FakeNativeCall.cs
  5. 163
      src/csharp/Grpc.Core.Tests/Internal/SliceBufferSafeHandleTest.cs
  6. 32
      src/csharp/Grpc.Core/Internal/AsyncCall.cs
  7. 44
      src/csharp/Grpc.Core/Internal/AsyncCallBase.cs
  8. 39
      src/csharp/Grpc.Core/Internal/AsyncCallServer.cs
  9. 25
      src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
  10. 64
      src/csharp/Grpc.Core/Internal/DefaultSerializationContext.cs
  11. 10
      src/csharp/Grpc.Core/Internal/INativeCall.cs
  12. 96
      src/csharp/Grpc.Core/Internal/NativeMethods.Generated.cs
  13. 41
      src/csharp/Grpc.Core/Internal/ReusableSliceBuffer.cs
  14. 166
      src/csharp/Grpc.Core/Internal/SliceBufferSafeHandle.cs
  15. 65
      src/csharp/Grpc.Core/Internal/SliceMemoryManager.cs
  16. 10
      src/csharp/Grpc.Microbenchmarks/SendMessageBenchmark.cs
  17. 4
      src/csharp/Grpc.Microbenchmarks/UnaryCallOverheadBenchmark.cs
  18. 4
      src/csharp/Grpc.Microbenchmarks/Utf8Encode.cs
  19. 106
      src/csharp/ext/grpc_csharp_ext.c
  20. 2
      src/csharp/tests.json
  21. 24
      src/csharp/unitypackage/unitypackage_skeleton/Plugins/Grpc.Core/runtimes/grpc_csharp_ext_dummy_stubs.c
  22. 16
      templates/src/csharp/Grpc.Core/Internal/native_methods.include

@ -17,6 +17,7 @@
#endregion
using System;
using System.Buffers;
namespace Grpc.Core
{
@ -27,7 +28,7 @@ namespace Grpc.Core
{
/// <summary>
/// Use the byte array as serialized form of current message and mark serialization process as complete.
/// Complete() can only be called once. By calling this method the caller gives up the ownership of the
/// <c>Complete(byte[])</c> can only be called once. By calling this method the caller gives up the ownership of the
/// payload which must not be accessed afterwards.
/// </summary>
/// <param name="payload">the serialized form of current message</param>
@ -35,5 +36,23 @@ namespace Grpc.Core
{
throw new NotImplementedException();
}
/// <summary>
/// Gets buffer writer that can be used to write the serialized data. Once serialization is finished,
/// <c>Complete()</c> needs to be called.
/// </summary>
public virtual IBufferWriter<byte> GetBufferWriter()
{
throw new NotImplementedException();
}
/// <summary>
/// Complete the payload written to the buffer writer. <c>Complete()</c> can only be called once.
/// </summary>
public virtual void Complete()
{
throw new NotImplementedException();
}
}
}

@ -52,6 +52,8 @@ namespace Grpc.Core.Tests
}
if (str == "SERIALIZE_TO_NULL")
{
// for contextual marshaller, serializing to null payload corresponds
// to not calling the Complete() method in the serializer.
return;
}
var bytes = System.Text.Encoding.UTF8.GetBytes(str);

@ -0,0 +1,207 @@
#region Copyright notice and license
// Copyright 2019 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
using NUnit.Framework;
namespace Grpc.Core.Internal.Tests
{
public class DefaultSerializationContextTest
{
[TestCase]
public void CompleteAllowedOnlyOnce()
{
using (var scope = NewDefaultSerializationContextScope())
{
var context = scope.Context;
var buffer = GetTestBuffer(10);
context.Complete(buffer);
Assert.Throws(typeof(InvalidOperationException), () => context.Complete(buffer));
Assert.Throws(typeof(InvalidOperationException), () => context.Complete());
}
}
[TestCase]
public void CompleteAllowedOnlyOnce2()
{
using (var scope = NewDefaultSerializationContextScope())
{
var context = scope.Context;
context.Complete();
Assert.Throws(typeof(InvalidOperationException), () => context.Complete(GetTestBuffer(10)));
Assert.Throws(typeof(InvalidOperationException), () => context.Complete());
}
}
[TestCase(0)]
[TestCase(1)]
[TestCase(10)]
[TestCase(100)]
[TestCase(1000)]
public void ByteArrayPayload(int payloadSize)
{
using (var scope = NewDefaultSerializationContextScope())
{
var context = scope.Context;
var origPayload = GetTestBuffer(payloadSize);
context.Complete(origPayload);
var nativePayload = context.GetPayload().ToByteArray();
CollectionAssert.AreEqual(origPayload, nativePayload);
}
}
[TestCase(0)]
[TestCase(1)]
[TestCase(10)]
[TestCase(100)]
[TestCase(1000)]
public void BufferWriter_OneSegment(int payloadSize)
{
using (var scope = NewDefaultSerializationContextScope())
{
var context = scope.Context;
var origPayload = GetTestBuffer(payloadSize);
var bufferWriter = context.GetBufferWriter();
origPayload.AsSpan().CopyTo(bufferWriter.GetSpan(payloadSize));
bufferWriter.Advance(payloadSize);
context.Complete();
var nativePayload = context.GetPayload().ToByteArray();
CollectionAssert.AreEqual(origPayload, nativePayload);
}
}
[TestCase(0)]
[TestCase(1)]
[TestCase(10)]
[TestCase(100)]
[TestCase(1000)]
public void BufferWriter_OneSegment_GetMemory(int payloadSize)
{
using (var scope = NewDefaultSerializationContextScope())
{
var context = scope.Context;
var origPayload = GetTestBuffer(payloadSize);
var bufferWriter = context.GetBufferWriter();
origPayload.AsSpan().CopyTo(bufferWriter.GetMemory(payloadSize).Span);
bufferWriter.Advance(payloadSize);
context.Complete();
var nativePayload = context.GetPayload().ToByteArray();
CollectionAssert.AreEqual(origPayload, nativePayload);
}
}
[TestCase(1, 4)] // small slice size tests grpc_slice with inline data
[TestCase(10, 4)]
[TestCase(100, 4)]
[TestCase(1000, 4)]
[TestCase(1, 64)] // larger slice size tests allocated grpc_slices
[TestCase(10, 64)]
[TestCase(1000, 50)]
[TestCase(1000, 64)]
public void BufferWriter_MultipleSegments(int payloadSize, int maxSliceSize)
{
using (var scope = NewDefaultSerializationContextScope())
{
var context = scope.Context;
var origPayload = GetTestBuffer(payloadSize);
var bufferWriter = context.GetBufferWriter();
for (int offset = 0; offset < payloadSize; offset += maxSliceSize)
{
var sliceSize = Math.Min(maxSliceSize, payloadSize - offset);
// we allocate last slice as too big intentionally to test that shrinking works
var dest = bufferWriter.GetSpan(maxSliceSize);
origPayload.AsSpan(offset, sliceSize).CopyTo(dest);
bufferWriter.Advance(sliceSize);
}
context.Complete();
var nativePayload = context.GetPayload().ToByteArray();
CollectionAssert.AreEqual(origPayload, nativePayload);
}
}
[TestCase]
public void ContextIsReusable()
{
using (var scope = NewDefaultSerializationContextScope())
{
var context = scope.Context;
Assert.Throws(typeof(NullReferenceException), () => context.GetPayload());
var origPayload1 = GetTestBuffer(10);
context.Complete(origPayload1);
CollectionAssert.AreEqual(origPayload1, context.GetPayload().ToByteArray());
context.Reset();
var origPayload2 = GetTestBuffer(20);
var bufferWriter = context.GetBufferWriter();
origPayload2.AsSpan().CopyTo(bufferWriter.GetMemory(origPayload2.Length).Span);
bufferWriter.Advance(origPayload2.Length);
context.Complete();
CollectionAssert.AreEqual(origPayload2, context.GetPayload().ToByteArray());
context.Reset();
Assert.Throws(typeof(NullReferenceException), () => context.GetPayload());
}
}
[TestCase]
public void GetBufferWriterThrowsForCompletedContext()
{
using (var scope = NewDefaultSerializationContextScope())
{
var context = scope.Context;
context.Complete(GetTestBuffer(10));
Assert.Throws(typeof(InvalidOperationException), () => context.GetBufferWriter());
}
}
private DefaultSerializationContext.UsageScope NewDefaultSerializationContextScope()
{
return new DefaultSerializationContext.UsageScope(new DefaultSerializationContext());
}
private byte[] GetTestBuffer(int length)
{
var testBuffer = new byte[length];
for (int i = 0; i < testBuffer.Length; i++)
{
testBuffer[i] = (byte) i;
}
return testBuffer;
}
}
}

@ -101,13 +101,13 @@ namespace Grpc.Core.Internal.Tests
return "PEER";
}
public void StartUnary(IUnaryResponseClientCallback callback, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags)
public void StartUnary(IUnaryResponseClientCallback callback, SliceBufferSafeHandle payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags)
{
StartCallMaybeFail();
UnaryResponseClientCallback = callback;
}
public void StartUnary(BatchContextSafeHandle ctx, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags)
public void StartUnary(BatchContextSafeHandle ctx, SliceBufferSafeHandle payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags)
{
StartCallMaybeFail();
throw new NotImplementedException();
@ -119,7 +119,7 @@ namespace Grpc.Core.Internal.Tests
UnaryResponseClientCallback = callback;
}
public void StartServerStreaming(IReceivedStatusOnClientCallback callback, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags)
public void StartServerStreaming(IReceivedStatusOnClientCallback callback, SliceBufferSafeHandle payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags)
{
StartCallMaybeFail();
ReceivedStatusOnClientCallback = callback;
@ -146,7 +146,7 @@ namespace Grpc.Core.Internal.Tests
SendCompletionCallback = callback;
}
public void StartSendMessage(ISendCompletionCallback callback, byte[] payload, WriteFlags writeFlags, bool sendEmptyInitialMetadata)
public void StartSendMessage(ISendCompletionCallback callback, SliceBufferSafeHandle payload, WriteFlags writeFlags, bool sendEmptyInitialMetadata)
{
SendCompletionCallback = callback;
}
@ -157,7 +157,7 @@ namespace Grpc.Core.Internal.Tests
}
public void StartSendStatusFromServer(ISendStatusFromServerCompletionCallback callback, Status status, MetadataArraySafeHandle metadataArray, bool sendEmptyInitialMetadata,
byte[] optionalPayload, WriteFlags writeFlags)
SliceBufferSafeHandle payload, WriteFlags writeFlags)
{
SendStatusFromServerCallback = callback;
}

@ -0,0 +1,163 @@
#region Copyright notice and license
// Copyright 2019 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
using NUnit.Framework;
namespace Grpc.Core.Internal.Tests
{
public class SliceBufferSafeHandleTest
{
[TestCase]
public void Complete_EmptyBuffer()
{
using (var sliceBuffer = SliceBufferSafeHandle.Create())
{
sliceBuffer.Complete();
CollectionAssert.AreEqual(new byte[0], sliceBuffer.ToByteArray());
}
}
[TestCase]
public void Complete_TailSizeZero()
{
using (var sliceBuffer = SliceBufferSafeHandle.Create())
{
var origPayload = GetTestBuffer(10);
origPayload.AsSpan().CopyTo(sliceBuffer.GetSpan(origPayload.Length));
sliceBuffer.Advance(origPayload.Length);
// call complete where tail space size == 0
sliceBuffer.Complete();
CollectionAssert.AreEqual(origPayload, sliceBuffer.ToByteArray());
}
}
[TestCase]
public void Complete_TruncateTailSpace()
{
using (var sliceBuffer = SliceBufferSafeHandle.Create())
{
var origPayload = GetTestBuffer(10);
var dest = sliceBuffer.GetSpan(origPayload.Length + 10);
origPayload.AsSpan().CopyTo(dest);
sliceBuffer.Advance(origPayload.Length);
// call complete where tail space needs to be truncated
sliceBuffer.Complete();
CollectionAssert.AreEqual(origPayload, sliceBuffer.ToByteArray());
}
}
[TestCase]
public void SliceBufferIsReusable()
{
using (var sliceBuffer = SliceBufferSafeHandle.Create())
{
var origPayload = GetTestBuffer(10);
origPayload.AsSpan().CopyTo(sliceBuffer.GetSpan(origPayload.Length));
sliceBuffer.Advance(origPayload.Length);
sliceBuffer.Complete();
CollectionAssert.AreEqual(origPayload, sliceBuffer.ToByteArray());
sliceBuffer.Reset();
var origPayload2 = GetTestBuffer(20);
origPayload2.AsSpan().CopyTo(sliceBuffer.GetSpan(origPayload2.Length));
sliceBuffer.Advance(origPayload2.Length);
sliceBuffer.Complete();
CollectionAssert.AreEqual(origPayload2, sliceBuffer.ToByteArray());
sliceBuffer.Reset();
CollectionAssert.AreEqual(new byte[0], sliceBuffer.ToByteArray());
}
}
[TestCase]
public void SliceBuffer_SizeHintZero()
{
using (var sliceBuffer = SliceBufferSafeHandle.Create())
{
var destSpan = sliceBuffer.GetSpan(0);
Assert.IsTrue(destSpan.Length > 0); // some non-zero size memory is made available
sliceBuffer.Reset();
var destMemory = sliceBuffer.GetMemory(0);
Assert.IsTrue(destMemory.Length > 0);
}
}
[TestCase(0)]
[TestCase(1000)]
public void SliceBuffer_BigPayload(int sizeHint)
{
using (var sliceBuffer = SliceBufferSafeHandle.Create())
{
var bigPayload = GetTestBuffer(4 * 1024 * 1024);
int offset = 0;
while (offset < bigPayload.Length)
{
var destSpan = sliceBuffer.GetSpan(sizeHint);
int copySize = Math.Min(destSpan.Length, bigPayload.Length - offset);
bigPayload.AsSpan(offset, copySize).CopyTo(destSpan);
sliceBuffer.Advance(copySize);
offset += copySize;
}
sliceBuffer.Complete();
CollectionAssert.AreEqual(bigPayload, sliceBuffer.ToByteArray());
}
}
[TestCase]
public void SliceBuffer_NegativeSizeHint()
{
using (var sliceBuffer = SliceBufferSafeHandle.Create())
{
Assert.Throws(typeof(ArgumentException), () => sliceBuffer.GetSpan(-1));
Assert.Throws(typeof(ArgumentException), () => sliceBuffer.GetMemory(-1));
}
}
[TestCase]
public void SliceBuffer_AdvanceBadArg()
{
using (var sliceBuffer = SliceBufferSafeHandle.Create())
{
int size = 10;
var destSpan = sliceBuffer.GetSpan(size);
Assert.Throws(typeof(ArgumentException), () => sliceBuffer.Advance(size + 1));
Assert.Throws(typeof(ArgumentException), () => sliceBuffer.Advance(-1));
}
}
private byte[] GetTestBuffer(int length)
{
var testBuffer = new byte[length];
for (int i = 0; i < testBuffer.Length; i++)
{
testBuffer[i] = (byte) i;
}
return testBuffer;
}
}
}

@ -95,10 +95,10 @@ namespace Grpc.Core.Internal
readingDone = true;
}
byte[] payload = UnsafeSerialize(msg);
using (var serializationScope = DefaultSerializationContext.GetInitializedThreadLocalScope())
using (var metadataArray = MetadataArraySafeHandle.Create(details.Options.Headers))
{
var payload = UnsafeSerialize(msg, serializationScope.Context); // do before metadata array?
var ctx = details.Channel.Environment.BatchContextPool.Lease();
try
{
@ -160,13 +160,15 @@ namespace Grpc.Core.Internal
halfcloseRequested = true;
readingDone = true;
byte[] payload = UnsafeSerialize(msg);
unaryResponseTcs = new TaskCompletionSource<TResponse>();
using (var metadataArray = MetadataArraySafeHandle.Create(details.Options.Headers))
using (var serializationScope = DefaultSerializationContext.GetInitializedThreadLocalScope())
{
call.StartUnary(UnaryResponseClientCallback, payload, GetWriteFlagsForCall(), metadataArray, details.Options.Flags);
callStartedOk = true;
var payload = UnsafeSerialize(msg, serializationScope.Context);
unaryResponseTcs = new TaskCompletionSource<TResponse>();
using (var metadataArray = MetadataArraySafeHandle.Create(details.Options.Headers))
{
call.StartUnary(UnaryResponseClientCallback, payload, GetWriteFlagsForCall(), metadataArray, details.Options.Flags);
callStartedOk = true;
}
}
return unaryResponseTcs.Task;
@ -235,13 +237,15 @@ namespace Grpc.Core.Internal
halfcloseRequested = true;
byte[] payload = UnsafeSerialize(msg);
streamingResponseCallFinishedTcs = new TaskCompletionSource<object>();
using (var metadataArray = MetadataArraySafeHandle.Create(details.Options.Headers))
using (var serializationScope = DefaultSerializationContext.GetInitializedThreadLocalScope())
{
call.StartServerStreaming(ReceivedStatusOnClientCallback, payload, GetWriteFlagsForCall(), metadataArray, details.Options.Flags);
callStartedOk = true;
var payload = UnsafeSerialize(msg, serializationScope.Context);
streamingResponseCallFinishedTcs = new TaskCompletionSource<object>();
using (var metadataArray = MetadataArraySafeHandle.Create(details.Options.Headers))
{
call.StartServerStreaming(ReceivedStatusOnClientCallback, payload, GetWriteFlagsForCall(), metadataArray, details.Options.Flags);
callStartedOk = true;
}
}
call.StartReceiveInitialMetadata(ReceivedResponseHeadersCallback);
}

@ -115,23 +115,25 @@ namespace Grpc.Core.Internal
/// </summary>
protected Task SendMessageInternalAsync(TWrite msg, WriteFlags writeFlags)
{
byte[] payload = UnsafeSerialize(msg);
lock (myLock)
using (var serializationScope = DefaultSerializationContext.GetInitializedThreadLocalScope())
{
GrpcPreconditions.CheckState(started);
var earlyResult = CheckSendAllowedOrEarlyResult();
if (earlyResult != null)
var payload = UnsafeSerialize(msg, serializationScope.Context);
lock (myLock)
{
return earlyResult;
}
GrpcPreconditions.CheckState(started);
var earlyResult = CheckSendAllowedOrEarlyResult();
if (earlyResult != null)
{
return earlyResult;
}
call.StartSendMessage(SendCompletionCallback, payload, writeFlags, !initialMetadataSent);
call.StartSendMessage(SendCompletionCallback, payload, writeFlags, !initialMetadataSent);
initialMetadataSent = true;
streamingWritesCounter++;
streamingWriteTcs = new TaskCompletionSource<object>();
return streamingWriteTcs.Task;
initialMetadataSent = true;
streamingWritesCounter++;
streamingWriteTcs = new TaskCompletionSource<object>();
return streamingWriteTcs.Task;
}
}
}
@ -213,19 +215,11 @@ namespace Grpc.Core.Internal
/// </summary>
protected abstract Task CheckSendAllowedOrEarlyResult();
protected byte[] UnsafeSerialize(TWrite msg)
// runs the serializer, propagating any exceptions being thrown without modifying them
protected SliceBufferSafeHandle UnsafeSerialize(TWrite msg, DefaultSerializationContext context)
{
DefaultSerializationContext context = null;
try
{
context = DefaultSerializationContext.GetInitializedThreadLocal();
serializer(msg, context);
return context.GetPayload();
}
finally
{
context?.Reset();
}
serializer(msg, context);
return context.GetPayload();
}
protected Exception TryDeserialize(IBufferReader reader, out TRead msg)

@ -129,28 +129,31 @@ namespace Grpc.Core.Internal
/// </summary>
public Task SendStatusFromServerAsync(Status status, Metadata trailers, ResponseWithFlags? optionalWrite)
{
byte[] payload = optionalWrite.HasValue ? UnsafeSerialize(optionalWrite.Value.Response) : null;
var writeFlags = optionalWrite.HasValue ? optionalWrite.Value.WriteFlags : default(WriteFlags);
lock (myLock)
using (var serializationScope = DefaultSerializationContext.GetInitializedThreadLocalScope())
{
GrpcPreconditions.CheckState(started);
GrpcPreconditions.CheckState(!disposed);
GrpcPreconditions.CheckState(!halfcloseRequested, "Can only send status from server once.");
var payload = optionalWrite.HasValue ? UnsafeSerialize(optionalWrite.Value.Response, serializationScope.Context) : SliceBufferSafeHandle.NullInstance;
var writeFlags = optionalWrite.HasValue ? optionalWrite.Value.WriteFlags : default(WriteFlags);
using (var metadataArray = MetadataArraySafeHandle.Create(trailers))
{
call.StartSendStatusFromServer(SendStatusFromServerCompletionCallback, status, metadataArray, !initialMetadataSent,
payload, writeFlags);
}
halfcloseRequested = true;
initialMetadataSent = true;
sendStatusFromServerTcs = new TaskCompletionSource<object>();
if (optionalWrite.HasValue)
lock (myLock)
{
streamingWritesCounter++;
GrpcPreconditions.CheckState(started);
GrpcPreconditions.CheckState(!disposed);
GrpcPreconditions.CheckState(!halfcloseRequested, "Can only send status from server once.");
using (var metadataArray = MetadataArraySafeHandle.Create(trailers))
{
call.StartSendStatusFromServer(SendStatusFromServerCompletionCallback, status, metadataArray, !initialMetadataSent,
payload, writeFlags);
}
halfcloseRequested = true;
initialMetadataSent = true;
sendStatusFromServerTcs = new TaskCompletionSource<object>();
if (optionalWrite.HasValue)
{
streamingWritesCounter++;
}
return sendStatusFromServerTcs.Task;
}
return sendStatusFromServerTcs.Task;
}
}

@ -67,19 +67,19 @@ namespace Grpc.Core.Internal
Native.grpcsharp_call_set_credentials(this, credentials).CheckOk();
}
public void StartUnary(IUnaryResponseClientCallback callback, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags)
public void StartUnary(IUnaryResponseClientCallback callback, SliceBufferSafeHandle payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags)
{
using (completionQueue.NewScope())
{
var ctx = completionQueue.CompletionRegistry.RegisterBatchCompletion(CompletionHandler_IUnaryResponseClientCallback, callback);
Native.grpcsharp_call_start_unary(this, ctx, payload, new UIntPtr((ulong)payload.Length), writeFlags, metadataArray, callFlags)
Native.grpcsharp_call_start_unary(this, ctx, payload, writeFlags, metadataArray, callFlags)
.CheckOk();
}
}
public void StartUnary(BatchContextSafeHandle ctx, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags)
public void StartUnary(BatchContextSafeHandle ctx, SliceBufferSafeHandle payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags)
{
Native.grpcsharp_call_start_unary(this, ctx, payload, new UIntPtr((ulong)payload.Length), writeFlags, metadataArray, callFlags)
Native.grpcsharp_call_start_unary(this, ctx, payload, writeFlags, metadataArray, callFlags)
.CheckOk();
}
@ -92,12 +92,12 @@ namespace Grpc.Core.Internal
}
}
public void StartServerStreaming(IReceivedStatusOnClientCallback callback, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags)
public void StartServerStreaming(IReceivedStatusOnClientCallback callback, SliceBufferSafeHandle payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags)
{
using (completionQueue.NewScope())
{
var ctx = completionQueue.CompletionRegistry.RegisterBatchCompletion(CompletionHandler_IReceivedStatusOnClientCallback, callback);
Native.grpcsharp_call_start_server_streaming(this, ctx, payload, new UIntPtr((ulong)payload.Length), writeFlags, metadataArray, callFlags).CheckOk();
Native.grpcsharp_call_start_server_streaming(this, ctx, payload, writeFlags, metadataArray, callFlags).CheckOk();
}
}
@ -110,12 +110,12 @@ namespace Grpc.Core.Internal
}
}
public void StartSendMessage(ISendCompletionCallback callback, byte[] payload, WriteFlags writeFlags, bool sendEmptyInitialMetadata)
public void StartSendMessage(ISendCompletionCallback callback, SliceBufferSafeHandle payload, WriteFlags writeFlags, bool sendEmptyInitialMetadata)
{
using (completionQueue.NewScope())
{
var ctx = completionQueue.CompletionRegistry.RegisterBatchCompletion(CompletionHandler_ISendCompletionCallback, callback);
Native.grpcsharp_call_send_message(this, ctx, payload, new UIntPtr((ulong)payload.Length), writeFlags, sendEmptyInitialMetadata ? 1 : 0).CheckOk();
Native.grpcsharp_call_send_message(this, ctx, payload, writeFlags, sendEmptyInitialMetadata ? 1 : 0).CheckOk();
}
}
@ -129,13 +129,12 @@ namespace Grpc.Core.Internal
}
public void StartSendStatusFromServer(ISendStatusFromServerCompletionCallback callback, Status status, MetadataArraySafeHandle metadataArray, bool sendEmptyInitialMetadata,
byte[] optionalPayload, WriteFlags writeFlags)
SliceBufferSafeHandle optionalPayload, WriteFlags writeFlags)
{
using (completionQueue.NewScope())
{
var ctx = completionQueue.CompletionRegistry.RegisterBatchCompletion(CompletionHandler_ISendStatusFromServerCompletionCallback, callback);
var optionalPayloadLength = optionalPayload != null ? new UIntPtr((ulong)optionalPayload.Length) : UIntPtr.Zero;
const int MaxStackAllocBytes = 256;
int maxBytes = MarshalUtils.GetMaxByteCountUTF8(status.Detail);
if (maxBytes > MaxStackAllocBytes)
@ -156,7 +155,7 @@ namespace Grpc.Core.Internal
byte* ptr = stackalloc byte[maxBytes];
int statusBytes = MarshalUtils.GetBytesUTF8(status.Detail, ptr, maxBytes);
Native.grpcsharp_call_send_status_from_server(this, ctx, status.StatusCode, new IntPtr(ptr), new UIntPtr((ulong)statusBytes), metadataArray, sendEmptyInitialMetadata ? 1 : 0,
optionalPayload, optionalPayloadLength, writeFlags).CheckOk();
optionalPayload, writeFlags).CheckOk();
}
else
{ // for larger status (rare), rent a buffer from the pool and
@ -168,7 +167,7 @@ namespace Grpc.Core.Internal
{
int statusBytes = MarshalUtils.GetBytesUTF8(status.Detail, ptr, maxBytes);
Native.grpcsharp_call_send_status_from_server(this, ctx, status.StatusCode, new IntPtr(ptr), new UIntPtr((ulong)statusBytes), metadataArray, sendEmptyInitialMetadata ? 1 : 0,
optionalPayload, optionalPayloadLength, writeFlags).CheckOk();
optionalPayload, writeFlags).CheckOk();
}
}
finally

@ -17,6 +17,8 @@
#endregion
using Grpc.Core.Utils;
using System;
using System.Buffers;
using System.Threading;
namespace Grpc.Core.Internal
@ -27,7 +29,7 @@ namespace Grpc.Core.Internal
new ThreadLocal<DefaultSerializationContext>(() => new DefaultSerializationContext(), false);
bool isComplete;
byte[] payload;
SliceBufferSafeHandle sliceBuffer = SliceBufferSafeHandle.Create();
public DefaultSerializationContext()
{
@ -38,25 +40,71 @@ namespace Grpc.Core.Internal
{
GrpcPreconditions.CheckState(!isComplete);
this.isComplete = true;
this.payload = payload;
var destSpan = sliceBuffer.GetSpan(payload.Length);
payload.AsSpan().CopyTo(destSpan);
sliceBuffer.Advance(payload.Length);
sliceBuffer.Complete();
}
/// <summary>
/// Expose serializer as buffer writer
/// </summary>
public override IBufferWriter<byte> GetBufferWriter()
{
GrpcPreconditions.CheckState(!isComplete);
return sliceBuffer;
}
internal byte[] GetPayload()
/// <summary>
/// Complete the payload written so far.
/// </summary>
public override void Complete()
{
return this.payload;
GrpcPreconditions.CheckState(!isComplete);
sliceBuffer.Complete();
this.isComplete = true;
}
internal SliceBufferSafeHandle GetPayload()
{
if (!isComplete)
{
// mimic the legacy behavior when byte[] was used to represent the payload.
throw new NullReferenceException("No payload was set. Complete() needs to be called before payload can be used.");
}
return sliceBuffer;
}
public void Reset()
{
this.isComplete = false;
this.payload = null;
this.sliceBuffer.Reset();
}
public static DefaultSerializationContext GetInitializedThreadLocal()
// Get a cached thread local instance of deserialization context
// and wrap it in a disposable struct that allows easy resetting
// via "using" statement.
public static UsageScope GetInitializedThreadLocalScope()
{
var instance = threadLocalInstance.Value;
instance.Reset();
return instance;
return new UsageScope(instance);
}
public struct UsageScope : IDisposable
{
readonly DefaultSerializationContext context;
public UsageScope(DefaultSerializationContext context)
{
this.context = context;
}
public DefaultSerializationContext Context => context;
public void Dispose()
{
context.Reset();
}
}
}
}

@ -67,13 +67,13 @@ namespace Grpc.Core.Internal
string GetPeer();
void StartUnary(IUnaryResponseClientCallback callback, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags);
void StartUnary(IUnaryResponseClientCallback callback, SliceBufferSafeHandle payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags);
void StartUnary(BatchContextSafeHandle ctx, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags);
void StartUnary(BatchContextSafeHandle ctx, SliceBufferSafeHandle payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags);
void StartClientStreaming(IUnaryResponseClientCallback callback, MetadataArraySafeHandle metadataArray, CallFlags callFlags);
void StartServerStreaming(IReceivedStatusOnClientCallback callback, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags);
void StartServerStreaming(IReceivedStatusOnClientCallback callback, SliceBufferSafeHandle payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags);
void StartDuplexStreaming(IReceivedStatusOnClientCallback callback, MetadataArraySafeHandle metadataArray, CallFlags callFlags);
@ -83,11 +83,11 @@ namespace Grpc.Core.Internal
void StartSendInitialMetadata(ISendCompletionCallback callback, MetadataArraySafeHandle metadataArray);
void StartSendMessage(ISendCompletionCallback callback, byte[] payload, WriteFlags writeFlags, bool sendEmptyInitialMetadata);
void StartSendMessage(ISendCompletionCallback callback, SliceBufferSafeHandle payload, WriteFlags writeFlags, bool sendEmptyInitialMetadata);
void StartSendCloseFromClient(ISendCompletionCallback callback);
void StartSendStatusFromServer(ISendStatusFromServerCompletionCallback callback, Status status, MetadataArraySafeHandle metadataArray, bool sendEmptyInitialMetadata, byte[] optionalPayload, WriteFlags writeFlags);
void StartSendStatusFromServer(ISendStatusFromServerCompletionCallback callback, Status status, MetadataArraySafeHandle metadataArray, bool sendEmptyInitialMetadata, SliceBufferSafeHandle optionalPayload, WriteFlags writeFlags);
void StartServerSide(IReceivedCloseOnServerCallback callback);
}

@ -122,6 +122,12 @@ namespace Grpc.Core.Internal
public readonly Delegates.grpcsharp_auth_context_property_iterator_delegate grpcsharp_auth_context_property_iterator;
public readonly Delegates.grpcsharp_auth_property_iterator_next_delegate grpcsharp_auth_property_iterator_next;
public readonly Delegates.grpcsharp_auth_context_release_delegate grpcsharp_auth_context_release;
public readonly Delegates.grpcsharp_slice_buffer_create_delegate grpcsharp_slice_buffer_create;
public readonly Delegates.grpcsharp_slice_buffer_adjust_tail_space_delegate grpcsharp_slice_buffer_adjust_tail_space;
public readonly Delegates.grpcsharp_slice_buffer_slice_count_delegate grpcsharp_slice_buffer_slice_count;
public readonly Delegates.grpcsharp_slice_buffer_slice_peek_delegate grpcsharp_slice_buffer_slice_peek;
public readonly Delegates.grpcsharp_slice_buffer_reset_and_unref_delegate grpcsharp_slice_buffer_reset_and_unref;
public readonly Delegates.grpcsharp_slice_buffer_destroy_delegate grpcsharp_slice_buffer_destroy;
public readonly Delegates.gprsharp_now_delegate gprsharp_now;
public readonly Delegates.gprsharp_inf_future_delegate gprsharp_inf_future;
public readonly Delegates.gprsharp_inf_past_delegate gprsharp_inf_past;
@ -224,6 +230,12 @@ namespace Grpc.Core.Internal
this.grpcsharp_auth_context_property_iterator = GetMethodDelegate<Delegates.grpcsharp_auth_context_property_iterator_delegate>(library);
this.grpcsharp_auth_property_iterator_next = GetMethodDelegate<Delegates.grpcsharp_auth_property_iterator_next_delegate>(library);
this.grpcsharp_auth_context_release = GetMethodDelegate<Delegates.grpcsharp_auth_context_release_delegate>(library);
this.grpcsharp_slice_buffer_create = GetMethodDelegate<Delegates.grpcsharp_slice_buffer_create_delegate>(library);
this.grpcsharp_slice_buffer_adjust_tail_space = GetMethodDelegate<Delegates.grpcsharp_slice_buffer_adjust_tail_space_delegate>(library);
this.grpcsharp_slice_buffer_slice_count = GetMethodDelegate<Delegates.grpcsharp_slice_buffer_slice_count_delegate>(library);
this.grpcsharp_slice_buffer_slice_peek = GetMethodDelegate<Delegates.grpcsharp_slice_buffer_slice_peek_delegate>(library);
this.grpcsharp_slice_buffer_reset_and_unref = GetMethodDelegate<Delegates.grpcsharp_slice_buffer_reset_and_unref_delegate>(library);
this.grpcsharp_slice_buffer_destroy = GetMethodDelegate<Delegates.grpcsharp_slice_buffer_destroy_delegate>(library);
this.gprsharp_now = GetMethodDelegate<Delegates.gprsharp_now_delegate>(library);
this.gprsharp_inf_future = GetMethodDelegate<Delegates.gprsharp_inf_future_delegate>(library);
this.gprsharp_inf_past = GetMethodDelegate<Delegates.gprsharp_inf_past_delegate>(library);
@ -325,6 +337,12 @@ namespace Grpc.Core.Internal
this.grpcsharp_auth_context_property_iterator = DllImportsFromStaticLib.grpcsharp_auth_context_property_iterator;
this.grpcsharp_auth_property_iterator_next = DllImportsFromStaticLib.grpcsharp_auth_property_iterator_next;
this.grpcsharp_auth_context_release = DllImportsFromStaticLib.grpcsharp_auth_context_release;
this.grpcsharp_slice_buffer_create = DllImportsFromStaticLib.grpcsharp_slice_buffer_create;
this.grpcsharp_slice_buffer_adjust_tail_space = DllImportsFromStaticLib.grpcsharp_slice_buffer_adjust_tail_space;
this.grpcsharp_slice_buffer_slice_count = DllImportsFromStaticLib.grpcsharp_slice_buffer_slice_count;
this.grpcsharp_slice_buffer_slice_peek = DllImportsFromStaticLib.grpcsharp_slice_buffer_slice_peek;
this.grpcsharp_slice_buffer_reset_and_unref = DllImportsFromStaticLib.grpcsharp_slice_buffer_reset_and_unref;
this.grpcsharp_slice_buffer_destroy = DllImportsFromStaticLib.grpcsharp_slice_buffer_destroy;
this.gprsharp_now = DllImportsFromStaticLib.gprsharp_now;
this.gprsharp_inf_future = DllImportsFromStaticLib.gprsharp_inf_future;
this.gprsharp_inf_past = DllImportsFromStaticLib.gprsharp_inf_past;
@ -426,6 +444,12 @@ namespace Grpc.Core.Internal
this.grpcsharp_auth_context_property_iterator = DllImportsFromSharedLib.grpcsharp_auth_context_property_iterator;
this.grpcsharp_auth_property_iterator_next = DllImportsFromSharedLib.grpcsharp_auth_property_iterator_next;
this.grpcsharp_auth_context_release = DllImportsFromSharedLib.grpcsharp_auth_context_release;
this.grpcsharp_slice_buffer_create = DllImportsFromSharedLib.grpcsharp_slice_buffer_create;
this.grpcsharp_slice_buffer_adjust_tail_space = DllImportsFromSharedLib.grpcsharp_slice_buffer_adjust_tail_space;
this.grpcsharp_slice_buffer_slice_count = DllImportsFromSharedLib.grpcsharp_slice_buffer_slice_count;
this.grpcsharp_slice_buffer_slice_peek = DllImportsFromSharedLib.grpcsharp_slice_buffer_slice_peek;
this.grpcsharp_slice_buffer_reset_and_unref = DllImportsFromSharedLib.grpcsharp_slice_buffer_reset_and_unref;
this.grpcsharp_slice_buffer_destroy = DllImportsFromSharedLib.grpcsharp_slice_buffer_destroy;
this.gprsharp_now = DllImportsFromSharedLib.gprsharp_now;
this.gprsharp_inf_future = DllImportsFromSharedLib.gprsharp_inf_future;
this.gprsharp_inf_past = DllImportsFromSharedLib.gprsharp_inf_past;
@ -467,13 +491,13 @@ namespace Grpc.Core.Internal
public delegate void grpcsharp_call_credentials_release_delegate(IntPtr credentials);
public delegate CallError grpcsharp_call_cancel_delegate(CallSafeHandle call);
public delegate CallError grpcsharp_call_cancel_with_status_delegate(CallSafeHandle call, StatusCode status, string description);
public delegate CallError grpcsharp_call_start_unary_delegate(CallSafeHandle call, BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags);
public delegate CallError grpcsharp_call_start_unary_delegate(CallSafeHandle call, BatchContextSafeHandle ctx, SliceBufferSafeHandle sendBuffer, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags);
public delegate CallError grpcsharp_call_start_client_streaming_delegate(CallSafeHandle call, BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags);
public delegate CallError grpcsharp_call_start_server_streaming_delegate(CallSafeHandle call, BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags);
public delegate CallError grpcsharp_call_start_server_streaming_delegate(CallSafeHandle call, BatchContextSafeHandle ctx, SliceBufferSafeHandle sendBuffer, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags);
public delegate CallError grpcsharp_call_start_duplex_streaming_delegate(CallSafeHandle call, BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags);
public delegate CallError grpcsharp_call_send_message_delegate(CallSafeHandle call, BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, int sendEmptyInitialMetadata);
public delegate CallError grpcsharp_call_send_message_delegate(CallSafeHandle call, BatchContextSafeHandle ctx, SliceBufferSafeHandle sendBuffer, WriteFlags writeFlags, int sendEmptyInitialMetadata);
public delegate CallError grpcsharp_call_send_close_from_client_delegate(CallSafeHandle call, BatchContextSafeHandle ctx);
public delegate CallError grpcsharp_call_send_status_from_server_delegate(CallSafeHandle call, BatchContextSafeHandle ctx, StatusCode statusCode, IntPtr statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, byte[] optionalSendBuffer, UIntPtr optionalSendBufferLen, WriteFlags writeFlags);
public delegate CallError grpcsharp_call_send_status_from_server_delegate(CallSafeHandle call, BatchContextSafeHandle ctx, StatusCode statusCode, IntPtr statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, SliceBufferSafeHandle optionalSendBuffer, WriteFlags writeFlags);
public delegate CallError grpcsharp_call_recv_message_delegate(CallSafeHandle call, BatchContextSafeHandle ctx);
public delegate CallError grpcsharp_call_recv_initial_metadata_delegate(CallSafeHandle call, BatchContextSafeHandle ctx);
public delegate CallError grpcsharp_call_start_serverside_delegate(CallSafeHandle call, BatchContextSafeHandle ctx);
@ -530,6 +554,12 @@ namespace Grpc.Core.Internal
public delegate AuthContextSafeHandle.NativeAuthPropertyIterator grpcsharp_auth_context_property_iterator_delegate(AuthContextSafeHandle authContext);
public delegate IntPtr grpcsharp_auth_property_iterator_next_delegate(ref AuthContextSafeHandle.NativeAuthPropertyIterator iterator); // returns const auth_property*
public delegate void grpcsharp_auth_context_release_delegate(IntPtr authContext);
public delegate SliceBufferSafeHandle grpcsharp_slice_buffer_create_delegate();
public delegate IntPtr grpcsharp_slice_buffer_adjust_tail_space_delegate(SliceBufferSafeHandle sliceBuffer, UIntPtr availableTailSpace, UIntPtr requestedTailSpace);
public delegate UIntPtr grpcsharp_slice_buffer_slice_count_delegate(SliceBufferSafeHandle sliceBuffer);
public delegate void grpcsharp_slice_buffer_slice_peek_delegate(SliceBufferSafeHandle sliceBuffer, UIntPtr index, out UIntPtr sliceLen, out IntPtr sliceDataPtr);
public delegate void grpcsharp_slice_buffer_reset_and_unref_delegate(SliceBufferSafeHandle sliceBuffer);
public delegate void grpcsharp_slice_buffer_destroy_delegate(IntPtr sliceBuffer);
public delegate Timespec gprsharp_now_delegate(ClockType clockType);
public delegate Timespec gprsharp_inf_future_delegate(ClockType clockType);
public delegate Timespec gprsharp_inf_past_delegate(ClockType clockType);
@ -538,7 +568,7 @@ namespace Grpc.Core.Internal
public delegate CallError grpcsharp_test_callback_delegate([MarshalAs(UnmanagedType.FunctionPtr)] NativeCallbackTestDelegate callback);
public delegate IntPtr grpcsharp_test_nop_delegate(IntPtr ptr);
public delegate void grpcsharp_test_override_method_delegate(string methodName, string variant);
public delegate CallError grpcsharp_test_call_start_unary_echo_delegate(CallSafeHandle call, BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags);
public delegate CallError grpcsharp_test_call_start_unary_echo_delegate(CallSafeHandle call, BatchContextSafeHandle ctx, SliceBufferSafeHandle sendBuffer, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags);
}
/// <summary>
@ -624,25 +654,25 @@ namespace Grpc.Core.Internal
public static extern CallError grpcsharp_call_cancel_with_status(CallSafeHandle call, StatusCode status, string description);
[DllImport(ImportName)]
public static extern CallError grpcsharp_call_start_unary(CallSafeHandle call, BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags);
public static extern CallError grpcsharp_call_start_unary(CallSafeHandle call, BatchContextSafeHandle ctx, SliceBufferSafeHandle sendBuffer, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags);
[DllImport(ImportName)]
public static extern CallError grpcsharp_call_start_client_streaming(CallSafeHandle call, BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags);
[DllImport(ImportName)]
public static extern CallError grpcsharp_call_start_server_streaming(CallSafeHandle call, BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags);
public static extern CallError grpcsharp_call_start_server_streaming(CallSafeHandle call, BatchContextSafeHandle ctx, SliceBufferSafeHandle sendBuffer, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags);
[DllImport(ImportName)]
public static extern CallError grpcsharp_call_start_duplex_streaming(CallSafeHandle call, BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags);
[DllImport(ImportName)]
public static extern CallError grpcsharp_call_send_message(CallSafeHandle call, BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, int sendEmptyInitialMetadata);
public static extern CallError grpcsharp_call_send_message(CallSafeHandle call, BatchContextSafeHandle ctx, SliceBufferSafeHandle sendBuffer, WriteFlags writeFlags, int sendEmptyInitialMetadata);
[DllImport(ImportName)]
public static extern CallError grpcsharp_call_send_close_from_client(CallSafeHandle call, BatchContextSafeHandle ctx);
[DllImport(ImportName)]
public static extern CallError grpcsharp_call_send_status_from_server(CallSafeHandle call, BatchContextSafeHandle ctx, StatusCode statusCode, IntPtr statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, byte[] optionalSendBuffer, UIntPtr optionalSendBufferLen, WriteFlags writeFlags);
public static extern CallError grpcsharp_call_send_status_from_server(CallSafeHandle call, BatchContextSafeHandle ctx, StatusCode statusCode, IntPtr statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, SliceBufferSafeHandle optionalSendBuffer, WriteFlags writeFlags);
[DllImport(ImportName)]
public static extern CallError grpcsharp_call_recv_message(CallSafeHandle call, BatchContextSafeHandle ctx);
@ -812,6 +842,24 @@ namespace Grpc.Core.Internal
[DllImport(ImportName)]
public static extern void grpcsharp_auth_context_release(IntPtr authContext);
[DllImport(ImportName)]
public static extern SliceBufferSafeHandle grpcsharp_slice_buffer_create();
[DllImport(ImportName)]
public static extern IntPtr grpcsharp_slice_buffer_adjust_tail_space(SliceBufferSafeHandle sliceBuffer, UIntPtr availableTailSpace, UIntPtr requestedTailSpace);
[DllImport(ImportName)]
public static extern UIntPtr grpcsharp_slice_buffer_slice_count(SliceBufferSafeHandle sliceBuffer);
[DllImport(ImportName)]
public static extern void grpcsharp_slice_buffer_slice_peek(SliceBufferSafeHandle sliceBuffer, UIntPtr index, out UIntPtr sliceLen, out IntPtr sliceDataPtr);
[DllImport(ImportName)]
public static extern void grpcsharp_slice_buffer_reset_and_unref(SliceBufferSafeHandle sliceBuffer);
[DllImport(ImportName)]
public static extern void grpcsharp_slice_buffer_destroy(IntPtr sliceBuffer);
[DllImport(ImportName)]
public static extern Timespec gprsharp_now(ClockType clockType);
@ -837,7 +885,7 @@ namespace Grpc.Core.Internal
public static extern void grpcsharp_test_override_method(string methodName, string variant);
[DllImport(ImportName)]
public static extern CallError grpcsharp_test_call_start_unary_echo(CallSafeHandle call, BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags);
public static extern CallError grpcsharp_test_call_start_unary_echo(CallSafeHandle call, BatchContextSafeHandle ctx, SliceBufferSafeHandle sendBuffer, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags);
}
/// <summary>
@ -923,25 +971,25 @@ namespace Grpc.Core.Internal
public static extern CallError grpcsharp_call_cancel_with_status(CallSafeHandle call, StatusCode status, string description);
[DllImport(ImportName)]
public static extern CallError grpcsharp_call_start_unary(CallSafeHandle call, BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags);
public static extern CallError grpcsharp_call_start_unary(CallSafeHandle call, BatchContextSafeHandle ctx, SliceBufferSafeHandle sendBuffer, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags);
[DllImport(ImportName)]
public static extern CallError grpcsharp_call_start_client_streaming(CallSafeHandle call, BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags);
[DllImport(ImportName)]
public static extern CallError grpcsharp_call_start_server_streaming(CallSafeHandle call, BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags);
public static extern CallError grpcsharp_call_start_server_streaming(CallSafeHandle call, BatchContextSafeHandle ctx, SliceBufferSafeHandle sendBuffer, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags);
[DllImport(ImportName)]
public static extern CallError grpcsharp_call_start_duplex_streaming(CallSafeHandle call, BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags);
[DllImport(ImportName)]
public static extern CallError grpcsharp_call_send_message(CallSafeHandle call, BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, int sendEmptyInitialMetadata);
public static extern CallError grpcsharp_call_send_message(CallSafeHandle call, BatchContextSafeHandle ctx, SliceBufferSafeHandle sendBuffer, WriteFlags writeFlags, int sendEmptyInitialMetadata);
[DllImport(ImportName)]
public static extern CallError grpcsharp_call_send_close_from_client(CallSafeHandle call, BatchContextSafeHandle ctx);
[DllImport(ImportName)]
public static extern CallError grpcsharp_call_send_status_from_server(CallSafeHandle call, BatchContextSafeHandle ctx, StatusCode statusCode, IntPtr statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, byte[] optionalSendBuffer, UIntPtr optionalSendBufferLen, WriteFlags writeFlags);
public static extern CallError grpcsharp_call_send_status_from_server(CallSafeHandle call, BatchContextSafeHandle ctx, StatusCode statusCode, IntPtr statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, SliceBufferSafeHandle optionalSendBuffer, WriteFlags writeFlags);
[DllImport(ImportName)]
public static extern CallError grpcsharp_call_recv_message(CallSafeHandle call, BatchContextSafeHandle ctx);
@ -1111,6 +1159,24 @@ namespace Grpc.Core.Internal
[DllImport(ImportName)]
public static extern void grpcsharp_auth_context_release(IntPtr authContext);
[DllImport(ImportName)]
public static extern SliceBufferSafeHandle grpcsharp_slice_buffer_create();
[DllImport(ImportName)]
public static extern IntPtr grpcsharp_slice_buffer_adjust_tail_space(SliceBufferSafeHandle sliceBuffer, UIntPtr availableTailSpace, UIntPtr requestedTailSpace);
[DllImport(ImportName)]
public static extern UIntPtr grpcsharp_slice_buffer_slice_count(SliceBufferSafeHandle sliceBuffer);
[DllImport(ImportName)]
public static extern void grpcsharp_slice_buffer_slice_peek(SliceBufferSafeHandle sliceBuffer, UIntPtr index, out UIntPtr sliceLen, out IntPtr sliceDataPtr);
[DllImport(ImportName)]
public static extern void grpcsharp_slice_buffer_reset_and_unref(SliceBufferSafeHandle sliceBuffer);
[DllImport(ImportName)]
public static extern void grpcsharp_slice_buffer_destroy(IntPtr sliceBuffer);
[DllImport(ImportName)]
public static extern Timespec gprsharp_now(ClockType clockType);
@ -1136,7 +1202,7 @@ namespace Grpc.Core.Internal
public static extern void grpcsharp_test_override_method(string methodName, string variant);
[DllImport(ImportName)]
public static extern CallError grpcsharp_test_call_start_unary_echo(CallSafeHandle call, BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags);
public static extern CallError grpcsharp_test_call_start_unary_echo(CallSafeHandle call, BatchContextSafeHandle ctx, SliceBufferSafeHandle sendBuffer, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags);
}
}
}

@ -101,45 +101,6 @@ namespace Grpc.Core.Internal
{
Next = next;
}
}
// Allow creating instances of Memory<byte> from Slice.
// Represents a chunk of native memory, but doesn't manage its lifetime.
// Instances of this class are reuseable - they can be reset to point to a different memory chunk.
// That is important to make the instances cacheable (rather then creating new instances
// the old ones will be reused to reduce GC pressure).
private class SliceMemoryManager : MemoryManager<byte>
{
private Slice slice;
public void Reset(Slice slice)
{
this.slice = slice;
}
public void Reset()
{
Reset(new Slice(IntPtr.Zero, 0));
}
public override Span<byte> GetSpan()
{
return slice.ToSpanUnsafe();
}
public override MemoryHandle Pin(int elementIndex = 0)
{
throw new NotSupportedException();
}
public override void Unpin()
{
}
protected override void Dispose(bool disposing)
{
// NOP
}
}
}
}
}

@ -0,0 +1,166 @@
#region Copyright notice and license
// Copyright 2019 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using System;
using System.Buffers;
using System.Runtime.InteropServices;
using Grpc.Core;
using Grpc.Core.Logging;
using Grpc.Core.Utils;
namespace Grpc.Core.Internal
{
/// <summary>
/// Represents grpc_slice_buffer with some extra utility functions to allow
/// writing data to it using the <c>IBufferWriter</c> interface.
/// </summary>
internal class SliceBufferSafeHandle : SafeHandleZeroIsInvalid, IBufferWriter<byte>
{
const int DefaultTailSpaceSize = 4096; // default buffer to allocate if no size hint is provided
static readonly NativeMethods Native = NativeMethods.Get();
static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<SliceBufferSafeHandle>();
public static readonly SliceBufferSafeHandle NullInstance = new SliceBufferSafeHandle();
private IntPtr tailSpacePtr;
private int tailSpaceLen;
private SliceMemoryManager memoryManagerLazy;
private SliceBufferSafeHandle()
{
}
public static SliceBufferSafeHandle Create()
{
return Native.grpcsharp_slice_buffer_create();
}
public IntPtr Handle
{
get
{
return handle;
}
}
public void Advance(int count)
{
GrpcPreconditions.CheckArgument(count >= 0);
GrpcPreconditions.CheckArgument(tailSpacePtr != IntPtr.Zero || count == 0);
GrpcPreconditions.CheckArgument(tailSpaceLen >= count);
tailSpaceLen = tailSpaceLen - count;
tailSpacePtr += count;
memoryManagerLazy?.Reset();
}
// provides access to the "tail space" of this buffer.
// Use GetSpan when possible for better efficiency.
public Memory<byte> GetMemory(int sizeHint = 0)
{
EnsureBufferSpace(sizeHint);
if (memoryManagerLazy == null)
{
memoryManagerLazy = new SliceMemoryManager();
}
memoryManagerLazy.Reset(new Slice(tailSpacePtr, tailSpaceLen));
return memoryManagerLazy.Memory;
}
// provides access to the "tail space" of this buffer.
public unsafe Span<byte> GetSpan(int sizeHint = 0)
{
EnsureBufferSpace(sizeHint);
return new Span<byte>(tailSpacePtr.ToPointer(), tailSpaceLen);
}
public void Complete()
{
AdjustTailSpace(0);
}
// resets the data contained by this slice buffer
public void Reset()
{
// deletes all the data in the slice buffer
tailSpacePtr = IntPtr.Zero;
tailSpaceLen = 0;
memoryManagerLazy?.Reset();
Native.grpcsharp_slice_buffer_reset_and_unref(this);
}
// copies the content of the slice buffer to a newly allocated byte array
// Note that this method has a relatively high overhead and should maily be used for testing.
public byte[] ToByteArray()
{
ulong sliceCount = Native.grpcsharp_slice_buffer_slice_count(this).ToUInt64();
Slice[] slices = new Slice[sliceCount];
int totalLen = 0;
for (int i = 0; i < (int) sliceCount; i++)
{
Native.grpcsharp_slice_buffer_slice_peek(this, new UIntPtr((ulong) i), out UIntPtr sliceLen, out IntPtr dataPtr);
slices[i] = new Slice(dataPtr, (int) sliceLen.ToUInt64());
totalLen += (int) sliceLen.ToUInt64();
}
var result = new byte[totalLen];
int offset = 0;
for (int i = 0; i < (int) sliceCount; i++)
{
slices[i].ToSpanUnsafe().CopyTo(result.AsSpan(offset, slices[i].Length));
offset += slices[i].Length;
}
GrpcPreconditions.CheckState(totalLen == offset);
return result;
}
private void EnsureBufferSpace(int sizeHint)
{
GrpcPreconditions.CheckArgument(sizeHint >= 0);
if (sizeHint == 0)
{
// if no hint is provided, keep the available space within some "reasonable" boundaries.
// This is quite a naive approach which could use some fine-tuning, but currently in most case we know
// the required buffer size in advance anyway, so this approach seems good enough for now.
if (tailSpaceLen < DefaultTailSpaceSize / 2)
{
AdjustTailSpace(DefaultTailSpaceSize);
}
}
else if (tailSpaceLen < sizeHint)
{
// if hint is provided, always make sure we provide at least that much space
AdjustTailSpace(sizeHint);
}
}
// make sure there's exactly requestedSize bytes of continguous buffer space at the end of this slice buffer
private void AdjustTailSpace(int requestedSize)
{
GrpcPreconditions.CheckArgument(requestedSize >= 0);
tailSpacePtr = Native.grpcsharp_slice_buffer_adjust_tail_space(this, new UIntPtr((ulong) tailSpaceLen), new UIntPtr((ulong) requestedSize));
tailSpaceLen = requestedSize;
}
protected override bool ReleaseHandle()
{
Native.grpcsharp_slice_buffer_destroy(handle);
return true;
}
}
}

@ -0,0 +1,65 @@
#region Copyright notice and license
// Copyright 2019 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion
using Grpc.Core.Utils;
using System;
using System.Threading;
using System.Buffers;
namespace Grpc.Core.Internal
{
// Allow creating instances of Memory<byte> from Slice.
// Represents a chunk of native memory, but doesn't manage its lifetime.
// Instances of this class are reuseable - they can be reset to point to a different memory chunk.
// That is important to make the instances cacheable (rather then creating new instances
// the old ones will be reused to reduce GC pressure).
internal class SliceMemoryManager : MemoryManager<byte>
{
private Slice slice;
public void Reset(Slice slice)
{
this.slice = slice;
}
public void Reset()
{
Reset(new Slice(IntPtr.Zero, 0));
}
public override Span<byte> GetSpan()
{
return slice.ToSpanUnsafe();
}
public override MemoryHandle Pin(int elementIndex = 0)
{
throw new NotSupportedException();
}
public override void Unpin()
{
}
protected override void Dispose(bool disposing)
{
// NOP
}
}
}

@ -50,15 +50,21 @@ namespace Grpc.Microbenchmarks
var call = CreateFakeCall(cq);
var sendCompletionCallback = new NopSendCompletionCallback();
var payload = new byte[PayloadSize];
var sliceBuffer = SliceBufferSafeHandle.Create();
var writeFlags = default(WriteFlags);
for (int i = 0; i < Iterations; i++)
{
call.StartSendMessage(sendCompletionCallback, payload, writeFlags, false);
// SendMessage steals the slices from the slice buffer, so we need to repopulate in each iteration.
sliceBuffer.Reset();
sliceBuffer.GetSpan(PayloadSize);
sliceBuffer.Advance(PayloadSize);
call.StartSendMessage(sendCompletionCallback, sliceBuffer, writeFlags, false);
var callback = completionRegistry.Extract(completionRegistry.LastRegisteredKey);
callback.OnComplete(true);
}
sliceBuffer.Dispose();
cq.Dispose();
}

@ -70,8 +70,8 @@ namespace Grpc.Microbenchmarks
var native = NativeMethods.Get();
// replace the implementation of a native method with a fake
NativeMethods.Delegates.grpcsharp_call_start_unary_delegate fakeCallStartUnary = (CallSafeHandle call, BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags) => {
return native.grpcsharp_test_call_start_unary_echo(call, ctx, sendBuffer, sendBufferLen, writeFlags, metadataArray, metadataFlags);
NativeMethods.Delegates.grpcsharp_call_start_unary_delegate fakeCallStartUnary = (CallSafeHandle call, BatchContextSafeHandle ctx, SliceBufferSafeHandle sendBuffer, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags) => {
return native.grpcsharp_test_call_start_unary_echo(call, ctx, sendBuffer, writeFlags, metadataArray, metadataFlags);
};
native.GetType().GetField(nameof(native.grpcsharp_call_start_unary)).SetValue(native, fakeCallStartUnary);

@ -61,7 +61,7 @@ namespace Grpc.Microbenchmarks
var native = NativeMethods.Get();
// nop the native-call via reflection
NativeMethods.Delegates.grpcsharp_call_send_status_from_server_delegate nop = (CallSafeHandle call, BatchContextSafeHandle ctx, StatusCode statusCode, IntPtr statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, byte[] optionalSendBuffer, UIntPtr optionalSendBufferLen, WriteFlags writeFlags) => {
NativeMethods.Delegates.grpcsharp_call_send_status_from_server_delegate nop = (CallSafeHandle call, BatchContextSafeHandle ctx, StatusCode statusCode, IntPtr statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, SliceBufferSafeHandle optionalSendBuffer, WriteFlags writeFlags) => {
completionRegistry.Extract(ctx.Handle).OnComplete(true); // drain the dictionary as we go
return CallError.OK;
};
@ -117,7 +117,7 @@ namespace Grpc.Microbenchmarks
{
for (int i = 0; i < Iterations; i++)
{
call.StartSendStatusFromServer(this, status, metadata, false, null, WriteFlags.NoCompress);
call.StartSendStatusFromServer(this, status, metadata, false, SliceBufferSafeHandle.NullInstance, WriteFlags.NoCompress);
}
}

@ -41,10 +41,15 @@
#define GPR_CALLTYPE
#endif
grpc_byte_buffer* string_to_byte_buffer(const char* buffer, size_t len) {
grpc_slice slice = grpc_slice_from_copied_buffer(buffer, len);
grpc_byte_buffer* bb = grpc_raw_byte_buffer_create(&slice, 1);
grpc_slice_unref(slice);
static grpc_byte_buffer* grpcsharp_create_byte_buffer_from_stolen_slices(
grpc_slice_buffer* slice_buffer) {
grpc_byte_buffer* bb =
(grpc_byte_buffer*)gpr_zalloc(sizeof(grpc_byte_buffer));
bb->type = GRPC_BB_RAW;
bb->data.raw.compression = GRPC_COMPRESS_NONE;
grpc_slice_buffer_init(&bb->data.raw.slice_buffer);
grpc_slice_buffer_swap(&bb->data.raw.slice_buffer, slice_buffer);
return bb;
}
@ -582,8 +587,8 @@ static grpc_call_error grpcsharp_call_start_batch(grpc_call* call,
}
GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_start_unary(
grpc_call* call, grpcsharp_batch_context* ctx, const char* send_buffer,
size_t send_buffer_len, uint32_t write_flags,
grpc_call* call, grpcsharp_batch_context* ctx,
grpc_slice_buffer* send_buffer, uint32_t write_flags,
grpc_metadata_array* initial_metadata, uint32_t initial_metadata_flags) {
/* TODO: don't use magic number */
grpc_op ops[6];
@ -598,7 +603,8 @@ GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_start_unary(
ops[0].reserved = NULL;
ops[1].op = GRPC_OP_SEND_MESSAGE;
ctx->send_message = string_to_byte_buffer(send_buffer, send_buffer_len);
ctx->send_message =
grpcsharp_create_byte_buffer_from_stolen_slices(send_buffer);
ops[1].data.send_message.send_message = ctx->send_message;
ops[1].flags = write_flags;
ops[1].reserved = NULL;
@ -635,12 +641,12 @@ GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_start_unary(
/* Only for testing. Shortcircuits the unary call logic and only echoes the
message as if it was received from the server */
GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_test_call_start_unary_echo(
grpc_call* call, grpcsharp_batch_context* ctx, const char* send_buffer,
size_t send_buffer_len, uint32_t write_flags,
grpc_call* call, grpcsharp_batch_context* ctx,
grpc_slice_buffer* send_buffer, uint32_t write_flags,
grpc_metadata_array* initial_metadata, uint32_t initial_metadata_flags) {
// prepare as if we were performing a normal RPC.
grpc_byte_buffer* send_message =
string_to_byte_buffer(send_buffer, send_buffer_len);
grpcsharp_create_byte_buffer_from_stolen_slices(send_buffer);
ctx->recv_message = send_message; // echo message sent by the client as if
// received from server.
@ -693,8 +699,8 @@ GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_start_client_streaming(
}
GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_start_server_streaming(
grpc_call* call, grpcsharp_batch_context* ctx, const char* send_buffer,
size_t send_buffer_len, uint32_t write_flags,
grpc_call* call, grpcsharp_batch_context* ctx,
grpc_slice_buffer* send_buffer, uint32_t write_flags,
grpc_metadata_array* initial_metadata, uint32_t initial_metadata_flags) {
/* TODO: don't use magic number */
grpc_op ops[4];
@ -709,7 +715,8 @@ GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_start_server_streaming(
ops[0].reserved = NULL;
ops[1].op = GRPC_OP_SEND_MESSAGE;
ctx->send_message = string_to_byte_buffer(send_buffer, send_buffer_len);
ctx->send_message =
grpcsharp_create_byte_buffer_from_stolen_slices(send_buffer);
ops[1].data.send_message.send_message = ctx->send_message;
ops[1].flags = write_flags;
ops[1].reserved = NULL;
@ -776,15 +783,16 @@ GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_recv_initial_metadata(
}
GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_send_message(
grpc_call* call, grpcsharp_batch_context* ctx, const char* send_buffer,
size_t send_buffer_len, uint32_t write_flags,
grpc_call* call, grpcsharp_batch_context* ctx,
grpc_slice_buffer* send_buffer, uint32_t write_flags,
int32_t send_empty_initial_metadata) {
/* TODO: don't use magic number */
grpc_op ops[2];
memset(ops, 0, sizeof(ops));
size_t nops = send_empty_initial_metadata ? 2 : 1;
ops[0].op = GRPC_OP_SEND_MESSAGE;
ctx->send_message = string_to_byte_buffer(send_buffer, send_buffer_len);
ctx->send_message =
grpcsharp_create_byte_buffer_from_stolen_slices(send_buffer);
ops[0].data.send_message.send_message = ctx->send_message;
ops[0].flags = write_flags;
ops[0].reserved = NULL;
@ -811,8 +819,7 @@ GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_send_status_from_server(
grpc_call* call, grpcsharp_batch_context* ctx, grpc_status_code status_code,
const char* status_details, size_t status_details_len,
grpc_metadata_array* trailing_metadata, int32_t send_empty_initial_metadata,
const char* optional_send_buffer, size_t optional_send_buffer_len,
uint32_t write_flags) {
grpc_slice_buffer* optional_send_buffer, uint32_t write_flags) {
/* TODO: don't use magic number */
grpc_op ops[3];
memset(ops, 0, sizeof(ops));
@ -833,7 +840,7 @@ GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_send_status_from_server(
if (optional_send_buffer) {
ops[nops].op = GRPC_OP_SEND_MESSAGE;
ctx->send_message =
string_to_byte_buffer(optional_send_buffer, optional_send_buffer_len);
grpcsharp_create_byte_buffer_from_stolen_slices(optional_send_buffer);
ops[nops].data.send_message.send_message = ctx->send_message;
ops[nops].flags = write_flags;
ops[nops].reserved = NULL;
@ -1182,6 +1189,67 @@ GPR_EXPORT void GPR_CALLTYPE grpcsharp_redirect_log(grpcsharp_log_func func) {
typedef void(GPR_CALLTYPE* test_callback_funcptr)(int32_t success);
/* Slice buffer functionality */
GPR_EXPORT grpc_slice_buffer* GPR_CALLTYPE grpcsharp_slice_buffer_create() {
grpc_slice_buffer* slice_buffer =
(grpc_slice_buffer*)gpr_malloc(sizeof(grpc_slice_buffer));
grpc_slice_buffer_init(slice_buffer);
return slice_buffer;
}
GPR_EXPORT void GPR_CALLTYPE
grpcsharp_slice_buffer_reset_and_unref(grpc_slice_buffer* buffer) {
grpc_slice_buffer_reset_and_unref(buffer);
}
GPR_EXPORT void GPR_CALLTYPE
grpcsharp_slice_buffer_destroy(grpc_slice_buffer* buffer) {
grpc_slice_buffer_destroy(buffer);
gpr_free(buffer);
}
GPR_EXPORT size_t GPR_CALLTYPE
grpcsharp_slice_buffer_slice_count(grpc_slice_buffer* buffer) {
return buffer->count;
}
GPR_EXPORT void GPR_CALLTYPE
grpcsharp_slice_buffer_slice_peek(grpc_slice_buffer* buffer, size_t index,
size_t* slice_len, uint8_t** slice_data_ptr) {
GPR_ASSERT(buffer->count > index);
grpc_slice* slice_ptr = &buffer->slices[index];
*slice_len = GRPC_SLICE_LENGTH(*slice_ptr);
*slice_data_ptr = GRPC_SLICE_START_PTR(*slice_ptr);
}
GPR_EXPORT void* GPR_CALLTYPE grpcsharp_slice_buffer_adjust_tail_space(
grpc_slice_buffer* buffer, size_t available_tail_space,
size_t requested_tail_space) {
if (available_tail_space == requested_tail_space) {
// nothing to do
} else if (available_tail_space >= requested_tail_space) {
grpc_slice_buffer_trim_end(
buffer, available_tail_space - requested_tail_space, NULL);
} else {
if (available_tail_space > 0) {
grpc_slice_buffer_trim_end(buffer, available_tail_space, NULL);
}
grpc_slice new_slice = grpc_slice_malloc(requested_tail_space);
// grpc_slice_buffer_add_indexed always adds as a new slice entry into the
// sb (which is suboptimal in some cases) but it doesn't have the problem of
// sometimes splitting the continguous new_slice across two different slices
// (like grpc_slice_buffer_add would)
grpc_slice_buffer_add_indexed(buffer, new_slice);
}
if (buffer->count == 0) {
return NULL;
}
grpc_slice* last_slice = &(buffer->slices[buffer->count - 1]);
return GRPC_SLICE_END_PTR(*last_slice) - requested_tail_space;
}
/* Version info */
GPR_EXPORT const char* GPR_CALLTYPE grpcsharp_version_string() {
return grpc_version_string();

@ -9,9 +9,11 @@
"Grpc.Core.Internal.Tests.CompletionQueueSafeHandleTest",
"Grpc.Core.Internal.Tests.DefaultDeserializationContextTest",
"Grpc.Core.Internal.Tests.DefaultObjectPoolTest",
"Grpc.Core.Internal.Tests.DefaultSerializationContextTest",
"Grpc.Core.Internal.Tests.FakeBufferReaderManagerTest",
"Grpc.Core.Internal.Tests.MetadataArraySafeHandleTest",
"Grpc.Core.Internal.Tests.ReusableSliceBufferTest",
"Grpc.Core.Internal.Tests.SliceBufferSafeHandleTest",
"Grpc.Core.Internal.Tests.SliceTest",
"Grpc.Core.Internal.Tests.TimespecTest",
"Grpc.Core.Internal.Tests.WellKnownStringsTest",

@ -374,6 +374,30 @@ void grpcsharp_auth_context_release() {
fprintf(stderr, "Should never reach here");
abort();
}
void grpcsharp_slice_buffer_create() {
fprintf(stderr, "Should never reach here");
abort();
}
void grpcsharp_slice_buffer_adjust_tail_space() {
fprintf(stderr, "Should never reach here");
abort();
}
void grpcsharp_slice_buffer_slice_count() {
fprintf(stderr, "Should never reach here");
abort();
}
void grpcsharp_slice_buffer_slice_peek() {
fprintf(stderr, "Should never reach here");
abort();
}
void grpcsharp_slice_buffer_reset_and_unref() {
fprintf(stderr, "Should never reach here");
abort();
}
void grpcsharp_slice_buffer_destroy() {
fprintf(stderr, "Should never reach here");
abort();
}
void gprsharp_now() {
fprintf(stderr, "Should never reach here");
abort();

@ -25,13 +25,13 @@ native_method_signatures = [
'void grpcsharp_call_credentials_release(IntPtr credentials)',
'CallError grpcsharp_call_cancel(CallSafeHandle call)',
'CallError grpcsharp_call_cancel_with_status(CallSafeHandle call, StatusCode status, string description)',
'CallError grpcsharp_call_start_unary(CallSafeHandle call, BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags)',
'CallError grpcsharp_call_start_unary(CallSafeHandle call, BatchContextSafeHandle ctx, SliceBufferSafeHandle sendBuffer, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags)',
'CallError grpcsharp_call_start_client_streaming(CallSafeHandle call, BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags)',
'CallError grpcsharp_call_start_server_streaming(CallSafeHandle call, BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags)',
'CallError grpcsharp_call_start_server_streaming(CallSafeHandle call, BatchContextSafeHandle ctx, SliceBufferSafeHandle sendBuffer, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags)',
'CallError grpcsharp_call_start_duplex_streaming(CallSafeHandle call, BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags)',
'CallError grpcsharp_call_send_message(CallSafeHandle call, BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, int sendEmptyInitialMetadata)',
'CallError grpcsharp_call_send_message(CallSafeHandle call, BatchContextSafeHandle ctx, SliceBufferSafeHandle sendBuffer, WriteFlags writeFlags, int sendEmptyInitialMetadata)',
'CallError grpcsharp_call_send_close_from_client(CallSafeHandle call, BatchContextSafeHandle ctx)',
'CallError grpcsharp_call_send_status_from_server(CallSafeHandle call, BatchContextSafeHandle ctx, StatusCode statusCode, IntPtr statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, byte[] optionalSendBuffer, UIntPtr optionalSendBufferLen, WriteFlags writeFlags)',
'CallError grpcsharp_call_send_status_from_server(CallSafeHandle call, BatchContextSafeHandle ctx, StatusCode statusCode, IntPtr statusMessage, UIntPtr statusMessageLen, MetadataArraySafeHandle metadataArray, int sendEmptyInitialMetadata, SliceBufferSafeHandle optionalSendBuffer, WriteFlags writeFlags)',
'CallError grpcsharp_call_recv_message(CallSafeHandle call, BatchContextSafeHandle ctx)',
'CallError grpcsharp_call_recv_initial_metadata(CallSafeHandle call, BatchContextSafeHandle ctx)',
'CallError grpcsharp_call_start_serverside(CallSafeHandle call, BatchContextSafeHandle ctx)',
@ -88,6 +88,12 @@ native_method_signatures = [
'AuthContextSafeHandle.NativeAuthPropertyIterator grpcsharp_auth_context_property_iterator(AuthContextSafeHandle authContext)',
'IntPtr grpcsharp_auth_property_iterator_next(ref AuthContextSafeHandle.NativeAuthPropertyIterator iterator) // returns const auth_property*',
'void grpcsharp_auth_context_release(IntPtr authContext)',
'SliceBufferSafeHandle grpcsharp_slice_buffer_create()',
'IntPtr grpcsharp_slice_buffer_adjust_tail_space(SliceBufferSafeHandle sliceBuffer, UIntPtr availableTailSpace, UIntPtr requestedTailSpace)',
'UIntPtr grpcsharp_slice_buffer_slice_count(SliceBufferSafeHandle sliceBuffer)',
'void grpcsharp_slice_buffer_slice_peek(SliceBufferSafeHandle sliceBuffer, UIntPtr index, out UIntPtr sliceLen, out IntPtr sliceDataPtr)',
'void grpcsharp_slice_buffer_reset_and_unref(SliceBufferSafeHandle sliceBuffer)',
'void grpcsharp_slice_buffer_destroy(IntPtr sliceBuffer)',
'Timespec gprsharp_now(ClockType clockType)',
'Timespec gprsharp_inf_future(ClockType clockType)',
'Timespec gprsharp_inf_past(ClockType clockType)',
@ -96,7 +102,7 @@ native_method_signatures = [
'CallError grpcsharp_test_callback([MarshalAs(UnmanagedType.FunctionPtr)] NativeCallbackTestDelegate callback)',
'IntPtr grpcsharp_test_nop(IntPtr ptr)',
'void grpcsharp_test_override_method(string methodName, string variant)',
'CallError grpcsharp_test_call_start_unary_echo(CallSafeHandle call, BatchContextSafeHandle ctx, byte[] sendBuffer, UIntPtr sendBufferLen, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags)',
'CallError grpcsharp_test_call_start_unary_echo(CallSafeHandle call, BatchContextSafeHandle ctx, SliceBufferSafeHandle sendBuffer, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags metadataFlags)',
]
import re

Loading…
Cancel
Save