Skip to content

Commit 8257369

Browse files
Kahbazihalter73
authored andcommitted
Add support for cancellation token in typed client hub (#13816)
1 parent bea9802 commit 8257369

File tree

2 files changed

+60
-3
lines changed

2 files changed

+60
-3
lines changed

src/SignalR/server/Core/src/Internal/TypedClientBuilder.cs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,14 @@ private static void BuildMethod(TypeBuilder type, MethodInfo interfaceMethodInfo
132132
methodBuilder.DefineGenericParameters(genericTypeNames);
133133
}
134134

135+
// Check to see if the last parameter of the method is a CancellationToken
136+
bool hasCancellationToken = paramTypes.LastOrDefault() == typeof(CancellationToken);
137+
if (hasCancellationToken)
138+
{
139+
// remove CancellationToken from input paramTypes
140+
paramTypes = paramTypes.Take(paramTypes.Length - 1).ToArray();
141+
}
142+
135143
var generator = methodBuilder.GetILGenerator();
136144

137145
// Declare local variable to store the arguments to IClientProxy.SendCoreAsync
@@ -145,7 +153,7 @@ private static void BuildMethod(TypeBuilder type, MethodInfo interfaceMethodInfo
145153
generator.Emit(OpCodes.Ldstr, interfaceMethodInfo.Name);
146154

147155
// Create an new object array to hold all the parameters to this method
148-
generator.Emit(OpCodes.Ldc_I4, parameters.Length); // Stack:
156+
generator.Emit(OpCodes.Ldc_I4, paramTypes.Length); // Stack:
149157
generator.Emit(OpCodes.Newarr, typeof(object)); // allocate object array
150158
generator.Emit(OpCodes.Stloc_0);
151159

@@ -162,8 +170,16 @@ private static void BuildMethod(TypeBuilder type, MethodInfo interfaceMethodInfo
162170
// Load parameter array on to the stack.
163171
generator.Emit(OpCodes.Ldloc_0);
164172

165-
// Get 'CancellationToken.None' and put it on the stack, since we don't support CancellationToken right now
166-
generator.Emit(OpCodes.Call, CancellationTokenNoneProperty.GetMethod);
173+
if (hasCancellationToken)
174+
{
175+
// Get CancellationToken from input argument and put it on the stack
176+
generator.Emit(OpCodes.Ldarg, paramTypes.Length + 1);
177+
}
178+
else
179+
{
180+
// Get 'CancellationToken.None' and put it on the stack, for when method does not have CancellationToken
181+
generator.Emit(OpCodes.Call, CancellationTokenNoneProperty.GetMethod);
182+
}
167183

168184
// Send!
169185
generator.Emit(OpCodes.Callvirt, invokeMethod);

src/SignalR/server/SignalR/test/Internal/TypedClientBuilderTests.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,41 @@ public async Task SupportsSubInterfaces()
7575
await task2.OrTimeout();
7676
}
7777

78+
[Fact]
79+
public async Task SupportsCancellationToken()
80+
{
81+
var clientProxy = new MockProxy();
82+
var typedProxy = TypedClientBuilder<ICancellationTokenMethod>.Build(clientProxy);
83+
CancellationTokenSource cts1 = new CancellationTokenSource();
84+
var task1 = typedProxy.Method("foo", cts1.Token);
85+
Assert.False(task1.IsCompleted);
86+
87+
CancellationTokenSource cts2 = new CancellationTokenSource();
88+
var task2 = typedProxy.NoArgumentMethod(cts2.Token);
89+
Assert.False(task2.IsCompleted);
90+
91+
Assert.Collection(clientProxy.Sends,
92+
send1 =>
93+
{
94+
Assert.Equal("Method", send1.Method);
95+
Assert.Equal(1, send1.Arguments.Length);
96+
Assert.Collection(send1.Arguments,
97+
arg1 => Assert.Equal("foo", arg1));
98+
Assert.Equal(cts1.Token, send1.CancellationToken);
99+
send1.Complete();
100+
},
101+
send2 =>
102+
{
103+
Assert.Equal("NoArgumentMethod", send2.Method);
104+
Assert.Equal(0, send2.Arguments.Length);
105+
Assert.Equal(cts2.Token, send2.CancellationToken);
106+
send2.Complete();
107+
});
108+
109+
await task1.OrTimeout();
110+
await task2.OrTimeout();
111+
}
112+
78113
[Fact]
79114
public void ThrowsIfProvidedAClass()
80115
{
@@ -179,6 +214,12 @@ public interface IInheritedClient : ITestClient
179214
Task SubMethod(string foo);
180215
}
181216

217+
public interface ICancellationTokenMethod
218+
{
219+
Task Method(string foo, CancellationToken cancellationToken);
220+
Task NoArgumentMethod(CancellationToken cancellationToken);
221+
}
222+
182223
public interface IPropertiesClient
183224
{
184225
string Property { get; }

0 commit comments

Comments
 (0)