1+ // Licensed to the .NET Foundation under one or more agreements.
2+ // The .NET Foundation licenses this file to you under the MIT license.
3+
4+ using System . Runtime . ConstrainedExecution ;
5+ using System . Runtime . InteropServices ;
6+
7+ namespace System
8+ {
9+ /// <summary>
10+ /// Schedules a callback roughly every gen 2 GC (you may see a Gen 0 an Gen 1 but only once).
11+ /// Ported from https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Gen2GcCallback.cs.
12+ /// </summary>
13+ internal sealed class Gen2GcCallback : CriticalFinalizerObject
14+ {
15+ /// <summary>
16+ /// The callback to invoke at each GC.
17+ /// </summary>
18+ private readonly Action < object > callback ;
19+
20+ /// <summary>
21+ /// A weak <see cref="GCHandle"/> to the target object to pass to <see cref="callback"/>.
22+ /// </summary>
23+ private GCHandle handle ;
24+
25+ /// <summary>
26+ /// Initializes a new instance of the <see cref="Gen2GcCallback"/> class.
27+ /// </summary>
28+ /// <param name="callback">The callback to invoke at each GC.</param>
29+ /// <param name="target">The target object to pass as argument to <paramref name="callback"/>.</param>
30+ private Gen2GcCallback ( Action < object > callback , object target )
31+ {
32+ this . callback = callback ;
33+ this . handle = GCHandle . Alloc ( target , GCHandleType . Weak ) ;
34+ }
35+
36+ /// <summary>
37+ /// Schedules a callback to be called on each GC until the target is collected.
38+ /// </summary>
39+ /// <param name="callback">The callback to invoke at each GC.</param>
40+ /// <param name="target">The target object to pass as argument to <paramref name="callback"/>.</param>
41+ public static void Register ( Action < object > callback , object target )
42+ {
43+ _ = new Gen2GcCallback ( callback , target ) ;
44+ }
45+
46+ /// <summary>
47+ /// Finalizes an instance of the <see cref="Gen2GcCallback"/> class.
48+ /// This finalizer is re-registered with <see cref="GC.ReRegisterForFinalize(object)"/> as long as
49+ /// the target object is alive, which means it will be executed again every time a generation 2
50+ /// collection is triggered (as the <see cref="Gen2GcCallback"/> instance itself would be moved to
51+ /// that generation after surviving the generation 0 and 1 collections the first time).
52+ /// </summary>
53+ ~ Gen2GcCallback ( )
54+ {
55+ if ( this . handle . Target is object target )
56+ {
57+ try
58+ {
59+ this . callback ( target ) ;
60+ }
61+ catch
62+ {
63+ }
64+
65+ GC . ReRegisterForFinalize ( this ) ;
66+ }
67+ else
68+ {
69+ handle . Free ( ) ;
70+ }
71+ }
72+ }
73+ }
0 commit comments