Skip to content

Usage of await operator in the JsonRpcMethod. #147

Open
@posahok

Description

@posahok

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!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions