TaskToApm.cs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. using System;
  2. using System.Diagnostics;
  3. using System.Threading;
  4. using System.Threading.Tasks;
  5. namespace FastGithub.FlowAnalyze
  6. {
  7. static class TaskToApm
  8. {
  9. /// <summary>
  10. /// Marshals the Task as an IAsyncResult, using the supplied callback and state
  11. /// to implement the APM pattern.
  12. /// </summary>
  13. /// <param name="task">The Task to be marshaled.</param>
  14. /// <param name="callback">The callback to be invoked upon completion.</param>
  15. /// <param name="state">The state to be stored in the IAsyncResult.</param>
  16. /// <returns>An IAsyncResult to represent the task's asynchronous operation.</returns>
  17. public static IAsyncResult Begin(Task task, AsyncCallback? callback, object? state) =>
  18. new TaskAsyncResult(task, state, callback);
  19. /// <summary>Processes an IAsyncResult returned by Begin.</summary>
  20. /// <param name="asyncResult">The IAsyncResult to unwrap.</param>
  21. public static void End(IAsyncResult asyncResult)
  22. {
  23. if (asyncResult is TaskAsyncResult twar)
  24. {
  25. twar._task.GetAwaiter().GetResult();
  26. return;
  27. }
  28. throw new ArgumentNullException();
  29. }
  30. /// <summary>Processes an IAsyncResult returned by Begin.</summary>
  31. /// <param name="asyncResult">The IAsyncResult to unwrap.</param>
  32. public static TResult End<TResult>(IAsyncResult asyncResult)
  33. {
  34. if (asyncResult is TaskAsyncResult twar && twar._task is Task<TResult> task)
  35. {
  36. return task.GetAwaiter().GetResult();
  37. }
  38. throw new ArgumentNullException();
  39. }
  40. /// <summary>Provides a simple IAsyncResult that wraps a Task.</summary>
  41. /// <remarks>
  42. /// We could use the Task as the IAsyncResult if the Task's AsyncState is the same as the object state,
  43. /// but that's very rare, in particular in a situation where someone cares about allocation, and always
  44. /// using TaskAsyncResult simplifies things and enables additional optimizations.
  45. /// </remarks>
  46. internal sealed class TaskAsyncResult : IAsyncResult
  47. {
  48. /// <summary>The wrapped Task.</summary>
  49. internal readonly Task _task;
  50. /// <summary>Callback to invoke when the wrapped task completes.</summary>
  51. private readonly AsyncCallback? _callback;
  52. /// <summary>Initializes the IAsyncResult with the Task to wrap and the associated object state.</summary>
  53. /// <param name="task">The Task to wrap.</param>
  54. /// <param name="state">The new AsyncState value.</param>
  55. /// <param name="callback">Callback to invoke when the wrapped task completes.</param>
  56. internal TaskAsyncResult(Task task, object? state, AsyncCallback? callback)
  57. {
  58. Debug.Assert(task != null);
  59. _task = task;
  60. AsyncState = state;
  61. if (task.IsCompleted)
  62. {
  63. // Synchronous completion. Invoke the callback. No need to store it.
  64. CompletedSynchronously = true;
  65. callback?.Invoke(this);
  66. }
  67. else if (callback != null)
  68. {
  69. // Asynchronous completion, and we have a callback; schedule it. We use OnCompleted rather than ContinueWith in
  70. // order to avoid running synchronously if the task has already completed by the time we get here but still run
  71. // synchronously as part of the task's completion if the task completes after (the more common case).
  72. _callback = callback;
  73. _task.ConfigureAwait(continueOnCapturedContext: false)
  74. .GetAwaiter()
  75. .OnCompleted(InvokeCallback); // allocates a delegate, but avoids a closure
  76. }
  77. }
  78. /// <summary>Invokes the callback.</summary>
  79. private void InvokeCallback()
  80. {
  81. Debug.Assert(!CompletedSynchronously);
  82. Debug.Assert(_callback != null);
  83. _callback.Invoke(this);
  84. }
  85. /// <summary>Gets a user-defined object that qualifies or contains information about an asynchronous operation.</summary>
  86. public object? AsyncState { get; }
  87. /// <summary>Gets a value that indicates whether the asynchronous operation completed synchronously.</summary>
  88. /// <remarks>This is set lazily based on whether the <see cref="_task"/> has completed by the time this object is created.</remarks>
  89. public bool CompletedSynchronously { get; }
  90. /// <summary>Gets a value that indicates whether the asynchronous operation has completed.</summary>
  91. public bool IsCompleted => _task.IsCompleted;
  92. /// <summary>Gets a <see cref="WaitHandle"/> that is used to wait for an asynchronous operation to complete.</summary>
  93. public WaitHandle AsyncWaitHandle => ((IAsyncResult)_task).AsyncWaitHandle;
  94. }
  95. }
  96. }