Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Disable noncollectible alc finalization #21189

Merged

Conversation

janvorli
Copy link
Member

For non collectible AssemblyLoadContext, the finalizer should never be called and thus the AssemblyLoadContext should not be in the finalizer queue.

The constructor of AssemblyLoadContext was missing a call to
GC.SuppressFinalize for non-collectible AssemblyLoadContext. That means
that the finalizer can be executed and unloading being initialized even
for non-collectible AssemblyLoadContext.
This change fixes that.
The finalizer for the non-collectible AssemblyLoadContext is never
called and so it should not be in the finalization queue.
@janvorli janvorli added this to the 3.0 milestone Nov 26, 2018
@janvorli janvorli self-assigned this Nov 26, 2018
@janvorli janvorli requested a review from jkotas November 26, 2018 12:23
@jkotas
Copy link
Member

jkotas commented Nov 26, 2018

Is this fixing a real bug, or is this just "nice to have" ?

@@ -94,6 +101,7 @@ internal AssemblyLoadContext(bool fRepresentsTPALoadContext, bool isCollectible)
~AssemblyLoadContext()
{
// Only valid for a Collectible ALC. Non-collectible ALCs have the finalizer suppressed.
Debug.Assert(IsCollectible);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that you can get here for completely uninitialized objects, e.g. when the JITing of a constructor for a custom ALC fails: We have allocated the object by the time we are JITing the constructor already, but none of the constructor has run.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I didn't know that. I assume then that this could possibly be the real reason behind #21100. Then it seems that I could use e.g. the unloadLock as a guard (if it is null, the constructor was not executed and thus we should skip the whole finalizer code)

@janvorli
Copy link
Member Author

@jkotas I have discovered it when I was looking into #21100. I have then realized that it should not be causing that issue, but I have realized it would be nice to not to keep non-collectible ALCs on finalizer queue for no reason.

@jkotas
Copy link
Member

jkotas commented Nov 26, 2018

Sounds good. I think this needs to be fixed up to be robust for uninitialized objects (that may fix a real corner case bug actually).

Check if the constructor was executed before we run the finalizer code.
InitiateUnload();
// Use the unloadLock as a guard to detect the corner case when the constructor of the AssemblyLoadContext was not executed
// e.g. due to the JIT failing to JIT it.
if (unloadLock != null)
Copy link
Member

@jkotas jkotas Nov 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pretty subtle since unloadLock is created by the implicit assignment first, and IsCollectible that is assigned after is asserted below. It probably works ok as long as there is no thread abort, but it would be nice to make it more correct by construction.

InitiateUnload();
// Use the unloadLock as a guard to detect the corner case when the constructor of the AssemblyLoadContext was not executed
// e.g. due to the JIT failing to JIT it.
if (unloadLock != null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: While you are on it - could you please change unloadLock to _unloadLock to make it match the coding conventions

@janvorli
Copy link
Member Author

@jkotas I've made the initialization order of the IsCollectible and _unloadLock explicit and also modified all fields to start with underscore.

@janvorli
Copy link
Member Author

@dotnet-bot test OSX10.12 x64 Checked Innerloop Build and Test please

@jkotas
Copy link
Member

jkotas commented Nov 27, 2018

HardwareIntrinsics on OSX is known flaky test

@jkotas jkotas merged commit ac732ff into dotnet:master Nov 27, 2018
picenka21 pushed a commit to picenka21/runtime that referenced this pull request Feb 18, 2022
* Disable noncollectible AssemblyLoadContext finalization

The constructor of AssemblyLoadContext was missing a call to
GC.SuppressFinalize for non-collectible AssemblyLoadContext. That means
that the finalizer can be executed and unloading being initialized even
for non-collectible AssemblyLoadContext.
This change fixes that.

* SuppressFinalize non-collectible AssemblyLoadContext

The finalizer for the non-collectible AssemblyLoadContext is never
called and so it should not be in the finalization queue.

* Handle finalizer call without constructor executed

Check if the constructor was executed before we run the finalizer code.



Commit migrated from dotnet/coreclr@ac732ff
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants