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