Last active
May 2, 2023 11:32
-
-
Save noseratio/10004423 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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