Merge pull request #14619 from briansull/emitter-cleanup
[platform/upstream/coreclr.git] / src / vm / comcache.cpp
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
5
6
7 #include "common.h"
8
9 #include <crtwrap.h>
10 #include "comcache.h"
11 #include "runtimecallablewrapper.h"
12 #include "mtx.h"
13 #include "contxt.h"
14 #include "ctxtcall.h"
15 #include "win32threadpool.h"
16 #include "mdaassistants.h"
17
18 #ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT
19 #include "olecontexthelpers.h"
20 #endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT
21
22
23 //================================================================
24 // Guid definitions.
25 const GUID IID_IEnterActivityWithNoLock = { 0xd7174f82, 0x36b8, 0x4aa8, { 0x80, 0x0a, 0xe9, 0x63, 0xab, 0x2d, 0xfa, 0xb9 } };
26 const GUID IID_IFuncEvalAbort           = { 0xde6844f6, 0x95ac, 0x4e83, { 0x90, 0x8d, 0x9b, 0x1b, 0xea, 0x2f, 0xe0, 0x8c } };
27
28 // sanity check., to find stress bug #82137
29 VOID IUnkEntry::CheckValidIUnkEntry()
30 {
31     CONTRACTL
32     {
33         THROWS;
34         GC_TRIGGERS;
35         MODE_ANY;
36     }
37     CONTRACTL_END;
38         
39     if (IsDisconnected())
40     {
41         COMPlusThrow(kInvalidComObjectException, IDS_EE_COM_OBJECT_NO_LONGER_HAS_WRAPPER);
42     }
43 }
44
45 // Version that returns an HR instead of throwing.
46 HRESULT IUnkEntry::HRCheckValidIUnkEntry()
47 {
48     CONTRACTL
49     {
50         NOTHROW;
51         GC_NOTRIGGER;
52         MODE_ANY;
53     }
54     CONTRACTL_END;
55     
56     if (IsDisconnected())
57     {
58         return COR_E_INVALIDCOMOBJECT;
59     }
60     
61     return S_OK;
62 }
63
64 // Returns IErrorInfo corresponding to the exception injected by the debugger to abort a func eval,
65 // or NULL if there is no such exception.
66 static IErrorInfo *CheckForFuncEvalAbortNoThrow(HRESULT hr)
67 {
68     CONTRACTL
69     {
70         NOTHROW;
71         GC_TRIGGERS;
72         MODE_ANY;
73     }
74     CONTRACTL_END;
75
76     // the managed exception thrown by the debugger is translated to EXCEPTION_COMPLUS by COM
77     if (hr == EXCEPTION_COMPLUS)
78     {
79         GCX_PREEMP();
80
81         // we recognize the ones thrown by the debugger by QI'ing the IErrorInfo for a special IID
82         ReleaseHolder<IErrorInfo> pErrorInfo;
83         if (SafeGetErrorInfo(&pErrorInfo) == S_OK)
84         {
85             ReleaseHolder<IUnknown> pUnk;
86             if (SafeQueryInterface(pErrorInfo, IID_IFuncEvalAbort, &pUnk) == S_OK)
87             {
88                 // QI succeeded, this is a func eval abort
89                 return pErrorInfo.Extract();
90             }
91             else
92             {
93                 // QI failed, put the IErrorInfo back
94                 SetErrorInfo(0, pErrorInfo);
95             }
96         }
97     }
98
99     return NULL;
100 }
101
102 // Rethrows the exception injected by the debugger to abort a func eval, or does nothing if there is no such exception.
103 static void CheckForFuncEvalAbort(HRESULT hr)
104 {
105     CONTRACTL
106     {
107         THROWS;
108         GC_TRIGGERS;
109         MODE_ANY;
110     }
111     CONTRACTL_END;
112
113     IErrorInfo *pErrorInfo = CheckForFuncEvalAbortNoThrow(hr);
114     if (pErrorInfo != NULL)
115     {
116         // COMPlusThrowHR internally releases the pErrorInfo
117         COMPlusThrowHR(hr, pErrorInfo);
118     }
119 }
120
121 //+-------------------------------------------------------------------------
122 //
123 //  Function: STDAPI_(LPSTREAM) CreateMemStm(DWORD cb, BYTE** ppBuf))
124 //  Create a stream in the memory
125 // 
126 STDAPI_(LPSTREAM) CreateMemStm(DWORD cb, BYTE** ppBuf)
127 {
128     CONTRACT(LPSTREAM)
129     {
130         NOTHROW;
131         GC_NOTRIGGER;
132         MODE_PREEMPTIVE;
133         INJECT_FAULT(CONTRACT_RETURN NULL);
134         PRECONDITION(CheckPointer(ppBuf, NULL_OK));
135         PRECONDITION(CheckPointer(ppBuf, NULL_OK));
136         POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
137     }
138     CONTRACT_END;
139
140     LPSTREAM        pstm = NULL;
141     
142     BYTE* pMem = new(nothrow) BYTE[cb];
143     if (pMem)
144     {
145         HRESULT hr = CInMemoryStream::CreateStreamOnMemory(pMem, cb, &pstm, TRUE);
146         _ASSERTE(hr == S_OK || pstm == NULL);
147     }
148
149     if(ppBuf)
150         *ppBuf = pMem;
151
152     RETURN pstm;
153 }
154
155 //=====================================================================
156 // HRESULT wCoMarshalInterThreadInterfaceInStream
157 HRESULT wCoMarshalInterThreadInterfaceInStream(REFIID riid,
158                                        LPUNKNOWN pUnk,
159                                        LPSTREAM *ppStm)
160 {
161 #ifdef PLATFORM_CE
162     return E_NOTIMPL;
163 #endif // !PLATFORM_CE
164
165     CONTRACTL
166     {
167         NOTHROW;
168         GC_TRIGGERS;
169         MODE_PREEMPTIVE;
170         PRECONDITION(CheckPointer(pUnk));
171         PRECONDITION(CheckPointer(ppStm));
172     }
173     CONTRACTL_END;
174    
175     HRESULT hr;
176     LPSTREAM pStm = NULL;
177
178     DWORD mshlFlgs = MSHLFLAGS_NORMAL;
179
180     ULONG lSize;
181     hr = CoGetMarshalSizeMax(&lSize, IID_IUnknown, pUnk, MSHCTX_INPROC, NULL, mshlFlgs);
182
183     if (hr == S_OK)
184     {
185         // Create a stream
186         pStm = CreateMemStm(lSize, NULL);
187
188         if (pStm != NULL)
189         {
190             // Marshal the interface into the stream TABLE STRONG
191             hr = CoMarshalInterface(pStm, riid, pUnk, MSHCTX_INPROC, NULL, mshlFlgs);
192         }
193         else
194         {
195             hr = E_OUTOFMEMORY;
196         }
197     }
198
199     if (SUCCEEDED(hr))
200     {
201         // Reset the stream to the begining
202         LARGE_INTEGER li;
203         LISet32(li, 0);
204         ULARGE_INTEGER li2;
205         pStm->Seek(li, STREAM_SEEK_SET, &li2);
206
207         // Set the return value
208         *ppStm = pStm;
209     }
210     else
211     {
212         // Cleanup if failure
213         SafeReleasePreemp(pStm);
214         *ppStm = NULL;
215     }
216
217     // Return the result
218     return hr;
219 }
220
221 //================================================================
222 // Struct passed in to DoCallback.
223 struct CtxEntryEnterContextCallbackData
224 {
225     PFNCTXCALLBACK m_pUserCallbackFunc;
226     LPVOID         m_pUserData;
227     LPVOID         m_pCtxCookie;
228     HRESULT        m_UserCallbackHR;
229 #ifdef MDA_SUPPORTED
230     CLREvent*      m_hTimeoutEvent;
231 #endif
232 };
233
234 //================================================================
235 // Static members.
236 CtxEntryCache* CtxEntryCache::s_pCtxEntryCache = NULL;
237
238 CtxEntryCache::CtxEntryCache()
239 {
240     CONTRACTL
241     {
242         THROWS;
243         GC_NOTRIGGER;
244         MODE_ANY;
245     }
246     CONTRACTL_END;
247     
248     m_Lock.Init(LOCK_COMCTXENTRYCACHE);
249     LockOwner lock = {&m_Lock, IsOwnerOfSpinLock};
250 }
251
252 CtxEntryCache::~CtxEntryCache()
253 {
254     CONTRACTL
255     {
256         NOTHROW;
257         GC_NOTRIGGER;
258         MODE_ANY;
259     }
260     CONTRACTL_END;
261
262     for (SHash<CtxEntryCacheTraits>::Iterator it = m_CtxEntryHash.Begin(); it != m_CtxEntryHash.End(); it++)
263     {
264         CtxEntry *pCtxEntry = (CtxEntry *)*it;
265         _ASSERTE(pCtxEntry);
266         LPVOID CtxCookie = pCtxEntry->GetCtxCookie();
267         m_CtxEntryHash.Remove(CtxCookie);
268
269         LOG((LF_INTEROP, LL_INFO100, "Leaked CtxEntry %8.8x with CtxCookie %8.8x, ref count %d\n", pCtxEntry, pCtxEntry->GetCtxCookie(), pCtxEntry->m_dwRefCount));
270         pCtxEntry->m_dwRefCount = 0;
271         delete pCtxEntry;
272     }
273 }
274
275
276 void CtxEntryCache::Init()
277 {
278     CONTRACTL
279     {
280         THROWS;
281         GC_TRIGGERS;
282         MODE_ANY;
283
284         // This should never be called more than once.
285         PRECONDITION(NULL == s_pCtxEntryCache);
286     }
287     CONTRACTL_END;
288
289     // Allocate the one and only instance of the context entry cache.
290     s_pCtxEntryCache = new CtxEntryCache();
291 }
292
293 CtxEntryCache* CtxEntryCache::GetCtxEntryCache()
294 {
295     CONTRACTL
296     {
297         NOTHROW;
298         GC_NOTRIGGER;
299         MODE_ANY;
300         PRECONDITION(CheckPointer(s_pCtxEntryCache));
301     }
302     CONTRACTL_END;
303
304     return s_pCtxEntryCache;
305 }
306
307 CtxEntry* CtxEntryCache::CreateCtxEntry(LPVOID pCtxCookie, Thread * pSTAThread)
308 {
309     CONTRACTL
310     {
311         THROWS;
312         GC_TRIGGERS;
313         MODE_ANY;
314     }
315     CONTRACTL_END;
316
317     CtxEntry * pCtxEntry = NULL;
318     Thread * pThread = GetThread();
319
320     // If we don't already have a context entry for the context cookie,
321     // we need to create one.
322     NewHolder<CtxEntry> pNewCtxEntry = new CtxEntry(pCtxCookie, pSTAThread);
323     // tiggers GC, can't happen when we hold spin lock
324     pNewCtxEntry->Init();
325
326     {
327         TAKE_SPINLOCK_AND_DONOT_TRIGGER_GC(&m_Lock);
328
329         // double check for race
330         pCtxEntry = m_CtxEntryHash.Lookup(pCtxCookie);
331         if (pCtxEntry == NULL)
332         {
333             // We successfully allocated and initialized the entry.
334             m_CtxEntryHash.Add(pNewCtxEntry);
335             pCtxEntry = pNewCtxEntry.Extract();
336         }
337         // We must have an entry now; we need to addref it before
338         // we leave the lock.
339         pCtxEntry->AddRef ();
340     }
341
342     return pCtxEntry;
343 }
344
345 CtxEntry* CtxEntryCache::FindCtxEntry(LPVOID pCtxCookie, Thread *pThread)
346 {
347     CtxEntry *pCtxEntry = NULL;
348     Thread *pSTAThread = NULL;
349
350     CONTRACT (CtxEntry*)
351     {
352         THROWS;
353         GC_TRIGGERS;
354         MODE_ANY;
355         INJECT_FAULT(COMPlusThrowOM());
356         PRECONDITION(CheckPointer(pCtxCookie));
357         POSTCONDITION(CheckPointer(RETVAL));
358         POSTCONDITION(pCtxCookie == pCtxEntry->GetCtxCookie());
359         POSTCONDITION(pSTAThread == pCtxEntry->GetSTAThread());
360     }
361     CONTRACT_END;
362  
363     // Find our STA (if any)
364     if (pThread->GetApartment() == Thread::AS_InSTA)
365     {
366         // We are in an STA thread.  But we may be in a NA context, so do an extra
367         // check for that case.
368         BOOL fNAContext;
369
370         // try the simple cache on Thread first
371         if (pCtxCookie != pThread->GetLastSTACtxCookie(&fNAContext))
372         {
373             APTTYPE type;
374             fNAContext = (SUCCEEDED(GetCurrentApartmentTypeNT5((IObjectContext *)pCtxCookie, &type)) && type == APTTYPE_NA);
375             pThread->SetLastSTACtxCookie(pCtxCookie, fNAContext);
376         }
377
378         if (!fNAContext)
379             pSTAThread = pThread;
380     }
381
382     ASSERT (GetThread ());
383     BOOL bFound = FALSE;
384
385     ACQUIRE_SPINLOCK_NO_HOLDER(&m_Lock, pThread);
386     {
387         // Try to find a context entry for the context cookie.
388         pCtxEntry = m_CtxEntryHash.Lookup(pCtxCookie);
389         if (pCtxEntry)
390         {
391             // We must have an entry now; we need to addref it before
392             // we leave the lock.
393             pCtxEntry->AddRef ();
394             bFound = TRUE;
395         }
396     }
397     RELEASE_SPINLOCK_NO_HOLDER(&m_Lock, pThread);
398
399     if (!bFound)
400     {
401         pCtxEntry = CreateCtxEntry(pCtxCookie, pSTAThread);
402     } 
403
404     // Returned the found or allocated entry.
405     RETURN pCtxEntry;
406 }
407
408
409 void CtxEntryCache::TryDeleteCtxEntry(LPVOID pCtxCookie)
410 {
411     CONTRACTL
412     {
413         NOTHROW;
414         GC_TRIGGERS;
415         MODE_ANY;
416         PRECONDITION(CheckPointer(pCtxCookie));
417     }
418     CONTRACTL_END;
419
420     BOOL bDelete = FALSE;
421     CtxEntry *pCtxEntry = NULL;
422
423     {
424         TAKE_SPINLOCK_AND_DONOT_TRIGGER_GC(&m_Lock);
425
426         // Try to find a context entry for the context cookie.
427         pCtxEntry = m_CtxEntryHash.Lookup(pCtxCookie);
428         if (pCtxEntry)
429         {
430             // If the ref count of the context entry is still 0, then we can 
431             // remove the ctx entry and delete it.
432             if (pCtxEntry->m_dwRefCount == 0)
433             {
434                 // First remove the context entry from the list.
435                 m_CtxEntryHash.Remove(pCtxCookie);
436
437                 // We need to unlock the context entry cache before we delete the 
438                 // context entry since this can cause release to be called on
439                 // an IP which can cause us to re-enter the runtime thus causing a
440                 // deadlock.
441                 // We can now safely delete the context entry.
442                 bDelete = TRUE;
443             }
444         }
445     }
446
447     if (bDelete)
448     {
449         delete pCtxEntry;
450     }
451 }
452
453 //================================================================
454 // Get the RCW associated with this IUnkEntry
455 // We assert inside Init that this IUnkEntry is indeed within a RCW
456 RCW *IUnkEntry::GetRCW()
457 {
458     LIMITED_METHOD_CONTRACT;
459
460     return (RCW *) (((LPBYTE) this) - offsetof(RCW, m_UnkEntry));
461 }
462
463 //================================================================
464 // Initialize the entry
465 void IUnkEntry::Init(
466     IUnknown *pUnk,
467     BOOL bIsFreeThreaded,
468     Thread *pThread
469     DEBUGARG(RCW *pRCW)
470     )
471 {
472     CONTRACTL
473     {
474         THROWS;
475         GC_TRIGGERS;
476         MODE_PREEMPTIVE;
477         PRECONDITION(CheckPointer(pUnk));
478         INDEBUG(PRECONDITION(CheckPointer(pRCW));)
479     }
480     CONTRACTL_END;
481
482     // Make sure this IUnkEntry is part of a RCW so that we can get back to the RCW through offset
483     // if we have to
484     _ASSERTE(((LPBYTE)pRCW) + offsetof(RCW, m_UnkEntry) == (LPBYTE) this);
485         
486     // Find our context cookie
487     LPVOID pCtxCookie = GetCurrentCtxCookie();
488     _ASSERTE(pCtxCookie);
489
490     // Set up IUnkEntry's state.
491     if (bIsFreeThreaded)
492         m_pCtxEntry = NULL;
493     else
494         m_pCtxEntry = CtxEntryCache::GetCtxEntryCache()->FindCtxEntry(pCtxCookie, pThread);
495     
496     m_pUnknown = pUnk;
497     m_pCtxCookie = pCtxCookie;
498     m_pStream = NULL;
499
500     // Sanity check this IUnkEntry.
501     CheckValidIUnkEntry();
502 }
503
504 //================================================================
505 // Release the interface pointer held by the IUnkEntry.
506 VOID IUnkEntry::ReleaseInterface(RCW *pRCW)
507 {
508     CONTRACTL
509     {
510         NOTHROW;
511         GC_TRIGGERS;
512         MODE_PREEMPTIVE;
513     }
514     CONTRACTL_END;
515
516     if (g_fProcessDetach)
517     {
518         // The Release call is unsafe if the process is going away (calls into
519         // DLLs we don't know are even mapped).
520         LogInteropLeak(this);
521     }
522     else
523     {
524         // now release the IUnknown that we hold
525         if ((m_pUnknown != 0) && (m_pUnknown != (IUnknown *)0xBADF00D))
526         {
527             ULONG cbRef = SafeReleasePreemp(m_pUnknown, pRCW);
528             LogInteropRelease(m_pUnknown, cbRef, "IUnkEntry::Free: Releasing the held ref");
529         }
530
531         // mark the entry as dead
532         m_pUnknown = (IUnknown *)0xBADF00D;
533     }        
534 }
535
536 //================================================================
537 // Free the IUnknown entry. ReleaseInterface must have been called.
538 VOID IUnkEntry::Free()
539 {
540     CONTRACTL
541     {
542         NOTHROW;
543         GC_TRIGGERS;
544         MODE_PREEMPTIVE;
545         PRECONDITION(g_fProcessDetach || m_pUnknown == (IUnknown *)0xBADF00D);
546     }
547     CONTRACTL_END;
548
549     // Log the de-allocation of the IUnknown entry.
550     LOG((LF_INTEROP, LL_INFO10000, "IUnkEntry::Free called for context 0x%08X, to release entry with m_pUnknown %p, on thread %p\n", m_pCtxCookie, m_pUnknown, GetThread())); 
551
552     if (g_fProcessDetach)
553     {
554         IStream *pOldStream = m_pStream;
555         if (InterlockedExchangeT(&m_pStream, NULL) == pOldStream)
556             SafeReleasePreemp(pOldStream);
557     }
558     else
559     {
560         IStream *pStream = m_pStream;
561         m_pStream = NULL;
562
563         // This will release the stream, object in the stream and the memory on which the stream was created
564         if (pStream)
565             SafeReleaseStream(pStream);
566
567     }
568
569     // Release the ref count we have on the CtxEntry.
570     CtxEntry *pEntry = GetCtxEntry();
571     if (pEntry)
572     {
573         pEntry->Release();
574         m_pCtxEntry = NULL;
575     }
576 }
577
578 //================================================================
579 // Get IUnknown for the current context from IUnkEntry
580 IUnknown* IUnkEntry::GetIUnknownForCurrContext(bool fNoAddRef)
581 {
582     CONTRACT (IUnknown*)
583     {
584         THROWS;
585         GC_TRIGGERS;
586         MODE_ANY;
587         POSTCONDITION(CheckPointer(RETVAL, (fNoAddRef ? NULL_OK : NULL_NOT_OK)));
588     }
589     CONTRACT_END;
590     
591     IUnknown* pUnk = NULL;
592     LPVOID pCtxCookie = GetCurrentCtxCookie();
593     _ASSERTE(pCtxCookie);
594
595     CheckValidIUnkEntry();
596    
597     if (m_pCtxCookie == pCtxCookie || IsFreeThreaded())
598     {
599         pUnk = GetRawIUnknown_NoAddRef();
600
601         if (!fNoAddRef)
602         {
603             RCW_VTABLEPTR(GetRCW());
604             ULONG cbRef = SafeAddRef(pUnk);
605             LogInteropAddRef(pUnk, cbRef, "IUnkEntry::GetIUnknownForCurrContext: Addref pUnk, passing ref to caller");
606         }
607     }
608
609     if (pUnk == NULL && !fNoAddRef)
610         pUnk = UnmarshalIUnknownForCurrContext();
611
612     RETURN pUnk;
613 }
614
615 //================================================================
616 // Unmarshal IUnknown for the current context from IUnkEntry
617 IUnknown* IUnkEntry::UnmarshalIUnknownForCurrContext()
618 {
619     CONTRACT (IUnknown*)
620     {
621         THROWS;
622         GC_TRIGGERS;
623         MODE_ANY;
624         PRECONDITION(!IsFreeThreaded());
625         POSTCONDITION(CheckPointer(RETVAL));
626     }
627     CONTRACT_END;
628     
629     HRESULT     hrCDH               = S_OK;
630     IUnknown*   pUnk                = NULL;
631     BOOL        fRetry              = TRUE;
632     BOOL        fUnmarshalFailed    = FALSE;
633     BOOL        fCallHelper         = FALSE;
634
635     CheckValidIUnkEntry();
636
637     _ASSERTE(GetCtxEntry() != NULL);
638
639     
640     if(IsMarshalingInhibited() && (m_pCtxCookie != GetCurrentCtxCookie()))
641     {
642         // We want to use an interface in a different context but it can't be marshalled.
643         LOG((LF_INTEROP, LL_INFO100, "IUnkEntry::GetIUnknownForCurrContext failed as the COM object has inhibited marshaling")); 
644         COMPlusThrow(kInvalidCastException, IDS_EE_CANNOTCAST_NOMARSHAL);
645     }
646
647     // Make sure we are in preemptive GC mode before we call out to COM.
648     GCX_PREEMP();
649     
650     // Need to synchronize
651     while (fRetry)
652     {
653         // Marshal the interface to the stream if it hasn't been done yet.
654         if (m_pStream == NULL)
655         {
656             // If context transition failed, we'll return a failure HRESULT
657             // Otherwise, we return S_OK but m_pStream will stay being NULL
658             hrCDH = MarshalIUnknownToStreamCallback(this);
659             CheckForFuncEvalAbort(hrCDH);
660
661 #ifdef MDA_SUPPORTED
662             if (FAILED(hrCDH))
663             {
664                 MDA_TRIGGER_ASSISTANT(DisconnectedContext, ReportViolationDisconnected(m_pCtxCookie, hrCDH));
665             }
666             else if (m_pStream == NULL)
667             {
668                 MDA_TRIGGER_ASSISTANT(NotMarshalable, ReportViolation());
669             }
670 #endif
671         }
672
673         if (TryUpdateEntry())                
674         {
675             // If the interface is not marshalable or if we failed to 
676             // enter the context, then we don't have any choice but to 
677             // use the raw IP.
678             if (m_pStream == NULL)
679             {
680                 // We retrieved an IP so stop retrying.
681                 fRetry = FALSE;
682                     
683                 // Give out this IUnknown we are holding
684                 pUnk = GetRawIUnknown_NoAddRef();
685
686                 RCW_VTABLEPTR(GetRCW());
687                 ULONG cbRef = SafeAddRefPreemp(pUnk);
688
689                 LogInteropAddRef(pUnk, cbRef, "UnmarshalIUnknownForCurrContext handing out raw IUnknown");
690             }
691             else
692             {
693                 // we got control for this entry
694                 // GetInterface for the current context 
695                 HRESULT hr;
696                 hr = CoUnmarshalInterface(m_pStream, IID_IUnknown, (void **)&pUnk);
697
698                 // If the objref in the stream times out, we need to go an marshal into the 
699                 // stream once again.
700                 if (FAILED(hr))
701                 {
702                     _ASSERTE(m_pStream);
703
704                     CheckForFuncEvalAbort(hr);
705
706                     // This should release the stream, object in the stream and the memory on which the stream was created
707                     SafeReleaseStream(m_pStream);                        
708                     m_pStream = NULL;
709
710                     // If unmarshal failed twice, then bail out.
711                     if (fUnmarshalFailed)
712                     {
713                         fRetry = FALSE;
714
715                         // Handing out m_pUnknown in this case would be incorrect. We should fix other places that are doing the same thing in Dev10
716                         // To minimize code changes, throwing E_NOINTERFACE instead
717                         COMPlusThrowHR(E_NOINTERFACE);
718                     }
719
720                     // Remember we failed to unmarshal.
721                     fUnmarshalFailed = TRUE;
722                 }
723                 else
724                 {   
725                     // Reset the stream to the begining
726                     LARGE_INTEGER li;
727                     LISet32(li, 0);
728                     ULARGE_INTEGER li2;
729                     m_pStream->Seek(li, STREAM_SEEK_SET, &li2);
730
731                     // Marshal the interface into the stream with appropriate flags
732                     hr = CoMarshalInterface(m_pStream, 
733                         IID_IUnknown, pUnk, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL);
734
735                     if (FAILED(hr))
736                     {
737                         CheckForFuncEvalAbort(hr);
738
739                         // The proxy is no longer valid. This sometimes manifests itself by
740                         // a failure during re-marshaling it to the stream. When this happens, 
741                         // we need to release the the pUnk we extracted and the stream and try to
742                         // re-create the stream. We don't want to release the stream data since
743                         // we already extracted the proxy from the stream and released it.
744                         RCW_VTABLEPTR(GetRCW());
745                         SafeReleasePreemp(pUnk);
746
747                         SafeReleasePreemp(m_pStream);
748                         m_pStream = NULL;
749                     }
750                     else
751                     {
752                         // Reset the stream to the begining
753                         LISet32(li, 0);
754                         m_pStream->Seek(li, STREAM_SEEK_SET, &li2);
755
756                         // We managed to unmarshal the IP from the stream, stop retrying.
757                         fRetry = FALSE;
758                     }
759                 }
760             }
761         
762             // Done with the entry.
763             EndUpdateEntry();
764         }
765         else
766         {
767             //================================================================
768             // We can potentially collide with the COM+ activity lock so spawn off
769             // another call that does its stream marshalling on the stack without
770             // the need to do locking.
771             fCallHelper = TRUE;
772             fRetry = FALSE;
773         }
774     }
775
776     if (fCallHelper)
777     {
778         // If we hit a collision earlier, spawn off helper that repeats this operation without locking.
779         pUnk = UnmarshalIUnknownForCurrContextHelper();
780     }
781
782     RETURN pUnk;
783 }
784
785 //================================================================
786 // Release the stream. This will force UnmarshalIUnknownForCurrContext to transition
787 // into the context that owns the IP and re-marshal it to the stream.
788 void IUnkEntry::ReleaseStream()
789 {
790     CONTRACTL
791     {
792         NOTHROW;
793         GC_TRIGGERS;
794         MODE_ANY;
795     }
796     CONTRACTL_END;
797     
798     // This should release the stream, object in the stream and the memory on which the stream was created
799     if (m_pStream)
800     {
801         SafeReleaseStream(m_pStream);
802         m_pStream = NULL;
803     }
804 }
805
806 // Indicates if the COM component being wrapped by the IUnkEntry aggreates the FTM
807 bool IUnkEntry::IsFreeThreaded()
808 {
809     LIMITED_METHOD_CONTRACT;
810     return GetRCW()->IsFreeThreaded();
811 }
812
813 // Indicates if the COM component being wrapped by the IUnkEntry implements INoMashal.
814 bool IUnkEntry::IsMarshalingInhibited()
815 {
816     LIMITED_METHOD_CONTRACT;
817     return GetRCW()->IsMarshalingInhibited();
818 }
819
820 // Helper function to marshal the IUnknown pointer to the stream.
821 static HRESULT MarshalIUnknownToStreamHelper(IUnknown * pUnknown, IStream ** ppStream)
822 {
823     CONTRACTL
824     {
825         NOTHROW;
826         GC_TRIGGERS;
827         MODE_ANY;
828     }
829     CONTRACTL_END;
830     
831     IStream *pStream = NULL;
832
833     GCX_PREEMP();
834     
835     // ensure we register this cookie
836     HRESULT hr = wCoMarshalInterThreadInterfaceInStream(IID_IUnknown, pUnknown, &pStream);
837
838     if ((hr == REGDB_E_IIDNOTREG) ||
839         (hr == E_FAIL) ||
840         (hr == E_NOINTERFACE) ||
841         (hr == E_INVALIDARG) ||
842         (hr == E_UNEXPECTED))
843     {
844         // Interface is not marshallable.
845         pStream = NULL;
846         hr      = S_OK;
847     }
848
849     *ppStream = pStream;
850
851     return hr;
852 }
853
854 //================================================================
855 struct StreamMarshalData
856 {
857     IUnkEntry * m_pUnkEntry;
858     IStream * m_pStream;
859 };
860 // Fix for if the lock is held
861 HRESULT IUnkEntry::MarshalIUnknownToStreamCallback2(LPVOID pData)
862 {
863     CONTRACTL
864     {
865         NOTHROW;
866         GC_TRIGGERS;
867         MODE_ANY;
868         PRECONDITION(CheckPointer(pData));
869         PRECONDITION(g_fProcessDetach == FALSE);
870     }
871     CONTRACTL_END;
872
873     HRESULT hr = S_OK;
874     StreamMarshalData *psmd = reinterpret_cast<StreamMarshalData *>(pData);
875
876     // This should never be called during process detach.
877     
878     hr = psmd->m_pUnkEntry->HRCheckValidIUnkEntry();
879     if (hr != S_OK)
880     {
881         // Interface not marshallable
882         // We'll know marshaling failed because m_pStream == NULL
883         return S_OK;
884     }
885     
886     LPVOID pCurrentCtxCookie = GetCurrentCtxCookie();
887     _ASSERTE(pCurrentCtxCookie);
888
889     if (pCurrentCtxCookie == psmd->m_pUnkEntry->m_pCtxCookie)
890     {
891         // We are in the right context marshal the IUnknown to the 
892         // stream directly.
893         hr = MarshalIUnknownToStreamHelper(psmd->m_pUnkEntry->m_pUnknown, &psmd->m_pStream);
894     }
895     else
896     {
897         // Transition into the context to marshal the IUnknown to 
898         // the stream.
899         _ASSERTE(psmd->m_pUnkEntry->GetCtxEntry() != NULL);
900         hr = psmd->m_pUnkEntry->GetCtxEntry()->EnterContext(MarshalIUnknownToStreamCallback2, psmd);
901     }
902
903     return hr;
904 }
905
906 //================================================================
907 // Unmarshal IUnknown for the current context if the lock is held
908 IUnknown* IUnkEntry::UnmarshalIUnknownForCurrContextHelper()
909 {
910     CONTRACT (IUnknown*)
911     {
912         THROWS;
913         GC_TRIGGERS;
914         MODE_ANY;
915         PRECONDITION(!IsFreeThreaded());
916         POSTCONDITION(CheckPointer(RETVAL));
917     }
918     CONTRACT_END;
919     
920     HRESULT hrCDH = S_OK;
921     IUnknown * pUnk = NULL;
922     SafeComHolder<IStream> spStream;
923
924     CheckValidIUnkEntry();
925
926     // Make sure we are in preemptive GC mode before we call out to COM.
927     GCX_PREEMP();
928     
929     // Marshal the interface to the stream. Any call to this function
930     // would be from another apartment so marshalling is needed.
931     StreamMarshalData smd = {this, NULL};
932
933     // If context transition failed, we'll return a failure HRESULT
934     // Otherwise, we return S_OK but m_pStream will stay being NULL
935     hrCDH = MarshalIUnknownToStreamCallback2(&smd);
936     
937     spStream = smd.m_pStream;
938     smd.m_pStream = NULL;
939
940     CheckForFuncEvalAbort(hrCDH);
941
942 #ifdef MDA_SUPPORTED
943     if (FAILED(hrCDH))
944     {
945         MDA_TRIGGER_ASSISTANT(DisconnectedContext, ReportViolationDisconnected(m_pCtxCookie, hrCDH));
946     }
947     else if(spStream == NULL)
948     {
949         MDA_TRIGGER_ASSISTANT(NotMarshalable, ReportViolation());
950     }
951 #endif
952
953     // If the interface is not marshalable or if we failed to 
954     // enter the context, then we don't have any choice but to 
955     // use the raw IP.
956     if (spStream == NULL)
957     {
958         // Give out this IUnknown we are holding
959         pUnk = GetRawIUnknown_NoAddRef();
960     
961         RCW_VTABLEPTR(GetRCW());
962         ULONG cbRef = SafeAddRefPreemp(pUnk);
963         
964         LogInteropAddRef(pUnk, cbRef, "UnmarshalIUnknownForCurrContext handing out raw IUnknown");
965     }
966     else
967     {
968         // we got control for this entry
969         // GetInterface for the current context 
970         HRESULT hr;
971         hr = CoUnmarshalInterface(spStream, IID_IUnknown, reinterpret_cast<void**>(&pUnk));
972         spStream.Release();
973
974         if (FAILED(hr))
975         {
976             CheckForFuncEvalAbort(hr);
977
978             // Give out this IUnknown we are holding
979             pUnk = GetRawIUnknown_NoAddRef();
980
981             RCW_VTABLEPTR(GetRCW());
982             ULONG cbRef = SafeAddRefPreemp(pUnk);
983             
984             LogInteropAddRef(pUnk, cbRef, "UnmarshalIUnknownForCurrContext handing out raw IUnknown");
985         }
986     }
987
988     RETURN pUnk;
989 }
990
991 //================================================================
992 // Callback called to marshal the IUnknown into a stream lazily.
993 HRESULT IUnkEntry::MarshalIUnknownToStreamCallback(LPVOID pData)
994 {
995     CONTRACTL
996     {
997         NOTHROW;
998         GC_TRIGGERS;
999         MODE_ANY;
1000         PRECONDITION(CheckPointer(pData));
1001         PRECONDITION(g_fProcessDetach == FALSE);
1002     }
1003     CONTRACTL_END;
1004
1005     HRESULT hr = S_OK;
1006     IUnkEntry *pUnkEntry = (IUnkEntry *)pData;
1007
1008     // This should never be called during process detach.
1009
1010     hr = pUnkEntry->HRCheckValidIUnkEntry();
1011     if (hr != S_OK)
1012     {
1013         // Interface not marshallable
1014         // We'll know marshaling failed because m_pStream == NULL
1015         return S_OK;
1016     }
1017     
1018     LPVOID pCurrentCtxCookie = GetCurrentCtxCookie();
1019     _ASSERTE(pCurrentCtxCookie);
1020
1021     if (pCurrentCtxCookie == pUnkEntry->m_pCtxCookie)
1022     {
1023         // We are in the right context marshal the IUnknown to the 
1024         // stream directly.
1025         hr = pUnkEntry->MarshalIUnknownToStream();
1026     }
1027     else
1028     {
1029         _ASSERTE(pUnkEntry->GetCtxEntry() != NULL);
1030         
1031         // Transition into the context to marshal the IUnknown to 
1032         // the stream.
1033         hr = pUnkEntry->GetCtxEntry()->EnterContext(MarshalIUnknownToStreamCallback, pUnkEntry);
1034     }
1035
1036     return hr;
1037 }
1038
1039 //================================================================
1040 // Helper function to determine if a COM component aggregates the 
1041 // FTM.
1042 bool IUnkEntry::IsComponentFreeThreaded(IUnknown *pUnk)
1043 {
1044     CONTRACTL
1045     {
1046         THROWS;
1047         GC_TRIGGERS;
1048         MODE_PREEMPTIVE;
1049         PRECONDITION(CheckPointer(pUnk));
1050     }
1051     CONTRACTL_END;
1052
1053     // First see if the object implements the IAgileObject marker interface
1054     SafeComHolderPreemp<IAgileObject> pAgileObject;
1055     HRESULT hr = SafeQueryInterfacePreemp(pUnk, IID_IAgileObject, (IUnknown**)&pAgileObject);
1056     LogInteropQI(pUnk, IID_IAgileObject, hr, "IUnkEntry::IsComponentFreeThreaded: QI for IAgileObject");
1057
1058     if (SUCCEEDED(hr))
1059     {
1060         return true;
1061     }
1062     else
1063     {
1064         SafeComHolderPreemp<IMarshal> pMarshal = NULL;
1065
1066         // If not, then we can try to determine if the component agregates the FTM via IMarshal.
1067         hr = SafeQueryInterfacePreemp(pUnk, IID_IMarshal, (IUnknown **)&pMarshal);
1068         LogInteropQI(pUnk, IID_IMarshal, hr, "IUnkEntry::IsComponentFreeThreaded: QI for IMarshal");
1069         if (SUCCEEDED(hr))
1070         {
1071             CLSID clsid;
1072
1073             // The COM component implements IMarshal so we now check to see if the un-marshal class 
1074             // for this IMarshal is the FTM's un-marshaler.
1075             hr = pMarshal->GetUnmarshalClass(IID_IUnknown, NULL, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL, &clsid);
1076             if (SUCCEEDED(hr) && clsid == CLSID_InProcFreeMarshaler)
1077             {
1078                 // The un-marshaler is indeed the unmarshaler for the FTM so this object 
1079                 // is free threaded.
1080                 return true;
1081             }
1082         }
1083     }
1084
1085     return false;
1086 }
1087
1088 //================================================================
1089 // Helper function to marshal the IUnknown pointer to the stream.
1090 HRESULT IUnkEntry::MarshalIUnknownToStream()
1091 {
1092     CONTRACTL
1093     {
1094         NOTHROW;
1095         GC_TRIGGERS;
1096         MODE_ANY;
1097         
1098         // This must always be called in the right context.
1099         PRECONDITION(m_pCtxCookie == GetCurrentCtxCookie());
1100     }
1101     CONTRACTL_END;
1102     
1103     IStream *pStream = NULL;
1104
1105     GCX_PREEMP();
1106     
1107     HRESULT hr = S_OK;
1108
1109     // ensure we register this cookie
1110     IUnknown *pUnk = m_pUnknown;
1111     if (pUnk == (IUnknown *)0xBADF00D)
1112     {
1113         hr = COR_E_INVALIDCOMOBJECT;
1114     }
1115     else
1116     {
1117         hr = wCoMarshalInterThreadInterfaceInStream(IID_IUnknown, pUnk, &pStream);
1118
1119         if ((hr == REGDB_E_IIDNOTREG) ||
1120             (hr == E_FAIL) ||
1121             (hr == E_NOINTERFACE) ||
1122             (hr == E_INVALIDARG) ||
1123             (hr == E_UNEXPECTED))
1124         {
1125             // Interface is not marshallable.
1126             pStream = NULL;
1127             hr      = S_OK;
1128         }
1129     }
1130
1131     // Try to set the stream in the IUnkEntry. If another thread already set it,
1132     // then we need to release the stream we just set up.
1133     if (FastInterlockCompareExchangePointer(&m_pStream, pStream, NULL) != NULL)
1134         SafeReleaseStream(pStream);
1135
1136     return hr;
1137 }
1138
1139
1140 // Method to try to start updating the entry.
1141 bool IUnkEntry::TryUpdateEntry()
1142 {
1143     WRAPPER_NO_CONTRACT;
1144
1145     CtxEntry *pOldEntry = m_pCtxEntry;
1146     if (((DWORD_PTR)pOldEntry & 1) == 0)
1147     {
1148         CtxEntry *pNewEntry = (CtxEntry *)((DWORD_PTR)pOldEntry | 1);
1149         return (InterlockedExchangeT(&m_pCtxEntry, pNewEntry) == pOldEntry);
1150     }
1151     return false;
1152 }
1153
1154 // Method to end updating the entry.
1155 VOID IUnkEntry::EndUpdateEntry()
1156 {
1157     LIMITED_METHOD_CONTRACT;
1158     
1159     CtxEntry *pOldEntry = m_pCtxEntry;
1160     
1161     // we should hold the lock
1162     _ASSERTE(((DWORD_PTR)pOldEntry & 1) == 1);
1163
1164     CtxEntry *pNewEntry = (CtxEntry *)((DWORD_PTR)pOldEntry & ~1);
1165
1166     // and it's us who resets the bit
1167     VERIFY(InterlockedExchangeT(&m_pCtxEntry, pNewEntry) == pOldEntry);
1168 }
1169
1170
1171 #ifdef MDA_SUPPORTED
1172
1173 // Default to a 60 second timeout
1174 #define MDA_CONTEXT_SWITCH_DEADLOCK_TIMEOUT 60000
1175 #define MDA_CONTEXT_SWITCH_DEADLOCK_ITERATION_COUNT 1000
1176
1177 struct MDAContextSwitchDeadlockArgs
1178 {
1179     CLREvent* hEvent;
1180     LPVOID  OriginContext;
1181     LPVOID  DestinationContext;
1182 };
1183
1184 DWORD WINAPI MDAContextSwitchDeadlockThreadProc(LPVOID lpParameter)
1185 {
1186     CONTRACTL
1187     {
1188         NOTHROW;
1189         GC_TRIGGERS;
1190         MODE_PREEMPTIVE;
1191         PRECONDITION(CheckPointer(MDA_GET_ASSISTANT(ContextSwitchDeadlock)));
1192     }
1193     CONTRACTL_END;
1194
1195     // We need to ensure a thread object has been set up since we will toggle to cooperative GC mode
1196     // inside the wait loop.
1197     Thread *pThread = SetupThreadNoThrow();
1198     if (pThread == NULL)
1199     {
1200         // If we failed to allocate the thread object, we will skip running the watchdog thread
1201         // and simply return.
1202         return 0;
1203     }
1204
1205     DWORD retval = 0;
1206     NewHolder<MDAContextSwitchDeadlockArgs> args = (MDAContextSwitchDeadlockArgs*)lpParameter;
1207
1208     // This interesting piece of code allows us to avoid firing the MDA when a process is
1209     // being debugged while the context transition is in progress. It is needed because
1210     // CLREvent::Wait will timeout after the specified amount of wall time, even if the
1211     // process is broken into the debugger for a portion of the time. By splitting the 
1212     // wait into a bunch of smaller waits, we allow many more step/continue operations to
1213     // occur before we signal the timeout.
1214     for (int i = 0; i < MDA_CONTEXT_SWITCH_DEADLOCK_ITERATION_COUNT; i++)
1215     {
1216         retval = args->hEvent->Wait(MDA_CONTEXT_SWITCH_DEADLOCK_TIMEOUT / MDA_CONTEXT_SWITCH_DEADLOCK_ITERATION_COUNT, FALSE);
1217         if (retval != WAIT_TIMEOUT)
1218             break;
1219
1220         // Transition to cooperative GC mode and back. This will allow us to stop the timeout
1221         // if we are broken in while managed only debugging.
1222         {
1223             GCX_COOP();
1224         }
1225     }
1226
1227     if (retval == WAIT_TIMEOUT)
1228     {
1229         // We didn't transition into the context within the alloted timeout period.
1230         // We'll fire the mda and close the event, but we can't delete is as the
1231         //  thread may still complete the transition and attempt to signal the event.
1232         //  So we'll just leak it and let the transition thread recognize that the
1233         //  event is no longer valid so it can simply delete it.
1234         MDA_TRIGGER_ASSISTANT(ContextSwitchDeadlock, ReportDeadlock(args->OriginContext, args->DestinationContext));
1235         
1236         args->hEvent->CloseEvent();       
1237         return 1;
1238     }
1239
1240     delete args->hEvent;
1241     
1242     return 0;
1243 }
1244
1245
1246 void QueueMDAThread(CtxEntryEnterContextCallbackData* data)
1247 {
1248     CONTRACTL
1249     {
1250         NOTHROW;
1251         GC_TRIGGERS;
1252         MODE_ANY;
1253         PRECONDITION(CheckPointer(data->m_hTimeoutEvent));
1254     }
1255     CONTRACTL_END;
1256
1257     MDAContextSwitchDeadlockArgs* args = NULL;
1258     
1259     EX_TRY
1260     {
1261         args = new MDAContextSwitchDeadlockArgs;
1262         
1263         args->OriginContext = GetCurrentCtxCookie();
1264         args->DestinationContext = data->m_pCtxCookie;
1265         args->hEvent = data->m_hTimeoutEvent;
1266
1267         // Will execute in the Default AppDomain
1268         ThreadpoolMgr::QueueUserWorkItem(MDAContextSwitchDeadlockThreadProc, (LPVOID)args, WT_EXECUTELONGFUNCTION);
1269     }
1270     EX_CATCH
1271     {
1272         delete data->m_hTimeoutEvent;
1273         data->m_hTimeoutEvent = NULL;
1274
1275         if (args)
1276             delete args;
1277     }
1278     EX_END_CATCH(SwallowAllExceptions);
1279 }
1280
1281 #endif // MDA_SUPPORTED
1282
1283
1284 // Initialize the entry, returns true on success (i.e. the entry was free).
1285 bool InterfaceEntry::Init(MethodTable* pMT, IUnknown* pUnk)
1286 {
1287     CONTRACTL
1288     {
1289         NOTHROW;
1290         GC_NOTRIGGER;
1291         MODE_ANY;
1292     }
1293     CONTRACTL_END;
1294
1295     // It is important the fields be set in this order.
1296     if (InterlockedCompareExchangeT(&m_pUnknown, pUnk, NULL) == NULL)
1297     {
1298         m_pMT = (IE_METHODTABLE_PTR)pMT;
1299         return true;
1300     }
1301     return false;
1302 }
1303
1304 // Helper to determine if the entry is free.
1305 BOOL InterfaceEntry::IsFree()
1306 {
1307     LIMITED_METHOD_CONTRACT;
1308     return m_pUnknown.Load() == NULL;
1309 }
1310
1311 void InterfaceEntry::Free()
1312 {
1313     LIMITED_METHOD_CONTRACT;
1314
1315     // We use the m_pUnknown field to synchronize access to the entry so that's the only
1316     // one we need to reset. After all, the set of interfaces that the object is known to
1317     // support is one of the most important debugging cues so let's keep m_pMT intact.
1318     m_pUnknown.Store(NULL);
1319 }
1320
1321 //================================================================
1322 // Constructor for the context entry.
1323 CtxEntry::CtxEntry(LPVOID pCtxCookie, Thread *pSTAThread)
1324 : m_pCtxCookie(pCtxCookie)
1325 , m_pObjCtx(NULL)
1326 , m_dwRefCount(0)
1327 , m_pSTAThread(pSTAThread)
1328 {
1329     WRAPPER_NO_CONTRACT;
1330 }
1331
1332 //================================================================
1333 // Destructor for the context entry.
1334 CtxEntry::~CtxEntry()
1335 {
1336     CONTRACTL
1337     {
1338         NOTHROW;
1339         GC_TRIGGERS;
1340         MODE_ANY;
1341         PRECONDITION(m_dwRefCount == 0);
1342     }
1343     CONTRACTL_END;
1344
1345     // If the context is a valid context then release it.
1346     if (m_pObjCtx && !g_fProcessDetach)
1347     {
1348         SafeRelease(m_pObjCtx);
1349         m_pObjCtx = NULL;
1350     }
1351
1352     // Set the context cookie to 0xBADF00D to indicate the current context
1353     // has been deleted.
1354     m_pCtxCookie = (LPVOID)0xBADF00D;
1355 }
1356
1357 //================================================================
1358 // Initialization method for the context entry.
1359 VOID CtxEntry::Init()
1360 {
1361     CONTRACTL
1362     {
1363         THROWS;
1364         GC_TRIGGERS;
1365         MODE_ANY;
1366         INJECT_FAULT(COMPlusThrowOM());
1367         
1368         // Make sure COM has been started
1369         PRECONDITION(g_fComStarted == TRUE);
1370     }
1371     CONTRACTL_END;
1372
1373     // Retrieve the IObjectContext.
1374     HRESULT hr = GetCurrentObjCtx(&m_pObjCtx);
1375
1376     // In case the call to GetCurrentObjCtx fails (which should never really happen)
1377     // we will throw an exception.
1378     if (FAILED(hr))
1379         COMPlusThrowHR(hr);
1380 }
1381
1382
1383 // Add a reference to the CtxEntry.
1384 DWORD CtxEntry::AddRef()
1385 {
1386     CONTRACTL
1387     {
1388         NOTHROW;
1389         GC_NOTRIGGER;
1390         MODE_ANY;
1391     }
1392     CONTRACTL_END;
1393     
1394     ULONG cbRef = FastInterlockIncrement((LONG*)&m_dwRefCount);
1395     LOG((LF_INTEROP, LL_INFO100, "CtxEntry::Addref %8.8x with %d\n", this, cbRef));
1396     return cbRef;
1397 }
1398
1399
1400 //================================================================
1401 // Method to decrement the ref count of the context entry.
1402 DWORD CtxEntry::Release()
1403 {
1404     CONTRACTL
1405     {
1406         NOTHROW;
1407         GC_TRIGGERS;
1408         MODE_ANY;
1409         PRECONDITION(m_dwRefCount > 0);
1410     }
1411     CONTRACTL_END;
1412     
1413     LPVOID pCtxCookie = m_pCtxCookie;
1414     
1415     LONG cbRef = FastInterlockDecrement((LONG*)&m_dwRefCount);
1416     LOG((LF_INTEROP, LL_INFO100, "CtxEntry::Release %8.8x with %d\n", this, cbRef));
1417
1418     // If the ref count falls to 0, try and delete the ctx entry.
1419     // This might not end up deleting it if another thread tries to
1420     // retrieve this ctx entry at the same time this one tries
1421     // to delete it.
1422     if (cbRef == 0)
1423         CtxEntryCache::GetCtxEntryCache()->TryDeleteCtxEntry(pCtxCookie);
1424
1425     // WARNING: The this pointer cannot be used at this point.
1426     return cbRef;
1427 }
1428
1429 //================================================================
1430 // Method to transition into the context and call the callback
1431 // from within the context.
1432 HRESULT CtxEntry::EnterContext(PFNCTXCALLBACK pCallbackFunc, LPVOID pData)
1433 {  
1434     CONTRACTL
1435     {
1436         NOTHROW;
1437         GC_TRIGGERS;
1438         MODE_ANY;
1439
1440         PRECONDITION(CheckPointer(pCallbackFunc));
1441         PRECONDITION(CheckPointer(pData));
1442         // This should not be called if the this context is the current context.
1443         PRECONDITION(m_pCtxCookie != GetCurrentCtxCookie());
1444     }
1445     CONTRACTL_END;
1446
1447     HRESULT hr = S_OK;
1448
1449     // If we are in process detach, we cannot safely try to enter another context
1450     // since we don't know if OLE32 is still loaded.
1451     if (g_fProcessDetach)
1452     {
1453         LOG((LF_INTEROP, LL_INFO100, "Entering into context 0x08%x has failed since we are in process detach\n", m_pCtxCookie)); 
1454         return RPC_E_DISCONNECTED;
1455     }
1456
1457     // Make sure we are in preemptive GC mode before we call out to COM.
1458     GCX_PREEMP();
1459     
1460     // Prepare the information struct passed into the callback.
1461     CtxEntryEnterContextCallbackData CallbackInfo;
1462     CallbackInfo.m_pUserCallbackFunc = pCallbackFunc;
1463     CallbackInfo.m_pUserData = pData;
1464     CallbackInfo.m_pCtxCookie = m_pCtxCookie;
1465     CallbackInfo.m_UserCallbackHR = E_FAIL;
1466 #ifdef MDA_SUPPORTED
1467     CallbackInfo.m_hTimeoutEvent = NULL;
1468
1469     MdaContextSwitchDeadlock* mda = MDA_GET_ASSISTANT(ContextSwitchDeadlock);
1470     if (mda)
1471     {
1472         EX_TRY
1473         {          
1474             CallbackInfo.m_hTimeoutEvent = new CLREvent();
1475             CallbackInfo.m_hTimeoutEvent->CreateAutoEvent(FALSE);
1476         }
1477         EX_CATCH
1478         {
1479             if (CallbackInfo.m_hTimeoutEvent)
1480             {
1481                 delete CallbackInfo.m_hTimeoutEvent;
1482                 CallbackInfo.m_hTimeoutEvent = NULL;
1483             }
1484         }
1485         EX_END_CATCH(SwallowAllExceptions);
1486     }
1487 #endif // MDA_SUPPORTED
1488
1489     // Retrieve the IContextCallback interface from the IObjectContext.
1490     SafeComHolderPreemp<IContextCallback> pCallback;
1491     hr = SafeQueryInterfacePreemp(m_pObjCtx, IID_IContextCallback, (IUnknown**)&pCallback);
1492     LogInteropQI(m_pObjCtx, IID_IContextCallback, hr, "QI for IID_IContextCallback");
1493     _ASSERTE(SUCCEEDED(hr) && pCallback);
1494
1495     // Setup the callback data structure with the callback Args
1496     ComCallData callBackData;  
1497     callBackData.dwDispid = 0;
1498     callBackData.dwReserved = 0;
1499     callBackData.pUserDefined = &CallbackInfo;
1500
1501 #ifdef MDA_SUPPORTED
1502     // Make sure we don't deadlock when trying to enter the context.
1503     if (mda && CallbackInfo.m_hTimeoutEvent)
1504     {
1505         QueueMDAThread(&CallbackInfo);
1506     }
1507 #endif
1508
1509     EX_TRY
1510     {
1511         hr = ((IContextCallback*)pCallback)->ContextCallback(EnterContextCallback, &callBackData, IID_IEnterActivityWithNoLock, 2, NULL);
1512     }
1513     EX_CATCH
1514     {
1515         hr = GET_EXCEPTION()->GetHR();
1516     }
1517     EX_END_CATCH(SwallowAllExceptions);
1518
1519     if (FAILED(hr))
1520     {
1521         // If the transition failed because of an aborted func eval, simply propagate
1522         // the HRESULT/IErrorInfo back to the caller as we cannot throw here.
1523         SafeComHolder<IErrorInfo> pErrorInfo = CheckForFuncEvalAbortNoThrow(hr);
1524         if (pErrorInfo != NULL)
1525         {
1526             LOG((LF_INTEROP, LL_INFO100, "Entering into context 0x08X has failed since the debugger is blocking it\n", m_pCtxCookie)); 
1527
1528             // put the IErrorInfo back 
1529             SetErrorInfo(0, pErrorInfo);
1530         }
1531         else
1532         {
1533             // The context is disconnected so we cannot transition into it.
1534             LOG((LF_INTEROP, LL_INFO100, "Entering into context 0x08X has failed since the context has disconnected\n", m_pCtxCookie)); 
1535         }
1536     }
1537
1538     return hr;
1539 }
1540
1541
1542 //================================================================
1543 // Callback function called by DoCallback.
1544 HRESULT __stdcall CtxEntry::EnterContextCallback(ComCallData* pComCallData)
1545 {
1546     CONTRACTL
1547     {
1548         NOTHROW;
1549         GC_TRIGGERS;
1550         MODE_PREEMPTIVE;
1551         SO_NOT_MAINLINE;
1552         PRECONDITION(CheckPointer(pComCallData));
1553     }
1554     CONTRACTL_END;
1555
1556     // Retrieve the callback data.
1557     CtxEntryEnterContextCallbackData *pData = (CtxEntryEnterContextCallbackData*)pComCallData->pUserDefined;
1558
1559
1560 #ifdef MDA_SUPPORTED
1561     // If active, signal the MDA watcher so we don't accidentally trigger a timeout.
1562     MdaContextSwitchDeadlock* mda = MDA_GET_ASSISTANT(ContextSwitchDeadlock);
1563     if (mda)
1564     {
1565         // If our watchdog worker is still waiting on us, the event will be valid.
1566         if (pData->m_hTimeoutEvent->IsValid())
1567         {
1568             pData->m_hTimeoutEvent->Set();
1569         }
1570         else
1571         {
1572             // If we did timeout, we will have already cleaned up the event...just delete it now.
1573             delete pData->m_hTimeoutEvent;
1574         }
1575     }
1576 #endif // MDA_SUPPORTED
1577     
1578     Thread *pThread = GetThread();
1579     
1580     // Make sure the thread has been set before we call the user callback function.
1581     if (!pThread)
1582     {
1583         // huh! we are in the middle of shutdown
1584         // and there is no way we can add a new thread
1585         // so let us just return RPC_E_DISCONNECTED
1586         // look at the pCallBack->DoCallback above
1587         // to see why we are returning this SCODE
1588         if(g_fEEShutDown)
1589             return RPC_E_DISCONNECTED;
1590
1591         // Otherwise, we need to create a managed thread object for this new thread
1592         else
1593         {
1594             HRESULT hr;
1595             pThread = SetupThreadNoThrow(&hr);
1596             if (pThread == NULL)
1597                 return hr;
1598         }
1599     }
1600     
1601     // at this point we should be in the right context on NT4,
1602     // if not then it is possible that the actual apartment state for this
1603     // thread has changed and we have stale info in our thread or the CtxEntry
1604     LPVOID pCtxCookie = GetCurrentCtxCookie();
1605     _ASSERTE(pCtxCookie);
1606     if (pData->m_pCtxCookie != pCtxCookie)
1607         return RPC_E_DISCONNECTED;
1608     
1609     // Call the user callback function and store the return value the 
1610     // callback data.
1611     pData->m_UserCallbackHR = pData->m_pUserCallbackFunc(pData->m_pUserData);
1612     
1613     // Return S_OK to indicate the context transition was successfull.
1614     return S_OK;
1615 }