Skip to content
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

Add a thread-safe wrapping implementation #54

Merged
merged 7 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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
186 changes: 143 additions & 43 deletions src/interfaces/dispenser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,49 @@
#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,
Expand Down Expand Up @@ -60,16 +95,13 @@ 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)(
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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_
Loading
Loading