summaryrefslogtreecommitdiff
path: root/src/vm/comcache.h
diff options
context:
space:
mode:
authordotnet-bot <dotnet-bot@microsoft.com>2015-01-30 14:14:42 -0800
committerdotnet-bot <dotnet-bot@microsoft.com>2015-01-30 14:14:42 -0800
commitef1e2ab328087c61a6878c1e84f4fc5d710aebce (patch)
treedee1bbb89e9d722e16b0d1485e3cdd1b6c8e2cfa /src/vm/comcache.h
downloadcoreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.tar.gz
coreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.tar.bz2
coreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.zip
Initial commit to populate CoreCLR repo
[tfs-changeset: 1407945]
Diffstat (limited to 'src/vm/comcache.h')
-rw-r--r--src/vm/comcache.h308
1 files changed, 308 insertions, 0 deletions
diff --git a/src/vm/comcache.h b/src/vm/comcache.h
new file mode 100644
index 0000000000..77d3e26860
--- /dev/null
+++ b/src/vm/comcache.h
@@ -0,0 +1,308 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+// ComCache.h
+//
+
+//
+// Classes/Structures used to represent and store info on COM interfaces and contexts.
+
+
+#ifndef _H_COMCACHE
+#define _H_COMCACHE
+
+#ifndef FEATURE_COMINTEROP
+#error FEATURE_COMINTEROP is required for this file
+#endif // FEATURE_COMINTEROP
+
+#include "contxt.h"
+#include "ctxtcall.h"
+
+//================================================================
+// Forward declarations.
+class CtxEntryCache;
+class CtxEntry;
+class Thread;
+
+//================================================================
+// OLE32 helpers.
+HRESULT wCoMarshalInterThreadInterfaceInStream(REFIID riid, LPUNKNOWN pUnk, LPSTREAM* ppStm);
+STDAPI_(LPSTREAM) CreateMemStm(DWORD cb, BYTE** ppBuf);
+
+
+typedef DPTR(CtxEntry) PTR_CtxEntry;
+
+//==============================================================
+// An entry representing a COM+ 1.0 context or an appartment.
+class CtxEntry
+{
+ // The CtxEntryCache needs to be able to see the internals
+ // of the CtxEntry.
+ friend CtxEntryCache;
+
+ // NewHolder<CtxEntry> needs to be able to call the destructor of CtxEntry.
+ // DISABLE Warning C4396, the inline specifier cannot be used when a friend declaration refers to a specialization of a function template
+#pragma warning(push) // store original warning levels
+#pragma warning(disable: 4396)
+ friend void Delete<CtxEntry>(CtxEntry *);
+#pragma warning(pop) // restore original warning levels
+
+
+private:
+ // Disallow creation and deletion of the CtxEntries.
+ CtxEntry(LPVOID pCtxCookie, Thread* pSTAThread);
+ ~CtxEntry();
+
+ // Initialization method called from the CtxEntryCache.
+ VOID Init();
+
+public:
+ // Add a reference to the CtxEntry.
+ DWORD AddRef();
+
+ // Release a reference to the CtxEntry.
+ DWORD Release();
+
+ // Function to enter the context. The specified callback function will
+ // be called from within the context.
+ HRESULT EnterContext(PFNCTXCALLBACK pCallbackFunc, LPVOID pData);
+
+ // Accessor for the context cookie.
+ LPVOID GetCtxCookie()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return m_pCtxCookie;
+ }
+
+ // Accessor for the STA thread.
+ Thread* GetSTAThread()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return m_pSTAThread;
+ }
+
+private:
+ // Callback function called by DoCallback.
+ static HRESULT __stdcall EnterContextCallback(ComCallData* pData);
+
+ LPVOID m_pCtxCookie; // The OPAQUE context cookie.
+ IUnknown* m_pObjCtx; // The object context interface.
+ DWORD m_dwRefCount; // The ref count.
+ Thread* m_pSTAThread; // STA thread associated with the context, if any
+};
+
+//==============================================================
+// IUnkEntry: represent a single COM component
+struct IUnkEntry
+{
+ // The context entry needs to be a friend to be able to call InitSpecial.
+ friend CtxEntry;
+ // RCW need to access IUnkEntry
+ friend RCW;
+
+#ifdef _DEBUG
+ // Does not throw if m_pUnknown is no longer valid, debug only.
+ IUnknown *GetRawIUnknown_NoAddRef_NoThrow()
+ {
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(m_pUnknown != NULL && m_pUnknown != (IUnknown*)0xBADF00D);
+
+ return m_pUnknown;
+ }
+#endif // _DEBUG
+
+ IUnknown *GetRawIUnknown_NoAddRef()
+ {
+ CONTRACTL
+ {
+ THROWS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ IUnknown *pUnk = m_pUnknown;
+#ifndef DACCESS_COMPILE
+ if (pUnk == (IUnknown *)0xBADF00D)
+ {
+ // All callers of this method had checked the pUnk before so this must be a race.
+ COMPlusThrow(kInvalidComObjectException, IDS_EE_COM_OBJECT_RELEASE_RACE);
+ }
+#endif // !DACCESS_COMPILE
+
+ return pUnk;
+ }
+
+ LPVOID GetCtxCookie()
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return m_pCtxCookie;
+ }
+
+ // Is the RCW disconnected from its COM object?
+ inline bool IsDisconnected()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return (m_pUnknown == (IUnknown*)0xBADF00D ||
+ (GetCtxEntry() != NULL && m_pCtxCookie != GetCtxEntry()->GetCtxCookie()));
+ }
+
+
+private :
+ // Initialize the entry, returns true if we are in an STA.
+ // We assert inside Init that this IUnkEntry is indeed within a RCW
+ void Init(IUnknown* pUnk, BOOL bIsFreeThreaded, Thread *pThread DEBUGARG(RCW *pRCW));
+
+ // Release the interface pointer held by the IUnkEntry.
+ VOID ReleaseInterface(RCW *pRCW);
+
+ // Free the IUnknown entry. ReleaseInterface must have been called.
+ VOID Free();
+
+ // Get the RCW associated with this IUnkEntry
+ // We assert inside Init that this IUnkEntry is indeed within a RCW
+ RCW *GetRCW();
+
+ // Get IUnknown for the current context from IUnkEntry
+ IUnknown* GetIUnknownForCurrContext(bool fNoAddRef);
+
+ // Unmarshal IUnknown for the current context from IUnkEntry
+ IUnknown* UnmarshalIUnknownForCurrContext();
+
+ // Release the stream. This will force UnmarshalIUnknownForCurrContext to transition
+ // into the context that owns the IP and re-marshal it to the stream.
+ void ReleaseStream();
+
+ // Indicates if the COM component being wrapped by the IUnkEntry aggregates the FTM
+ inline bool IsFreeThreaded();
+
+ // Indicates if the COM component being wrapped by the IUnkEntry implements INoMashal.
+ inline bool IsMarshalingInhibited();
+
+ VOID CheckValidIUnkEntry();
+
+ HRESULT HRCheckValidIUnkEntry();
+
+ // Unmarshal IUnknown for the current context if the lock is held
+ IUnknown* UnmarshalIUnknownForCurrContextHelper();
+
+ // Fix for if the lock is held that works on a stack allocated stream
+ // instead of the member variable stream
+ static HRESULT MarshalIUnknownToStreamCallback2(LPVOID pData);
+
+ // Callback called to marshal the IUnknown into a stream lazily.
+ static HRESULT MarshalIUnknownToStreamCallback(LPVOID pData);
+
+ // Helper function called from MarshalIUnknownToStreamCallback.
+ HRESULT MarshalIUnknownToStream();
+
+ // Method to try and start updating the the entry.
+ bool TryUpdateEntry();
+
+ // Method to end updating the entry.
+ VOID EndUpdateEntry();
+
+ // Helper function to determine if a COM component aggregates the FTM.
+ static bool IsComponentFreeThreaded(IUnknown *pUnk);
+
+ inline PTR_CtxEntry GetCtxEntry()
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ PTR_CtxEntry pCtxEntry = dac_cast<PTR_CtxEntry>(dac_cast<TADDR>(m_pCtxEntry) & ~1);
+
+ return pCtxEntry;
+ }
+
+ // Context cookie at the point where we acquired the interface pointer
+ LPVOID m_pCtxCookie;
+
+ // Context entry representing the context where we acquired the interface pointer.
+ // We use the lowest bit for synchronization and we rely on the fact that the
+ // context itself (the rest of the bits) does not change throughout the lifetime
+ // of this object.
+ PTR_CtxEntry m_pCtxEntry;
+
+ // IUnknown interface
+ IUnknown* m_pUnknown;
+
+ // IStream used for marshalling
+ IStream* m_pStream;
+};
+
+// Don't use this directly as the methodtable could have been released
+// by an AD Unload.
+typedef MethodTable* IE_METHODTABLE_PTR;
+
+//==============================================================
+// Interface Entry represents a single COM IP
+struct InterfaceEntry
+{
+ // Initialize the entry, returns true on success (i.e. the entry was free).
+ bool Init(MethodTable* pMT, IUnknown* pUnk);
+
+ // Helper to determine if the entry is free.
+ BOOL IsFree();
+
+ // Mark the entry as free.
+ void Free();
+
+ // Member of the entry. These must be volatile so the compiler
+ // will not try and optimize reads and writes to them.
+ Volatile<IE_METHODTABLE_PTR> m_pMT; // Interface asked for
+ Volatile<IUnknown*> m_pUnknown; // Result of query
+};
+
+class CtxEntryCacheTraits : public DefaultSHashTraits<CtxEntry *>
+{
+public:
+ typedef LPVOID key_t;
+ static CtxEntry *Null() { LIMITED_METHOD_CONTRACT; return NULL; }
+ static bool IsNull(CtxEntry *e) { LIMITED_METHOD_CONTRACT; return (e == NULL); }
+ static const LPVOID GetKey(CtxEntry *e) { LIMITED_METHOD_CONTRACT; return e->GetCtxCookie(); }
+ static count_t Hash(LPVOID key_t) { LIMITED_METHOD_CONTRACT; return (count_t) key_t; }
+ static BOOL Equals(LPVOID lhs, LPVOID rhs) { LIMITED_METHOD_CONTRACT; return (lhs == rhs); }
+ static CtxEntry *Deleted() { LIMITED_METHOD_CONTRACT; return (CtxEntry *)-1; }
+ static bool IsDeleted(CtxEntry *e) { LIMITED_METHOD_CONTRACT; return e == (CtxEntry *)-1; }
+};
+
+//==============================================================
+// The cache of context entries.
+class CtxEntryCache
+{
+ // The CtxEntry needs to be able to call some of the private
+ // method of the CtxEntryCache.
+ friend CtxEntry;
+
+private:
+ // Disallow creation and deletion of the CtxEntryCache.
+ CtxEntryCache();
+ ~CtxEntryCache();
+
+public:
+ // Static initialization routine for the CtxEntryCache.
+ static VOID Init();
+
+ // Static accessor for the one and only instance of the CtxEntryCache.
+ static CtxEntryCache *GetCtxEntryCache();
+
+ // Method to retrieve/create a CtxEntry for the specified context cookie.
+ CtxEntry *FindCtxEntry(LPVOID pCtxCookie, Thread *pSTAThread);
+
+private:
+ CtxEntry * CreateCtxEntry(LPVOID pCtxCookie, Thread * pSTAThread);
+
+ // Helper function called from the CtxEntry.
+ void TryDeleteCtxEntry(LPVOID pCtxCookie);
+
+ SHash<CtxEntryCacheTraits> m_CtxEntryHash;
+
+ // spin lock for fast synchronization
+ SpinLock m_Lock;
+
+ // The one and only instance for the context entry cache.
+ static CtxEntryCache* s_pCtxEntryCache;
+};
+
+#endif