From 3fa0deef67bd1e246678efcb9fd373416333d173 Mon Sep 17 00:00:00 2001 From: Fraser Waters Date: Thu, 21 Nov 2019 18:02:57 +0000 Subject: [PATCH 1/2] Fix LinkedSubSource leak in Async.Choice --- src/fsharp/FSharp.Core/async.fs | 55 ++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/src/fsharp/FSharp.Core/async.fs b/src/fsharp/FSharp.Core/async.fs index 1d4eb37ecc3..d2c6c9cec6e 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 count = ref computations.Length let noneCount = ref 0 - let exnCount = ref 0 + let someOrExnCount = ref 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 From b59d0530f3eb56260d29fc153b8d82f20f45dbf4 Mon Sep 17 00:00:00 2001 From: Fraser Waters Date: Sat, 23 Nov 2019 21:04:27 +0000 Subject: [PATCH 2/2] ref -> mutable --- src/fsharp/FSharp.Core/async.fs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/fsharp/FSharp.Core/async.fs b/src/fsharp/FSharp.Core/async.fs index d2c6c9cec6e..ece4bbe44e0 100644 --- a/src/fsharp/FSharp.Core/async.fs +++ b/src/fsharp/FSharp.Core/async.fs @@ -1293,51 +1293,51 @@ namespace Microsoft.FSharp.Control | Choice1Of2 computations -> ProtectedCode ctxt (fun ctxt -> let ctxtWithSync = DelimitSyncContext ctxt - let count = ref computations.Length - let noneCount = ref 0 - let someOrExnCount = 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) = let result = match result with | Some _ -> - if Interlocked.Increment someOrExnCount = 1 then + 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 + if Interlocked.Increment &noneCount = computations.Length then innerCts.Cancel(); ctxtWithSync.trampolineHolder.ExecuteWithTrampoline (fun () -> ctxtWithSync.cont None) else fake() - if Interlocked.Decrement count = 0 then + if Interlocked.Decrement &count = 0 then innerCts.Dispose() result let econt (exn: ExceptionDispatchInfo) = let result = - if Interlocked.Increment someOrExnCount = 1 then + if Interlocked.Increment &someOrExnCount = 1 then innerCts.Cancel(); ctxtWithSync.trampolineHolder.ExecuteWithTrampoline (fun () -> ctxtWithSync.econt exn) else fake() - if Interlocked.Decrement count = 0 then + if Interlocked.Decrement &count = 0 then innerCts.Dispose() result let ccont (exn: OperationCanceledException) = let result = - if Interlocked.Increment someOrExnCount = 1 then + if Interlocked.Increment &someOrExnCount = 1 then innerCts.Cancel(); ctxtWithSync.trampolineHolder.ExecuteWithTrampoline (fun () -> ctxtWithSync.ccont exn) else fake() - if Interlocked.Decrement count = 0 then + if Interlocked.Decrement &count = 0 then innerCts.Dispose() result