Skip to content

Commit e407274

Browse files
authored
add support for IAsyncDisposable; add test for IDisposable and IAsyncDisposable (#272)
1 parent cf9c503 commit e407274

File tree

3 files changed

+72
-0
lines changed

3 files changed

+72
-0
lines changed

docs/releasenotes.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Release Notes
22

3+
## unreleased
4+
5+
- improve handling of `IAsyncDisposable`
6+
37
## 1.1.1
48

59
- update library references

src/protobuf-net.Grpc/Internal/ProxyEmitter.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using System.Reflection.Emit;
1010
using System.Runtime.CompilerServices;
1111
using System.Threading;
12+
using System.Threading.Tasks;
1213

1314
namespace ProtoBuf.Grpc.Internal
1415
{
@@ -215,7 +216,18 @@ FieldBuilder Marshaller(Type forType)
215216
// it is frequent for some infrastructure code to always call Dispose() on IDisposable,
216217
// for instance Asp.Net Core dependency injection, so we don't want to throw in this case
217218
if (iType == typeof(IDisposable) && iMethod.Name == nameof(IDisposable.Dispose))
219+
{
218220
il.Emit(OpCodes.Ret);
221+
}
222+
else if (iType == typeof(IAsyncDisposable) && iMethod.Name == nameof(IAsyncDisposable.DisposeAsync))
223+
{
224+
// "return default;"
225+
var loc = il.DeclareLocal(typeof(ValueTask));
226+
il.Emit(OpCodes.Ldloca, loc);
227+
il.Emit(OpCodes.Initobj, typeof(ValueTask));
228+
il.Emit(OpCodes.Ldloc, loc);
229+
il.Emit(OpCodes.Ret);
230+
}
219231
else
220232
il.ThrowException(typeof(NotSupportedException));
221233
continue;
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using Grpc.Core;
2+
using ProtoBuf.Grpc.Client;
3+
using ProtoBuf.Grpc.Configuration;
4+
using System;
5+
using System.Threading.Tasks;
6+
using Xunit;
7+
8+
namespace protobuf_net.Grpc.Test
9+
{
10+
public class DisposeTests
11+
{
12+
[Service]
13+
public interface IDisposableService : IDisposable, IAsyncDisposable
14+
{}
15+
16+
[Fact]
17+
public void DisposeWorks()
18+
{
19+
using var client = DummyChannel.Instance.CreateGrpcService<IDisposableService>();
20+
}
21+
22+
[Fact]
23+
public async Task DisposeAsyncWorks()
24+
{
25+
await using var client = DummyChannel.Instance.CreateGrpcService<IDisposableService>();
26+
}
27+
}
28+
29+
internal sealed class DummyChannel : ChannelBase
30+
{
31+
public static DummyChannel Instance { get; } = new();
32+
private DummyChannel() : base("") { }
33+
public override CallInvoker CreateCallInvoker() => DummyCallInvoker.Instance;
34+
35+
private sealed class DummyCallInvoker : CallInvoker
36+
{
37+
public static DummyCallInvoker Instance { get; } = new();
38+
private DummyCallInvoker() { }
39+
40+
public override AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string? host, CallOptions options)
41+
=> throw new NotSupportedException();
42+
43+
public override AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string? host, CallOptions options)
44+
=> throw new NotSupportedException();
45+
46+
public override AsyncServerStreamingCall<TResponse> AsyncServerStreamingCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string? host, CallOptions options, TRequest request)
47+
=> throw new NotSupportedException();
48+
49+
public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string? host, CallOptions options, TRequest request)
50+
=> throw new NotSupportedException();
51+
52+
public override TResponse BlockingUnaryCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string? host, CallOptions options, TRequest request)
53+
=> throw new NotSupportedException();
54+
}
55+
}
56+
}

0 commit comments

Comments
 (0)