JSON-RPC.NET icon indicating copy to clipboard operation
JSON-RPC.NET copied to clipboard

Usage of await operator in the JsonRpcMethod.

Open posahok opened this issue 5 months ago • 1 comments

Hello. I was wondering if it is possible to use the await operator in [JsonRpcMethod] methods? Inside the [JsonRpcMethod] I would like to call asynchronous methods and return Task<T> from the function. But this approach throws an exception. All I could do was call synchronous versions of the functions and return Task.FromResult from the function.

Some examples:

using AustinHarris.JsonRpc;

object[] services = new object[] { new ExampleService() };

for (string line = Console.ReadLine(); !string.IsNullOrEmpty(line); line = Console.ReadLine())
{
    try
    {
        var response = await JsonRpcProcessor.Process(line);
        Console.WriteLine(response);
    }
    catch(Exception ex)
    {
        Console.WriteLine(ex.ToString());
    }

}
public class ExampleService : JsonRpcService
{
    [JsonRpcMethod]
    private async Task<double> DoSomethingAsync(double l, double r) // {'method':'DoSomethingAsync','params':[1.0,2.0],'id':1}
    {
        return await Worker.SummAsync(l, r);
    }
}
public class Worker
{
    public static async Task<double> SummAsync(double a, double b)
    {
        await Task.Delay(1000);
        return await Task.FromResult(a + b);
    }
}

Exception:

Newtonsoft.Json.JsonSerializationException: Self referencing loop detected for property 'Task' with type 'System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1+AsyncStateMachineBox`1[System.Double,ExampleService+<DoSomethingAsync>d__0]'. Path 'StateMachine.<>t__builder'.
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CheckForCircularReference(JsonWriter writer, Object value, JsonProperty property, JsonContract contract, JsonContainerContract containerContract, JsonProperty containerProperty)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CalculatePropertyValues(JsonWriter writer, Object value, JsonContainerContract contract, JsonProperty member, JsonProperty property, JsonContract& memberContract, Object& memberValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter jsonWriter, Object value, Type objectType)
   at Newtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter jsonWriter, Object value, Type objectType)
   at Newtonsoft.Json.JsonSerializer.Serialize(JsonWriter jsonWriter, Object value, Type objectType)
   at Newtonsoft.Json.JsonConvert.SerializeObjectInternal(Object value, Type type, JsonSerializer jsonSerializer)
   at Newtonsoft.Json.JsonConvert.SerializeObject(Object value, Type type, JsonSerializerSettings settings)
   at Newtonsoft.Json.JsonConvert.SerializeObject(Object value)
   at AustinHarris.JsonRpc.JsonRpcProcessor.ProcessSync(String sessionId, String jsonRpc, Object jsonRpcContext, JsonSerializerSettings settings)
   at AustinHarris.JsonRpc.JsonRpcProcessor.<>c.<Process>b__3_0(Object _)
   at System.Threading.Tasks.Task`1.InnerInvoke()
   at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
   at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)
--- End of stack trace from previous location ---
   at Program.<Main>$(String[] args) in D:\Work\other\JsonRpcNetAsync\JsonRpcNetAsync\Program.cs:line 9

Working example:

using AustinHarris.JsonRpc;

object[] services = new object[] { new ExampleService() };

for (string line = Console.ReadLine(); !string.IsNullOrEmpty(line); line = Console.ReadLine())
{
    try
    {
        Task<string> task1 = JsonRpcProcessor.Process(line);
        Task<string> task2 = JsonRpcProcessor.Process(line);
        Task<string> task3 = JsonRpcProcessor.Process(line);
        Task<string>[] tasks = [task1, task2, task3];
        string[] result = await Task.WhenAll(tasks);
        foreach (string res in result) Console.WriteLine(res);
    }
    catch(Exception ex)
    {
        Console.WriteLine(ex.ToString());
    }

}
public class ExampleService : JsonRpcService
{
    [JsonRpcMethod]
    private Task<double> DoSomethingAsync(double l, double r) // {'method':'DoSomethingAsync','params':[1.0,2.0],'id':1}
    {
        return Task.FromResult(Worker.SummSync(l, r));
    }
}
public class Worker
{
    public static double SummSync(double a, double b)
    {
        Thread.Sleep(10000);
        return a + b;
    }
}

If I understand correctly, calling JsonRpcProcessor.Process(line) runs in a separate thread, so blocking synchronous methods do not block the calling thread. But in the future, I might want to call asynchronous methods, and I was wondering if it is possible to do so using your framework? Thanks a lot for your answer!

posahok avatar Sep 17 '24 16:09 posahok