Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit6ebbd2c

Browse files
forkilatkin
authored andcommitted
Fix memory leak in Async.AwaitHandle
fixesdotnet#131closesdotnet#136commit8341ea5Author: Steffen Forkmann <steffen.forkmann@msu-solutions.de>Date: Tue Jan 27 08:43:48 2015 +0100 Fix memory leak in Async.AwaitHandle -closesdotnet#131commit93594b2Author: Steffen Forkmann <steffen.forkmann@msu-solutions.de>Date: Tue Jan 27 09:15:59 2015 +0100 Add a test for the memory leak in Async.AwaitHandle - relates todotnet#131
1 parent52a7252 commit6ebbd2c

File tree

2 files changed

+65
-17
lines changed

2 files changed

+65
-17
lines changed

‎src/fsharp/FSharp.Core.Unittests/FSharp.Core/Microsoft.FSharp.Control/AsyncModule.fs‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@ open System
99
openFSharp.Core.Unittests.LibraryTestFx
1010
openNUnit.Framework
1111

12+
moduleLeakUtils=
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+
typeToRun<'a>(f: unit->'a)=
17+
memberthis.Invoke()= f()
18+
19+
letrun(toRun:ToRun<'a>)= toRun.Invoke()
1220

1321
// ---------------------------------------------------
1422

@@ -191,6 +199,41 @@ type AsyncModule() =
191199

192200
for_i=1to50do test()
193201

202+
203+
[<Test>]
204+
memberthis.``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+
lettryToLeak()=
210+
letresource=
211+
LeakUtils.ToRun(fun()->
212+
letresource= obj()
213+
letwork=
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_in1..100do tryToLeak()
236+
194237
[<Test>]
195238
memberthis.``AwaitWaitHandle.DisposedWaitHandle2``()=
196239
letwh=new System.Threading.ManualResetEvent(false)

‎src/fsharp/FSharp.Core/control.fs‎

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1604,29 +1604,34 @@ namespace Microsoft.FSharp.Control
16041604
Action<obj>(fun _->
16051605
if latch.Enter()then
16061606
// if we got here - then we need to unregister RegisteredWaitHandle + trigger cancellation
1607-
// entrance to TP callback is protected by latch - so savedCont will never be called
1608-
match!rwhwith
1609-
| None->()
1610-
| Some rwh-> rwh.Unregister(null)|> ignore
1607+
// entrance to TP callback is protected by latch - so savedCont will never be called
1608+
lock rwh(fun()->
1609+
match!rwhwith
1610+
| None->()
1611+
| Some rwh-> rwh.Unregister(null)|> ignore)
16111612
Async.Start(async{do(aux.ccont(OperationCanceledException())|> unfake)}))
16121613

1613-
and registration: CancellationTokenRegistration= aux.token.Register(cancelHandler,null)
1614+
and registration: CancellationTokenRegistration= aux.token.Register(cancelHandler,null)
16141615

16151616
letsavedCont= args.cont
16161617
try
1617-
rwh:= Some(ThreadPool.RegisterWaitForSingleObject
1618-
(waitObject=waitHandle,
1619-
callBack=WaitOrTimerCallback(fun _ timeOut->
1620-
if latch.Enter()then
1621-
rwh:= None
1622-
registration.Dispose()
1623-
aux.trampolineHolder.Protect(fun()-> savedCont(not timeOut))|> unfake),
1624-
state=null,
1625-
millisecondsTimeOutInterval=millisecondsTimeout,
1626-
executeOnlyOnce=true));
1627-
FakeUnit
1618+
lock rwh(fun()->
1619+
rwh:= Some(ThreadPool.RegisterWaitForSingleObject
1620+
(waitObject=waitHandle,
1621+
callBack=WaitOrTimerCallback(fun _ timeOut->
1622+
if latch.Enter()then
1623+
lock rwh(fun()-> rwh.Value.Value.Unregister(null)|> ignore)
1624+
rwh:= None
1625+
registration.Dispose()
1626+
aux.trampolineHolder.Protect(fun()-> savedCont(not timeOut))|> unfake),
1627+
state=null,
1628+
millisecondsTimeOutInterval=millisecondsTimeout,
1629+
executeOnlyOnce=true));
1630+
FakeUnit)
16281631
with_->
1629-
if latch.Enter()then reraise()// reraise exception only if we successfully enter the latch (no other continuations were called)
1632+
if latch.Enter()then
1633+
registration.Dispose()
1634+
reraise()// reraise exception only if we successfully enter the latch (no other continuations were called)
16301635
else FakeUnit
16311636
)
16321637
#endif

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp