Merge pull request #19569 from jtattermusch/csharp_internal_span_use

C#: add System.Memory dependency and use Span<> internally for all target frameworks
reviewable/pr19588/r5
Jan Tattermusch 5 years ago committed by GitHub
commit 11c9374b93
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      src/csharp/Grpc.Core.Api/DeserializationContext.cs
  2. 9
      src/csharp/Grpc.Core.Api/Grpc.Core.Api.csproj
  3. 4
      src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj
  4. 14
      src/csharp/Grpc.Core.Tests/Internal/DefaultDeserializationContextTest.cs
  5. 2
      src/csharp/Grpc.Core.Tests/Internal/FakeBufferReaderManagerTest.cs
  6. 17
      src/csharp/Grpc.Core.Tests/Internal/ReusableSliceBufferTest.cs
  7. 21
      src/csharp/Grpc.Core.Tests/Internal/SliceTest.cs
  8. 6
      src/csharp/Grpc.Core/Grpc.Core.csproj
  9. 31
      src/csharp/Grpc.Core/Internal/DefaultDeserializationContext.cs
  10. 3
      src/csharp/Grpc.Core/Internal/ReusableSliceBuffer.cs
  11. 9
      src/csharp/Grpc.Core/Internal/Slice.cs
  12. 3
      src/csharp/build_unitypackage.bat
  13. 10
      src/csharp/unitypackage/unitypackage_skeleton/Plugins/System.Buffers/lib.meta
  14. 10
      src/csharp/unitypackage/unitypackage_skeleton/Plugins/System.Buffers/lib/net45.meta
  15. 32
      src/csharp/unitypackage/unitypackage_skeleton/Plugins/System.Buffers/lib/net45/System.Buffers.dll.meta
  16. 9
      src/csharp/unitypackage/unitypackage_skeleton/Plugins/System.Buffers/lib/net45/System.Buffers.xml.meta
  17. 10
      src/csharp/unitypackage/unitypackage_skeleton/Plugins/System.Memory/lib.meta
  18. 10
      src/csharp/unitypackage/unitypackage_skeleton/Plugins/System.Memory/lib/net45.meta
  19. 32
      src/csharp/unitypackage/unitypackage_skeleton/Plugins/System.Memory/lib/net45/System.Memory.dll.meta
  20. 9
      src/csharp/unitypackage/unitypackage_skeleton/Plugins/System.Memory/lib/net45/System.Memory.xml.meta
  21. 10
      src/csharp/unitypackage/unitypackage_skeleton/Plugins/System.Runtime.CompilerServices.Unsafe/lib.meta
  22. 10
      src/csharp/unitypackage/unitypackage_skeleton/Plugins/System.Runtime.CompilerServices.Unsafe/lib/net45.meta
  23. 32
      src/csharp/unitypackage/unitypackage_skeleton/Plugins/System.Runtime.CompilerServices.Unsafe/lib/net45/System.Runtime.CompilerServices.Unsafe.dll.meta
  24. 9
      src/csharp/unitypackage/unitypackage_skeleton/Plugins/System.Runtime.CompilerServices.Unsafe/lib/net45/System.Runtime.CompilerServices.Unsafe.xml.meta
  25. 3
      templates/src/csharp/build_unitypackage.bat.template

@ -48,13 +48,12 @@ namespace Grpc.Core
throw new NotImplementedException();
}
#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
/// <summary>
/// Gets the entire payload as a ReadOnlySequence.
/// The ReadOnlySequence is only valid for the duration of the deserializer routine and the caller must not access it after the deserializer returns.
/// Using the read only sequence is the most efficient way to access the message payload. Where possible it allows directly
/// accessing the received payload without needing to perform any buffer copying or buffer allocations.
/// NOTE: This method is only available in the netstandard2.0 build of the library.
/// NOTE: When using this method, it is recommended to use C# 7.2 compiler to make it more useful (using Span type directly from your code requires C# 7.2)."
/// NOTE: Deserializers are expected not to call this method (or other payload accessor methods) more than once per received message
/// (as there is no practical reason for doing so) and <c>DeserializationContext</c> implementations are free to assume so.
/// </summary>
@ -63,6 +62,5 @@ namespace Grpc.Core
{
throw new NotImplementedException();
}
#endif
}
}

@ -20,18 +20,11 @@
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<DefineConstants>$(DefineConstants);GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY</DefineConstants>
</PropertyGroup>
<Import Project="..\Grpc.Core\SourceLink.csproj.include" />
<ItemGroup>
<PackageReference Include="System.Interactive.Async" Version="3.2.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<PackageReference Include="System.Memory" Version="4.5.2" />
<PackageReference Include="System.Memory" Version="4.5.3" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">

@ -9,10 +9,6 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netcoreapp2.1' ">
<DefineConstants>$(DefineConstants);GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY</DefineConstants>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../Grpc.Core/Grpc.Core.csproj" />
</ItemGroup>

@ -17,18 +17,14 @@
#endregion
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
using NUnit.Framework;
using System.Runtime.InteropServices;
#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
using System.Buffers;
#endif
namespace Grpc.Core.Internal.Tests
{
public class DefaultDeserializationContextTest
@ -47,7 +43,6 @@ namespace Grpc.Core.Internal.Tests
fakeBufferReaderManager.Dispose();
}
#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
[TestCase]
public void PayloadAsReadOnlySequence_ZeroSegmentPayload()
{
@ -118,7 +113,6 @@ namespace Grpc.Core.Internal.Tests
Assert.IsFalse(segmentEnumerator.MoveNext());
}
#endif
[TestCase]
public void NullPayloadNotAllowed()
@ -196,9 +190,7 @@ namespace Grpc.Core.Internal.Tests
// Getting payload multiple times is illegal
Assert.Throws(typeof(InvalidOperationException), () => context.PayloadAsNewBuffer());
#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
Assert.Throws(typeof(InvalidOperationException), () => context.PayloadAsReadOnlySequence());
#endif
}
[TestCase]
@ -215,9 +207,7 @@ namespace Grpc.Core.Internal.Tests
Assert.AreEqual(0, context.PayloadLength);
Assert.Throws(typeof(NullReferenceException), () => context.PayloadAsNewBuffer());
#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
Assert.Throws(typeof(NullReferenceException), () => context.PayloadAsReadOnlySequence());
#endif
// Previously reset context can be initialized again
var origBuffer2 = GetTestBuffer(50);

@ -103,7 +103,7 @@ namespace Grpc.Core.Internal.Tests
private void AssertSliceDataEqual(byte[] expected, Slice actual)
{
var actualSliceData = new byte[actual.Length];
actual.CopyTo(new ArraySegment<byte>(actualSliceData));
actual.ToSpanUnsafe().CopyTo(actualSliceData);
CollectionAssert.AreEqual(expected, actualSliceData);
}

@ -17,19 +17,15 @@
#endregion
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using Grpc.Core;
using Grpc.Core.Internal;
using Grpc.Core.Utils;
using NUnit.Framework;
using System.Runtime.InteropServices;
#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
using System.Buffers;
#endif
namespace Grpc.Core.Internal.Tests
{
// Converts IBufferReader into instances of ReadOnlySequence<byte>
@ -50,7 +46,6 @@ namespace Grpc.Core.Internal.Tests
fakeBufferReaderManager.Dispose();
}
#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
[TestCase]
public void NullPayload()
{
@ -131,13 +126,7 @@ namespace Grpc.Core.Internal.Tests
}
return result;
}
#else
[TestCase]
public void OnlySupportedOnNetCore()
{
// Test case needs to exist to make C# sanity test happy.
}
#endif
private byte[] GetTestBuffer(int length)
{
var testBuffer = new byte[length];

@ -33,7 +33,7 @@ namespace Grpc.Core.Internal.Tests
[TestCase(10)]
[TestCase(100)]
[TestCase(1000)]
public void SliceFromNativePtr_CopyToArraySegment(int bufferLength)
public void SliceFromNativePtr_Copy(int bufferLength)
{
var origBuffer = GetTestBuffer(bufferLength);
var gcHandle = GCHandle.Alloc(origBuffer, GCHandleType.Pinned);
@ -43,7 +43,7 @@ namespace Grpc.Core.Internal.Tests
Assert.AreEqual(bufferLength, slice.Length);
var newBuffer = new byte[bufferLength];
slice.CopyTo(new ArraySegment<byte>(newBuffer));
slice.ToSpanUnsafe().CopyTo(newBuffer);
CollectionAssert.AreEqual(origBuffer, newBuffer);
}
finally
@ -52,23 +52,6 @@ namespace Grpc.Core.Internal.Tests
}
}
[TestCase]
public void SliceFromNativePtr_CopyToArraySegmentTooSmall()
{
var origBuffer = GetTestBuffer(100);
var gcHandle = GCHandle.Alloc(origBuffer, GCHandleType.Pinned);
try
{
var slice = new Slice(gcHandle.AddrOfPinnedObject(), origBuffer.Length);
var tooSmall = new byte[origBuffer.Length - 1];
Assert.Catch(typeof(ArgumentException), () => slice.CopyTo(new ArraySegment<byte>(tooSmall)));
}
finally
{
gcHandle.Free();
}
}
// create a buffer of given size and fill it with some data
private byte[] GetTestBuffer(int length)
{

@ -23,9 +23,8 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<PropertyGroup>
<LangVersion>7.2</LangVersion>
<DefineConstants>$(DefineConstants);GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY</DefineConstants>
</PropertyGroup>
<ItemGroup>
@ -100,8 +99,7 @@
<ItemGroup>
<PackageReference Include="System.Interactive.Async" Version="3.2.0" />
<!-- System.Buffers *may* come in transitively, but: we can *always* use ArrayPool -->
<PackageReference Include="System.Buffers" Version="4.5.0" />
<PackageReference Include="System.Memory" Version="4.5.3" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">

@ -16,13 +16,10 @@
#endregion
using Grpc.Core.Utils;
using System;
using System.Threading;
#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
using System.Buffers;
#endif
using System.Threading;
using Grpc.Core.Utils;
namespace Grpc.Core.Internal
{
@ -33,9 +30,7 @@ namespace Grpc.Core.Internal
IBufferReader bufferReader;
int payloadLength;
#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
ReusableSliceBuffer cachedSliceBuffer = new ReusableSliceBuffer();
#endif
public DefaultDeserializationContext()
{
@ -47,18 +42,16 @@ namespace Grpc.Core.Internal
public override byte[] PayloadAsNewBuffer()
{
var buffer = new byte[payloadLength];
FillContinguousBuffer(bufferReader, buffer);
PayloadAsReadOnlySequence().CopyTo(buffer);
return buffer;
}
#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
public override ReadOnlySequence<byte> PayloadAsReadOnlySequence()
{
var sequence = cachedSliceBuffer.PopulateFrom(bufferReader);
GrpcPreconditions.CheckState(sequence.Length == payloadLength);
return sequence;
}
#endif
public void Initialize(IBufferReader bufferReader)
{
@ -70,9 +63,7 @@ namespace Grpc.Core.Internal
{
this.bufferReader = null;
this.payloadLength = 0;
#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
this.cachedSliceBuffer.Invalidate();
#endif
}
public static DefaultDeserializationContext GetInitializedThreadLocal(IBufferReader bufferReader)
@ -81,21 +72,5 @@ namespace Grpc.Core.Internal
instance.Initialize(bufferReader);
return instance;
}
private void FillContinguousBuffer(IBufferReader reader, byte[] destination)
{
#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
PayloadAsReadOnlySequence().CopyTo(new Span<byte>(destination));
#else
int offset = 0;
while (reader.TryGetNextSlice(out Slice slice))
{
slice.CopyTo(new ArraySegment<byte>(destination, offset, (int)slice.Length));
offset += (int)slice.Length;
}
// check that we filled the entire destination
GrpcPreconditions.CheckState(offset == payloadLength);
#endif
}
}
}

@ -16,8 +16,6 @@
#endregion
#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
using Grpc.Core.Utils;
using System;
using System.Threading;
@ -145,4 +143,3 @@ namespace Grpc.Core.Internal
}
}
}
#endif

@ -40,14 +40,6 @@ namespace Grpc.Core.Internal
public int Length => length;
// copies data of the slice to given span.
// there needs to be enough space in the destination buffer
public void CopyTo(ArraySegment<byte> destination)
{
Marshal.Copy(dataPtr, destination.Array, destination.Offset, length);
}
#if GRPC_CSHARP_SUPPORT_SYSTEM_MEMORY
public Span<byte> ToSpanUnsafe()
{
unsafe
@ -55,7 +47,6 @@ namespace Grpc.Core.Internal
return new Span<byte>((byte*) dataPtr, length);
}
}
#endif
/// <summary>
/// Returns a <see cref="System.String"/> that represents the current <see cref="Grpc.Core.Internal.Slice"/>.

@ -65,6 +65,9 @@ copy /Y nativelibs\csharp_ext_macos_ios\libgrpc.a unitypackage\unitypackage_skel
@rem add gRPC dependencies
@rem TODO(jtattermusch): also include XMLdoc
copy /Y Grpc.Core\bin\Release\net45\System.Interactive.Async.dll unitypackage\unitypackage_skeleton\Plugins\System.Interactive.Async\lib\net45\System.Interactive.Async.dll || goto :error
copy /Y Grpc.Core\bin\Release\net45\System.Runtime.CompilerServices.Unsafe.dll unitypackage\unitypackage_skeleton\Plugins\System.Runtime.CompilerServices.Unsafe\lib\net45\System.Runtime.CompilerServices.Unsafe.dll || goto :error
copy /Y Grpc.Core\bin\Release\net45\System.Buffers.dll unitypackage\unitypackage_skeleton\Plugins\System.Buffers\lib\net45\System.Buffers.dll || goto :error
copy /Y Grpc.Core\bin\Release\net45\System.Memory.dll unitypackage\unitypackage_skeleton\Plugins\System.Memory\lib\net45\System.Memory.dll || goto :error
@rem add Google.Protobuf
@rem TODO(jtattermusch): also include XMLdoc

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 0cb4be3dca2a49e6a920da037ea13d80
folderAsset: yes
timeCreated: 1531219385
licenseType: Free
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 53b3f7a608814da5a3e3207d10c85b4e
folderAsset: yes
timeCreated: 1531219385
licenseType: Free
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

@ -0,0 +1,32 @@
fileFormatVersion: 2
guid: bb037a236f584460af82c59c5d5ad972
timeCreated: 1531219386
licenseType: Free
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
isPreloaded: 0
isOverridable: 0
platformData:
- first:
Any:
second:
enabled: 1
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 4b9fff86d3b2471eb0003735b3ce3a51
timeCreated: 1531219386
licenseType: Free
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 3d6c20bf92b74c03b1ba691cbce610e4
folderAsset: yes
timeCreated: 1531219385
licenseType: Free
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 7164a0a387b24d1a9d76f6d558fc44d2
folderAsset: yes
timeCreated: 1531219385
licenseType: Free
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

@ -0,0 +1,32 @@
fileFormatVersion: 2
guid: e774d51b8ba64a988dd37135e487105c
timeCreated: 1531219386
licenseType: Free
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
isPreloaded: 0
isOverridable: 0
platformData:
- first:
Any:
second:
enabled: 1
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 3d27c5afe3114f67b0f6e27e36b89d5c
timeCreated: 1531219386
licenseType: Free
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: f51cc0f065424fb2928eee7c2804bfd8
folderAsset: yes
timeCreated: 1531219385
licenseType: Free
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: aad21c5c1f2f4c1391b1e4a475f163bb
folderAsset: yes
timeCreated: 1531219385
licenseType: Free
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

@ -0,0 +1,32 @@
fileFormatVersion: 2
guid: d378b9cd266a4a448f071c114e5f18cb
timeCreated: 1531219386
licenseType: Free
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
isPreloaded: 0
isOverridable: 0
platformData:
- first:
Any:
second:
enabled: 1
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: cfa8e546fee54a5ea3b20cf9dcf90fdf
timeCreated: 1531219386
licenseType: Free
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

@ -67,6 +67,9 @@
@rem add gRPC dependencies
@rem TODO(jtattermusch): also include XMLdoc
copy /Y Grpc.Core\bin\Release\net45\System.Interactive.Async.dll unitypackage\unitypackage_skeleton\Plugins\System.Interactive.Async\lib\net45\System.Interactive.Async.dll || goto :error
copy /Y Grpc.Core\bin\Release\net45\System.Runtime.CompilerServices.Unsafe.dll unitypackage\unitypackage_skeleton\Plugins\System.Runtime.CompilerServices.Unsafe\lib\net45\System.Runtime.CompilerServices.Unsafe.dll || goto :error
copy /Y Grpc.Core\bin\Release\net45\System.Buffers.dll unitypackage\unitypackage_skeleton\Plugins\System.Buffers\lib\net45\System.Buffers.dll || goto :error
copy /Y Grpc.Core\bin\Release\net45\System.Memory.dll unitypackage\unitypackage_skeleton\Plugins\System.Memory\lib\net45\System.Memory.dll || goto :error
@rem add Google.Protobuf
@rem TODO(jtattermusch): also include XMLdoc

Loading…
Cancel
Save