Skip to content

Commit

Permalink
[Mono] Implement eager finalization of WeakReference (#76173)
Browse files Browse the repository at this point in the history
* [Mono] Implement eager finalization of WeakReference

* Make the finalization really eager

* Move the eager finalization a bit earlier - to sgen_finalize_in_range  (PR suggestion)

Co-authored-by: vsadov <[email protected]>
  • Loading branch information
filipnavara and VSadov authored Sep 29, 2022
1 parent f518c05 commit 4287453
Show file tree
Hide file tree
Showing 8 changed files with 40 additions and 20 deletions.
17 changes: 0 additions & 17 deletions src/libraries/System.Private.CoreLib/src/System/WeakReference.T.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,6 @@ private T? Target
}
}

// eager finalization is NYI on Mono
#if !MONO
// Note: While WeakReference<T> is formally a finalizable type, the finalizer does not actually run.
// Instead the instances are treated specially in GC when scanning for no longer strongly-reachable
// finalizable objects.
Expand All @@ -142,21 +140,6 @@ private T? Target
}
#pragma warning restore CA1821 // Remove empty Finalizers

#else
// Free all system resources associated with this reference.
~WeakReference()
{
IntPtr handle = Handle;
if (handle != default(IntPtr))
{
GCHandle.InternalFree(handle);

// keep the bit that indicates whether this reference was tracking resurrection
_handleAndKind &= TracksResurrectionBit;
}
}
#endif

#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,7 @@ public virtual object? Target
// Unlike WeakReference<T> case, the instance could be of a derived type and
// in such case it is finalized via a finalizer.

// eager finalization is NYI on Mono
#if !MONO
Debug.Assert(this.GetType() != typeof(WeakReference));
#endif

IntPtr handle = Handle;
if (handle != default(IntPtr))
Expand Down
2 changes: 2 additions & 0 deletions src/mono/mono/metadata/class-internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -921,6 +921,8 @@ typedef struct {
MonoClass *generic_ienumerator_class;
MonoClass *alc_class;
MonoClass *appcontext_class;
MonoClass *weakreference_class;
MonoClass *generic_weakreference_class;
} MonoDefaults;

/* If you need a MonoType, use one of the mono_get_*_type () functions in class-inlines.h */
Expand Down
5 changes: 5 additions & 0 deletions src/mono/mono/metadata/domain.c
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,11 @@ mono_init_internal (const char *root_domain_name)
mono_defaults.alc_class = mono_class_get_assembly_load_context_class ();
mono_defaults.appcontext_class = mono_class_try_load_from_name (mono_defaults.corlib, "System", "AppContext");

mono_defaults.weakreference_class = mono_class_try_load_from_name (
mono_defaults.corlib, "System", "WeakReference");
mono_defaults.generic_weakreference_class = mono_class_try_load_from_name (
mono_defaults.corlib, "System", "WeakReference`1");

// in the past we got a filename as the root_domain_name so try to get the basename
domain->friendly_name = g_path_get_basename (root_domain_name);

Expand Down
5 changes: 5 additions & 0 deletions src/mono/mono/metadata/object-internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,11 @@ typedef struct {
guint32 intType;
} MonoClassInterfaceAttribute;

typedef struct {
MonoObject object;
gsize handleAndKind;
} MonoWeakReference;

/* Safely access System.Delegate from native code */
TYPED_HANDLE_DECL (MonoDelegate);

Expand Down
15 changes: 15 additions & 0 deletions src/mono/mono/metadata/sgen-mono.c
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,21 @@ is_finalization_aware (MonoObject *obj)
return (vt->gc_bits & SGEN_GC_BIT_FINALIZER_AWARE) == SGEN_GC_BIT_FINALIZER_AWARE;
}

gboolean
sgen_client_object_finalize_eagerly (GCObject *obj)
{
if (obj->vtable->klass == mono_defaults.weakreference_class ||
obj->vtable->klass == mono_defaults.generic_weakreference_class) {
MonoWeakReference *wr = (MonoWeakReference*)obj;
MonoGCHandle gc_handle = (MonoGCHandle)(wr->handleAndKind & ~(gsize)1);
mono_gchandle_free_internal (gc_handle);
wr->handleAndKind &= (gsize)1;
return TRUE;
}

return FALSE;
}

void
sgen_client_object_queued_for_finalization (GCObject *obj)
{
Expand Down
6 changes: 6 additions & 0 deletions src/mono/mono/sgen/sgen-client.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ gboolean sgen_client_object_is_array_fill (GCObject *o);
*/
gboolean sgen_client_object_has_critical_finalizer (GCObject *obj);

/*
* Called when object is ready for finalization. Returns whether the object was finalized
* eagerly. Otherwise `sgen_client_object_queued_for_finalization` is called.
*/
gboolean sgen_client_object_finalize_eagerly (GCObject *obj);

/*
* Called after an object is enqueued for finalization. This is a very low-level callback.
* It should almost certainly be a NOP.
Expand Down
7 changes: 7 additions & 0 deletions src/mono/mono/sgen/sgen-fin-weak-hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,13 @@ sgen_finalize_in_range (int generation, ScanCopyContext ctx)
object = tagged_object_get_object (object);
if (!sgen_major_collector.is_object_live (object)) {
gboolean is_fin_ready = sgen_gc_is_object_ready_for_finalization (object);
if (is_fin_ready && sgen_client_object_finalize_eagerly (object)) {
/* just remove an eagerly finalized object */
SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE);

SGEN_LOG (5, "Eagerly finalized object: %p (%s) (was at %p)", object, sgen_client_vtable_get_name (SGEN_LOAD_VTABLE (object)), object);
continue;
}
GCObject *copy = object;
copy_func (&copy, queue);
if (is_fin_ready) {
Expand Down

0 comments on commit 4287453

Please sign in to comment.