Merge pull request #14619 from briansull/emitter-cleanup
[platform/upstream/coreclr.git] / src / vm / comcache.h
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 // ComCache.h
5 //
6
7 //
8 // Classes/Structures used to represent and store info on COM interfaces and contexts.
9
10
11 #ifndef _H_COMCACHE
12 #define _H_COMCACHE
13
14 #ifndef FEATURE_COMINTEROP
15 #error FEATURE_COMINTEROP is required for this file
16 #endif // FEATURE_COMINTEROP
17
18 #include "contxt.h"
19 #include "ctxtcall.h"
20
21 //================================================================
22 // Forward declarations.
23 class CtxEntryCache;
24 class CtxEntry;
25 class Thread;
26
27 //================================================================
28 // OLE32 helpers.
29 HRESULT             wCoMarshalInterThreadInterfaceInStream(REFIID riid, LPUNKNOWN pUnk, LPSTREAM* ppStm);
30 STDAPI_(LPSTREAM)   CreateMemStm(DWORD cb, BYTE** ppBuf);
31
32
33 typedef DPTR(CtxEntry) PTR_CtxEntry;
34
35 //==============================================================
36 // An entry representing a COM+ 1.0 context or an appartment.
37 class CtxEntry
38 {
39     // The CtxEntryCache needs to be able to see the internals
40     // of the CtxEntry.
41     friend CtxEntryCache;
42
43     // NewHolder<CtxEntry> needs to be able to call the destructor of CtxEntry.
44     // DISABLE Warning C4396, the inline specifier cannot be used when a friend declaration refers to a specialization of a function template
45 #pragma warning(push)           // store original warning levels
46 #pragma warning(disable: 4396)
47     friend void Delete<CtxEntry>(CtxEntry *);
48 #pragma warning(pop)            // restore original warning levels
49
50
51 private:
52     // Disallow creation and deletion of the CtxEntries.
53     CtxEntry(LPVOID pCtxCookie, Thread* pSTAThread);
54     ~CtxEntry();
55
56     // Initialization method called from the CtxEntryCache.
57     VOID Init();
58     
59 public:
60     // Add a reference to the CtxEntry.
61     DWORD AddRef();
62
63     // Release a reference to the CtxEntry.
64     DWORD Release();
65     
66     // Function to enter the context. The specified callback function will
67     // be called from within the context.
68     HRESULT EnterContext(PFNCTXCALLBACK pCallbackFunc, LPVOID pData);
69
70     // Accessor for the context cookie.
71     LPVOID GetCtxCookie()
72     {
73         LIMITED_METHOD_CONTRACT;
74         return m_pCtxCookie;
75     }
76
77     // Accessor for the STA thread.
78     Thread* GetSTAThread()
79     {
80         LIMITED_METHOD_CONTRACT;
81         return m_pSTAThread;
82     }
83
84 private:
85     // Callback function called by DoCallback.
86     static HRESULT __stdcall EnterContextCallback(ComCallData* pData);
87
88     LPVOID          m_pCtxCookie;           // The OPAQUE context cookie.
89     IUnknown*       m_pObjCtx;              // The object context interface.
90     DWORD           m_dwRefCount;           // The ref count.
91     Thread*         m_pSTAThread;           // STA thread associated with the context, if any
92 };
93
94 //==============================================================
95 // IUnkEntry: represent a single COM component 
96 struct IUnkEntry
97 {
98     // The context entry needs to be a friend to be able to call InitSpecial.
99     friend CtxEntry;
100     // RCW need to access IUnkEntry
101     friend RCW;
102
103 #ifdef _DEBUG
104     // Does not throw if m_pUnknown is no longer valid, debug only.
105     IUnknown *GetRawIUnknown_NoAddRef_NoThrow()
106     {
107         LIMITED_METHOD_CONTRACT;
108         _ASSERTE(m_pUnknown != NULL && m_pUnknown != (IUnknown*)0xBADF00D);
109         
110         return m_pUnknown;
111     }
112 #endif // _DEBUG
113
114     IUnknown *GetRawIUnknown_NoAddRef()
115     {
116         CONTRACTL
117         {
118             THROWS;
119             MODE_ANY;
120         }
121         CONTRACTL_END;
122
123         IUnknown *pUnk = m_pUnknown;
124 #ifndef DACCESS_COMPILE
125         if (pUnk == (IUnknown *)0xBADF00D)
126         {
127             // All callers of this method had checked the pUnk before so this must be a race.
128             COMPlusThrow(kInvalidComObjectException, IDS_EE_COM_OBJECT_RELEASE_RACE);
129         }
130 #endif // !DACCESS_COMPILE
131
132         return pUnk;
133     }
134
135     LPVOID GetCtxCookie()
136     {
137         LIMITED_METHOD_CONTRACT;
138         
139         return m_pCtxCookie;
140     }
141
142     // Is the RCW disconnected from its COM object?
143     inline bool IsDisconnected()
144     {
145         LIMITED_METHOD_CONTRACT;
146         return (m_pUnknown == (IUnknown*)0xBADF00D || 
147            (GetCtxEntry() != NULL && m_pCtxCookie != GetCtxEntry()->GetCtxCookie()));
148     }
149     
150     
151 private :    
152     // Initialize the entry, returns true if we are in an STA.
153     // We assert inside Init that this IUnkEntry is indeed within a RCW
154     void Init(IUnknown* pUnk, BOOL bIsFreeThreaded, Thread *pThread DEBUGARG(RCW *pRCW));
155
156     // Release the interface pointer held by the IUnkEntry.
157     VOID ReleaseInterface(RCW *pRCW);
158
159     // Free the IUnknown entry. ReleaseInterface must have been called.
160     VOID Free();
161
162     // Get the RCW associated with this IUnkEntry
163     // We assert inside Init that this IUnkEntry is indeed within a RCW
164     RCW *GetRCW();
165     
166     // Get IUnknown for the current context from IUnkEntry
167     IUnknown* GetIUnknownForCurrContext(bool fNoAddRef);
168
169     // Unmarshal IUnknown for the current context from IUnkEntry
170     IUnknown* UnmarshalIUnknownForCurrContext();
171
172     // Release the stream. This will force UnmarshalIUnknownForCurrContext to transition
173     // into the context that owns the IP and re-marshal it to the stream.
174     void ReleaseStream();
175
176     // Indicates if the COM component being wrapped by the IUnkEntry aggregates the FTM
177     inline bool IsFreeThreaded();
178
179     // Indicates if the COM component being wrapped by the IUnkEntry implements INoMashal.
180     inline bool IsMarshalingInhibited();
181
182     VOID CheckValidIUnkEntry();
183     
184     HRESULT HRCheckValidIUnkEntry();
185
186     // Unmarshal IUnknown for the current context if the lock is held
187     IUnknown* UnmarshalIUnknownForCurrContextHelper();
188
189     // Fix for if the lock is held that works on a stack allocated stream
190     // instead of the member variable stream
191     static HRESULT MarshalIUnknownToStreamCallback2(LPVOID pData);
192
193     // Callback called to marshal the IUnknown into a stream lazily.
194     static HRESULT MarshalIUnknownToStreamCallback(LPVOID pData);
195
196     // Helper function called from MarshalIUnknownToStreamCallback.
197     HRESULT MarshalIUnknownToStream();
198
199     // Method to try and start updating the the entry.
200     bool TryUpdateEntry();
201     
202     // Method to end updating the entry.
203     VOID EndUpdateEntry();
204     
205     // Helper function to determine if a COM component aggregates the FTM.
206     static bool IsComponentFreeThreaded(IUnknown *pUnk);
207
208     inline PTR_CtxEntry GetCtxEntry()
209     {
210         LIMITED_METHOD_DAC_CONTRACT;
211
212         PTR_CtxEntry pCtxEntry = dac_cast<PTR_CtxEntry>(dac_cast<TADDR>(m_pCtxEntry) & ~1);
213
214         return pCtxEntry;
215     }
216
217     // Context cookie at the point where we acquired the interface pointer
218     LPVOID          m_pCtxCookie;
219
220     // Context entry representing the context where we acquired the interface pointer.
221     // We use the lowest bit for synchronization and we rely on the fact that the
222     // context itself (the rest of the bits) does not change throughout the lifetime
223     // of this object.
224     PTR_CtxEntry    m_pCtxEntry;    
225
226     // IUnknown interface
227     IUnknown*       m_pUnknown;
228
229     // IStream used for marshalling
230     IStream*        m_pStream;
231 };
232
233 // Don't use this directly as the methodtable could have been released
234 //  by an AD Unload.
235 typedef MethodTable* IE_METHODTABLE_PTR;
236
237 //==============================================================
238 // Interface Entry represents a single COM IP
239 struct InterfaceEntry
240 {
241     // Initialize the entry, returns true on success (i.e. the entry was free).
242     bool Init(MethodTable* pMT, IUnknown* pUnk);
243
244     // Helper to determine if the entry is free.
245     BOOL IsFree();
246
247     // Mark the entry as free.
248     void Free();
249
250     // Member of the entry. These must be volatile so the compiler
251     // will not try and optimize reads and writes to them.
252     Volatile<IE_METHODTABLE_PTR> m_pMT;                  // Interface asked for
253     Volatile<IUnknown*>          m_pUnknown;             // Result of query    
254 };
255
256 class CtxEntryCacheTraits : public DefaultSHashTraits<CtxEntry *>
257 {
258 public:
259     typedef LPVOID key_t;
260     static CtxEntry *Null()                     { LIMITED_METHOD_CONTRACT; return NULL; }
261     static bool IsNull(CtxEntry *e)             { LIMITED_METHOD_CONTRACT; return (e == NULL); }
262     static const LPVOID GetKey(CtxEntry *e)     { LIMITED_METHOD_CONTRACT; return e->GetCtxCookie(); }
263     static count_t Hash(LPVOID key_t)           { LIMITED_METHOD_CONTRACT; return (count_t) key_t; }
264     static BOOL Equals(LPVOID lhs, LPVOID rhs)  { LIMITED_METHOD_CONTRACT; return (lhs == rhs); }
265     static CtxEntry *Deleted()                  { LIMITED_METHOD_CONTRACT; return (CtxEntry *)-1; }
266     static bool IsDeleted(CtxEntry *e)          { LIMITED_METHOD_CONTRACT; return e == (CtxEntry *)-1; }
267 };
268
269 //==============================================================
270 // The cache of context entries.
271 class CtxEntryCache
272 {
273     // The CtxEntry needs to be able to call some of the private
274     // method of the CtxEntryCache.
275     friend CtxEntry;
276
277 private:
278     // Disallow creation and deletion of the CtxEntryCache.
279     CtxEntryCache();
280     ~CtxEntryCache();
281
282 public:
283     // Static initialization routine for the CtxEntryCache.
284     static VOID Init();
285
286     // Static accessor for the one and only instance of the CtxEntryCache.
287     static CtxEntryCache *GetCtxEntryCache();
288
289     // Method to retrieve/create a CtxEntry for the specified context cookie.
290     CtxEntry *FindCtxEntry(LPVOID pCtxCookie, Thread *pSTAThread);
291     
292 private:
293     CtxEntry * CreateCtxEntry(LPVOID pCtxCookie, Thread * pSTAThread);
294
295     // Helper function called from the CtxEntry.
296     void TryDeleteCtxEntry(LPVOID pCtxCookie);
297
298     SHash<CtxEntryCacheTraits>  m_CtxEntryHash;
299
300     // spin lock for fast synchronization
301     SpinLock                m_Lock;
302     
303     // The one and only instance for the context entry cache.
304     static CtxEntryCache*   s_pCtxEntryCache;
305 };
306
307 #endif