Skip to content

Commit

Permalink
Add a thread-safe wrapping implementation (#54)
Browse files Browse the repository at this point in the history
  • Loading branch information
jkoritzinsky authored Jul 23, 2024
1 parent 7a3ace4 commit 9e7cd7e
Show file tree
Hide file tree
Showing 6 changed files with 2,019 additions and 53 deletions.
192 changes: 146 additions & 46 deletions src/interfaces/dispenser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,54 @@
#include "controllingiunknown.hpp"
#include "metadataimportro.hpp"
#include "metadataemit.hpp"
#include "threadsafe.hpp"

#include <cstring>

namespace
{
class MDDispenserStateless final : IMetaDataDispenser
class MDDispenser final : public TearOffBase<IMetaDataDispenserEx>
{
bool _threadSafe;
private:
dncp::com_ptr<ControllingIUnknown> CreateExposedObject(dncp::com_ptr<ControllingIUnknown> unknown, DNMDOwner* owner)
{
mdhandle_view handle_view{ owner };
MetadataEmit* emit = unknown->CreateAndAddTearOff<MetadataEmit>(handle_view);
MetadataImportRO* import = unknown->CreateAndAddTearOff<MetadataImportRO>(std::move(handle_view));
if (!_threadSafe)
{
return unknown;
}
dncp::com_ptr<ControllingIUnknown> threadSafeUnknown;
threadSafeUnknown.Attach(new ControllingIUnknown());

// Define an IDNMDOwner* tear-off here so the thread-safe object can be identified as a DNMD object.
(void)threadSafeUnknown->CreateAndAddTearOff<DelegatingDNMDOwner>(handle_view);
(void)threadSafeUnknown->CreateAndAddTearOff<ThreadSafeImportEmit<MetadataImportRO, MetadataEmit>>(std::move(unknown), import, emit);
// ThreadSafeImportEmit took ownership of owner through unknown.
return threadSafeUnknown;
}

protected:
virtual bool TryGetInterfaceOnThis(REFIID riid, void** ppvObject) override
{
if (riid == IID_IMetaDataDispenserEx || riid == IID_IMetaDataDispenser)
{
*ppvObject = static_cast<IMetaDataDispenserEx*>(this);
return true;
}
return false;
}

public: // IMetaDataDispenser
using TearOffBase<IMetaDataDispenserEx>::TearOffBase;

STDMETHOD(DefineScope)(
REFCLSID rclsid,
DWORD dwCreateFlags,
REFIID riid,
IUnknown** ppIUnk)
IUnknown** ppIUnk) override
{
if (rclsid != CLSID_CLR_v2_MetaData)
{
Expand Down Expand Up @@ -60,23 +95,20 @@ namespace

try
{
mdhandle_view handle_view{ obj->CreateAndAddTearOff<DNMDOwner>(std::move(md_ptr)) };
(void)obj->CreateAndAddTearOff<MetadataEmit>(handle_view);
(void)obj->CreateAndAddTearOff<MetadataImportRO>(std::move(handle_view));
DNMDOwner* owner = obj->CreateAndAddTearOff<DNMDOwner>(std::move(md_ptr));
return CreateExposedObject(std::move(obj), owner)->QueryInterface(riid, (void**)ppIUnk);
}
catch(std::bad_alloc const&)
{
return E_OUTOFMEMORY;
}

return obj->QueryInterface(riid, (void**)ppIUnk);
}

STDMETHOD(OpenScope)(
LPCWSTR szScope,
DWORD dwOpenFlags,
REFIID riid,
IUnknown** ppIUnk)
IUnknown** ppIUnk) override
{
UNREFERENCED_PARAMETER(szScope);
UNREFERENCED_PARAMETER(dwOpenFlags);
Expand All @@ -90,7 +122,7 @@ namespace
ULONG cbData,
DWORD dwOpenFlags,
REFIID riid,
IUnknown** ppIUnk)
IUnknown** ppIUnk) override
{
if (ppIUnk == nullptr)
return E_INVALIDARG;
Expand Down Expand Up @@ -123,74 +155,142 @@ namespace

try
{
mdhandle_view handle_view{ obj->CreateAndAddTearOff<DNMDOwner>(std::move(md_ptr), std::move(copiedMem), std::move(nowOwned)) };

if (!(dwOpenFlags & ofReadOnly))
(void)obj->CreateAndAddTearOff<MetadataEmit>(handle_view);
DNMDOwner* owner = obj->CreateAndAddTearOff<DNMDOwner>(std::move(md_ptr), std::move(copiedMem), std::move(nowOwned));
mdhandle_view handle_view{ owner };

(void)obj->CreateAndAddTearOff<MetadataImportRO>(std::move(handle_view));
if (dwOpenFlags & ofReadOnly)
{
// If we're read-only, then we don't need to deal with thread safety.
(void)obj->CreateAndAddTearOff<MetadataImportRO>(std::move(handle_view));
return obj->QueryInterface(riid, (void**)ppIUnk);
}

// If we're read-write, go through our helper to create an object that respects all of the options
// (as the various options affect writing operations only).
return CreateExposedObject(std::move(obj), owner)->QueryInterface(riid, (void**)ppIUnk);
}
catch(std::bad_alloc const&)
{
return E_OUTOFMEMORY;
}

return obj->QueryInterface(riid, (void**)ppIUnk);
}

public: // IUnknown
virtual HRESULT STDMETHODCALLTYPE QueryInterface(
/* [in] */ REFIID riid,
/* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject)
public: // IMetaDataDispenserEx
STDMETHOD(SetOption)(
REFGUID optionid,
VARIANT const *value) override
{
if (ppvObject == nullptr)
return E_POINTER;

if (riid == IID_IUnknown)
{
*ppvObject = static_cast<IUnknown*>(this);
}
else if (riid == IID_IMetaDataDispenser)
if (optionid == MetaDataThreadSafetyOptions)
{
*ppvObject = static_cast<IMetaDataDispenser*>(this);
_threadSafe = V_UI4(value) == CorThreadSafetyOptions::MDThreadSafetyOn;
return S_OK;
}
else
return E_INVALIDARG;
}

STDMETHOD(GetOption)(
REFGUID optionid,
VARIANT *pvalue) override
{
if (optionid == MetaDataThreadSafetyOptions)
{
*ppvObject = nullptr;
return E_NOINTERFACE;
V_UI4(pvalue) = _threadSafe ? CorThreadSafetyOptions::MDThreadSafetyOn : CorThreadSafetyOptions::MDThreadSafetyOff;
return S_OK;
}
return E_INVALIDARG;
}

STDMETHOD(OpenScopeOnITypeInfo)(
ITypeInfo *pITI,
DWORD dwOpenFlags,
REFIID riid,
IUnknown **ppIUnk) override
{
UNREFERENCED_PARAMETER(pITI);
UNREFERENCED_PARAMETER(dwOpenFlags);
UNREFERENCED_PARAMETER(riid);
UNREFERENCED_PARAMETER(ppIUnk);
return E_NOTIMPL;
}

(void)AddRef();
return S_OK;
STDMETHOD(GetCORSystemDirectory)(
_Out_writes_to_opt_(cchBuffer, *pchBuffer)
LPWSTR szBuffer,
DWORD cchBuffer,
DWORD* pchBuffer) override
{
UNREFERENCED_PARAMETER(szBuffer);
UNREFERENCED_PARAMETER(cchBuffer);
UNREFERENCED_PARAMETER(pchBuffer);
return E_NOTIMPL;
}

virtual ULONG STDMETHODCALLTYPE AddRef(void)
STDMETHOD(FindAssembly)(
LPCWSTR szAppBase,
LPCWSTR szPrivateBin,
LPCWSTR szGlobalBin,
LPCWSTR szAssemblyName,
LPCWSTR szName,
ULONG cchName,
ULONG *pcName) override
{
return 1;
UNREFERENCED_PARAMETER(szAppBase);
UNREFERENCED_PARAMETER(szPrivateBin);
UNREFERENCED_PARAMETER(szGlobalBin);
UNREFERENCED_PARAMETER(szAssemblyName);
UNREFERENCED_PARAMETER(szName);
UNREFERENCED_PARAMETER(cchName);
UNREFERENCED_PARAMETER(pcName);
return E_NOTIMPL;
}

virtual ULONG STDMETHODCALLTYPE Release(void)
STDMETHOD(FindAssemblyModule)(
LPCWSTR szAppBase,
LPCWSTR szPrivateBin,
LPCWSTR szGlobalBin,
LPCWSTR szAssemblyName,
LPCWSTR szModuleName,
_Out_writes_to_opt_(cchName, *pcName)
LPWSTR szName,
ULONG cchName,
ULONG *pcName) override
{
return 1;
UNREFERENCED_PARAMETER(szAppBase);
UNREFERENCED_PARAMETER(szPrivateBin);
UNREFERENCED_PARAMETER(szGlobalBin);
UNREFERENCED_PARAMETER(szAssemblyName);
UNREFERENCED_PARAMETER(szModuleName);
UNREFERENCED_PARAMETER(szName);
UNREFERENCED_PARAMETER(cchName);
UNREFERENCED_PARAMETER(pcName);
return E_NOTIMPL;
}
};

// The only available dispenser is stateless and
// statically allocated. There is no lifetime management
// needed.
MDDispenserStateless g_dispenser;
}

extern "C" DNMD_EXPORT
HRESULT GetDispenser(
REFGUID riid,
void** ppObj)
{
if (riid != IID_IMetaDataDispenser)
if (riid != IID_IMetaDataDispenser
&& riid != IID_IMetaDataDispenserEx)
{
return E_INVALIDARG;
}

if (ppObj == nullptr)
return E_INVALIDARG;

return g_dispenser.QueryInterface(riid, (void**)ppObj);
}
try
{
dncp::com_ptr<ControllingIUnknown> obj;
obj.Attach(new ControllingIUnknown());
(void)obj->CreateAndAddTearOff<MDDispenser>();
return obj->QueryInterface(riid, (void**)ppObj);
}
catch(std::bad_alloc const&)
{
return E_OUTOFMEMORY;
}
}
16 changes: 10 additions & 6 deletions src/interfaces/dnmdowner.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ struct IDNMDOwner : IUnknown
virtual mdhandle_t MetaData() = 0;
};

class DNMDOwner;

// We use a reference wrapper around the handle to allow the handle to be swapped out.
// We plan to use swapping to implement table sorting as DNMD itself does not support
// sorting tables or remapping tokens.
Expand All @@ -27,9 +29,9 @@ struct IDNMDOwner : IUnknown
class mdhandle_view final
{
private:
IDNMDOwner* _owner;
DNMDOwner* _owner;
public:
explicit mdhandle_view(IDNMDOwner* owner)
explicit mdhandle_view(DNMDOwner* owner)
: _owner{ owner }
{
}
Expand All @@ -42,10 +44,7 @@ class mdhandle_view final

mdhandle_view& operator=(mdhandle_view&& other) = default;

mdhandle_t get() const
{
return _owner->MetaData();
}
mdhandle_t get() const;

bool operator==(std::nullptr_t) const
{
Expand Down Expand Up @@ -110,4 +109,9 @@ class DNMDOwner final : public TearOffBase<IDNMDOwner>
}
};

inline mdhandle_t mdhandle_view::get() const
{
return _owner->MetaData();
}

#endif // !_SRC_INTERFACES_DNMDOWNER_HPP_
2 changes: 2 additions & 0 deletions src/interfaces/iids.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

// Define the IMetaDataImport IID here - cor.h provides the declaration.
MIDL_DEFINE_GUID(IID_IMetaDataDispenser,0x809c652e,0x7396,0x11d2,0x97,0x71,0x00,0xa0,0xc9,0xb4,0xd5,0x0c);
MIDL_DEFINE_GUID(IID_IMetaDataDispenserEx, 0x31bcfce2, 0xdafb, 0x11d2, 0x9f, 0x81, 0x0, 0xc0, 0x4f, 0x79, 0xa0, 0xa3);
MIDL_DEFINE_GUID(IID_IMetaDataImport,0x7dac8207,0xd3ae,0x4c75,0x9b,0x67,0x92,0x80,0x1a,0x49,0x7d,0x44);
MIDL_DEFINE_GUID(IID_IMetaDataImport2,0xfce5efa0,0x8bba,0x4f8e,0xa0,0x36,0x8f,0x20,0x22,0xb0,0x84,0x66);
MIDL_DEFINE_GUID(IID_IMetaDataAssemblyImport,0xee62470b,0xe94b,0x424e,0x9b,0x7c,0x2f,0x00,0xc9,0x24,0x9f,0x93);
Expand All @@ -20,6 +21,7 @@ MIDL_DEFINE_GUID(IID_IMetaDataAssemblyEmit, 0x211ef15b, 0x5317, 0x4438, 0xb1, 0x
MIDL_DEFINE_GUID(IID_ISymUnmanagedBinder, 0xaa544d42, 0x28cb, 0x11d3, 0xbd, 0x22, 0x00, 0x00, 0xf8, 0x08, 0x49, 0xbd);

// Define option IIDs here - cor.h provides the declaration.
MIDL_DEFINE_GUID(MetaDataThreadSafetyOptions, 0xf7559806, 0xf266, 0x42ea, 0x8c, 0x63, 0xa, 0xdb, 0x45, 0xe8, 0xb2, 0x34);
MIDL_DEFINE_GUID(CLSID_CLR_v2_MetaData, 0xefea471a, 0x44fd, 0x4862, 0x92, 0x92, 0xc, 0x58, 0xd4, 0x6e, 0x1f, 0x3a);

// Define an IID for our own marker interface
Expand Down
Loading

0 comments on commit 9e7cd7e

Please sign in to comment.