Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Usage of await operator in the JsonRpcMethod. #147

Open
posahok opened this issue Sep 17, 2024 · 1 comment
Open

Usage of await operator in the JsonRpcMethod. #147

posahok opened this issue Sep 17, 2024 · 1 comment

Comments

@posahok
Copy link

posahok commented Sep 17, 2024

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!

@Astn
Copy link
Owner

Astn commented Oct 1, 2024

Hi @posahok

This project does not currently have native support for the async and await style JsonRpcMethods as it was created long before async and await keywords were added to the language, but a workaround does exist.

Could you give something like the following example a try?:

public class ExampleService : JsonRpcService
{
    [JsonRpcMethod]
    private double DoSomethingAsync(double l, double r) // {'method':'DoSomethingAsync','params':[1.0,2.0],'id':1}
    {
        // Call the async method and wait for it synchronously using GetAwaiter().GetResult()
        return Worker.SummAsync(l, r).GetAwaiter().GetResult();
    }
}

public class Worker
{
    // An asynchronous method that uses Task.Delay to simulate async work
    public static async Task<double> SummAsync(double a, double b)
    {
        await Task.Delay(1000); // Simulate some asynchronous delay
        return a + b;
    }
}

If you would like to add native support for async and await as handler functions, then some places to start looking at the code would be:

var results = handle.DynamicInvoke(parameters);

and
var data = handler.Handle(jsonRequest, jsonRpcContext);

Then a few tests to https://github.com/Astn/JSON-RPC.NET/blob/master/AustinHarris.JsonRpcTestN/service.cs

I would be happy to review a pull request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants