0

I have this code :

ManualResetEvent EventListenerStopped;
...
while (true)
{
    IAsyncResult iar = this.ListenerHttp.BeginGetContext(ProcessRequest, null);
    if (WaitHandle.WaitAny(new[] { this.EventListenerStopped, iar.AsyncWaitHandle }) == 0)
        return;
}

Basically it waits for any of two events :

  • if a request is received, it processes it and wait for the next one.
  • if EventListenerStopped is raised, it exits the loop.

This code has been running in production beautifully for quite some time now.

I wanted to try and convert it to the new await/async mechanism and can't seem to find a good simple way to do it.

I tried with a boolean the caller can turn to false. It obviously does not work as it exits the loop only after a new request has been received and processed :

bool RunLoop;
...
while (this.RunLoop)
{
    HttpListenerContext listenerContext = await this.ListenerHttp.GetContextAsync();
    ProcessRequest(listenerContext);
}

I'm wondering if it's even possible to rewrite my simple old-style loop with async/await. If yes, would someone be willing to show me how ?

  • Have you tried to stop the listener instead of using a bool? – alexm Jan 17 '16 at 18:56
  • @Micky I understand they're not the same. My point was : is there a way to achieve something similar with async/await (I was confusedly thinking of some kind of Task.WaitAny() where one of the tasks would be checking the raise of some boolean...) – user1409737 Jan 17 '16 at 19:51
  • @alexm I haven't tried it myself but read somewhere on SO that this should not be done as it would break any request currently processed (well the ListenerContext object is passed to a threadpool for processing in my case...) – user1409737 Jan 17 '16 at 19:53

2 Answers2

0

It's not specific to async-await, but you're probably looking for CancellationToken (which is used with a lot of async-await code anyway):

http://blogs.msdn.com/b/pfxteam/archive/2009/05/22/9635790.aspx

The 'BlockingOperation' example code seems similar to what you're trying to do:

void BlockingOperation(CancellationToken token) 
{ 
   ManualResetEvent mre = new ManualResetEvent(false); 
   //register a callback that will set the MRE 
   CancellationTokenRegistration registration = 
      token.Register(() => mre.Set()); 
   using (registration) 
   { 
      mre.WaitOne(); 
      if (token.IsCancellationRequested) //did cancellation wake us? 
          throw new OperationCanceledException(token); 
   } //dispose the registration, which performs the deregisteration. 
}
sellotape
  • 8,034
  • 2
  • 26
  • 30
  • I read about CancellationToken which I agree could do the trick. Yet it appears much more complicated than the two lines of code I currently have. I'm slowly coming to the conclusion async/await is not always a better way to do things... – user1409737 Jan 17 '16 at 19:55
  • I tend to agree. These days I pretty-much use async-await exclusively for all IO-bound stuff, but I still find the pattern you're trying to replace helpful in some circumstances, notably for the worker method for a separate thread. I don't know the full context of your code, but it might well be an example where it's not necessarily a good idea to use async-await just because it's the new thing. – sellotape Jan 17 '16 at 20:04
  • (I'm exactly in the case of a separate worker thread) Thanks for sharing your thoughts ! – user1409737 Jan 17 '16 at 20:36
0

Well, first I must point out that the old code is not quite correct. When dealing with the Begin/End pattern, you must always call End, even if you want to (or did) cancel the operation. End is often used to dispose resources.

If you do want to use cancellation, a CancellationToken is likely the best approach:

while (true)
{
  // Throws an OperationCanceledException when cancellationToken is canceled.
  var request = await this.ListenerHttp.GetContextAsync(cancellationToken);
  ProcessRequest(request);
}

There are alternatives - it's possible to do something like Task.WhenAny, and there are even implementations of AsyncManualResetEvent, so it's possible to create an almost line-by-line equivalent to the old code, but IMO the cancellation token approach would be cleaner.

For example, using AsyncManualResetEvent from my AsyncEx library:

AsyncManualResetEvent eventListenerStopped;
while (true)
{
  var task = GetContextAndProcessRequestAsync();
  if (await Task.WhenAny(eventListenerStopped.WaitAsync(), task) != task)
    return;
}

async Task GetContextAndProcessRequestAsync()
{
  var request = await this.ListenerHttp.GetContextAsync();
  ProcessRequest(request);
}

But personally, I would change to use CancellationToken.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810