Skip to content

Commit

Permalink
Address flakey behavior testing COM lifetime cleanup. (dotnet#49266)
Browse files Browse the repository at this point in the history
* Address flakey behavior testing COM lifetime cleanup.

* Add new lifetime test for COM.
  • Loading branch information
AaronRobinsonMSFT authored Mar 12, 2021
1 parent 4e2491d commit 6a95503
Show file tree
Hide file tree
Showing 13 changed files with 241 additions and 8 deletions.
18 changes: 18 additions & 0 deletions src/tests/Interop/COM/NETClients/Lifetime/App.manifest
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity
type="win32"
name="NetClientLifetime"
version="1.0.0.0" />

<dependency>
<dependentAssembly>
<!-- RegFree COM -->
<assemblyIdentity
type="win32"
name="COMNativeServer.X"
version="1.0.0.0"/>
</dependentAssembly>
</dependency>

</assembly>
17 changes: 17 additions & 0 deletions src/tests/Interop/COM/NETClients/Lifetime/NETClientLifetime.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<ApplicationManifest>App.manifest</ApplicationManifest>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="../../ServerContracts/Server.CoClasses.cs" />
<Compile Include="../../ServerContracts/Server.Contracts.cs" />
<Compile Include="../../ServerContracts/ServerGuids.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../NativeServer/CMakeLists.txt" />
<ProjectReference Include="$(TestSourceDir)Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj" />
</ItemGroup>
</Project>
111 changes: 111 additions & 0 deletions src/tests/Interop/COM/NETClients/Lifetime/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace NetClient
{
using System;
using System.Threading;
using System.Runtime.InteropServices;

using TestLibrary;
using Server.Contract;
using Server.Contract.Servers;

unsafe class Program
{
static delegate* unmanaged<int> GetAllocationCount;

// Initialize for all tests
static void Initialize()
{
var inst = new TrackMyLifetimeTesting();
GetAllocationCount = (delegate* unmanaged<int>)inst.GetAllocationCountCallback();
}

static int AllocateInstances(int a)
{
var insts = new object[a];
for (int i = 0; i < a; ++i)
{
insts[i] = new TrackMyLifetimeTesting();
}
return a;
}

static void ForceGC()
{
for (int i = 0; i < 3; ++i)
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
}

static void Validate_COMServer_CleanUp()
{
Console.WriteLine($"Calling {nameof(Validate_COMServer_CleanUp)}...");

int allocated = 0;
allocated += AllocateInstances(1);
allocated += AllocateInstances(2);
allocated += AllocateInstances(3);
Assert.AreNotEqual(0, GetAllocationCount());

ForceGC();

Assert.AreEqual(0, GetAllocationCount());
}

static void Validate_COMServer_DisableEagerCleanUp()
{
Console.WriteLine($"Calling {nameof(Validate_COMServer_DisableEagerCleanUp)}...");
Assert.AreEqual(0, GetAllocationCount());

Thread.CurrentThread.DisableComObjectEagerCleanup();

int allocated = 0;
allocated += AllocateInstances(1);
allocated += AllocateInstances(2);
allocated += AllocateInstances(3);
Assert.AreNotEqual(0, GetAllocationCount());

ForceGC();

Assert.AreNotEqual(0, GetAllocationCount());

Marshal.CleanupUnusedObjectsInCurrentContext();

ForceGC();

Assert.AreEqual(0, GetAllocationCount());
Assert.IsFalse(Marshal.AreComObjectsAvailableForCleanup());
}

[STAThread]
static int Main(string[] doNotUse)
{
// RegFree COM is not supported on Windows Nano
if (Utilities.IsWindowsNanoServer)
{
return 100;
}

try
{
// Initialization for all future tests
Initialize();
Assert.IsTrue(GetAllocationCount != null);

Validate_COMServer_CleanUp();
Validate_COMServer_DisableEagerCleanUp();
}
catch (Exception e)
{
Console.WriteLine($"Test Failure: {e}");
return 101;
}

return 100;
}
}
}
7 changes: 1 addition & 6 deletions src/tests/Interop/COM/NETClients/Primitives/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,10 @@ static int Main(string[] doNotUse)
try
{
RunTests();
Console.WriteLine("Testing COM object lifetime control methods.");
Thread.CurrentThread.DisableComObjectEagerCleanup();
RunTests();
Marshal.CleanupUnusedObjectsInCurrentContext();
Assert.IsFalse(Marshal.AreComObjectsAvailableForCleanup());
}
catch (Exception e)
{
Console.WriteLine($"Test Failure: {e}");
Console.WriteLine($"Test object interop failure: {e}");
return 101;
}

Expand Down
5 changes: 5 additions & 0 deletions src/tests/Interop/COM/NativeServer/COMNativeServer.X.manifest
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@
<comClass
clsid="{CE137261-6F19-44F5-A449-EF963B3F987E}"
threadingModel="Both" />

<!-- TrackMyLifetimeTesting -->
<comClass
clsid="{4F54231D-9E11-4C0B-8E0B-2EBD8B0E5811}"
threadingModel="Both" />
</file>

</assembly>
5 changes: 5 additions & 0 deletions src/tests/Interop/COM/NativeServer/Servers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ STDAPI DllRegisterServer(void)
RETURN_IF_FAILED(RegisterClsid(__uuidof(AggregationTesting), L"Both"));
RETURN_IF_FAILED(RegisterClsid(__uuidof(ColorTesting), L"Both"));
RETURN_IF_FAILED(RegisterClsid(__uuidof(InspectableTesting), L"Both"));
RETURN_IF_FAILED(RegisterClsid(__uuidof(TrackMyLifetimeTesting), L"Both"));

return S_OK;
}
Expand All @@ -185,6 +186,7 @@ STDAPI DllUnregisterServer(void)
RETURN_IF_FAILED(RemoveClsid(__uuidof(AggregationTesting)));
RETURN_IF_FAILED(RemoveClsid(__uuidof(ColorTesting)));
RETURN_IF_FAILED(RemoveClsid(__uuidof(InspectableTesting)));
RETURN_IF_FAILED(RemoveClsid(__uuidof(TrackMyLifetimeTesting)));

return S_OK;
}
Expand Down Expand Up @@ -221,5 +223,8 @@ STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _Out_ LPVOID FA
if (rclsid == __uuidof(InspectableTesting))
return ClassFactoryBasic<InspectableTesting>::Create(riid, ppv);

if (rclsid == __uuidof(TrackMyLifetimeTesting))
return ClassFactoryBasic<TrackMyLifetimeTesting>::Create(riid, ppv);

return CLASS_E_CLASSNOTAVAILABLE;
}
6 changes: 5 additions & 1 deletion src/tests/Interop/COM/NativeServer/Servers.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class DECLSPEC_UUID("C222F472-DA5A-4FC6-9321-92F4F7053A65") ColorTesting;
class DECLSPEC_UUID("66DB7882-E2B0-471D-92C7-B2B52A0EA535") LicenseTesting;
class DECLSPEC_UUID("FAEF42AE-C1A4-419F-A912-B768AC2679EA") DefaultInterfaceTesting;
class DECLSPEC_UUID("CE137261-6F19-44F5-A449-EF963B3F987E") InspectableTesting;
class DECLSPEC_UUID("4F54231D-9E11-4C0B-8E0B-2EBD8B0E5811") TrackMyLifetimeTesting;

#define CLSID_NumericTesting __uuidof(NumericTesting)
#define CLSID_ArrayTesting __uuidof(ArrayTesting)
Expand All @@ -31,7 +32,8 @@ class DECLSPEC_UUID("CE137261-6F19-44F5-A449-EF963B3F987E") InspectableTesting;
#define CLSID_ColorTesting __uuidof(ColorTesting)
#define CLSID_LicenseTesting __uuidof(LicenseTesting)
#define CLSID_DefaultInterfaceTesting __uuidof(DefaultInterfaceTesting)
#define CLSID_InspectableTesting __uidof(InspectableTesting)
#define CLSID_InspectableTesting __uuidof(InspectableTesting)
#define CLSID_TrackMyLifetimeTesting __uuidof(TrackMyLifetimeTesting)

#define IID_INumericTesting __uuidof(INumericTesting)
#define IID_IArrayTesting __uuidof(IArrayTesting)
Expand All @@ -47,6 +49,7 @@ class DECLSPEC_UUID("CE137261-6F19-44F5-A449-EF963B3F987E") InspectableTesting;
#define IID_IDefaultInterfaceTesting2 __uuidof(IDefaultInterfaceTesting2)
#define IID_IInspectableTesting __uuidof(IInspectableTesting)
#define IID_IInspectableTesting2 __uuidof(IInspectableTesting2)
#define IID_ITrackMyLifetimeTesting __uuidof(ITrackMyLifetimeTesting)

// Class used for COM activation when using CoreShim
struct CoreShimComActivation
Expand Down Expand Up @@ -86,4 +89,5 @@ struct CoreShimComActivation
#include "ColorTesting.h"
#include "LicenseTesting.h"
#include "InspectableTesting.h"
#include "TrackMyLifetimeTesting.h"
#endif
48 changes: 48 additions & 0 deletions src/tests/Interop/COM/NativeServer/TrackMyLifetimeTesting.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#pragma once

#include "Servers.h"

class TrackMyLifetimeTesting : public UnknownImpl, public ITrackMyLifetimeTesting
{
static std::atomic<ULONG> _instanceCount;

static ULONG GetAllocatedTypes()
{
return _instanceCount;
}

public:
TrackMyLifetimeTesting()
{
_instanceCount++;
}
~TrackMyLifetimeTesting()
{
_instanceCount--;
}

public: // ITrackMyLifetimeTesting
DEF_FUNC(GetAllocationCountCallback)(_Outptr_ void** fptr)
{
if (fptr == nullptr)
return E_POINTER;

*fptr = (void*)&GetAllocatedTypes;
return S_OK;
}

public: // IUnknown
STDMETHOD(QueryInterface)(
/* [in] */ REFIID riid,
/* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR *__RPC_FAR *ppvObject)
{
return DoQueryInterface(riid, ppvObject, static_cast<ITrackMyLifetimeTesting *>(this));
}

DEFINE_REF_COUNTING();
};

std::atomic<ULONG> TrackMyLifetimeTesting::_instanceCount = 0;
13 changes: 13 additions & 0 deletions src/tests/Interop/COM/ServerContracts/Server.CoClasses.cs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,19 @@ internal interface InspectableTesting : Server.Contract.IInspectableTesting
internal class InspectableTestingClass
{
}

[ComImport]
[CoClass(typeof(TrackMyLifetimeTestingClass))]
[Guid("57f396a1-58a0-425f-8807-9f938a534984")]
internal interface TrackMyLifetimeTesting : Server.Contract.ITrackMyLifetimeTesting
{
}

[ComImport]
[Guid(Server.Contract.Guids.TrackMyLifetimeTesting)]
internal class TrackMyLifetimeTestingClass
{
}
}

#pragma warning restore 618 // Must test deprecated features
Expand Down
9 changes: 8 additions & 1 deletion src/tests/Interop/COM/ServerContracts/Server.Contracts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -332,13 +332,20 @@ internal interface IInspectableTesting
{
}

[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIInspectable)]
[Guid("e9e1ccf9-8e93-4850-ac1c-a71692cb68c5")]
internal interface IInspectableTesting2
{
int Add(int i, int j);
}

[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("57f396a1-58a0-425f-8807-9f938a534984")]
internal interface ITrackMyLifetimeTesting
{
IntPtr GetAllocationCountCallback();
}
}

#pragma warning restore 618 // Must test deprecated features
6 changes: 6 additions & 0 deletions src/tests/Interop/COM/ServerContracts/Server.Contracts.h
Original file line number Diff line number Diff line change
Expand Up @@ -498,4 +498,10 @@ IInspectableTesting2 : IInspectable
virtual HRESULT STDMETHODCALLTYPE Add(_In_ int i, _In_ int j, _Out_ _Ret_ int* retVal) = 0;
};

struct __declspec(uuid("57f396a1-58a0-425f-8807-9f938a534984"))
ITrackMyLifetimeTesting : IUnknown
{
virtual HRESULT STDMETHODCALLTYPE GetAllocationCountCallback(_Outptr_ void** fptr) = 0;
};

#pragma pack(pop)
1 change: 1 addition & 0 deletions src/tests/Interop/COM/ServerContracts/ServerGuids.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ internal sealed class Guids
public const string DefaultInterfaceTesting = "FAEF42AE-C1A4-419F-A912-B768AC2679EA";
public const string ConsumeNETServerTesting = "DE4ACF53-5957-4D31-8BE2-EA6C80683246";
public const string InspectableTesting = "CE137261-6F19-44F5-A449-EF963B3F987E";
public const string TrackMyLifetimeTesting = "4F54231D-9E11-4C0B-8E0B-2EBD8B0E5811";
}
}
3 changes: 3 additions & 0 deletions src/tests/issues.targets
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,9 @@
<ExcludeList Include="$(XunitTestBinBase)/Interop/COM/NativeClients/Primitives/*">
<Issue>https://github.com/dotnet/runtime/issues/11360</Issue>
</ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/Interop/COM/NETClients/Lifetime/*">
<Issue>https://github.com/dotnet/runtime/issues/11360</Issue>
</ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/Interop/COM/NativeClients/Licensing/*">
<Issue>https://github.com/dotnet/runtime/issues/11360</Issue>
</ExcludeList>
Expand Down

0 comments on commit 6a95503

Please sign in to comment.