using System.Diagnostics;
using System.Threading.Tasks;
public static void Main()
var sw = Stopwatch.StartNew();
var t = Task.Factory.StartNew(() =>
Console.WriteLine($"{sw.ElapsedMilliseconds}ms: Task Finished");
catch (AggregateException e)when (e.InnerException.GetType() == typeof (TimeoutException))
Console.WriteLine($"{sw.ElapsedMilliseconds}ms: caught {e.InnerException.GetType()}");
Console.WriteLine($"{sw.ElapsedMilliseconds}ms: task.Wait() done");
public static class ExtensionMethods
public static Task<Result> TimeoutAfter<Result>(this Task<Result> task, int millisecondsTimeout)
if (task.IsCompleted || (millisecondsTimeout == Timeout.Infinite))
Console.WriteLine("task.IsCompleted");
var tcs = new TaskCompletionSource<Result>();
if (millisecondsTimeout == 0)
tcs.SetException(new TimeoutException());
var timer = new Timer(state => tcs.TrySetException(new TimeoutException()), null, millisecondsTimeout, Timeout.Infinite);
task.ContinueWith(antecedent =>
MarshalTaskResults(antecedent, tcs);
, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
public static void MarshalTaskResults<TResult>(Task source, TaskCompletionSource<TResult> proxy)
proxy.TrySetException(source.Exception);
case TaskStatus.Canceled:
case TaskStatus.RanToCompletion:
Task<TResult> castedSource = source as Task<TResult>;
proxy.TrySetResult(castedSource == null ? default (TResult) :