Skip to content

Commit 0cb6a6a

Browse files
committed
Added SuspendException overload that returns Result<T> (#258)
1 parent 1678252 commit 0cb6a6a

File tree

3 files changed

+89
-1
lines changed

3 files changed

+89
-1
lines changed

src/DotNext.Tests/Threading/Tasks/ConversionTests.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ public static async Task SuspendException()
4646
{
4747
await Task.FromException(new Exception()).SuspendException();
4848
await ValueTask.FromException(new Exception()).SuspendException();
49+
50+
var result = await Task.FromException<int>(new ArithmeticException()).SuspendException();
51+
False(result.IsSuccessful);
52+
IsType<ArithmeticException>(result.Error);
4953
}
5054

5155
[Fact]

src/DotNext/Runtime/CompilerServices/SuspendedExceptionTaskAwaitable.cs

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ public void GetResult()
9797
}
9898

9999
/// <summary>
100-
/// Represents awaitable object that can suspend exception raised by the underlying task.
100+
/// Represents awaitable object that can suspend the exception raised by the underlying task.
101101
/// </summary>
102102
/// <typeparam name="TArg">The type of the argument to be passed to the exception filter.</typeparam>
103103
[StructLayout(LayoutKind.Auto)]
@@ -187,4 +187,80 @@ public void GetResult()
187187
}
188188
}
189189
}
190+
}
191+
192+
/// <summary>
193+
/// Represents awaitable object that can suspend the exception raised by the underlying task.
194+
/// </summary>
195+
/// <typeparam name="T">The type of the task.</typeparam>
196+
[StructLayout(LayoutKind.Auto)]
197+
public readonly struct AwaitableResult<T>
198+
{
199+
private readonly Task<T> task;
200+
201+
internal AwaitableResult(Task<T> task) => this.task = task;
202+
203+
internal bool ContinueOnCapturedContext
204+
{
205+
get;
206+
init;
207+
}
208+
209+
/// <summary>
210+
/// Configures an awaiter for this value.
211+
/// </summary>
212+
/// <param name="continueOnCapturedContext">
213+
/// <see langword="true"/> to attempt to marshal the continuation back to the captured context;
214+
/// otherwise, <see langword="false"/>.
215+
/// </param>
216+
/// <returns>The configured object.</returns>
217+
public AwaitableResult<T> ConfigureAwait(bool continueOnCapturedContext)
218+
=> this with { ContinueOnCapturedContext = continueOnCapturedContext };
219+
220+
/// <summary>
221+
/// Gets the awaiter for this object.
222+
/// </summary>
223+
/// <returns>The awaiter for this object.</returns>
224+
public Awaiter GetAwaiter() => new(task, ContinueOnCapturedContext);
225+
226+
/// <summary>
227+
/// Represents the awaiter that suspends exception.
228+
/// </summary>
229+
[StructLayout(LayoutKind.Auto)]
230+
public readonly struct Awaiter : ICriticalNotifyCompletion
231+
{
232+
private readonly ConfiguredTaskAwaitable<T>.ConfiguredTaskAwaiter awaiter;
233+
234+
internal Awaiter(Task<T> task, bool continueOnCapturedContext)
235+
=> awaiter = task.ConfigureAwait(continueOnCapturedContext).GetAwaiter();
236+
237+
/// <summary>
238+
/// Gets a value indicating that <see cref="AwaitableResult{T}"/> has completed.
239+
/// </summary>
240+
public bool IsCompleted => awaiter.IsCompleted;
241+
242+
/// <inheritdoc/>
243+
public void OnCompleted(Action action) => awaiter.OnCompleted(action);
244+
245+
/// <inheritdoc/>
246+
public void UnsafeOnCompleted(Action action) => awaiter.UnsafeOnCompleted(action);
247+
248+
/// <summary>
249+
/// Obtains a result of asynchronous operation, and suspends exception if needed.
250+
/// </summary>
251+
public Result<T> GetResult()
252+
{
253+
Result<T> result;
254+
try
255+
{
256+
result = new(awaiter.GetResult());
257+
}
258+
catch (Exception e)
259+
{
260+
result = new(e);
261+
}
262+
263+
return result;
264+
}
265+
}
190266
}

src/DotNext/Threading/Tasks/Conversion.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,14 @@ public static async Task<TOutput> Convert<TInput, TOutput>(this Task<TInput> tas
6767
[RequiresUnreferencedCode("Runtime binding may be incompatible with IL trimming")]
6868
public static DynamicTaskAwaitable AsDynamic(this Task task) => new(task);
6969

70+
/// <summary>
71+
/// Returns a task that never throws an exception.
72+
/// </summary>
73+
/// <param name="task">The task to convert.</param>
74+
/// <typeparam name="T">The type of the task.</typeparam>
75+
/// <returns>The task that never throws an exception. Instead, the <see cref="Result{T}"/> contains an exception.</returns>
76+
public static AwaitableResult<T> SuspendException<T>(this Task<T> task) => new(task);
77+
7078
/// <summary>
7179
/// Suspends the exception that can be raised by the task.
7280
/// </summary>

0 commit comments

Comments
 (0)