|
6 | 6 |
|
7 | 7 | #if !NETCOREAPP3_1_OR_GREATER
|
8 | 8 | using System;
|
| 9 | +using System.Runtime.ExceptionServices; |
9 | 10 | using System.Threading.Tasks;
|
10 | 11 | using Datadog.Trace.DuckTyping;
|
11 | 12 | using Datadog.Trace.Vendors.Serilog.Events;
|
@@ -89,43 +90,59 @@ public SyncCallbackHandler(ContinuationMethodDelegate continuation, bool preserv
|
89 | 90 | return ValueTaskActivator<TReturn, TResult?>.CreateInstance(secondTask);
|
90 | 91 | }
|
91 | 92 |
|
92 |
| - private async Task<TResult?> ContinuationAction(Task<TResult?> previousValueTask, TTarget? target, CallTargetState state) |
| 93 | + private async Task<TResult?> ContinuationAction(Task<TResult?> previousTask, TTarget? target, CallTargetState state) |
93 | 94 | {
|
94 |
| - TResult? result = default; |
95 |
| - try |
| 95 | + if (!previousTask.IsCompleted) |
96 | 96 | {
|
97 |
| - result = await previousValueTask.ConfigureAwait(_preserveContext); |
| 97 | + await new NoThrowAwaiter(previousTask, _preserveContext); |
98 | 98 | }
|
99 |
| - catch (Exception ex) |
| 99 | + |
| 100 | + TResult? taskResult = default; |
| 101 | + Exception? exception = null; |
| 102 | + TResult? continuationResult = default; |
| 103 | + |
| 104 | + if (previousTask.Status == TaskStatus.RanToCompletion) |
| 105 | + { |
| 106 | + taskResult = previousTask.Result; |
| 107 | + } |
| 108 | + else if (previousTask.Status == TaskStatus.Faulted) |
| 109 | + { |
| 110 | + exception = previousTask.Exception?.GetBaseException(); |
| 111 | + } |
| 112 | + else if (previousTask.Status == TaskStatus.Canceled) |
100 | 113 | {
|
101 | 114 | try
|
102 | 115 | {
|
103 |
| - // * |
104 |
| - // Calls the CallTarget integration continuation, exceptions here should never bubble up to the application |
105 |
| - // * |
106 |
| - _continuation(target, result, ex, in state); |
| 116 | + // The only supported way to extract the cancellation exception is to await the task |
| 117 | + await previousTask.ConfigureAwait(_preserveContext); |
107 | 118 | }
|
108 |
| - catch (Exception contEx) |
| 119 | + catch (Exception ex) |
109 | 120 | {
|
110 |
| - IntegrationOptions<TIntegration, TTarget>.LogException(contEx); |
| 121 | + exception = ex; |
111 | 122 | }
|
112 |
| - |
113 |
| - throw; |
114 | 123 | }
|
115 | 124 |
|
116 | 125 | try
|
117 | 126 | {
|
118 | 127 | // *
|
119 | 128 | // Calls the CallTarget integration continuation, exceptions here should never bubble up to the application
|
120 | 129 | // *
|
121 |
| - return _continuation(target, result, null, in state); |
| 130 | + continuationResult = _continuation(target, taskResult, exception, in state); |
122 | 131 | }
|
123 |
| - catch (Exception contEx) |
| 132 | + catch (Exception ex) |
124 | 133 | {
|
125 |
| - IntegrationOptions<TIntegration, TTarget>.LogException(contEx); |
| 134 | + IntegrationOptions<TIntegration, TTarget>.LogException(ex); |
126 | 135 | }
|
127 | 136 |
|
128 |
| - return result; |
| 137 | + // * |
| 138 | + // If the original task throws an exception we rethrow it here. |
| 139 | + // * |
| 140 | + if (exception != null) |
| 141 | + { |
| 142 | + ExceptionDispatchInfo.Capture(exception).Throw(); |
| 143 | + } |
| 144 | + |
| 145 | + return continuationResult; |
129 | 146 | }
|
130 | 147 | }
|
131 | 148 |
|
@@ -166,48 +183,57 @@ public override TReturn ExecuteCallback(TTarget? instance, TReturn? returnValue,
|
166 | 183 | return ValueTaskActivator<TReturn, TResult?>.CreateInstance(secondTask);
|
167 | 184 | }
|
168 | 185 |
|
169 |
| - private async Task<TResult?> ContinuationAction(Task<TResult?> previousValueTask, TTarget? target, CallTargetState state, Exception? exception) |
| 186 | + private async Task<TResult?> ContinuationAction(Task<TResult?> previousTask, TTarget? target, CallTargetState state, Exception? exception) |
170 | 187 | {
|
171 |
| - if (exception != null) |
| 188 | + TResult? taskResult = default; |
| 189 | + if (!previousTask.IsCompleted) |
172 | 190 | {
|
173 |
| - return await _asyncContinuation(target, default, exception, in state).ConfigureAwait(_preserveContext); |
| 191 | + await new NoThrowAwaiter(previousTask, _preserveContext); |
174 | 192 | }
|
175 | 193 |
|
176 |
| - TResult? result = default; |
177 |
| - try |
| 194 | + if (previousTask.Status == TaskStatus.RanToCompletion) |
178 | 195 | {
|
179 |
| - result = await previousValueTask.ConfigureAwait(_preserveContext); |
| 196 | + taskResult = previousTask.Result; |
180 | 197 | }
|
181 |
| - catch (Exception ex) |
| 198 | + else if (previousTask.Status == TaskStatus.Faulted) |
| 199 | + { |
| 200 | + exception ??= previousTask.Exception?.GetBaseException(); |
| 201 | + } |
| 202 | + else if (previousTask.Status == TaskStatus.Canceled) |
182 | 203 | {
|
183 | 204 | try
|
184 | 205 | {
|
185 |
| - // * |
186 |
| - // Calls the CallTarget integration continuation, exceptions here should never bubble up to the application |
187 |
| - // * |
188 |
| - await _asyncContinuation(target, result, ex, in state).ConfigureAwait(_preserveContext); |
| 206 | + // The only supported way to extract the cancellation exception is to await the task |
| 207 | + await previousTask.ConfigureAwait(_preserveContext); |
189 | 208 | }
|
190 |
| - catch (Exception contEx) |
| 209 | + catch (Exception ex) |
191 | 210 | {
|
192 |
| - IntegrationOptions<TIntegration, TTarget>.LogException(contEx); |
| 211 | + exception ??= ex; |
193 | 212 | }
|
194 |
| - |
195 |
| - throw; |
196 | 213 | }
|
197 | 214 |
|
| 215 | + TResult? continuationResult = default; |
198 | 216 | try
|
199 | 217 | {
|
200 | 218 | // *
|
201 | 219 | // Calls the CallTarget integration continuation, exceptions here should never bubble up to the application
|
202 | 220 | // *
|
203 |
| - return await _asyncContinuation(target, result, null, in state).ConfigureAwait(_preserveContext); |
| 221 | + continuationResult = await _asyncContinuation(target, taskResult, exception, in state).ConfigureAwait(_preserveContext); |
| 222 | + } |
| 223 | + catch (Exception ex) |
| 224 | + { |
| 225 | + IntegrationOptions<TIntegration, TTarget>.LogException(ex); |
204 | 226 | }
|
205 |
| - catch (Exception contEx) |
| 227 | + |
| 228 | + // * |
| 229 | + // If the original task throws an exception we rethrow it here. |
| 230 | + // * |
| 231 | + if (exception != null) |
206 | 232 | {
|
207 |
| - IntegrationOptions<TIntegration, TTarget>.LogException(contEx); |
| 233 | + ExceptionDispatchInfo.Capture(exception).Throw(); |
208 | 234 | }
|
209 | 235 |
|
210 |
| - return result; |
| 236 | + return continuationResult; |
211 | 237 | }
|
212 | 238 | }
|
213 | 239 |
|
|
0 commit comments