@@ -336,26 +336,32 @@ public static bool TryGetSignal(string message, out HostSignal signal)
336
336
// https://github.com/dotnet/runtime/issues/101536#issuecomment-2077647417
337
337
private readonly struct FinalizerBlocker : IDisposable
338
338
{
339
- private readonly ManualResetEventSlim hangEvent ;
339
+ private readonly object hangLock ;
340
340
341
- private FinalizerBlocker ( ManualResetEventSlim hangEvent ) => this . hangEvent = hangEvent ;
341
+ private FinalizerBlocker ( object hangLock ) => this . hangLock = hangLock ;
342
342
343
343
private sealed class Impl
344
344
{
345
- private readonly ManualResetEventSlim hangEvent = new ( false ) ;
345
+ // ManualResetEvent(Slim) allocates when it is waited and yields the thread,
346
+ // so we use Monitor.Wait instead which does not allocate managed memory.
347
+ // This behavior is not documented, but was observed with the VS Profiler.
348
+ private readonly object hangLock = new ( ) ;
346
349
private readonly ManualResetEventSlim enteredFinalizerEvent = new ( false ) ;
347
350
348
351
~ Impl ( )
349
352
{
350
- enteredFinalizerEvent . Set ( ) ;
351
- hangEvent . Wait ( ) ;
353
+ lock ( hangLock )
354
+ {
355
+ enteredFinalizerEvent . Set ( ) ;
356
+ Monitor . Wait ( hangLock ) ;
357
+ }
352
358
}
353
359
354
360
[ MethodImpl ( MethodImplOptions . NoInlining ) ]
355
- internal static ( ManualResetEventSlim hangEvent , ManualResetEventSlim enteredFinalizerEvent ) CreateWeakly ( )
361
+ internal static ( object hangLock , ManualResetEventSlim enteredFinalizerEvent ) CreateWeakly ( )
356
362
{
357
363
var impl = new Impl ( ) ;
358
- return ( impl . hangEvent , impl . enteredFinalizerEvent ) ;
364
+ return ( impl . hangLock , impl . enteredFinalizerEvent ) ;
359
365
}
360
366
}
361
367
@@ -365,17 +371,26 @@ internal static FinalizerBlocker MaybeStart()
365
371
{
366
372
return default ;
367
373
}
368
- var ( hangEvent , enteredFinalizerEvent ) = Impl . CreateWeakly ( ) ;
374
+ var ( hangLock , enteredFinalizerEvent ) = Impl . CreateWeakly ( ) ;
369
375
do
370
376
{
371
377
GC . Collect ( ) ;
372
378
// Do NOT call GC.WaitForPendingFinalizers.
373
379
}
374
380
while ( ! enteredFinalizerEvent . IsSet ) ;
375
- return new FinalizerBlocker ( hangEvent ) ;
381
+ return new FinalizerBlocker ( hangLock ) ;
376
382
}
377
383
378
- public void Dispose ( ) => hangEvent ? . Set ( ) ;
384
+ public void Dispose ( )
385
+ {
386
+ if ( hangLock is not null )
387
+ {
388
+ lock ( hangLock )
389
+ {
390
+ Monitor . Pulse ( hangLock ) ;
391
+ }
392
+ }
393
+ }
379
394
}
380
395
}
381
396
}
0 commit comments