/
ApmAsyncFactory.cs
107 lines (101 loc) · 4.75 KB
/
ApmAsyncFactory.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
using System;
using System.Threading.Tasks;
using Nito.AsyncEx.Synchronous;
namespace Nito.AsyncEx.Interop
{
/// <summary>
/// Creation methods for tasks wrapping the Asynchronous Programming Model (APM), and APM wrapper methods around tasks.
/// </summary>
public static class ApmAsyncFactory
{
/// <summary>
/// Wraps a <see cref="Task"/> into the Begin method of an APM pattern.
/// </summary>
/// <param name="task">The task to wrap.</param>
/// <param name="callback">The callback method passed into the Begin method of the APM pattern.</param>
/// <param name="state">The state passed into the Begin method of the APM pattern.</param>
/// <returns>The asynchronous operation, to be returned by the Begin method of the APM pattern.</returns>
public static IAsyncResult ToBegin(Task task, AsyncCallback callback, object state)
{
var tcs = new TaskCompletionSource<object?>(state, TaskCreationOptions.RunContinuationsAsynchronously);
SynchronizationContextSwitcher.NoContext(() => CompleteAsync(task, callback, tcs));
return tcs.Task;
}
// `async void` is on purpose, to raise `callback` exceptions directly on the thread pool.
private static async void CompleteAsync(Task task, AsyncCallback callback, TaskCompletionSource<object?> tcs)
{
try
{
await task.ConfigureAwait(false);
tcs.TrySetResult(null);
}
catch (OperationCanceledException ex)
{
tcs.TrySetCanceled(ex.CancellationToken);
}
#pragma warning disable CA1031 // Do not catch general exception types
catch (Exception ex)
#pragma warning restore CA1031 // Do not catch general exception types
{
tcs.TrySetException(ex);
}
finally
{
callback?.Invoke(tcs.Task);
}
}
/// <summary>
/// Wraps a <see cref="Task"/> into the End method of an APM pattern.
/// </summary>
/// <param name="asyncResult">The asynchronous operation returned by the matching Begin method of this APM pattern.</param>
/// <returns>The result of the asynchronous operation, to be returned by the End method of the APM pattern.</returns>
public static void ToEnd(IAsyncResult asyncResult)
{
((Task)asyncResult).WaitAndUnwrapException();
}
/// <summary>
/// Wraps a <see cref="Task{TResult}"/> into the Begin method of an APM pattern.
/// </summary>
/// <param name="task">The task to wrap. May not be <c>null</c>.</param>
/// <param name="callback">The callback method passed into the Begin method of the APM pattern.</param>
/// <param name="state">The state passed into the Begin method of the APM pattern.</param>
/// <returns>The asynchronous operation, to be returned by the Begin method of the APM pattern.</returns>
public static IAsyncResult ToBegin<TResult>(Task<TResult> task, AsyncCallback callback, object state)
{
var tcs = new TaskCompletionSource<TResult>(state, TaskCreationOptions.RunContinuationsAsynchronously);
SynchronizationContextSwitcher.NoContext(() => CompleteAsync(task, callback, tcs));
return tcs.Task;
}
// `async void` is on purpose, to raise `callback` exceptions directly on the thread pool.
private static async void CompleteAsync<TResult>(Task<TResult> task, AsyncCallback callback, TaskCompletionSource<TResult> tcs)
{
try
{
tcs.TrySetResult(await task.ConfigureAwait(false));
}
catch (OperationCanceledException ex)
{
tcs.TrySetCanceled(ex.CancellationToken);
}
#pragma warning disable CA1031 // Do not catch general exception types
catch (Exception ex)
#pragma warning restore CA1031 // Do not catch general exception types
{
tcs.TrySetException(ex);
}
finally
{
callback?.Invoke(tcs.Task);
}
}
/// <summary>
/// Wraps a <see cref="Task{TResult}"/> into the End method of an APM pattern.
/// </summary>
/// <param name="asyncResult">The asynchronous operation returned by the matching Begin method of this APM pattern.</param>
/// <returns>The result of the asynchronous operation, to be returned by the End method of the APM pattern.</returns>
public static TResult ToEnd<TResult>(IAsyncResult asyncResult)
{
return ((Task<TResult>)asyncResult).WaitAndUnwrapException();
}
}
}