| | | 1 | | // Licensed to the .NET Foundation under one or more agreements. |
| | | 2 | | // The .NET Foundation licenses this file to you under the MIT license. |
| | | 3 | | |
| | | 4 | | using System; |
| | | 5 | | using System.Threading; |
| | | 6 | | using System.Threading.Tasks; |
| | | 7 | | |
| | | 8 | | namespace CoreWCF.Runtime |
| | | 9 | | { |
| | | 10 | | internal static class TaskHelpers |
| | | 11 | | { |
| | | 12 | | // Helper method when implementing an APM wrapper around a Task based async method which returns a result. |
| | | 13 | | // In the BeginMethod method, you would call use ToApm to wrap a call to MethodAsync: |
| | | 14 | | // return MethodAsync(params).ToApm(callback, state); |
| | | 15 | | // In the EndMethod, you would use ToApmEnd<TResult> to ensure the correct exception handling |
| | | 16 | | // This will handle throwing exceptions in the correct place and ensure the IAsyncResult contains the provided |
| | | 17 | | // state object |
| | | 18 | | public static IAsyncResult ToApm<T>(this Task<T> task, AsyncCallback callback, object state) |
| | 0 | 19 | | => ToApm<T>(new ValueTask<T>(task), callback, state); |
| | | 20 | | |
| | | 21 | | /// <summary> |
| | | 22 | | /// Helper method to convert from Task async method to "APM" (IAsyncResult with Begin/End calls) |
| | | 23 | | /// </summary> |
| | | 24 | | public static IAsyncResult ToApm<T>(this ValueTask<T> valueTask, AsyncCallback callback, object state) |
| | | 25 | | { |
| | 0 | 26 | | var result = new AsyncResult<T>(valueTask, callback, state); |
| | 0 | 27 | | if (result.CompletedSynchronously) |
| | | 28 | | { |
| | 0 | 29 | | result.ExecuteCallback(); |
| | | 30 | | } |
| | 0 | 31 | | else if (callback != null) |
| | | 32 | | { |
| | | 33 | | // We use OnCompleted rather than ContinueWith in order to avoid running synchronously |
| | | 34 | | // if the task has already completed by the time we get here. |
| | | 35 | | // This will allocate a delegate and some extra data to add it as a TaskContinuation |
| | 0 | 36 | | valueTask.ConfigureAwait(false) |
| | 0 | 37 | | .GetAwaiter() |
| | 0 | 38 | | .OnCompleted(result.ExecuteCallback); |
| | | 39 | | } |
| | | 40 | | |
| | 0 | 41 | | return result; |
| | | 42 | | } |
| | | 43 | | |
| | | 44 | | /// <summary> |
| | | 45 | | /// Helper method to convert from Task async method to "APM" (IAsyncResult with Begin/End calls) |
| | | 46 | | /// </summary> |
| | | 47 | | public static IAsyncResult ToApm(this Task task, AsyncCallback callback, object state) |
| | | 48 | | { |
| | 0 | 49 | | var result = new AsyncResult(task, callback, state); |
| | 0 | 50 | | if (result.CompletedSynchronously) |
| | | 51 | | { |
| | 0 | 52 | | result.ExecuteCallback(); |
| | | 53 | | } |
| | 0 | 54 | | else if (callback != null) |
| | | 55 | | { |
| | | 56 | | // We use OnCompleted rather than ContinueWith in order to avoid running synchronously |
| | | 57 | | // if the task has already completed by the time we get here. |
| | | 58 | | // This will allocate a delegate and some extra data to add it as a TaskContinuation |
| | 0 | 59 | | task.ConfigureAwait(false) |
| | 0 | 60 | | .GetAwaiter() |
| | 0 | 61 | | .OnCompleted(result.ExecuteCallback); |
| | | 62 | | } |
| | | 63 | | |
| | 0 | 64 | | return result; |
| | | 65 | | } |
| | | 66 | | |
| | | 67 | | public static T ToApmEnd<T>(this IAsyncResult asyncResult) |
| | | 68 | | { |
| | 0 | 69 | | if (asyncResult is AsyncResult<T> asyncResultInstance) |
| | | 70 | | { |
| | 0 | 71 | | return asyncResultInstance.GetResult(); |
| | | 72 | | } |
| | | 73 | | else |
| | | 74 | | { |
| | | 75 | | // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( |
| | | 76 | | // new ArgumentException(SRCommon.SFxInvalidCallbackIAsyncResult)); |
| | 0 | 77 | | throw new ArgumentException(nameof(asyncResult)); |
| | | 78 | | } |
| | | 79 | | } |
| | | 80 | | |
| | | 81 | | public static void ToApmEnd(this IAsyncResult asyncResult) |
| | | 82 | | { |
| | 0 | 83 | | if (asyncResult is AsyncResult asyncResultInstance) |
| | | 84 | | { |
| | 0 | 85 | | asyncResultInstance.GetResult(); |
| | | 86 | | } |
| | | 87 | | else |
| | | 88 | | { |
| | | 89 | | // throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( |
| | | 90 | | // new ArgumentException(SRCommon.SFxInvalidCallbackIAsyncResult)); |
| | 0 | 91 | | throw new ArgumentException(nameof(asyncResult)); |
| | | 92 | | } |
| | | 93 | | } |
| | | 94 | | |
| | | 95 | | private class AsyncResult : IAsyncResult |
| | | 96 | | { |
| | | 97 | | private readonly Task _task; |
| | | 98 | | private readonly AsyncCallback _asyncCallback; |
| | | 99 | | |
| | 0 | 100 | | public AsyncResult(Task task, AsyncCallback asyncCallback, object asyncState) |
| | | 101 | | { |
| | 0 | 102 | | _task = task; |
| | 0 | 103 | | _asyncCallback = asyncCallback; |
| | 0 | 104 | | AsyncState = asyncState; |
| | 0 | 105 | | CompletedSynchronously = task.IsCompleted; |
| | 0 | 106 | | } |
| | | 107 | | |
| | 0 | 108 | | public void GetResult() => _task.GetAwaiter().GetResult(); |
| | | 109 | | |
| | 0 | 110 | | public void ExecuteCallback() => _asyncCallback?.Invoke(this); |
| | | 111 | | |
| | 0 | 112 | | public object AsyncState { get; } |
| | 0 | 113 | | WaitHandle IAsyncResult.AsyncWaitHandle => ((IAsyncResult)_task).AsyncWaitHandle; |
| | | 114 | | |
| | 0 | 115 | | public bool CompletedSynchronously { get; } |
| | 0 | 116 | | public bool IsCompleted => _task.IsCompleted; |
| | | 117 | | } |
| | | 118 | | |
| | | 119 | | internal class AsyncResult<T> : IAsyncResult |
| | | 120 | | { |
| | | 121 | | private readonly ValueTask<T> _task; |
| | | 122 | | private readonly AsyncCallback _asyncCallback; |
| | | 123 | | |
| | 0 | 124 | | public AsyncResult(ValueTask<T> task, AsyncCallback asyncCallback, object asyncState) |
| | | 125 | | { |
| | 0 | 126 | | _task = task; |
| | 0 | 127 | | _asyncCallback = asyncCallback; |
| | 0 | 128 | | AsyncState = asyncState; |
| | 0 | 129 | | CompletedSynchronously = task.IsCompleted; |
| | 0 | 130 | | } |
| | | 131 | | |
| | 0 | 132 | | public T GetResult() => _task.GetAwaiter().GetResult(); |
| | | 133 | | |
| | 0 | 134 | | public bool IsFaulted => _task.IsFaulted; |
| | 0 | 135 | | public AggregateException Exception => _task.AsTask().Exception; |
| | | 136 | | |
| | | 137 | | // Calls the async callback with this as parameter |
| | 0 | 138 | | public void ExecuteCallback() => _asyncCallback?.Invoke(this); |
| | 0 | 139 | | public object AsyncState { get; } |
| | | 140 | | |
| | 0 | 141 | | WaitHandle IAsyncResult.AsyncWaitHandle => !CompletedSynchronously |
| | 0 | 142 | | ? ((IAsyncResult)_task.AsTask()).AsyncWaitHandle |
| | 0 | 143 | | : throw new NotImplementedException(); |
| | | 144 | | |
| | 0 | 145 | | public bool CompletedSynchronously { get; } |
| | 0 | 146 | | public bool IsCompleted => _task.IsCompleted; |
| | | 147 | | } |
| | | 148 | | } |
| | | 149 | | } |