@@ -9,6 +9,14 @@ open System
99open FSharp.Core .Unittests .LibraryTestFx
1010open NUnit.Framework
1111
12+ module LeakUtils =
13+ // when testing for liveness, the things that we want to observe must always be created in
14+ // a nested function call to avoid the GC (possibly) treating them as roots past the last use in the block.
15+ // We also need something non trivial to disuade the compiler from inlining in Release builds.
16+ type ToRun < 'a >( f : unit -> 'a ) =
17+ member this.Invoke () = f()
18+
19+ let run ( toRun : ToRun < 'a >) = toRun.Invoke()
1220
1321// ---------------------------------------------------
1422
@@ -191,6 +199,41 @@ type AsyncModule() =
191199
192200for _ i= 1 to 50 do test()
193201
202+
203+ [<Test>]
204+ member this. ``Async.AwaitWaitHandle does not leak memory`` () =
205+ // This test checks that AwaitWaitHandle does not leak continuations (described in #131),
206+ // We only test the worst case - when the AwaitWaitHandle is already set.
207+ use manualResetEvent= new System.Threading.ManualResetEvent( true )
208+
209+ let tryToLeak () =
210+ let resource =
211+ LeakUtils.ToRun( fun () ->
212+ let resource = obj()
213+ let work =
214+ async {
215+ let! _ = Async.AwaitWaitHandle manualResetEvent
216+ GC.KeepAlive( resource)
217+ return ()
218+ }
219+
220+ work|> Async.RunSynchronously|> ignore
221+ WeakReference( resource))
222+ |> LeakUtils.run
223+
224+ Assert.IsTrue( resource.IsAlive)
225+
226+ GC.Collect()
227+ GC.WaitForPendingFinalizers()
228+ GC.Collect()
229+ GC.WaitForPendingFinalizers()
230+ GC.Collect()
231+
232+ Assert.IsFalse( resource.IsAlive)
233+
234+ // The leak hangs on a race condition which is really hard to trigger in F# 3.0, hence the 100000 runs...
235+ for _ in 1 .. 100000 do tryToLeak()
236+
194237[<Test>]
195238member this. ``AwaitWaitHandle.DisposedWaitHandle2`` () =
196239let wh = new System.Threading.ManualResetEvent( false )