I'm implementing network layer of my application, that is using an asynchronous JSON-RPC protocol.
In order to communicate with the server, i'd like to make a method that will send a proper request, wait until the server sends response, and return it. Everything with use of async/await keywords.
Here's simplified sample code:
string response;
Task<string> SendRequest(string methodName, string methodParams)
{
string request = generateRequest(methodName, methodParams);
await Send(request); // this will send using DataWriter, and StreamSocket
// block Task until response arrives
return response;
}
async void ReceiveLoop()
{
while (true)
{
uint numStrBytes = await _reader.LoadAsync(BufferSize);
string msg = _reader.ReadString(numStrBytes);
response = msg;
// unblock previously blocked SendRequest
}
}
}
async void main()
{
RecieveLoop();
}
async void SendButtonPressed()
{
string response = await SendRequest("Test method", "Test params");
Debug.WriteLine("Response = " + response);
}
The main problem with this pattern it this blocking action. This action should block current Task, and allow to handle timeout. I've tried to use ManualResetEvent, and WaitOne(int) to handle this, but it freezes whole Thread, and because I'm using async/await only, it freezes whole application (UI Thread to me more precise).
The solution, that looks quite hacky for me is that I can use Task.Delay with CancellationTokens.
It looks like this:
...
CancellationTokenSource cts;
int timeout = 10000;
Task<string> SendRequest(string methodName, string methodParams)
{
... (prepare request, and send)
cts = new CancellationTokenSource();
try
{
await Task.Delay(timeout, cts.Token);
} catch(TaskCanceledException)
{
}
// do rest
}
async void ReceiveLoop()
{
// init recieve loop, and recieve message
cts.Cancel();
}
The problem with that solution (besides it looks like a hack) is performance - every request there is an excetion thrown, that needs to be handled (skipped in that case). This one is slow, and it hurts :)
How can I do it in more elegant way? Is there any other option to block a Task?