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