async Task that needs to wait for IObserver - c#

I have an IObserver class that writes packets to a stream and waits for the correct response, however I am not happy with part of the code:
bool ResponseReceived = false;
public async Task<IResponse> WriteAsync(Stream stream, bool returnResponse = false, bool flush = true, CancellationToken token = default(CancellationToken))
{
if (returnResponse)
{
//subscribe to IObserveable
PacketRouter router = new PacketRouter();
Subscribe(router);
//write the packet to the stream
await base.WriteAsync(stream, flush, token);
//I dont like the way this is done, is it possible to use task.WhenAny or WhenAll or even something I havent tried
if (!ResponseReceived)
{
var ts = TimeSpan.FromSeconds(Timeout);
DateTime maximumTime = DateTime.Now + ts;
while (!ResponseReceived && DateTime.Now < maximumTime)
{
await Task.Delay(10);
}
}
//Unsubscribe when the correct type of packet has been received or it has timed out
Unsubscribe();
}
else
{
//we dont need the return packet so we will just write to the stream and exit
await base.WriteAsync(stream, flush, token);
}
//return the response packet
return ResponseData;
}
public virtual void OnNext(Packet packet)
{
//when a packet is received, validate it
if (ValidResponse(packet))
{
//if valid set the response data
ResponseData.Payload = packet.Payload;
ResponseReceived = true; //The right to return the response is set here
}
}
I have tried using TaskCompletionResult and Task.WaitAny(responseReceived, TaskDelay(ts)); but I couldn't get it to work either.
Is there a a better way to do this?!?
Updated with a little more context:
The Write class does not read a packet. A separate class (PacketHandler) does this and then passes it to an IObservable Class for dissemination to any class that wishes to listen. The reason for this is broadcast messages are also received which may come between the request and the response, also other packets maybe waiting for a response (although this should never technically happen).

You can directly await an observable, like so:
var router = new PacketRouter();
// write the packet to the stream
await base.WriteAsync(stream, flush, token);
try
{
// await the observable PacketRouter.
Packet p = await router
.FirstAsync()
.Timeout(DateTime.Now.AddSeconds(Timeout));
}
catch(TimeoutException)
{
// ...
}

Related

Wait for Async function before returning

I am creating a messaging system and I am facing a problem. I need to Publish a message and wait for the response before returning the Publish function.
This is what my functions look like
public async Task<bool> Publish(int ClientId, string msg){
...
// Wait and check if the client the message was sent to respond
// if that does not happen within 5 seconds, return false, else true
}
private async Task MessageIntercept(int ClientId, string msg){
// Intercepts all messages
...
}
Both of these functions are on the server and the MessageIntercept task is automatically run whenever a message is sent (including the one sent with the Publish method). I can send a message from my asp.net website project by calling on the server project's Publish function mentioned above
Basically what i want to be able to do is call bool Success = Publish(1,"This is a test") and be able to determine whether or not the message was successfully sent, that the client understood and revived the message within 5 seconds.
This is what happens step by step:
I send a message from the server to the device with Publish
The message is intercepted by the MessageIntercept method (which I do not really care about, but the code is written so that all messages are intercepted)
The client receives and handles the message
The client responds, and the message is intercepted in MessageIntercept which is where I would like to verify the message before returning the Publish method
Example Message;
Server Message:
{
ClientId: 13,
msg: "Hello World"
}
Client Response:
{
ClientId: 13,
msg: "{Success: true}"
}
MessageIntercept intercepts all messages, including the request just send which should be ignored due to it beeing a request not a response. However once the client responds with a message would I like to tell the Publish method that the response has been successfully completed and then return true. Else if the client does not respond within 5 seconds it should presume false.
You can use a helper method such as this:
public static async Task<bool> WaitFor(Task task, TimeSpan timeout)
{
return await Task.WhenAny(task, Task.Delay(timeout)) == task;
}
Sample usage:
using System;
using System.Threading.Tasks;
namespace Demo
{
public class Program
{
public static async Task Main()
{
if (await WaitFor(MyAsyncMethod(), TimeSpan.FromSeconds(1)))
Console.WriteLine("First await completed");
else
Console.WriteLine("First await didn't complete");
if (await WaitFor(MyAsyncMethod(), TimeSpan.FromSeconds(3)))
Console.WriteLine("Second await completed");
else
Console.WriteLine("Second await didn't complete");
}
public static async Task MyAsyncMethod()
{
await Task.Delay(2000);
}
public static async Task<bool> WaitFor(Task task, TimeSpan timeout)
{
return await Task.WhenAny(task, Task.Delay(timeout)) == task;
}
}
}
For your Publish() method, the call may look like this:
if (await WaitFor(Publish(1, "msg"), TimeSpan.FromSeconds(5)))
...
However, be aware that the disadvantage of using this approach is that if the timeout is exceeded, then any exceptions thrown by the task will not be observed.
If you need to handle any exceptions that may occur after you've given up waiting for the task, you could pass an exception-handling delegate like so:
public static async Task<bool> WaitFor(Task task, TimeSpan timeout, Action<Exception> handleException)
{
var wrapperTask = task.ContinueWith(
t => handleException(t.Exception.InnerException),
TaskContinuationOptions.OnlyOnFaulted);
return await Task.WhenAny(wrapperTask, Task.Delay(timeout)) == task;
}
Then you could call it like this:
public static async Task Main()
{
if (await WaitFor(
MyAsyncMethod(),
TimeSpan.FromSeconds(1),
exception => Console.WriteLine("Exception: " + exception.Message))
)
Console.WriteLine("First await completed");
else
Console.WriteLine("First await didn't complete");
Console.ReadLine();
}
There is nothing for Publish to wait on, so you need to add a notification hook. An event like "OnMessageIntercept" makes sense to me.
You can then wait on a Task that will be completed by a call to the notification hook.
public async Task<bool> PublishAsync(int clientId, string msg)
{
// Wait and check if the client the message was sent to respond
// if that does not happen within 5 seconds, return false, else true
var messageRecievedSource = new TaskCompletionSource<int>();
void intercept(object sender, MessageInterceptEventArgs args)
{
if (args.ClientId == clientId)
messageRecievedSource.SetResult(clientId);
}
OnMessageIntercept += intercept;
// EDIT
// var completed = Task.WaitAny(Task.Delay(TimeSpan.FromSeconds(5)), messageRecievedSource.Task) > 0;
var completed = await Task.WhenAny(Task.Delay(TimeSpan.FromSeconds(5)), messageRecievedSource.Task);
OnMessageIntercept -= intercept;
// EDIT
// return completed;
return completed == messageRecievedSource.Task;
}
event EventHandler<MessageInterceptEventArgs> OnMessageIntercept;
private async Task MessageIntercept(int clientId, string msg)
{
OnMessageIntercept?.Invoke(this, new MessageInterceptEventArgs(clientId, msg));
// Intercepts all messages
}
class MessageInterceptEventArgs
{
public MessageInterceptEventArgs(int clientId, string msg)
{
ClientId = clientId;
Msg = msg ?? throw new ArgumentNullException(nameof(msg));
}
public int ClientId { get; }
public string Msg { get; }
}
It is an async function, you will have to do "something" in case a result has returned. For example, you could fill in a hidden field value and read it from codebehind again. Or fire another async function and trigger a codebehind method.
To be able to write something after a response, you have to listen to it.
If you want to run an async function synchronously you can use the following code in the System.Threading.Tasks library
Task.Run(async () => await { async method here }).Result
This will cause your asynchronous methods to become blocking, and your code will not continue until you have a response back.
Within your async method you can add a timeout Thread.Sleep(5000) or iterate over the result within the async function (perhaps with a stopwatch if you to avoid Thread.Sleep).
bool Success = await Publish(1,"This is a test")

Read stream "freezes" despite data available after a certain amount of messages

I'm working on a C# application that communicates with the clangd language server.
clangd is started as a separate process managed by my C# program and communication works via I/O redirection.
My program is exchanging language server protocol request-response pairs with clangd. Requests are sent to clangd via its process' StandardInput stream and responses are read using its process' StandardOutput stream. clangd emits debug information using its process' StandardError stream.
I am using async methods for reading and writing in order to keep the user interface responsive.
However, after sending the third textDocument/didOpen message, my program freezes while trying to read the response.
Making use of the StandardError stream I found out, that clangd processes the third textDocument/didOpen message correctly, as it emits debug messages, meaning the response should be available on StandardOutput.
I saved the requests in a file and sent those to a clangd instance running on the command line, which worked like a charm. I attached that file in case you need it.
Furthermore, the debug messages which read at the bottom of the SendRequest method indicate that the file was opened:
I[09:55:53.552] <-- textDocument/didOpen(26)
I[09:55:56.512] Updating file C:\Temp\crossrail\src\ClLogic.cpp with command [C:\Temp\crossrail\src] clang C:\Temp\crossrail\src\ClLogic.cpp -resource-dir=C:\Program Files (x86)\LLVM\bin\..\lib\clang\8.0.0
Below you can see the LSP client code for reading and writing the responses. I marked the location that gets blocked.
private Process languageServer;
private StreamWriter requestWriter;
private StreamReader responseReader;
private StreamReader errorReader;
// ...
public void Connect(String workingDirectory)
{
if (this.Connected == false)
{
this.currentMessageID = LSP_FIRST_MESSAGE_ID;
this.languageServer.StartInfo.WorkingDirectory = workingDirectory;
this.languageServer.Start();
this.Connected = true;
this.requestWriter = this.languageServer.StandardInput;
this.responseReader = this.languageServer.StandardOutput;
this.errorReader = this.languageServer.StandardError;
}
}
public async Task<String> Query<T>(JsonRpcRequest<T> request)
{
await mutex.WaitAsync();
try
{
await this.SendRequest(request);
return await this.ReadResponse();
}
finally
{
mutex.Release();
}
}
private async Task SendRequest<T>(JsonRpcRequest<T> request)
{
request.ID = this.currentMessageID;
++this.currentMessageID;
String requestBody = request.ToString();
Console.WriteLine(requestBody);
await this.requestWriter.WriteAsync(requestBody.ToCharArray(), 0, requestBody.Length);
await this.requestWriter.FlushAsync();
if (request.ID == 26) // ID of the third textDocument/didOpen message
{
//await this.ReadErrors(); // the debug messages following the third textDocument/didOpen request are printed correctly
}
}
private async Task<String> ReadResponse()
{
String contentLengthHeader = await this.responseReader.ReadLineAsync(); // blocks after the third textDocument/didOpen message
int responseLength = Int32.Parse
(
contentLengthHeader.Substring(contentLengthHeader.IndexOf(LSP_HEADER_KEY_VALUE_DELIMITER) + LSP_HEADER_VALUE_OFFSET)
.Trim()
);
await this.responseReader.ReadLineAsync();
char[] buffer = new char[BUFFER_SIZE];
StringBuilder response = new StringBuilder();
int totalReadBytes = 0;
while (totalReadBytes < responseLength)
{
int readBytes = await this.responseReader.ReadAsync(buffer, 0, BUFFER_SIZE);
response.Append(buffer, 0, readBytes);
totalReadBytes += readBytes;
}
Console.WriteLine(response.ToString());
return response.ToString();
}
public async Task SendFileCloseMessage(DocumentCloseRequest request)
{
await mutex.WaitAsync();
try
{
await this.SendRequest(request);
this.responseReader.DiscardBufferedData();
}
finally
{
mutex.Release();
}
}
Here is my code using the LSP client's methods for sending the textDocument/didOpen message:
private async Task InitializeLanguageServer(bool highlightFunctionDeclarations)
{
if (this.languageServer.Connected == false)
{
this.languageServer.Connect(this.workingDirectory);
await this.SendInitializationMessage();
}
await this.SendOpenFileMessage();
await this.LoadSymbolDeclarationLocations(highlightFunctionDeclarations);
await this.LoadSymbolUsages();
}
private async Task SendInitializationMessage()
{
InitializationRequest request = new InitializationRequest
(
this.workingDirectory,
System.Diagnostics.Process.GetCurrentProcess().Id,
false,
ApplicationSettings.LANGUAGE_SERVER_PROTOCOL_SUPPORTED_SYMBOLS
);
Console.WriteLine(await this.languageServer.Query(request));
}
private async Task SendOpenFileMessage()
{
DocumentOpenRequest request = new DocumentOpenRequest(this.filePath, "cpp", 1, this.SourceCode);
Console.WriteLine(await this.languageServer.Query(request));
}
InitializeLanguageServer is called in the constructor without await, but that shouldn't be a problem, as clangd is fast enough to process every source code file in a maximum of 2.5 seconds.
The languageServer member is retrieved using TinyIoC:
public SourceViewerVM()
{
// ...
this.languageServer = TinyIoCContainer.Current.Resolve<LanguageServerProtocolClient>();
#pragma warning disable CS4014
this.InitializeLanguageServer(highlightFunctionDeclarations);
#pragma warning restore CS4014
}
Edit:
The reading really blocks and isn't just waiting for a new line character. If I put the follwing code at my breakpoint which usually reads the StandardError, execution is blocked too:
if (request.ID == 26) // 26 is the ID of the third textRequest/didOpen message
{
char[] buffer = new char[BUFFER_SIZE];
int readBytes = await this.responseReader.ReadAsync(buffer, 0, BUFFER_SIZE); // blocking
Console.WriteLine(new String(buffer, 0, readBytes));
//await this.ReadErrors();
}

Return value from a function based on which event handler is called

I am publishing XML messages from WebAPI to a queue using RabbitMQ (.Net client library). If the published message is successfully persisted to the disk I need to send status code 200 if not code 400 should be returned. RabbitMQ client library provides two separate events to send ACK or NACK events indicating if the message was saved or not. So my publish function needs to wait for either of the two event handles to be called before I return http response.
How can I achieve this?
Api Controller Action
[HttpPost]
public HttpResponseMessage SendSomething()
{
...
bool success = _publisher.Publish(bytes);
if(success) // Send status 200
else // Send status 400
...
}
Message Publish Code
public bool Publish(byte[] data)
{
..
channel.BasicAcks += OnAck;
channel.BasicNacks += OnNack;
channel.BasicPublish("", "test", null, data);
..
// Depending on if OnAck or OnNack is called I need to return true or false
return ??
}
private void OnNack(IModel model, BasicNackEventArgs args)
{
...
}
private void OnAck(IModel model, BasicAckEventArgs args)
{
...
}
If I read this right, you need to transform an asynchronous callback-based routine into a synchronous one. The naive answer would be to sleep the thread and wait for something to happen:
public bool Publish(byte[] data)
{
//..
bool? response = null;
channel.BasicAcks += (model, args) => response = true;
channel.BasicNacks += (model, args) => response = false;
channel.BasicPublish("", "test", null, data);
while (response == null)
Thread.Sleep(300);
return response.Value;
}
However, this means your response time will always be a multiple of 300ms, and you have the problem of implementing timeout logic the hard way. A better answer would probably be to use a ManualResetEvent. This allows your callback to notify the blocked thread as soon as the response has been received, by "setting" the event:
public bool Publish(byte[] data)
{
//..
bool successful = false;
var responseReceivedEvent = new ManualResetEvent(false);
channel.BasicAcks += (model, args) =>
{
successful = true;
responseReceivedEvent.Set();
};
channel.BasicNacks += (model, args) =>
{
successful = false;
responseReceivedEvent.Set();
};
channel.BasicPublish("", "test", null, data);
responseReceivedEvent.WaitOne();
return successful;
}
In either case, you can (or should) implement some sort of timeout/retry logic, unless RabbitMQ does this for you. You can do this in the last example with the WaitOne(int) or WaitOne(TimeSpan) overloads of ManualResetEvent.WaitOne().

Flags, loops and locks in async code

What I'm trying to do is create a 'Listener' which listens to several different Tcp ports at once, and pipes the messages to any Observers.
Pseudo-ish code:
private bool _Listen = false;
public void Start()
{
_Listen = true;
Task.Factory.StartNew(() => Listen(1);
Task.Factory.StartNew(() => Listen(2);
}
public void Stop()
{
_Listen = false;
}
private async void Listen(int port)
{
var tcp = new TcpClient();
while(_Listen)
{
await tcp.ConnectAsync(ip, port);
using (/*networkStream, BinaryReader, etc*/)
{
while(_Listen)
{
//Read from binary reader and OnNext to IObservable
}
}
}
}
(For brevity, I've omitted the try/catch inside the two whiles, both of which also check the flag)
My question is: should I be locking the flag, and if so, how does that tie-in with the async/await bits?
First of all, you should change your return type to Task, not void. async void methods are essentially fire-and-forget and can't be awaited or cancelled. They exist primarily to allow the creation of asynchronous event handlers or event-like code. They should never be used for normal asynchronous operations.
The TPL way to cooperatively cancel/abort/stop an asynchronous operation is to use a CancellationToken. You can check the token's IsCancellationRequested property to see if you need to cancel your operation and stop.
Even better, most asynchronous methods provided by the framework accept a CancellationToken so you can stop them immediatelly without waiting for them to return. You can use NetworkStream's ReadAsync(Byte[], Int32, Int32, CancellationToken) to read data and cancel immediatelly when someone calls your Stop method.
You could change your code to something like this:
CancellationTokenSource _source;
public void Start()
{
_source = new CancellationTokenSource();
Task.Factory.StartNew(() => Listen(1, _source.Token),_source.Token);
Task.Factory.StartNew(() => Listen(2, _source.Token), _source.Token);
}
public void Stop()
{
_source.Cancel();
}
private async Task Listen(int port,CancellationToken token)
{
var tcp = new TcpClient();
while(!token.IsCancellationRequested)
{
await tcp.ConnectAsync(ip, port);
using (var stream=tcp.GetStream())
{
...
try
{
await stream.ReadAsync(buffer, offset, count, token);
}
catch (OperationCanceledException ex)
{
//Handle Cancellation
}
...
}
}
}
You can read a lot more about cancellation in Cancellation in Managed Threads, including advice on how to poll, register a callback for cancellation, listen to multiple tokens etc.
The try/catch block exists because await throws an Exception if a Task is cancelled. You can avoid this by calling ContinueWith on the Task returned by ReadAsync and checking the IsCanceled flag:
private async Task Listen(int port,CancellationToken token)
{
var tcp = new TcpClient();
while(!token.IsCancellationRequested)
{
await tcp.ConnectAsync(ip, port);
using (var stream=tcp.GetStream())
{
///...
await stream.ReadAsync(buffer, offset, count, token)
.ContinueWith(t =>
{
if (t.IsCanceled)
{
//Do some cleanup?
}
else
{
//Process the buffer and send notifications
}
});
///...
}
}
}
await now awaits a simple Task that finishes when the continuation finishes
You would probably be better of sticking with RX all the way through instead of using Task. Here is some code I wrote for connecting to UDP sockets with RX.
public IObservable<UdpReceiveResult> StreamObserver
(int localPort, TimeSpan? timeout = null)
{
return Linq.Observable.Create<UdpReceiveResult>(observer =>
{
UdpClient client = new UdpClient(localPort);
var o = Linq.Observable.Defer(() => client.ReceiveAsync().ToObservable());
IDisposable subscription = null;
if ((timeout != null)) {
subscription = Linq.Observable.Timeout(o.Repeat(), timeout.Value).Subscribe(observer);
} else {
subscription = o.Repeat().Subscribe(observer);
}
return Disposable.Create(() =>
{
client.Close();
subscription.Dispose();
// Seems to take some time to close a socket so
// when we resubscribe there is an error. I
// really do NOT like this hack. TODO see if
// this can be improved
Thread.Sleep(TimeSpan.FromMilliseconds(200));
});
});
}
should I be locking the flag, and if so, how does that tie-in with the async/await bits?
You need to synchronize access to the flag somehow. If you don't, the compiler is allowed to make the following optimization:
bool compilerGeneratedLocal = _Listen;
while (compilerGeneratedLocal)
{
// body of the loop
}
Which would make your code wrong.
Some options how you can fix that:
Mark the bool flag volatile. This will ensure that the current value of the flag is always read.
Use CancellationToken (as suggested by Panagiotis Kanavos). This will make sure that the underlying flag is accessed in a thread-safe manner for you. It has also the advantage that many async methods support CancellationToken, so you can cancel them too.
Some form of Event (such as ManualResetEventSlim) would be a more obvious choice when you're potentially dealing with multiple threads.
private ManualResetEventSlim _Listen;
public void Start()
{
_Listen = new ManualResetEventSlim(true);
Task.Factory.StartNew(() => Listen(1);
Task.Factory.StartNew(() => Listen(2);
}
public void Stop()
{
_Listen.Reset();
}
private async void Listen(int port)
{
var tcp = new TcpClient();
while(_Listen.IsSet)
{

Simple Task-returning Asynchronous HtppListener with async/await and handling high load

I have created the following simple HttpListener to serve multiple requests at the same time (on .NET 4.5):
class Program {
static void Main(string[] args) {
HttpListener listener = new HttpListener();
listener.Prefixes.Add("http://+:8088/");
listener.Start();
ProcessAsync(listener).ContinueWith(task => { });
Console.ReadLine();
}
static async Task ProcessAsync(HttpListener listener) {
HttpListenerContext ctx = await listener.GetContextAsync();
// spin up another listener
Task.Factory.StartNew(() => ProcessAsync(listener));
// Simulate long running operation
Thread.Sleep(1000);
// Perform
Perform(ctx);
await ProcessAsync(listener);
}
static void Perform(HttpListenerContext ctx) {
HttpListenerResponse response = ctx.Response;
string responseString = "<HTML><BODY> Hello world!</BODY></HTML>";
byte[] buffer = Encoding.UTF8.GetBytes(responseString);
// Get a response stream and write the response to it.
response.ContentLength64 = buffer.Length;
Stream output = response.OutputStream;
output.Write(buffer, 0, buffer.Length);
// You must close the output stream.
output.Close();
}
}
I use Apache Benchmark Tool to load test this. When I make a 1 request, I get the max wait time for a request as 1 second. If I make 10 requests, for example, max wait time for a response goes up to 2 seconds.
How would you change my above code to make it as efficient as it can be?
Edit
After #JonSkeet's answer, I changed the code as below. Initially, I tried to simulate a blocking call but I guess it was the core problem. So,I took #JonSkeet's suggestion and change that to Task.Delay(1000). Now, the below code gives max. wait time as approx. 1 sec for 10 concurrent requests:
class Program {
static bool KeepGoing = true;
static List<Task> OngoingTasks = new List<Task>();
static void Main(string[] args) {
HttpListener listener = new HttpListener();
listener.Prefixes.Add("http://+:8088/");
listener.Start();
ProcessAsync(listener).ContinueWith(async task => {
await Task.WhenAll(OngoingTasks.ToArray());
});
var cmd = Console.ReadLine();
if (cmd.Equals("q", StringComparison.OrdinalIgnoreCase)) {
KeepGoing = false;
}
Console.ReadLine();
}
static async Task ProcessAsync(HttpListener listener) {
while (KeepGoing) {
HttpListenerContext context = await listener.GetContextAsync();
HandleRequestAsync(context);
// TODO: figure out the best way add ongoing tasks to OngoingTasks.
}
}
static async Task HandleRequestAsync(HttpListenerContext context) {
// Do processing here, possibly affecting KeepGoing to make the
// server shut down.
await Task.Delay(1000);
Perform(context);
}
static void Perform(HttpListenerContext ctx) {
HttpListenerResponse response = ctx.Response;
string responseString = "<HTML><BODY> Hello world!</BODY></HTML>";
byte[] buffer = Encoding.UTF8.GetBytes(responseString);
// Get a response stream and write the response to it.
response.ContentLength64 = buffer.Length;
Stream output = response.OutputStream;
output.Write(buffer, 0, buffer.Length);
// You must close the output stream.
output.Close();
}
}
It looks to me like you'll end up with a bifurcation of listeners. Within ProcessAsync, you start a new task to listen (via Task.Factory.StartNew), and then you call ProcessAsync again at the end of the method. How can that ever finish? It's not clear whether that's the cause of your performance problems, but it definitely looks like an issue in general.
I'd suggest changing your code to be just a simple loop:
static async Task ProcessAsync(HttpListener listener) {
while (KeepGoing) {
var context = await listener.GetContextAsync();
HandleRequestAsync(context);
}
}
static async Task HandleRequestAsync(HttpListenerContext context) {
// Do processing here, possibly affecting KeepGoing to make the
// server shut down.
}
Now currently the above code ignores the return value of HandleRequestAsync. You may want to keep a list of the "currently in flight" tasks, and when you've been asked to shut down, use await Task.WhenAll(inFlightTasks) to avoid bringing the server down too quickly.
Also note that Thread.Sleep is a blocking delay. An asynchronous delay would be await Task.Delay(1000).

Categories