Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions src/coreclr/interop/comwrappers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -791,17 +791,6 @@ void NativeObjectWrapperContext::Destroy(_In_ NativeObjectWrapperContext* wrappe
{
_ASSERTE(wrapper != nullptr);

// Check if the tracker object manager should be informed prior to being destroyed.
IReferenceTracker* trackerMaybe = wrapper->GetReferenceTracker();
if (trackerMaybe != nullptr)
{
// We only call this during a GC so ignore the failure as
// there is no way we can handle it at this point.
HRESULT hr = TrackerObjectManager::BeforeWrapperDestroyed(trackerMaybe);
_ASSERTE(SUCCEEDED(hr));
(void)hr;
}

// Manually trigger the destructor since placement
// new was used to allocate the object.
wrapper->~NativeObjectWrapperContext();
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/interop/comwrappers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,8 @@ class TrackerObjectManager
// Called after wrapper has been created.
static HRESULT AfterWrapperCreated(_In_ IReferenceTracker* obj);

// Called before wrapper is about to be destroyed (the same lifetime as short weak handle).
static HRESULT BeforeWrapperDestroyed(_In_ IReferenceTracker* obj);
// Called before wrapper is about to be finalized (the same lifetime as short weak handle).
static HRESULT BeforeWrapperFinalized(_In_ IReferenceTracker* obj);

public:
// Begin the reference tracking process for external objects.
Expand Down
8 changes: 6 additions & 2 deletions src/coreclr/interop/inc/interoplib.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,12 @@ namespace InteropLib
_In_ size_t contextSize,
_Out_ ExternalWrapperResult* result) noexcept;

// Destroy the supplied wrapper.
void DestroyWrapperForExternal(_In_ void* context) noexcept;
// Inform the wrapper it is being collected.
void NotifyWrapperForExternalIsBeingCollected(_In_ void* context) noexcept;

// Destroy the supplied wrapper.
// Optionally notify the wrapper of collection at the same time.
void DestroyWrapperForExternal(_In_ void* context, _In_ bool notifyIsBeingCollected = false) noexcept;

// Separate the supplied wrapper from the tracker runtime.
void SeparateWrapperFromTrackerRuntime(_In_ void* context) noexcept;
Expand Down
28 changes: 25 additions & 3 deletions src/coreclr/interop/interoplib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,15 +166,37 @@ namespace InteropLib
return S_OK;
}

void DestroyWrapperForExternal(_In_ void* contextMaybe) noexcept
void NotifyWrapperForExternalIsBeingCollected(_In_ void* contextMaybe) noexcept
{
NativeObjectWrapperContext* context = NativeObjectWrapperContext::MapFromRuntimeContext(contextMaybe);

// A caller should not be destroying a context without knowing if the context is valid.
_ASSERTE(context != nullptr);

// Check if the tracker object manager should be informed of collection.
IReferenceTracker* trackerMaybe = context->GetReferenceTracker();
if (trackerMaybe != nullptr)
{
// We only call this during a GC so ignore the failure as
// there is no way we can handle it at this point.
HRESULT hr = TrackerObjectManager::BeforeWrapperFinalized(trackerMaybe);
_ASSERTE(SUCCEEDED(hr));
(void)hr;
}
}

void DestroyWrapperForExternal(_In_ void* contextMaybe, _In_ bool notifyIsBeingCollected) noexcept
{
NativeObjectWrapperContext* context = NativeObjectWrapperContext::MapFromRuntimeContext(contextMaybe);

// A caller should not be destroying a context without knowing if the context is valid.
_ASSERTE(context != nullptr);

NativeObjectWrapperContext::Destroy(context);
}
if (notifyIsBeingCollected)
NotifyWrapperForExternalIsBeingCollected(contextMaybe);

NativeObjectWrapperContext::Destroy(context);
}

void SeparateWrapperFromTrackerRuntime(_In_ void* contextMaybe) noexcept
{
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/interop/trackerobjectmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -305,13 +305,13 @@ HRESULT TrackerObjectManager::AfterWrapperCreated(_In_ IReferenceTracker* obj)
return S_OK;
}

HRESULT TrackerObjectManager::BeforeWrapperDestroyed(_In_ IReferenceTracker* obj)
HRESULT TrackerObjectManager::BeforeWrapperFinalized(_In_ IReferenceTracker* obj)
{
_ASSERTE(obj != nullptr);

HRESULT hr;

// Notify tracker runtime that we are about to destroy a wrapper
// Notify tracker runtime that we are about to finalize a wrapper
// (same timing as short weak handle) for this object.
// They need this information to disconnect weak refs and stop firing events,
// so that they can avoid resurrecting the object.
Expand Down
9 changes: 8 additions & 1 deletion src/coreclr/vm/interoplibinterface_comwrappers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,10 @@ namespace
if (Result.Context != NULL)
{
GCX_PREEMP();
InteropLib::Com::DestroyWrapperForExternal(Result.Context);
// We also request collection notification since this holder is presently
// only used for new activation of wrappers therefore the notification won't occur
// at the typical time of before finalization.
InteropLib::Com::DestroyWrapperForExternal(Result.Context, /* notifyIsBeingCollected */ true);
}
}
InteropLib::Com::ExternalWrapperResult* operator&()
Expand Down Expand Up @@ -478,7 +481,11 @@ namespace
if (!cxt->IsSet(ExternalObjectContext::Flags_Detached)
&& !GCHeapUtilities::GetGCHeap()->IsPromoted(OBJECTREFToObject(cxt->GetObjectRef())))
{
// Indicate the wrapper entry has been detached.
cxt->MarkDetached();

// Notify the wrapper it was not promoted and is being collected.
InteropLib::Com::NotifyWrapperForExternalIsBeingCollected(cxt);
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/tests/Interop/COM/ComWrappers/API/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,9 @@ static int Main(string[] doNotUse)
ValidateQueryInterfaceAfterManagedObjectCollected();
ValidateAggregationWithComObject();
ValidateAggregationWithReferenceTrackerObject();

// Ensure all objects have been cleaned up.
ForceGC();
}
catch (Exception e)
{
Expand Down
12 changes: 12 additions & 0 deletions src/tests/Interop/COM/ComWrappers/Common.cs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,12 @@ public static AllocationCountResult CountTrackerObjectAllocations()

[DllImport(nameof(MockReferenceTrackerRuntime))]
extern public static int TrackerTarget_ReleaseFromReferenceTracker(IntPtr ptr);

// Suppressing the GC transition here as we want to make sure we are in-sync
// with the GC which is setting the connected value.
[SuppressGCTransition]
[DllImport(nameof(MockReferenceTrackerRuntime))]
extern public static byte IsTrackerObjectConnected(IntPtr instance);
}

[Guid("42951130-245C-485E-B60B-4ED4254256F8")]
Expand Down Expand Up @@ -219,6 +225,12 @@ static IntPtr CreateInstance(IntPtr outer, out IntPtr inner)
}
else
{
byte isConnected = MockReferenceTrackerRuntime.IsTrackerObjectConnected(this.classNative.Instance);
if (isConnected != 0)
{
throw new Exception("TrackerObject should be disconnected prior to finalization");
}

ComWrappersHelper.Cleanup(ref this.classNative);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,11 @@ namespace
}
}

bool IsConnected()
{
return _connected;
}

STDMETHOD(AddObjectRef)(_In_ IUnknown* c, _Out_ int* id)
{
assert(c != nullptr && id != nullptr);
Expand Down Expand Up @@ -546,6 +551,12 @@ extern "C" DLL_EXPORT int STDMETHODCALLTYPE Trigger_NotifyEndOfReferenceTracking
return TrackerRuntimeManager.NotifyEndOfReferenceTrackingOnThread();
}

extern "C" DLL_EXPORT bool STDMETHODCALLTYPE IsTrackerObjectConnected(IUnknown* inst)
{
auto trackerObject = reinterpret_cast<TrackerObject::TrackerObjectImpl*>(inst);
return trackerObject->IsConnected();
}

extern "C" DLL_EXPORT void* STDMETHODCALLTYPE TrackerTarget_AddRefFromReferenceTrackerAndReturn(IUnknown *obj)
{
assert(obj != nullptr);
Expand Down