6

Motivation

C# 5.0 async/await constructs are awesome, unfortunately yet Microsoft only shown a release candidate of both .NET 4.5 and VS 2012, and it will take some time until these technologies will get widely adopted in our projects.

In Stephen Toub's Asynchronous methods, C# iterators, and Tasks I've found a replacement that can be nicely used in .NET 4.0. There are also a dozen of other implementations that make it possible using the approach even in .NET 2.0 though they seem little outdated and less feature-rich.

Example

So now my .NET 4.0 code looks like (the commented sections show how it is done in .NET 4.5):

//private async Task ProcessMessageAsync()
private IEnumerable<Task> ProcessMessageAsync()
{
    //var udpReceiveResult = await udpClient.ReceiveAsync();

    var task = Task<UdpAsyncReceiveResult>
               .Factory
               .FromAsync(udpClient.BeginReceive, udpClient.EndReceive, null);

    yield return task;

    var udpReceiveResult = task.Result;

    //... blah blah blah

    if (message is BootstrapRequest)
    {
        var typedMessage = ((BootstrapRequest)(message));

        // !!! .NET 4.0 has no overload for CancellationTokenSource that 
        // !!! takes timeout parameter :(
        var cts 
          = new CancellationTokenSource(BootstrapResponseTimeout); // Error here

        //... blah blah blah

        // Say(messageIPEndPoint, responseMessage, cts.Token);

        Task.Factory.Iterate(Say(messageIPEndPoint, responseMessage, cts.Token));
    }
}

Looks little ugly though it does the job

The question

When using CancellationTokenSource in .NET 4.5 there is a constructor that takes timespan as a timeout parameter, so that resulting CancellationTokenSource cancels within specified period of time.
.Net 4.0 is not able to timeout, so what is the correct way of doing that in .Net 4.0?

Community
  • 1
  • 1
Lu4
  • 14,873
  • 15
  • 79
  • 132

3 Answers3

5

FWIW, you can use async/await in 4.0 projects, just use the async targeting pack. Works Great For Me!

James Manning
  • 13,429
  • 2
  • 40
  • 64
4

Does this really have anything to do with async/await? Looks like you're just needing a way to cancel the token, independently of async/await, right? In that case, could you simply create a Timer that calls Cancel after the timeout?

new Timer(state => cts.Cancel(), null, BootstrapResponseTimeout, Timeout.Infinite);

EDIT

My initial response above is the basic idea, but a more robust solution is can be found in Is CancellationTokenSource.CancelAfter() leaky? (actually the .Net 4.5 implementation of the constructor you're seeking). Here's a function you can use to create timeout tokens based on that code.

public static CancellationTokenSource CreateTimeoutToken(int dueTime) {
    if (dueTime < -1) {
        throw new ArgumentOutOfRangeException("dueTime");
    }
    var source = new CancellationTokenSource();
    var timer = new Timer(self => {
        ((Timer)self).Dispose();
        try {
            source.Cancel();
        } catch (ObjectDisposedException) {}
    });
    timer.Change(dueTime, -1);
    return source;
}
Community
  • 1
  • 1
Dax Fohl
  • 10,654
  • 6
  • 46
  • 90
  • What kind of timer you propose to use Dax? – Lu4 Jul 02 '12 at 21:09
  • `System.Threading.Timer` is what I was thinking, but I'd imagine any of them should work--I'm not an expert on the differences between the various kinds of .Net `Timer`s. – Dax Fohl Jul 02 '12 at 21:11
  • But it will block the thread and we'll start loosing all the benefits of asynchronous approach... :( – Lu4 Jul 02 '12 at 21:13
  • 1
    It shouldn't block anything; you're thinking of Thread.Sleep()? Timer will just grab a new thread from the thread pool and run on it each time the timer is triggered. Should be very low overhead. – Dax Fohl Jul 02 '12 at 21:17
  • Ok I see, but then how many threads would it require to wait for 10-20 simultaneous operations in the background? I have these timeouts widely used in my code... – Lu4 Jul 02 '12 at 21:23
  • 1
    If they're truly simultaneous, you should be able to reuse the same cts token on all those calls, right? And even if not, all the thread does is call cts.Cancel() and exit--so unless you're calling them microseconds apart, it's unlikely to ever need more than one thread at a time. – Dax Fohl Jul 03 '12 at 01:03
  • 1
    @Lu4 Actually you can see the code for the .Net 4.5 `CancellationTokenSource.CancelAfter` method, which is called by the `TimeSpan` constructor here: http://stackoverflow.com/questions/10715688/is-cancellationtokensource-cancelafter-leaky. Same basic idea as the above; should be exactly what you need. – Dax Fohl Jul 03 '12 at 16:01
0

You can still use CancelAfter(), which is an extension method in Microsoft.Bcl.Async which is very similar to accepted answer above.

This is the refereance souce code when I press F12 to see the implementation of CancelAfter():

  /// <summary>Cancels the <see cref="T:System.Threading.CancellationTokenSource" /> after the specified duration.</summary>
  /// <param name="source">The CancellationTokenSource.</param>
  /// <param name="dueTime">The due time in milliseconds for the source to be canceled.</param>
  public static void CancelAfter(this CancellationTokenSource source, int dueTime)
  {
    if (source == null)
      throw new NullReferenceException();
    if (dueTime < -1)
      throw new ArgumentOutOfRangeException("dueTime");
    Contract.EndContractBlock();
    Timer timer = (Timer) null;
    timer = new Timer((TimerCallback) (state =>
    {
      timer.Dispose();
      TimerManager.Remove(timer);
      try
      {
        source.Cancel();
      }
      catch (ObjectDisposedException ex)
      {
      }
    }), (object) null, -1, -1);
    TimerManager.Add(timer);
    timer.Change(dueTime, -1);
  }
Felix
  • 2,673
  • 3
  • 30
  • 38