-
Notifications
You must be signed in to change notification settings - Fork 851
Open
Labels
Area-Compiler-StateMachinesSequence, list, task and other state machine compilationSequence, list, task and other state machine compilationBreaking-changeDescribes a bug which is also a breaking change.Describes a bug which is also a breaking change.BugImpact-Medium(Internal MS Team use only) Describes an issue with moderate impact on existing code.(Internal MS Team use only) Describes an issue with moderate impact on existing code.
Milestone
Description
Currently implementation of TryFinallyAsync is defined as:
member inline internal this.TryFinallyAsync
(
body: TaskCode<'TOverall, 'T>,
compensation: unit -> ValueTask
) : TaskCode<'TOverall, 'T> =
ResumableCode.TryFinallyAsync(
body,
ResumableCode<_, _>(fun sm ->
if __useResumableCode then
let mutable __stack_condition_fin = true
let __stack_vtask = compensation ()
if not __stack_vtask.IsCompleted then
let mutable awaiter = __stack_vtask.GetAwaiter()
let __stack_yield_fin = ResumableCode.Yield().Invoke(&sm)
__stack_condition_fin <- __stack_yield_fin
if not __stack_condition_fin then
sm.Data.MethodBuilder.AwaitUnsafeOnCompleted(&awaiter, &sm)
__stack_condition_fin
else
let vtask = compensation ()
let mutable awaiter = vtask.GetAwaiter()
let cont =
TaskResumptionFunc<'TOverall>(fun sm ->
awaiter.GetResult() |> ignore
true)
// shortcut to continue immediately
if awaiter.IsCompleted then
true
else
sm.ResumptionDynamicInfo.ResumptionData <- (awaiter :> ICriticalNotifyCompletion)
sm.ResumptionDynamicInfo.ResumptionFunc <- cont
false)
)Note that it never calls awaiter.GetResult() meaning that if ValueTask was compelted synchronously it will throw but if not then it will just ignore result of the async computation.
Proposed fix is (matches .Bind implementation for example):
member inline internal this.TryFinallyAsync
(
body: TaskCode<'TOverall, 'T>,
compensation: unit -> ValueTask
) : TaskCode<'TOverall, 'T> =
ResumableCode.TryFinallyAsync(
body,
ResumableCode<_, _>(fun sm ->
if __useResumableCode then
let mutable __stack_condition_fin = true
let __stack_vtask = compensation ()
let mutable awaiter = __stack_vtask.GetAwaiter()
if not awaiter.IsCompleted then
let __stack_yield_fin = ResumableCode.Yield().Invoke(&sm)
__stack_condition_fin <- __stack_yield_fin
if __stack_condition_fin then
awaiter.GetResult()
else
sm.Data.MethodBuilder.AwaitUnsafeOnCompleted(&awaiter, &sm)
__stack_condition_fin
else
let vtask = compensation ()
let mutable awaiter = vtask.GetAwaiter()
let cont =
TaskResumptionFunc<'TOverall>(fun sm ->
awaiter.GetResult() |> ignore
true)
// shortcut to continue immediately
if awaiter.IsCompleted then
cont.Invoke(&sm)
else
sm.ResumptionDynamicInfo.ResumptionData <- (awaiter :> ICriticalNotifyCompletion)
sm.ResumptionDynamicInfo.ResumptionFunc <- cont
false)
)Test code is:
[<Fact>]
let``task should propagate exception if thrown in DisposeAsync``() = task {
let disposable = { new IAsyncDisposable with
member _.DisposeAsync() = ValueTask(task {
do! Task.Delay(1)
failwith "boom"
})
}
let! _ = Assert.ThrowsAsync<Exception>(fun () -> task {
use _ = disposable
()
})
()
}Current behavior:
Assert.Throws() Failure
Expected: typeof(System.Exception)
Actual: (No exception was thrown)
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
Area-Compiler-StateMachinesSequence, list, task and other state machine compilationSequence, list, task and other state machine compilationBreaking-changeDescribes a bug which is also a breaking change.Describes a bug which is also a breaking change.BugImpact-Medium(Internal MS Team use only) Describes an issue with moderate impact on existing code.(Internal MS Team use only) Describes an issue with moderate impact on existing code.
Type
Projects
Status
New