From acafb503488caf2f962469fec3b8093313b50e43 Mon Sep 17 00:00:00 2001 From: Fraser Waters Date: Thu, 28 Nov 2019 08:43:19 +0000 Subject: [PATCH] Fix LinkedSubSource leak in Async.Choice (#7892) * Fix LinkedSubSource leak in Async.Choice * ref -> mutable --- src/fsharp/FSharp.Core/async.fs | 57 ++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/src/fsharp/FSharp.Core/async.fs b/src/fsharp/FSharp.Core/async.fs index 1d4eb37ec..ece4bbe44 100644 --- a/src/fsharp/FSharp.Core/async.fs +++ b/src/fsharp/FSharp.Core/async.fs @@ -1293,35 +1293,54 @@ namespace Microsoft.FSharp.Control | Choice1Of2 computations -> ProtectedCode ctxt (fun ctxt -> let ctxtWithSync = DelimitSyncContext ctxt - let noneCount = ref 0 - let exnCount = ref 0 + let mutable count = computations.Length + let mutable noneCount = 0 + let mutable someOrExnCount = 0 let innerCts = new LinkedSubSource(ctxtWithSync.token) let scont (result: 'T option) = - match result with - | Some _ -> - if Interlocked.Increment exnCount = 1 then - innerCts.Cancel(); ctxtWithSync.trampolineHolder.ExecuteWithTrampoline (fun () -> ctxtWithSync.cont result) + let result = + match result with + | Some _ -> + if Interlocked.Increment &someOrExnCount = 1 then + innerCts.Cancel(); ctxtWithSync.trampolineHolder.ExecuteWithTrampoline (fun () -> ctxtWithSync.cont result) + else + fake() + + | None -> + if Interlocked.Increment &noneCount = computations.Length then + innerCts.Cancel(); ctxtWithSync.trampolineHolder.ExecuteWithTrampoline (fun () -> ctxtWithSync.cont None) + else + fake() + + if Interlocked.Decrement &count = 0 then + innerCts.Dispose() + + result + + let econt (exn: ExceptionDispatchInfo) = + let result = + if Interlocked.Increment &someOrExnCount = 1 then + innerCts.Cancel(); ctxtWithSync.trampolineHolder.ExecuteWithTrampoline (fun () -> ctxtWithSync.econt exn) else fake() - | None -> - if Interlocked.Increment noneCount = computations.Length then - innerCts.Cancel(); ctxtWithSync.trampolineHolder.ExecuteWithTrampoline (fun () -> ctxtWithSync.cont None) + if Interlocked.Decrement &count = 0 then + innerCts.Dispose() + + result + + let ccont (exn: OperationCanceledException) = + let result = + if Interlocked.Increment &someOrExnCount = 1 then + innerCts.Cancel(); ctxtWithSync.trampolineHolder.ExecuteWithTrampoline (fun () -> ctxtWithSync.ccont exn) else fake() - let econt (exn: ExceptionDispatchInfo) = - if Interlocked.Increment exnCount = 1 then - innerCts.Cancel(); ctxtWithSync.trampolineHolder.ExecuteWithTrampoline (fun () -> ctxtWithSync.econt exn) - else - fake() + if Interlocked.Decrement &count = 0 then + innerCts.Dispose() - let ccont (exn: OperationCanceledException) = - if Interlocked.Increment exnCount = 1 then - innerCts.Cancel(); ctxtWithSync.trampolineHolder.ExecuteWithTrampoline (fun () -> ctxtWithSync.ccont exn) - else - fake() + result for c in computations do QueueAsync innerCts.Token scont econt ccont c |> unfake -- GitLab