Search code examples
c#asp.netasynchronousmessage-queuemessaging

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:

  1. I send a message from the server to the device with Publish
  2. 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)
  3. The client receives and handles the message
  4. 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.


Solution

  • 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; }
    }