Skip to content

Instantly share code, notes, and snippets.

@noseratio
Last active May 2, 2023 11:32
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save noseratio/10004423 to your computer and use it in GitHub Desktop.
Save noseratio/10004423 to your computer and use it in GitHub Desktop.
using System;
using System.Collections;
using System.Threading;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Collections.Generic;
namespace ConsoleApplication
{
public class Program
{
// http://stackoverflow.com/a/18760624/1768303
// .NET 4.0/C# 4.0: convert EAP pattern into TAP pattern with timeout
Task<AsyncCompletedEventArgs> BlackBoxOperationAsync(
object state,
CancellationToken token,
int timeout = Timeout.Infinite)
{
return TaskExt.Start<Task<AsyncCompletedEventArgs>>(
(continuator) => BlackBoxOperationAsyncHelper(state, token, timeout, continuator));
}
IEnumerator<Task<AsyncCompletedEventArgs>> BlackBoxOperationAsyncHelper(
object state,
CancellationToken token,
int timeout,
Action continuator)
{
var tcs = new TaskCompletionSource<AsyncCompletedEventArgs>();
// prepare the timeout
CancellationToken newToken;
if (timeout != Timeout.Infinite)
{
var cts = CancellationTokenSource.CreateLinkedTokenSource(token);
cts.CancelAfterEx(timeout);
newToken = cts.Token;
}
else
newToken = token;
// handle completion
AsyncCompletedEventHandler handler = (sender, args) =>
{
if (args.Cancelled)
tcs.TrySetCanceled();
else if (args.Error != null)
tcs.SetException(args.Error);
else
tcs.SetResult(args);
};
this.BlackBoxOperationCompleted += handler;
try
{
using (newToken.Register(() => tcs.SetCanceled(), useSynchronizationContext: false))
{
this.StartBlackBoxOperation(null);
yield return tcs.Task.Continuation(continuator);
}
}
finally
{
this.BlackBoxOperationCompleted -= handler;
}
}
// emulate async operation
AsyncCompletedEventHandler BlackBoxOperationCompleted = delegate { };
void StartBlackBoxOperation(object state)
{
ThreadPool.QueueUserWorkItem(s =>
{
Thread.Sleep(1000);
this.BlackBoxOperationCompleted(this, new AsyncCompletedEventArgs(error: null, cancelled: false, userState: state));
}, state);
}
// test
static void Main()
{
try
{
new Program().BlackBoxOperationAsync(null, CancellationToken.None, 1200).Wait();
Console.WriteLine("Completed.");
new Program().BlackBoxOperationAsync(null, CancellationToken.None, 900).Wait();
}
catch (Exception ex)
{
while (ex is AggregateException)
ex = ex.InnerException;
Console.WriteLine(ex.Message);
}
Console.ReadLine();
}
}
// NET 4.0 helpers
public static class TaskExt
{
public struct Empty { public static readonly Empty Value = default(Empty); }
static IEnumerator<Task<Empty>> DelayExHelper(int milliseconds, CancellationToken token, Action continuator)
{
var tcs = new TaskCompletionSource<Empty>();
using (token.Register(() => tcs.TrySetCanceled()))
using (var timer = new System.Threading.Timer(_ => tcs.TrySetResult(Empty.Value), null, milliseconds, Timeout.Infinite))
{
yield return tcs.Task.Continuation(continuator);
}
}
public static Task DelayEx(int milliseconds, CancellationToken token)
{
return Start<Task<Empty>>((continuator) => DelayExHelper(milliseconds, token, continuator));
}
public static void CancelAfterEx(this CancellationTokenSource @this, int milliseconds)
{
DelayEx(milliseconds, @this.Token).ContinueWith(
_ => @this.Cancel(),
TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.ExecuteSynchronously);
}
public static T Start<T>(Func<Action, IEnumerator<T>> taskHelper)
{
IEnumerator<T> enumerator = null;
var continuator = GetContinuator(() => enumerator);
enumerator = taskHelper(continuator);
continuator();
return enumerator.Current;
}
public static Task<T> Continuation<T>(this Task<T> @this, Action continuator)
{
return @this.ContinueWith(
t => { continuator(); return t; },
TaskContinuationOptions.ExecuteSynchronously).Unwrap();
}
static Action GetContinuator<T>(Func<IEnumerator<T>> getEnumerator)
{
return () =>
{
var enumerator = getEnumerator();
try
{
enumerator.MoveNext();
}
catch
{
enumerator.Dispose();
throw;
}
};
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment