Skip to content

Commit

Permalink
Invoke-and-forget for client-proxy notification implementation.
Browse files Browse the repository at this point in the history
This could reduce the chance of deadlocks caused by sync-wait in async method body.
  • Loading branch information
CXuesong committed Sep 2, 2017
1 parent 55c60c9 commit 01f295b
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 24 deletions.
15 changes: 9 additions & 6 deletions JsonRpc.Standard/Client/JsonRpcProxyBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,8 @@
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using JsonRpc.Standard.Client;
using JsonRpc.Standard.Contracts;
using Newtonsoft.Json.Linq;

namespace JsonRpc.Standard.Client
{
Expand All @@ -19,6 +13,7 @@ namespace JsonRpc.Standard.Client
[Browsable(false)]
public class JsonRpcProxyBase
{

protected JsonRpcProxyBase(JsonRpcClient client, IList<JsonRpcMethod> methodTable)
{
if (client == null) throw new ArgumentNullException(nameof(client));
Expand All @@ -39,6 +34,14 @@ protected TResult Send<TResult>(int methodIndex, IList paramValues)
return SendAsync<TResult>(methodIndex, paramValues).GetAwaiter().GetResult();
}

/// <summary>
/// Infrastructure. Sends the notification; do not wait for the response.
/// </summary>
protected void Send(int methodIndex, IList paramValues)
{
var forgetit = SendAsync<object>(methodIndex, paramValues);
}

/// <summary>
/// Infrastructure. Asynchronously sends the request and wait for the response.
/// </summary>
Expand Down
55 changes: 37 additions & 18 deletions JsonRpc.Standard/Client/JsonRpcProxyBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public IJsonRpcContractResolver ContractResolver
protected string ImplementedProxyNamespace { get; }

protected string ImplementedProxyAssemblyName { get; }

protected AssemblyBuilder AssemblyBuilder => _AssemblyBuilder.Value;

protected ModuleBuilder ModuleBuilder => _ModuleBuilder.Value;
Expand Down Expand Up @@ -118,7 +118,7 @@ public object CreateProxy(JsonRpcClient client, Type stubType)
/// <returns>The implemented proxy instance.</returns>
public T CreateProxy<T>(JsonRpcClient client)
{
return (T) CreateProxy(client, typeof(T));
return (T)CreateProxy(client, typeof(T));
}

private static readonly ConstructorInfo JsonRpcProxyBase_ctor =
Expand All @@ -128,19 +128,22 @@ public T CreateProxy<T>(JsonRpcClient client)
typeof(JsonRpcProxyBase).GetRuntimeMethods().First(m => m.Name == "SendAsync");

private static readonly MethodInfo JsonRpcProxyBase_Send =
typeof(JsonRpcProxyBase).GetRuntimeMethods().First(m => m.Name == "Send");
typeof(JsonRpcProxyBase).GetRuntimeMethods().First(m => m.Name == "Send" && m.IsGenericMethod);

private static readonly MethodInfo JsonRpcProxyBase_SendNotification =
typeof(JsonRpcProxyBase).GetRuntimeMethods().First(m => m.Name == "Send" && !m.IsGenericMethod);

private static readonly ConstructorInfo NotSupportedException_ctor1 =
typeof(NotSupportedException).GetTypeInfo().DeclaredConstructors.First(c => c.GetParameters().Length == 1);
private static readonly ConstructorInfo NotImplementedException_ctor1 =
typeof(NotImplementedException).GetTypeInfo().DeclaredConstructors.First(c => c.GetParameters().Length == 1);

protected virtual ProxyBuilderEntry ImplementProxy(Type stubType)
{
var contract = ContractResolver.CreateClientContract(new[] {stubType});
var contract = ContractResolver.CreateClientContract(new[] { stubType });
var builder = ModuleBuilder.DefineType(NextProxyTypeName(), TypeAttributes.Class | TypeAttributes.Sealed,
typeof(JsonRpcProxyBase), new[] {stubType});
typeof(JsonRpcProxyBase), new[] { stubType });
{
var ctor = builder.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis,
new[] {typeof(JsonRpcClient), typeof(IList<JsonRpcMethod>)});
new[] { typeof(JsonRpcClient), typeof(IList<JsonRpcMethod>) });
var gen = ctor.GetILGenerator();
gen.Emit(OpCodes.Ldarg_0); // this
gen.Emit(OpCodes.Ldarg_1); // client
Expand Down Expand Up @@ -172,11 +175,11 @@ protected virtual ProxyBuilderEntry ImplementProxy(Type stubType)
{
var gen = impl.GetILGenerator();
gen.Emit(OpCodes.Ldstr, $"\"{method.Name}\" is not implemented as a JSON RPC method.");
gen.Emit(OpCodes.Newobj, NotSupportedException_ctor1);
gen.Emit(OpCodes.Newobj, NotImplementedException_ctor1);
gen.Emit(OpCodes.Throw);
}
}
else if(member is PropertyInfo property)
else if (member is PropertyInfo property)
{
throw new InvalidOperationException($"Cannot implement property member in \"{stubType}\".");
}
Expand Down Expand Up @@ -209,18 +212,34 @@ protected virtual void ImplementProxyMethod(MethodInfo method, JsonRpcMethod rpc
gen.Emit(OpCodes.Stelem_Ref);
}
}
var TResult = rpcMethod.ReturnParameter.ParameterType == typeof(void)
? typeof(bool)
: rpcMethod.ReturnParameter.ParameterType;
var TResult = rpcMethod.ReturnParameter.ParameterType;
if (rpcMethod.ReturnParameter.IsTask)
{
gen.Emit(OpCodes.Call, JsonRpcProxyBase_SendAsync.MakeGenericMethod(TResult));
gen.Emit(OpCodes.Call, JsonRpcProxyBase_SendAsync.MakeGenericMethod(
TResult == typeof(void) ? typeof(object) : TResult));
}
else
{
gen.Emit(OpCodes.Call, JsonRpcProxyBase_Send.MakeGenericMethod(TResult));
if (rpcMethod.ReturnParameter.ParameterType == typeof(void))
gen.Emit(OpCodes.Pop);
if (rpcMethod.IsNotification)
{
// Notification. invoke and forget.
if (rpcMethod.ReturnParameter.ParameterType != typeof(void))
throw new InvalidOperationException("Notification method can only return void or Task.");
gen.Emit(OpCodes.Call, JsonRpcProxyBase_SendNotification);
}
else
{
// Message. invoke and wait.
if (TResult == typeof(void))
{
gen.Emit(OpCodes.Call, JsonRpcProxyBase_Send.MakeGenericMethod(typeof(object)));
gen.Emit(OpCodes.Pop);
}
else
{
gen.Emit(OpCodes.Call, JsonRpcProxyBase_Send.MakeGenericMethod(TResult));
}
}
}
gen.Emit(OpCodes.Ret);
}
Expand All @@ -234,7 +253,7 @@ public ProxyBuilderEntry(Type proxyType, IList<JsonRpcMethod> methodTable)
ProxyType = proxyType;
MethodTable = methodTable;
}

public Type ProxyType { get; }

public IList<JsonRpcMethod> MethodTable { get; }
Expand Down

0 comments on commit 01f295b

Please sign in to comment.