-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Enable COM interop for collectible classes #20919
Enable COM interop for collectible classes #20919
Conversation
Do we need any tests for this? |
m_pDefaultContext = NULL; | ||
#ifdef FEATURE_COMINTEROP | ||
m_pComCallWrapperCache = NULL; | ||
m_pRCWCache = NULL; |
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.
Are the RCW caches fine wrt. unloading?
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.
They (or I should rather say it as there is only one in one app domain) don't cause any issues while unloading. It seems that the only reason why they were per AppDomain was the isolation (based on the doc I was able to find in BOTR) and they seem to be cleaned on a regular basis.
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.
E.g. RCW has InterfaceEntry m_aInterfaceEntries[INTERFACE_ENTRY_CACHE_SIZE];
in it. This array contains MethodTable pointers. What guarantees that none of these pointers point to MethodTable that got unloaded?
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.
Let me look into that
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.
GCToEEInterface::AfterGcScanRoots
calls AppDomain::DetachRCWs
which loops over all entries in the RCW
cache and marks all RCW
s whose exposed objects were not promoted as detached.
They are then cleaned in RCWCleanupList::CleanupAllWrappers()
which is called from SyncBlockCache::CleanupSyncBlocks()
which in turn is called from Thread::DoExtraWorkForFinalizer()
. This happens right before the call to SystemDomain::System()->ProcessDelayedUnloadLoaderAllocators()
, so the RCWs
don't outlive the related MethodTable
instances.
So unless I am missing something, it seems we should be good.
// | ||
if (pClassMT->Collectible()) | ||
{ | ||
COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleCOM")); |
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.
Delete the NotSupported_CollectibleCOM
message?
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.
Thanks for spotting that.
We already have a couple of coreclr tests for that. Interop/MarshalAPI/IUnknown and tests in Interop/COM. |
I'd like to see us have a subset of "interesting" tests for collectible loadercontexts that execute during normal test runs, and don't require some sort of special test pass. |
It feels like there may be some issue with WinRT. are we sure that no managed WinRT type could be exposed from a collectible type? |
@davidwrighton what kind of problems with WinRT do you expect if we exposed managed WinRT objects from collectible types? |
ece8b8d
to
f56a91d
Compare
f56a91d
to
66ecc14
Compare
@dotnet-bot test CentOS7.1 x64 Checked Innerloop Build and Test please |
@dotnet-bot test Windows_NT arm64 Cross Checked Innerloop Build and Test please |
@dotnet-bot test Ubuntu x64 Checked Innerloop Build and Test (Jit - TieredCompilation=0) please |
@dotnet-bot test Ubuntu x64 Checked Innerloop Build and Test (Jit - TieredCompilation=0) please |
b30aafa
to
7aa7836
Compare
@jkotas I believe I've addressed feedback and responded to question. I have also added two unloadability tests. Could you please take another look? |
Is it the right design to neuter all COM object references on unload? AppDomains needed because of they have done forceful unload regardless of whether somebody was holding reference to inside of the AppDomain or not. We are not doing forceful unload here. I would think that COM object references should be just like any other object references. As long as there are any alive, the load context should stay alive. Once all of them are gone, the load context can be unloaded. Thoughts? |
@jkotas We don't do that. If there are COM references, they keep the LoaderAllocator alive. |
src/vm/comcallablewrapper.cpp
Outdated
// we have a template, create a wrapper and initialize from the template | ||
// alloc wrapper, aligned 32 bytes | ||
if (pWrapperCache->IsDomainUnloading()) | ||
if (pWrapperCache->IsLoaderAllocatorUnloading()) |
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.
f there are COM references, they keep the LoaderAllocator alive.
So why do we need to throw AppDomainUnloadedException here?
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 is for other purpose. After the unloading is initiated, this code prevents new CCW creation. But the ones that were created before that still work.
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.
Regular P/Invoke interop is allowed even after the unloading is initialized. Why do we want to disallow COM?
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.
I don't have a strong opinion on this. This was the way the AppDomain behaved after the Unload was initiated on them, so I have kept it. But I don't think there would be any issue if we removed this check.
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.
I still have doubts about this. I do not think we should be blocking any interop when the stuff is unloading.
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.
AppDomains had to do it because of they would not be able to forcefully unload otherwise. ALCs do not have forceful unload, so they do not need it.
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.
Ok, let's remove this check.
I have pulled this down to do some ad-hoc testing to see how well it works. I am hitting a crashes when I load&unload a trivial program that uses COM in a loop. Repro: https://gist.github.com/jkotas/db2536c79dd61899781406af5316037f Result:
|
* Remove collectible check from IsTypeVisibleFromCom. That fixes three new COM interop tests * Add collectible check to GetComClassFactory when we check for unsupported interop with WinRT * Fix two contracts
Add two tests to test COM unloadability: * One for using native COM server from managed COM client * One for using managed COM objects from native client
It was missing copying the native server dll to the test folder.
3d43ccf
to
4e53c70
Compare
@dotnet-bot test CentOS7.1 x64 Checked Innerloop Build and Test please |
@dotnet-bot test OSX10.12 x64 Checked Innerloop Build and Test please |
@dotnet-bot test Ubuntu arm Cross Release crossgen_comparison Build and Test please |
@dotnet-bot test Windows_NT x86 Release Innerloop Build and Test please |
@dotnet-bot test OSX10.12 x64 Checked Innerloop Build and Test please |
The NETClientPrimitives is disabled there too.
There is some flakyness with this on x86 and ARM. I am unable to reproduce this locally nor is it occurring on any other architectures. I will be fixing the warnings and disabling the test and fixing this tomorrow. See #21294. |
@dotnet-bot test Windows_NT arm Cross Debug Innerloop Build please |
@jkotas I've added the new IUnknownTestInALC that serves as a regression test for the ILStubCache issue and also verified that your example works fine. Do you think we can merge this change now? |
I think so. I do not see any other issues. I am sure there are more, but they are hard to find. |
@jkotas I've removed the check for the LoaderAllocator unloading and all the stuff that become obsolete due to that. |
* Enable COM interop for collectible classes * Modify DispatchInfo to use LoaderAllocator handles The DispatchMemberInfo was using global handles to refer to the managed MemberInfo instances. That doesn't work with unloadability. This change modifies it to use handles allocated from LoaderAllocator. * Disable COM interop for WinRT types * Remove collectible check from IsTypeVisibleFromCom. That fixes three new COM interop tests * Add collectible check to GetComClassFactory when we check for unsupported interop with WinRT * Add COM unloadability tests Add two tests to test COM unloadability: * One for using native COM server from managed COM client * One for using managed COM objects from native client * Add unloading test for IUnknownTest * Disable NETClientPrimitivesInALC on Win ARM The NETClientPrimitives is disabled there too. Commit migrated from dotnet/coreclr@4c461d7
This is a regression from NETFX with the introduction of collectable assemblies in .NET Core 3.1 (dotnet/coreclr#20919).
This is a regression from NETFX with the introduction of collectable assemblies in .NET Core 3.1 (dotnet/coreclr#20919).
This is a regression from NETFX with the introduction of collectable assemblies in .NET Core 3.1 (dotnet/coreclr#20919).
This is a regression from NETFX with the introduction of collectable assemblies in .NET Core 3.1 (dotnet/coreclr#20919). Co-authored-by: Aaron Robinson <[email protected]>
This change is relatively straightforward:
Closes #20164