-
Notifications
You must be signed in to change notification settings - Fork 5.3k
[NativeAOT] allow creating CCW for resurrected objects #86882
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
This comment was marked as resolved.
This comment was marked as resolved.
3ef9bd5 to
46508ec
Compare
This comment was marked as resolved.
This comment was marked as resolved.
The WrappedObjectHolder will be used to keep the managed object alive when the COM reference count is positive. And the ManagedObjectWrapper object will be responsible for cleaning up the unmanaged MOW when the wrapped object is truly dead. The design is that as long as there is a postive reference count, the WrappedObjectHolder should stay alive. Any time the ref count is increased from the managed side from 0 to 1, the WrappedObjectHolder will be restored if it was collected. It should not be possible for the MOW to observe a null WrappedObjectHolder.
46508ec to
66245fc
Compare
I'm still not 100% sure that _exposed is thread safe.
...nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.NativeAot.cs
Outdated
Show resolved
Hide resolved
|
Cc @dotnet/interop-contrib |
|
Tagging subscribers to this area: @agocke, @MichalStrehovsky, @jkotas Issue DetailsThis PR adds a test that on main passes for CoreCLR but fails for NativeAOT. It also includes fixes to make the test pass for NativeAOT. The key problem is the conditional weak table will keep the The solution this PR implements is to extend the lifetime of the The references between objects looks like this: graph TD
WO[wrapped object];
MOW["ManagedObjectWrapper (unmanaged struct)"];
MOWH[ManagedObjectWrapperHolder];
WOH[WrappedObjectHolder];
WO -->|CWT| MOWH;
MOWH --> MOW;
MOW -->|ref-counted handle| WOH;
WOH --> WO;
WO -->|dependant handle owned by MOWH| WOH;
Some other race conditions are also fixed by this PR:
|
|
I will need to take some time to look at this. Based on a quick look at the test I'm not sure I understand what this is validating. |
On the main branch, the test passes on CoreClr but segfaults on NativeAOT. This is because the |
...nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.NativeAot.cs
Outdated
Show resolved
Hide resolved
This is to avoid allocating a closure if possible.
This code was copy-pasted.
|
I updated how the responsibility is split for keeping objects alive and collecting them. I think this new design is much easier to understand. The description of the PR has been updated. The main problem this PR does not address is the issue of extra ManagedObjectWrappers hanging around for the lifetime of a wrapped object when there is a race condition adding values to the |
This saves a handle. The refcounted handle behaves like a long-weak, so this should not get cleared out until the conditional weak table is also cleared out.
|
I realized that I did not need to use an extra handle to monitor the lifetime of the holder object from the releaser object. Instead I could just use the ref counted handle. That saves allocating an extra GC handle. |
jkoritzinsky
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good to me, but I want @AaronRobinsonMSFT to take another look.
My biggest issue here is the above statement. If an object is "alive", I assume "reachable", then it will not be finalized. The GC only adds objects to the finalizer queue if they are no longer reachable so I am confused by this statement. |
Oh right. The conditional weak table doesn't keep it alive, merely a reference to acquire. Its state is indeterminant in this case and that is the issue you are attempting to mitigate. This is simply loose terminology. |
This PR adds a test that on main passes for CoreCLR but segfaults for NativeAOT. It also includes fixes to make the test pass for NativeAOT.
The key problem is the conditional weak table will keep the
ManagedObjectWrapperHolderalive but not prevent it from being finalized. This means that if the wrapped object is resurrected, calls toGetOrCreateComInterfaceForObjectcould return a pointer freed memory.The solution this PR implements is to move the finialization of the
ManagedObjectWrapperto a separate class. This class defers finialization until the wrapped object has been completely collected. The newManagedObjectWrapperReleaseris referenced by theManagedObjectWrapperHolderclass, so it should not be finialized until the wrapped object becomes eligible for finialization.The references between objects looks like this:
Some other race conditions are also fixed by this PR:
ManagedObjectWrapperwas not set until after theManagedObjectWrapperHolderwas added to the CWT. Hypothetically if a holder was added to the CWT but not yet initialized with the ref-counted handle, another thread could read the object out of the CWT and observe the null handle. This is fixed by doing all the initialization of the holder in its constructor.GetOrCreateComInterfaceForObjectwas assuming thatConditionalWeakTable.GetValuealways returns a new object and callingAddRefon the holder was not needed. However,GetValuecan potentially callTryGetValueand return an existing instance. This means that if many threads calledGetOrCreateComInterfaceForObjectat the same time, the ref count on theManagedObjectWrappermight not be as high as it should be. This is fixed by unconditionally callingAddRefon the holder.