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 | #include "common.h" |
7 | |
8 | #include "appdomain.hpp" |
9 | #include "peimagelayout.inl" |
10 | #include "field.h" |
11 | #include "strongnameinternal.h" |
12 | #include "excep.h" |
13 | #include "eeconfig.h" |
14 | #include "gcheaputilities.h" |
15 | #include "eventtrace.h" |
16 | #include "perfcounters.h" |
17 | #include "assemblyname.hpp" |
18 | #include "eeprofinterfaces.h" |
19 | #include "dbginterface.h" |
20 | #ifndef DACCESS_COMPILE |
21 | #include "eedbginterfaceimpl.h" |
22 | #endif |
23 | #include "comdynamic.h" |
24 | #include "mlinfo.h" |
25 | #include "posterror.h" |
26 | #include "assemblynative.hpp" |
27 | #include "shimload.h" |
28 | #include "stringliteralmap.h" |
29 | #include "codeman.h" |
30 | #include "comcallablewrapper.h" |
31 | #include "apithreadstress.h" |
32 | #include "eventtrace.h" |
33 | #include "comdelegate.h" |
34 | #include "siginfo.hpp" |
35 | #include "typekey.h" |
36 | |
37 | #include "caparser.h" |
38 | #include "ecall.h" |
39 | #include "finalizerthread.h" |
40 | #include "threadsuspend.h" |
41 | |
42 | #ifdef FEATURE_PREJIT |
43 | #include "corcompile.h" |
44 | #include "compile.h" |
45 | #endif // FEATURE_PREJIT |
46 | |
47 | #ifdef FEATURE_COMINTEROP |
48 | #include "comtoclrcall.h" |
49 | #include "runtimecallablewrapper.h" |
50 | #include "mngstdinterfaces.h" |
51 | #include "olevariant.h" |
52 | #include "rcwrefcache.h" |
53 | #include "olecontexthelpers.h" |
54 | #endif // FEATURE_COMINTEROP |
55 | |
56 | #include "typeequivalencehash.hpp" |
57 | |
58 | #include "appdomain.inl" |
59 | #include "typeparse.h" |
60 | #include "mdaassistants.h" |
61 | #include "threadpoolrequest.h" |
62 | |
63 | #include "nativeoverlapped.h" |
64 | |
65 | #ifndef FEATURE_PAL |
66 | #include "dwreport.h" |
67 | #endif // !FEATURE_PAL |
68 | |
69 | #include "stringarraylist.h" |
70 | |
71 | #include "../binder/inc/clrprivbindercoreclr.h" |
72 | |
73 | |
74 | #include "clrprivtypecachewinrt.h" |
75 | |
76 | // this file handles string conversion errors for itself |
77 | #undef MAKE_TRANSLATIONFAILED |
78 | |
79 | // Define these macro's to do strict validation for jit lock and class |
80 | // init entry leaks. This defines determine if the asserts that |
81 | // verify for these leaks are defined or not. These asserts can |
82 | // sometimes go off even if no entries have been leaked so this |
83 | // defines should be used with caution. |
84 | // |
85 | // If we are inside a .cctor when the application shut's down then the |
86 | // class init lock's head will be set and this will cause the assert |
87 | // to go off. |
88 | // |
89 | // If we are jitting a method when the application shut's down then |
90 | // the jit lock's head will be set causing the assert to go off. |
91 | |
92 | //#define STRICT_CLSINITLOCK_ENTRY_LEAK_DETECTION |
93 | |
94 | static const WCHAR DEFAULT_DOMAIN_FRIENDLY_NAME[] = W("DefaultDomain" ); |
95 | static const WCHAR OTHER_DOMAIN_FRIENDLY_NAME_PREFIX[] = W("Domain" ); |
96 | |
97 | #define STATIC_OBJECT_TABLE_BUCKET_SIZE 1020 |
98 | |
99 | //#define _DEBUG_ADUNLOAD 1 |
100 | |
101 | // Statics |
102 | |
103 | SPTR_IMPL(AppDomain, AppDomain, m_pTheAppDomain); |
104 | SPTR_IMPL(SystemDomain, SystemDomain, m_pSystemDomain); |
105 | SVAL_IMPL(ArrayListStatic, SystemDomain, m_appDomainIndexList); |
106 | SVAL_IMPL(BOOL, SystemDomain, s_fForceDebug); |
107 | SVAL_IMPL(BOOL, SystemDomain, s_fForceProfiling); |
108 | SVAL_IMPL(BOOL, SystemDomain, s_fForceInstrument); |
109 | |
110 | #ifndef DACCESS_COMPILE |
111 | |
112 | // Base Domain Statics |
113 | CrstStatic BaseDomain::m_SpecialStaticsCrst; |
114 | |
115 | int BaseDomain::m_iNumberOfProcessors = 0; |
116 | |
117 | // System Domain Statics |
118 | GlobalStringLiteralMap* SystemDomain::m_pGlobalStringLiteralMap = NULL; |
119 | |
120 | DECLSPEC_ALIGN(16) |
121 | static BYTE g_pSystemDomainMemory[sizeof(SystemDomain)]; |
122 | |
123 | #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING |
124 | size_t SystemDomain::m_totalSurvivedBytes = 0; |
125 | #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING |
126 | |
127 | CrstStatic SystemDomain::m_SystemDomainCrst; |
128 | CrstStatic SystemDomain::m_DelayedUnloadCrst; |
129 | |
130 | ULONG SystemDomain::s_dNumAppDomains = 0; |
131 | |
132 | ArrayListStatic SystemDomain::m_appDomainIdList; |
133 | |
134 | DWORD SystemDomain::m_dwLowestFreeIndex = 0; |
135 | |
136 | |
137 | |
138 | // comparison function to be used for matching clsids in our clsid hash table |
139 | BOOL CompareCLSID(UPTR u1, UPTR u2) |
140 | { |
141 | CONTRACTL |
142 | { |
143 | THROWS; |
144 | GC_TRIGGERS; |
145 | MODE_ANY; |
146 | SO_INTOLERANT; |
147 | INJECT_FAULT(COMPlusThrowOM();); |
148 | } |
149 | CONTRACTL_END; |
150 | |
151 | GUID *pguid = (GUID *)(u1 << 1); |
152 | _ASSERTE(pguid != NULL); |
153 | |
154 | MethodTable *pMT= (MethodTable *)u2; |
155 | _ASSERTE(pMT!= NULL); |
156 | |
157 | GUID guid; |
158 | pMT->GetGuid(&guid, TRUE); |
159 | if (!IsEqualIID(guid, *pguid)) |
160 | return FALSE; |
161 | |
162 | return TRUE; |
163 | } |
164 | |
165 | #ifndef CROSSGEN_COMPILE |
166 | // Constructor for the LargeHeapHandleBucket class. |
167 | LargeHeapHandleBucket::LargeHeapHandleBucket(LargeHeapHandleBucket *pNext, DWORD Size, BaseDomain *pDomain, BOOL bCrossAD) |
168 | : m_pNext(pNext) |
169 | , m_ArraySize(Size) |
170 | , m_CurrentPos(0) |
171 | , m_CurrentEmbeddedFreePos(0) // hint for where to start a search for an embedded free item |
172 | { |
173 | CONTRACTL |
174 | { |
175 | THROWS; |
176 | GC_TRIGGERS; |
177 | MODE_COOPERATIVE; |
178 | PRECONDITION(CheckPointer(pDomain)); |
179 | INJECT_FAULT(COMPlusThrowOM();); |
180 | } |
181 | CONTRACTL_END; |
182 | |
183 | PTRARRAYREF HandleArrayObj; |
184 | |
185 | // Allocate the array in the large object heap. |
186 | if (!bCrossAD) |
187 | { |
188 | OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED); |
189 | HandleArrayObj = (PTRARRAYREF)AllocateObjectArray(Size, g_pObjectClass, TRUE); |
190 | } |
191 | else |
192 | { |
193 | // During AD creation we don't want to assign the handle array to the currently running AD but |
194 | // to the AD being created. Ensure that AllocateArrayEx doesn't set the AD and then set it here. |
195 | AppDomain *pAD = pDomain->AsAppDomain(); |
196 | _ASSERTE(pAD); |
197 | _ASSERTE(pAD->IsBeingCreated()); |
198 | |
199 | OBJECTREF array; |
200 | { |
201 | OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED); |
202 | array = AllocateArrayEx( |
203 | ClassLoader::LoadArrayTypeThrowing(g_pObjectClass), |
204 | (INT32 *)(&Size), |
205 | 1, |
206 | TRUE |
207 | DEBUG_ARG(TRUE)); |
208 | } |
209 | |
210 | array->SetAppDomain(pAD); |
211 | |
212 | HandleArrayObj = (PTRARRAYREF)array; |
213 | } |
214 | |
215 | // Retrieve the pointer to the data inside the array. This is legal since the array |
216 | // is located in the large object heap and is guaranteed not to move. |
217 | m_pArrayDataPtr = (OBJECTREF *)HandleArrayObj->GetDataPtr(); |
218 | |
219 | // Store the array in a strong handle to keep it alive. |
220 | m_hndHandleArray = pDomain->CreatePinningHandle((OBJECTREF)HandleArrayObj); |
221 | } |
222 | |
223 | |
224 | // Destructor for the LargeHeapHandleBucket class. |
225 | LargeHeapHandleBucket::~LargeHeapHandleBucket() |
226 | { |
227 | CONTRACTL |
228 | { |
229 | NOTHROW; |
230 | GC_NOTRIGGER; |
231 | } |
232 | CONTRACTL_END; |
233 | |
234 | if (m_hndHandleArray) |
235 | { |
236 | DestroyPinningHandle(m_hndHandleArray); |
237 | m_hndHandleArray = NULL; |
238 | } |
239 | } |
240 | |
241 | |
242 | // Allocate handles from the bucket. |
243 | OBJECTREF *LargeHeapHandleBucket::AllocateHandles(DWORD nRequested) |
244 | { |
245 | CONTRACTL |
246 | { |
247 | NOTHROW; |
248 | GC_NOTRIGGER; |
249 | MODE_COOPERATIVE; |
250 | } |
251 | CONTRACTL_END; |
252 | |
253 | _ASSERTE(nRequested > 0 && nRequested <= GetNumRemainingHandles()); |
254 | _ASSERTE(m_pArrayDataPtr == (OBJECTREF*)((PTRARRAYREF)ObjectFromHandle(m_hndHandleArray))->GetDataPtr()); |
255 | |
256 | // Store the handles in the buffer that was passed in |
257 | OBJECTREF* ret = &m_pArrayDataPtr[m_CurrentPos]; |
258 | m_CurrentPos += nRequested; |
259 | |
260 | return ret; |
261 | } |
262 | |
263 | // look for a free item embedded in the table |
264 | OBJECTREF *LargeHeapHandleBucket::TryAllocateEmbeddedFreeHandle() |
265 | { |
266 | CONTRACTL |
267 | { |
268 | NOTHROW; |
269 | GC_NOTRIGGER; |
270 | MODE_COOPERATIVE; |
271 | } |
272 | CONTRACTL_END; |
273 | |
274 | OBJECTREF pPreallocatedSentinalObject = ObjectFromHandle(g_pPreallocatedSentinelObject); |
275 | _ASSERTE(pPreallocatedSentinalObject != NULL); |
276 | |
277 | for (int i = m_CurrentEmbeddedFreePos; i < m_CurrentPos; i++) |
278 | { |
279 | if (m_pArrayDataPtr[i] == pPreallocatedSentinalObject) |
280 | { |
281 | m_CurrentEmbeddedFreePos = i; |
282 | m_pArrayDataPtr[i] = NULL; |
283 | return &m_pArrayDataPtr[i]; |
284 | } |
285 | } |
286 | |
287 | // didn't find it (we don't bother wrapping around for a full search, it's not worth it to try that hard, we'll get it next time) |
288 | |
289 | m_CurrentEmbeddedFreePos = 0; |
290 | return NULL; |
291 | } |
292 | |
293 | |
294 | // Maximum bucket size will be 64K on 32-bit and 128K on 64-bit. |
295 | // We subtract out a small amount to leave room for the object |
296 | // header and length of the array. |
297 | |
298 | #define MAX_BUCKETSIZE (16384 - 4) |
299 | |
300 | // Constructor for the LargeHeapHandleTable class. |
301 | LargeHeapHandleTable::LargeHeapHandleTable(BaseDomain *pDomain, DWORD InitialBucketSize) |
302 | : m_pHead(NULL) |
303 | , m_pDomain(pDomain) |
304 | , m_NextBucketSize(InitialBucketSize) |
305 | , m_pFreeSearchHint(NULL) |
306 | , m_cEmbeddedFree(0) |
307 | { |
308 | CONTRACTL |
309 | { |
310 | THROWS; |
311 | GC_TRIGGERS; |
312 | MODE_COOPERATIVE; |
313 | PRECONDITION(CheckPointer(pDomain)); |
314 | INJECT_FAULT(COMPlusThrowOM();); |
315 | } |
316 | CONTRACTL_END; |
317 | |
318 | #ifdef _DEBUG |
319 | m_pCrstDebug = NULL; |
320 | #endif |
321 | } |
322 | |
323 | |
324 | // Destructor for the LargeHeapHandleTable class. |
325 | LargeHeapHandleTable::~LargeHeapHandleTable() |
326 | { |
327 | CONTRACTL |
328 | { |
329 | NOTHROW; |
330 | GC_NOTRIGGER; |
331 | } |
332 | CONTRACTL_END; |
333 | |
334 | // Delete the buckets. |
335 | while (m_pHead) |
336 | { |
337 | LargeHeapHandleBucket *pOld = m_pHead; |
338 | m_pHead = pOld->GetNext(); |
339 | delete pOld; |
340 | } |
341 | } |
342 | |
343 | //***************************************************************************** |
344 | // |
345 | // LOCKING RULES FOR AllocateHandles() and ReleaseHandles() 12/08/2004 |
346 | // |
347 | // |
348 | // These functions are not protected by any locking in this location but rather the callers are |
349 | // assumed to be doing suitable locking for the handle table. The handle table itself is |
350 | // behaving rather like a thread-agnostic collection class -- it doesn't want to know |
351 | // much about the outside world and so it is just doing its job with no awareness of |
352 | // thread notions. |
353 | // |
354 | // The instance in question is |
355 | // There are two locations you can find a LargeHeapHandleTable |
356 | // 1) there is one in every BaseDomain, it is used to keep track of the static members |
357 | // in that domain |
358 | // 2) there is one in the System Domain that is used for the GlobalStringLiteralMap |
359 | // |
360 | // the one in (2) is not the same as the one that is in the BaseDomain object that corresponds |
361 | // to the SystemDomain -- that one is basically stilborn because the string literals don't go |
362 | // there and of course the System Domain has no code loaded into it -- only regular |
363 | // AppDomains (like Domain 0) actually execute code. As a result handle tables are in |
364 | // practice used either for string literals or for static members but never for both. |
365 | // At least not at this writing. |
366 | // |
367 | // Now it's useful to consider what the locking discipline is for these classes. |
368 | // |
369 | // --------- |
370 | // |
371 | // First case: (easiest) is the statics members |
372 | // |
373 | // Each BaseDomain has its own critical section |
374 | // |
375 | // BaseDomain::AllocateObjRefPtrsInLargeTable takes a lock with |
376 | // CrstHolder ch(&m_LargeHeapHandleTableCrst); |
377 | // |
378 | // it does this before it calls AllocateHandles which suffices. It does not call ReleaseHandles |
379 | // at any time (although ReleaseHandles may be called via AllocateHandles if the request |
380 | // doesn't fit in the current block, the remaining handles at the end of the block are released |
381 | // automatically as part of allocation/recycling) |
382 | // |
383 | // note: Recycled handles are only used during String Literal allocation because we only try |
384 | // to recycle handles if the allocation request is for exactly one handle. |
385 | // |
386 | // The handles in the BaseDomain handle table are released when the Domain is unloaded |
387 | // as the GC objects become rootless at that time. |
388 | // |
389 | // This dispenses with all of the Handle tables except the one that is used for string literals |
390 | // |
391 | // --------- |
392 | // |
393 | // Second case: Allocation for use in a string literal |
394 | // |
395 | // AppDomainStringLiteralMap::GetStringLiteral |
396 | // leads to calls to |
397 | // LargeHeapHandleBlockHolder constructor |
398 | // leads to calls to |
399 | // m_Data = pOwner->AllocateHandles(nCount); |
400 | // |
401 | // before doing this AppDomainStringLiteralMap::GetStringLiteral takes this lock |
402 | // |
403 | // CrstHolder gch(&(SystemDomain::GetGlobalStringLiteralMap()->m_HashTableCrstGlobal)); |
404 | // |
405 | // which is the lock for the hash table that it owns |
406 | // |
407 | // STRINGREF *AppDomainStringLiteralMap::GetInternedString |
408 | // |
409 | // has a similar call path and uses the same approach and the same lock |
410 | // this covers all the paths which allocate |
411 | // |
412 | // --------- |
413 | // |
414 | // Third case: Releases for use in a string literal entry |
415 | // |
416 | // CrstHolder gch(&(SystemDomain::GetGlobalStringLiteralMap()->m_HashTableCrstGlobal)); |
417 | // taken in the AppDomainStringLiteralMap functions below protects the 4 ways that this can happen |
418 | // |
419 | // case 3a) |
420 | // |
421 | // in an appdomain unload case |
422 | // |
423 | // AppDomainStringLiteralMap::~AppDomainStringLiteralMap() takes the lock then |
424 | // leads to calls to |
425 | // StringLiteralEntry::Release |
426 | // which leads to |
427 | // SystemDomain::GetGlobalStringLiteralMapNoCreate()->RemoveStringLiteralEntry(this) |
428 | // which leads to |
429 | // m_LargeHeapHandleTable.ReleaseHandles((OBJECTREF*)pObjRef, 1); |
430 | // |
431 | // case 3b) |
432 | // |
433 | // AppDomainStringLiteralMap::GetStringLiteral() can call StringLiteralEntry::Release in some |
434 | // error cases, leading to the same stack as above |
435 | // |
436 | // case 3c) |
437 | // |
438 | // AppDomainStringLiteralMap::GetInternedString() can call StringLiteralEntry::Release in some |
439 | // error cases, leading to the same stack as above |
440 | // |
441 | // case 3d) |
442 | // |
443 | // The same code paths in 3b and 3c and also end up releasing if an exception is thrown |
444 | // during their processing. Both these paths use a StringLiteralEntryHolder to assist in cleanup, |
445 | // the StaticRelease method of the StringLiteralEntry gets called, which in turn calls the |
446 | // Release method. |
447 | |
448 | |
449 | // Allocate handles from the large heap handle table. |
450 | OBJECTREF* LargeHeapHandleTable::AllocateHandles(DWORD nRequested, BOOL bCrossAD) |
451 | { |
452 | CONTRACTL |
453 | { |
454 | THROWS; |
455 | GC_TRIGGERS; |
456 | MODE_COOPERATIVE; |
457 | PRECONDITION(nRequested > 0); |
458 | INJECT_FAULT(COMPlusThrowOM();); |
459 | } |
460 | CONTRACTL_END; |
461 | |
462 | // SEE "LOCKING RULES FOR AllocateHandles() and ReleaseHandles()" above |
463 | |
464 | // the lock must be registered and already held by the caller per contract |
465 | #ifdef _DEBUG |
466 | _ASSERTE(m_pCrstDebug != NULL); |
467 | _ASSERTE(m_pCrstDebug->OwnedByCurrentThread()); |
468 | #endif |
469 | |
470 | if (nRequested == 1 && m_cEmbeddedFree != 0) |
471 | { |
472 | // special casing singleton requests to look for slots that can be re-used |
473 | |
474 | // we need to do this because string literals are allocated one at a time and then sometimes |
475 | // released. we do not wish for the number of handles consumed by string literals to |
476 | // increase forever as assemblies are loaded and unloaded |
477 | |
478 | if (m_pFreeSearchHint == NULL) |
479 | m_pFreeSearchHint = m_pHead; |
480 | |
481 | while (m_pFreeSearchHint) |
482 | { |
483 | OBJECTREF* pObjRef = m_pFreeSearchHint->TryAllocateEmbeddedFreeHandle(); |
484 | if (pObjRef != NULL) |
485 | { |
486 | // the slot is to have been prepared with a null ready to go |
487 | _ASSERTE(*pObjRef == NULL); |
488 | m_cEmbeddedFree--; |
489 | return pObjRef; |
490 | } |
491 | m_pFreeSearchHint = m_pFreeSearchHint->GetNext(); |
492 | } |
493 | |
494 | // the search doesn't wrap around so it's possible that we might have embedded free items |
495 | // and not find them but that's ok, we'll get them on the next alloc... all we're trying to do |
496 | // is to not have big leaks over time. |
497 | } |
498 | |
499 | |
500 | // Retrieve the remaining number of handles in the bucket. |
501 | DWORD NumRemainingHandlesInBucket = (m_pHead != NULL) ? m_pHead->GetNumRemainingHandles() : 0; |
502 | |
503 | // create a new block if this request doesn't fit in the current block |
504 | if (nRequested > NumRemainingHandlesInBucket) |
505 | { |
506 | if (m_pHead != NULL) |
507 | { |
508 | // mark the handles in that remaining region as available for re-use |
509 | ReleaseHandles(m_pHead->CurrentPos(), NumRemainingHandlesInBucket); |
510 | |
511 | // mark what's left as having been used |
512 | m_pHead->ConsumeRemaining(); |
513 | } |
514 | |
515 | // create a new bucket for this allocation |
516 | |
517 | // We need a block big enough to hold the requested handles |
518 | DWORD NewBucketSize = max(m_NextBucketSize, nRequested); |
519 | |
520 | m_pHead = new LargeHeapHandleBucket(m_pHead, NewBucketSize, m_pDomain, bCrossAD); |
521 | |
522 | m_NextBucketSize = min(m_NextBucketSize * 2, MAX_BUCKETSIZE); |
523 | } |
524 | |
525 | return m_pHead->AllocateHandles(nRequested); |
526 | } |
527 | |
528 | //***************************************************************************** |
529 | // Release object handles allocated using AllocateHandles(). |
530 | void LargeHeapHandleTable::ReleaseHandles(OBJECTREF *pObjRef, DWORD nReleased) |
531 | { |
532 | CONTRACTL |
533 | { |
534 | NOTHROW; |
535 | GC_NOTRIGGER; |
536 | MODE_COOPERATIVE; |
537 | PRECONDITION(CheckPointer(pObjRef)); |
538 | } |
539 | CONTRACTL_END; |
540 | |
541 | // SEE "LOCKING RULES FOR AllocateHandles() and ReleaseHandles()" above |
542 | |
543 | // the lock must be registered and already held by the caller per contract |
544 | #ifdef _DEBUG |
545 | _ASSERTE(m_pCrstDebug != NULL); |
546 | _ASSERTE(m_pCrstDebug->OwnedByCurrentThread()); |
547 | #endif |
548 | |
549 | OBJECTREF pPreallocatedSentinalObject = ObjectFromHandle(g_pPreallocatedSentinelObject); |
550 | _ASSERTE(pPreallocatedSentinalObject != NULL); |
551 | |
552 | |
553 | // Add the released handles to the list of available handles. |
554 | for (DWORD i = 0; i < nReleased; i++) |
555 | { |
556 | SetObjectReference(&pObjRef[i], pPreallocatedSentinalObject, NULL); |
557 | } |
558 | |
559 | m_cEmbeddedFree += nReleased; |
560 | } |
561 | |
562 | |
563 | |
564 | |
565 | // Constructor for the ThreadStaticHandleBucket class. |
566 | ThreadStaticHandleBucket::ThreadStaticHandleBucket(ThreadStaticHandleBucket *pNext, DWORD Size, BaseDomain *pDomain) |
567 | : m_pNext(pNext) |
568 | , m_ArraySize(Size) |
569 | { |
570 | CONTRACTL |
571 | { |
572 | THROWS; |
573 | GC_TRIGGERS; |
574 | MODE_COOPERATIVE; |
575 | PRECONDITION(CheckPointer(pDomain)); |
576 | INJECT_FAULT(COMPlusThrowOM();); |
577 | } |
578 | CONTRACTL_END; |
579 | |
580 | PTRARRAYREF HandleArrayObj; |
581 | |
582 | // Allocate the array on the GC heap. |
583 | OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED); |
584 | HandleArrayObj = (PTRARRAYREF)AllocateObjectArray(Size, g_pObjectClass, FALSE); |
585 | |
586 | // Store the array in a strong handle to keep it alive. |
587 | m_hndHandleArray = pDomain->CreateStrongHandle((OBJECTREF)HandleArrayObj); |
588 | } |
589 | |
590 | // Destructor for the ThreadStaticHandleBucket class. |
591 | ThreadStaticHandleBucket::~ThreadStaticHandleBucket() |
592 | { |
593 | CONTRACTL |
594 | { |
595 | NOTHROW; |
596 | GC_NOTRIGGER; |
597 | MODE_COOPERATIVE; |
598 | } |
599 | CONTRACTL_END; |
600 | |
601 | if (m_hndHandleArray) |
602 | { |
603 | DestroyStrongHandle(m_hndHandleArray); |
604 | m_hndHandleArray = NULL; |
605 | } |
606 | } |
607 | |
608 | // Allocate handles from the bucket. |
609 | OBJECTHANDLE ThreadStaticHandleBucket::GetHandles() |
610 | { |
611 | CONTRACTL |
612 | { |
613 | NOTHROW; |
614 | GC_NOTRIGGER; |
615 | MODE_COOPERATIVE; |
616 | } |
617 | CONTRACTL_END; |
618 | |
619 | return m_hndHandleArray; |
620 | } |
621 | |
622 | // Constructor for the ThreadStaticHandleTable class. |
623 | ThreadStaticHandleTable::ThreadStaticHandleTable(BaseDomain *pDomain) |
624 | : m_pHead(NULL) |
625 | , m_pDomain(pDomain) |
626 | { |
627 | CONTRACTL |
628 | { |
629 | NOTHROW; |
630 | GC_NOTRIGGER; |
631 | MODE_ANY; |
632 | PRECONDITION(CheckPointer(pDomain)); |
633 | } |
634 | CONTRACTL_END; |
635 | } |
636 | |
637 | // Destructor for the ThreadStaticHandleTable class. |
638 | ThreadStaticHandleTable::~ThreadStaticHandleTable() |
639 | { |
640 | CONTRACTL |
641 | { |
642 | NOTHROW; |
643 | GC_NOTRIGGER; |
644 | } |
645 | CONTRACTL_END; |
646 | |
647 | // Delete the buckets. |
648 | while (m_pHead) |
649 | { |
650 | ThreadStaticHandleBucket *pOld = m_pHead; |
651 | m_pHead = pOld->GetNext(); |
652 | delete pOld; |
653 | } |
654 | } |
655 | |
656 | // Allocate handles from the large heap handle table. |
657 | OBJECTHANDLE ThreadStaticHandleTable::AllocateHandles(DWORD nRequested) |
658 | { |
659 | CONTRACTL |
660 | { |
661 | THROWS; |
662 | GC_TRIGGERS; |
663 | MODE_COOPERATIVE; |
664 | PRECONDITION(nRequested > 0); |
665 | INJECT_FAULT(COMPlusThrowOM();); |
666 | } |
667 | CONTRACTL_END; |
668 | |
669 | // create a new bucket for this allocation |
670 | m_pHead = new ThreadStaticHandleBucket(m_pHead, nRequested, m_pDomain); |
671 | |
672 | return m_pHead->GetHandles(); |
673 | } |
674 | |
675 | #endif // CROSSGEN_COMPILE |
676 | |
677 | |
678 | //***************************************************************************** |
679 | // BaseDomain |
680 | //***************************************************************************** |
681 | void BaseDomain::Attach() |
682 | { |
683 | m_SpecialStaticsCrst.Init(CrstSpecialStatics); |
684 | } |
685 | |
686 | BaseDomain::BaseDomain() |
687 | { |
688 | // initialize fields so the domain can be safely destructed |
689 | // shouldn't call anything that can fail here - use ::Init instead |
690 | CONTRACTL |
691 | { |
692 | THROWS; |
693 | GC_TRIGGERS; |
694 | MODE_ANY; |
695 | FORBID_FAULT; |
696 | } |
697 | CONTRACTL_END; |
698 | |
699 | m_fDisableInterfaceCache = FALSE; |
700 | |
701 | m_pFusionContext = NULL; |
702 | m_pTPABinderContext = NULL; |
703 | |
704 | // Make sure the container is set to NULL so that it gets loaded when it is used. |
705 | m_pLargeHeapHandleTable = NULL; |
706 | |
707 | #ifndef CROSSGEN_COMPILE |
708 | // Note that m_handleStore is overridden by app domains |
709 | m_handleStore = GCHandleUtilities::GetGCHandleManager()->GetGlobalHandleStore(); |
710 | #else |
711 | m_handleStore = NULL; |
712 | #endif |
713 | |
714 | m_pMarshalingData = NULL; |
715 | |
716 | #ifdef FEATURE_COMINTEROP |
717 | m_pMngStdInterfacesInfo = NULL; |
718 | m_pWinRtBinder = NULL; |
719 | #endif |
720 | m_FileLoadLock.PreInit(); |
721 | m_JITLock.PreInit(); |
722 | m_ClassInitLock.PreInit(); |
723 | m_ILStubGenLock.PreInit(); |
724 | |
725 | #ifdef FEATURE_CODE_VERSIONING |
726 | m_codeVersionManager.PreInit(); |
727 | #endif |
728 | |
729 | } //BaseDomain::BaseDomain |
730 | |
731 | //***************************************************************************** |
732 | void BaseDomain::Init() |
733 | { |
734 | CONTRACTL |
735 | { |
736 | THROWS; |
737 | GC_TRIGGERS; |
738 | MODE_ANY; |
739 | INJECT_FAULT(COMPlusThrowOM();); |
740 | } |
741 | CONTRACTL_END; |
742 | |
743 | // |
744 | // Initialize the domain locks |
745 | // |
746 | |
747 | if (this == reinterpret_cast<BaseDomain*>(&g_pSystemDomainMemory[0])) |
748 | m_DomainCrst.Init(CrstSystemBaseDomain); |
749 | else |
750 | m_DomainCrst.Init(CrstBaseDomain); |
751 | |
752 | m_DomainCacheCrst.Init(CrstAppDomainCache); |
753 | m_DomainLocalBlockCrst.Init(CrstDomainLocalBlock); |
754 | |
755 | m_InteropDataCrst.Init(CrstInteropData, CRST_REENTRANCY); |
756 | |
757 | m_WinRTFactoryCacheCrst.Init(CrstWinRTFactoryCache, CRST_UNSAFE_COOPGC); |
758 | |
759 | // NOTE: CRST_UNSAFE_COOPGC prevents a GC mode switch to preemptive when entering this crst. |
760 | // If you remove this flag, we will switch to preemptive mode when entering |
761 | // m_FileLoadLock, which means all functions that enter it will become |
762 | // GC_TRIGGERS. (This includes all uses of PEFileListLockHolder, LoadLockHolder, etc.) So be sure |
763 | // to update the contracts if you remove this flag. |
764 | m_FileLoadLock.Init(CrstAssemblyLoader, |
765 | CrstFlags(CRST_HOST_BREAKABLE), TRUE); |
766 | |
767 | // |
768 | // The JIT lock and the CCtor locks are at the same level (and marked as |
769 | // UNSAFE_SAME_LEVEL) because they are all part of the same deadlock detection mechanism. We |
770 | // see through cycles of JITting and .cctor execution and then explicitly allow the cycle to |
771 | // be broken by giving access to uninitialized classes. If there is no cycle or if the cycle |
772 | // involves other locks that arent part of this special deadlock-breaking semantics, then |
773 | // we continue to block. |
774 | // |
775 | m_JITLock.Init(CrstJit, CrstFlags(CRST_REENTRANCY | CRST_UNSAFE_SAMELEVEL), TRUE); |
776 | m_ClassInitLock.Init(CrstClassInit, CrstFlags(CRST_REENTRANCY | CRST_UNSAFE_SAMELEVEL), TRUE); |
777 | |
778 | m_ILStubGenLock.Init(CrstILStubGen, CrstFlags(CRST_REENTRANCY), TRUE); |
779 | |
780 | // Large heap handle table CRST. |
781 | m_LargeHeapHandleTableCrst.Init(CrstAppDomainHandleTable); |
782 | |
783 | m_crstLoaderAllocatorReferences.Init(CrstLoaderAllocatorReferences); |
784 | // Has to switch thread to GC_NOTRIGGER while being held (see code:BaseDomain#AssemblyListLock) |
785 | m_crstAssemblyList.Init(CrstAssemblyList, CrstFlags( |
786 | CRST_GC_NOTRIGGER_WHEN_TAKEN | CRST_DEBUGGER_THREAD | CRST_TAKEN_DURING_SHUTDOWN)); |
787 | |
788 | // Initialize the EE marshaling data to NULL. |
789 | m_pMarshalingData = NULL; |
790 | |
791 | #ifdef FEATURE_COMINTEROP |
792 | // Allocate the managed standard interfaces information. |
793 | m_pMngStdInterfacesInfo = new MngStdInterfacesInfo(); |
794 | |
795 | { |
796 | CLRPrivBinderWinRT::NamespaceResolutionKind fNamespaceResolutionKind = CLRPrivBinderWinRT::NamespaceResolutionKind_WindowsAPI; |
797 | if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_DesignerNamespaceResolutionEnabled) != FALSE) |
798 | { |
799 | fNamespaceResolutionKind = CLRPrivBinderWinRT::NamespaceResolutionKind_DesignerResolveEvent; |
800 | } |
801 | CLRPrivTypeCacheWinRT * pWinRtTypeCache = CLRPrivTypeCacheWinRT::GetOrCreateTypeCache(); |
802 | m_pWinRtBinder = CLRPrivBinderWinRT::GetOrCreateBinder(pWinRtTypeCache, fNamespaceResolutionKind); |
803 | } |
804 | #endif // FEATURE_COMINTEROP |
805 | |
806 | // Init the COM Interop data hash |
807 | { |
808 | LockOwner lock = {&m_InteropDataCrst, IsOwnerOfCrst}; |
809 | m_interopDataHash.Init(0, NULL, false, &lock); |
810 | } |
811 | |
812 | m_dwSizedRefHandles = 0; |
813 | if (!m_iNumberOfProcessors) |
814 | { |
815 | m_iNumberOfProcessors = GetCurrentProcessCpuCount(); |
816 | } |
817 | } |
818 | |
819 | #undef LOADERHEAP_PROFILE_COUNTER |
820 | |
821 | #ifndef CROSSGEN_COMPILE |
822 | //***************************************************************************** |
823 | void BaseDomain::Terminate() |
824 | { |
825 | CONTRACTL |
826 | { |
827 | NOTHROW; |
828 | GC_TRIGGERS; |
829 | MODE_ANY; |
830 | } |
831 | CONTRACTL_END; |
832 | |
833 | m_crstLoaderAllocatorReferences.Destroy(); |
834 | m_DomainCrst.Destroy(); |
835 | m_DomainCacheCrst.Destroy(); |
836 | m_DomainLocalBlockCrst.Destroy(); |
837 | m_InteropDataCrst.Destroy(); |
838 | |
839 | JitListLockEntry* pJitElement; |
840 | ListLockEntry* pElement; |
841 | |
842 | // All the threads that are in this domain had better be stopped by this |
843 | // point. |
844 | // |
845 | // We might be jitting or running a .cctor so we need to empty that queue. |
846 | pJitElement = m_JITLock.Pop(TRUE); |
847 | while (pJitElement) |
848 | { |
849 | #ifdef STRICT_JITLOCK_ENTRY_LEAK_DETECTION |
850 | _ASSERTE ((m_JITLock.m_pHead->m_dwRefCount == 1 |
851 | && m_JITLock.m_pHead->m_hrResultCode == E_FAIL) || |
852 | dbg_fDrasticShutdown || g_fInControlC); |
853 | #endif // STRICT_JITLOCK_ENTRY_LEAK_DETECTION |
854 | delete(pJitElement); |
855 | pJitElement = m_JITLock.Pop(TRUE); |
856 | |
857 | } |
858 | m_JITLock.Destroy(); |
859 | |
860 | pElement = m_ClassInitLock.Pop(TRUE); |
861 | while (pElement) |
862 | { |
863 | #ifdef STRICT_CLSINITLOCK_ENTRY_LEAK_DETECTION |
864 | _ASSERTE (dbg_fDrasticShutdown || g_fInControlC); |
865 | #endif |
866 | delete(pElement); |
867 | pElement = m_ClassInitLock.Pop(TRUE); |
868 | } |
869 | m_ClassInitLock.Destroy(); |
870 | |
871 | FileLoadLock* pFileElement; |
872 | pFileElement = (FileLoadLock*) m_FileLoadLock.Pop(TRUE); |
873 | while (pFileElement) |
874 | { |
875 | #ifdef STRICT_CLSINITLOCK_ENTRY_LEAK_DETECTION |
876 | _ASSERTE (dbg_fDrasticShutdown || g_fInControlC); |
877 | #endif |
878 | pFileElement->Release(); |
879 | pFileElement = (FileLoadLock*) m_FileLoadLock.Pop(TRUE); |
880 | } |
881 | m_FileLoadLock.Destroy(); |
882 | |
883 | pElement = m_ILStubGenLock.Pop(TRUE); |
884 | while (pElement) |
885 | { |
886 | #ifdef STRICT_JITLOCK_ENTRY_LEAK_DETECTION |
887 | _ASSERTE ((m_ILStubGenLock.m_pHead->m_dwRefCount == 1 |
888 | && m_ILStubGenLock.m_pHead->m_hrResultCode == E_FAIL) || |
889 | dbg_fDrasticShutdown || g_fInControlC); |
890 | #endif // STRICT_JITLOCK_ENTRY_LEAK_DETECTION |
891 | delete(pElement); |
892 | pElement = m_ILStubGenLock.Pop(TRUE); |
893 | } |
894 | m_ILStubGenLock.Destroy(); |
895 | |
896 | m_LargeHeapHandleTableCrst.Destroy(); |
897 | |
898 | if (m_pLargeHeapHandleTable != NULL) |
899 | { |
900 | delete m_pLargeHeapHandleTable; |
901 | m_pLargeHeapHandleTable = NULL; |
902 | } |
903 | |
904 | if (!IsAppDomain()) |
905 | { |
906 | // Kind of a workaround - during unloading, we need to have an EE halt |
907 | // around deleting this stuff. So it gets deleted in AppDomain::Terminate() |
908 | // for those things (because there is a convenient place there.) |
909 | GetLoaderAllocator()->CleanupStringLiteralMap(); |
910 | } |
911 | |
912 | #ifdef FEATURE_COMINTEROP |
913 | if (m_pMngStdInterfacesInfo) |
914 | { |
915 | delete m_pMngStdInterfacesInfo; |
916 | m_pMngStdInterfacesInfo = NULL; |
917 | } |
918 | |
919 | if (m_pWinRtBinder != NULL) |
920 | { |
921 | m_pWinRtBinder->Release(); |
922 | } |
923 | #endif // FEATURE_COMINTEROP |
924 | |
925 | ClearFusionContext(); |
926 | |
927 | m_dwSizedRefHandles = 0; |
928 | } |
929 | #endif // CROSSGEN_COMPILE |
930 | |
931 | void BaseDomain::InitVSD() |
932 | { |
933 | STANDARD_VM_CONTRACT; |
934 | |
935 | // This is a workaround for gcc, since it fails to successfully resolve |
936 | // "TypeIDMap::STARTING_SHARED_DOMAIN_ID" when used within the ?: operator. |
937 | UINT32 startingId; |
938 | if (IsSharedDomain()) |
939 | { |
940 | startingId = TypeIDMap::STARTING_SHARED_DOMAIN_ID; |
941 | } |
942 | else |
943 | { |
944 | startingId = TypeIDMap::STARTING_UNSHARED_DOMAIN_ID; |
945 | } |
946 | |
947 | // By passing false as the last parameter, interfaces loaded in the |
948 | // shared domain will not be given fat type ids if RequiresFatDispatchTokens |
949 | // is set. This is correct, as the fat dispatch tokens are only needed to solve |
950 | // uniqueness problems involving domain specific types. |
951 | m_typeIDMap.Init(startingId, 2, !IsSharedDomain()); |
952 | |
953 | #ifndef CROSSGEN_COMPILE |
954 | GetLoaderAllocator()->InitVirtualCallStubManager(this); |
955 | #endif |
956 | } |
957 | |
958 | #ifndef CROSSGEN_COMPILE |
959 | |
960 | void BaseDomain::ClearFusionContext() |
961 | { |
962 | CONTRACTL |
963 | { |
964 | NOTHROW; |
965 | GC_TRIGGERS; |
966 | MODE_PREEMPTIVE; |
967 | } |
968 | CONTRACTL_END; |
969 | |
970 | if(m_pFusionContext) { |
971 | m_pFusionContext->Release(); |
972 | m_pFusionContext = NULL; |
973 | } |
974 | if (m_pTPABinderContext) { |
975 | m_pTPABinderContext->Release(); |
976 | m_pTPABinderContext = NULL; |
977 | } |
978 | } |
979 | |
980 | #ifdef FEATURE_PREJIT |
981 | void AppDomain::DeleteNativeCodeRanges() |
982 | { |
983 | CONTRACTL |
984 | { |
985 | NOTHROW; |
986 | GC_NOTRIGGER; |
987 | MODE_PREEMPTIVE; |
988 | FORBID_FAULT; |
989 | } |
990 | CONTRACTL_END; |
991 | |
992 | // Fast path to skip using the assembly iterator when the appdomain has not yet completely been initialized |
993 | // and yet we are destroying it. (This is the case if we OOM during AppDomain creation.) |
994 | if (m_Assemblies.IsEmpty()) |
995 | return; |
996 | |
997 | // Shutdown assemblies |
998 | AssemblyIterator i = IterateAssembliesEx( (AssemblyIterationFlags)(kIncludeLoaded | kIncludeLoading | kIncludeExecution | kIncludeFailedToLoad) ); |
999 | CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly; |
1000 | |
1001 | while (i.Next(pDomainAssembly.This())) |
1002 | { |
1003 | Assembly * assembly = pDomainAssembly->m_pAssembly; |
1004 | if ((assembly != NULL)) |
1005 | assembly->DeleteNativeCodeRanges(); |
1006 | } |
1007 | } |
1008 | #endif |
1009 | |
1010 | void AppDomain::ShutdownAssemblies() |
1011 | { |
1012 | CONTRACTL |
1013 | { |
1014 | NOTHROW; |
1015 | GC_TRIGGERS; |
1016 | MODE_ANY; |
1017 | } |
1018 | CONTRACTL_END; |
1019 | |
1020 | // Fast path to skip using the assembly iterator when the appdomain has not yet completely been initialized |
1021 | // and yet we are destroying it. (This is the case if we OOM during AppDomain creation.) |
1022 | if (m_Assemblies.IsEmpty()) |
1023 | return; |
1024 | |
1025 | // Shutdown assemblies |
1026 | // has two stages because Terminate needs info from the Assembly's dependencies |
1027 | |
1028 | // Stage 1: call code:Assembly::Terminate |
1029 | AssemblyIterator i = IterateAssembliesEx((AssemblyIterationFlags)( |
1030 | kIncludeLoaded | kIncludeLoading | kIncludeExecution | kIncludeFailedToLoad | kIncludeCollected)); |
1031 | DomainAssembly * pDomainAssembly = NULL; |
1032 | |
1033 | while (i.Next_UnsafeNoAddRef(&pDomainAssembly)) |
1034 | { |
1035 | // Note: cannot use DomainAssembly::GetAssembly() here as it asserts that the assembly has been |
1036 | // loaded to at least the FILE_LOAD_ALLOCATE level. Since domain shutdown can take place |
1037 | // asynchronously this property cannot be guaranteed. Access the m_pAssembly field directly instead. |
1038 | Assembly * assembly = pDomainAssembly->m_pAssembly; |
1039 | if (assembly) |
1040 | assembly->Terminate(); |
1041 | } |
1042 | |
1043 | // Stage 2: Clear the list of assemblies |
1044 | i = IterateAssembliesEx((AssemblyIterationFlags)( |
1045 | kIncludeLoaded | kIncludeLoading | kIncludeExecution | kIncludeFailedToLoad | kIncludeCollected)); |
1046 | while (i.Next_UnsafeNoAddRef(&pDomainAssembly)) |
1047 | { |
1048 | // We are in shutdown path, no one else can get to the list anymore |
1049 | delete pDomainAssembly; |
1050 | } |
1051 | m_Assemblies.Clear(this); |
1052 | |
1053 | // Stage 2: Clear the loader allocators registered for deletion from code:Assembly:Terminate calls in |
1054 | // stage 1 |
1055 | // Note: It is not clear to me why we cannot delete the loader allocator from within |
1056 | // code:DomainAssembly::~DomainAssembly |
1057 | ShutdownFreeLoaderAllocators(FALSE); |
1058 | } // AppDomain::ShutdownAssemblies |
1059 | |
1060 | void AppDomain::ShutdownFreeLoaderAllocators(BOOL bFromManagedCode) |
1061 | { |
1062 | // If we're called from managed code (i.e. the finalizer thread) we take a lock in |
1063 | // LoaderAllocator::CleanupFailedTypeInit, which may throw. Otherwise we're called |
1064 | // from the app-domain shutdown path in which we can avoid taking the lock. |
1065 | CONTRACTL |
1066 | { |
1067 | GC_TRIGGERS; |
1068 | if (bFromManagedCode) THROWS; else NOTHROW; |
1069 | MODE_ANY; |
1070 | CAN_TAKE_LOCK; |
1071 | } |
1072 | CONTRACTL_END; |
1073 | |
1074 | CrstHolder ch(GetLoaderAllocatorReferencesLock()); |
1075 | |
1076 | // Shutdown the LoaderAllocators associated with collectible assemblies |
1077 | while (m_pDelayedLoaderAllocatorUnloadList != NULL) |
1078 | { |
1079 | LoaderAllocator * pCurrentLoaderAllocator = m_pDelayedLoaderAllocatorUnloadList; |
1080 | // Remove next loader allocator from the list |
1081 | m_pDelayedLoaderAllocatorUnloadList = m_pDelayedLoaderAllocatorUnloadList->m_pLoaderAllocatorDestroyNext; |
1082 | |
1083 | if (bFromManagedCode) |
1084 | { |
1085 | // For loader allocator finalization, we need to be careful about cleaning up per-appdomain allocations |
1086 | // and synchronizing with GC using delay unload list. We need to wait for next Gen2 GC to finish to ensure |
1087 | // that GC heap does not have any references to the MethodTables being unloaded. |
1088 | |
1089 | pCurrentLoaderAllocator->CleanupFailedTypeInit(); |
1090 | |
1091 | pCurrentLoaderAllocator->CleanupHandles(); |
1092 | |
1093 | GCX_COOP(); |
1094 | SystemDomain::System()->AddToDelayedUnloadList(pCurrentLoaderAllocator); |
1095 | } |
1096 | else |
1097 | { |
1098 | // For appdomain unload, delete the loader allocator right away |
1099 | delete pCurrentLoaderAllocator; |
1100 | } |
1101 | } |
1102 | } // AppDomain::ShutdownFreeLoaderAllocators |
1103 | |
1104 | //--------------------------------------------------------------------------------------- |
1105 | // |
1106 | // Register the loader allocator for deletion in code:AppDomain::ShutdownFreeLoaderAllocators. |
1107 | // |
1108 | void AppDomain::RegisterLoaderAllocatorForDeletion(LoaderAllocator * pLoaderAllocator) |
1109 | { |
1110 | CONTRACTL |
1111 | { |
1112 | GC_TRIGGERS; |
1113 | NOTHROW; |
1114 | MODE_ANY; |
1115 | CAN_TAKE_LOCK; |
1116 | } |
1117 | CONTRACTL_END; |
1118 | |
1119 | CrstHolder ch(GetLoaderAllocatorReferencesLock()); |
1120 | |
1121 | pLoaderAllocator->m_pLoaderAllocatorDestroyNext = m_pDelayedLoaderAllocatorUnloadList; |
1122 | m_pDelayedLoaderAllocatorUnloadList = pLoaderAllocator; |
1123 | } |
1124 | |
1125 | void AppDomain::SetNativeDllSearchDirectories(LPCWSTR wszNativeDllSearchDirectories) |
1126 | { |
1127 | STANDARD_VM_CONTRACT; |
1128 | |
1129 | SString sDirectories(wszNativeDllSearchDirectories); |
1130 | |
1131 | if (sDirectories.GetCount() > 0) |
1132 | { |
1133 | SString::CIterator start = sDirectories.Begin(); |
1134 | SString::CIterator itr = sDirectories.Begin(); |
1135 | SString::CIterator end = sDirectories.End(); |
1136 | SString qualifiedPath; |
1137 | |
1138 | while (itr != end) |
1139 | { |
1140 | start = itr; |
1141 | BOOL found = sDirectories.Find(itr, PATH_SEPARATOR_CHAR_W); |
1142 | if (!found) |
1143 | { |
1144 | itr = end; |
1145 | } |
1146 | |
1147 | SString qualifiedPath(sDirectories, start, itr); |
1148 | |
1149 | if (found) |
1150 | { |
1151 | itr++; |
1152 | } |
1153 | |
1154 | unsigned len = qualifiedPath.GetCount(); |
1155 | |
1156 | if (len > 0) |
1157 | { |
1158 | if (qualifiedPath[len - 1] != DIRECTORY_SEPARATOR_CHAR_W) |
1159 | { |
1160 | qualifiedPath.Append(DIRECTORY_SEPARATOR_CHAR_W); |
1161 | } |
1162 | |
1163 | NewHolder<SString> stringHolder(new SString(qualifiedPath)); |
1164 | IfFailThrow(m_NativeDllSearchDirectories.Append(stringHolder.GetValue())); |
1165 | stringHolder.SuppressRelease(); |
1166 | } |
1167 | } |
1168 | } |
1169 | } |
1170 | |
1171 | void AppDomain::ShutdownNativeDllSearchDirectories() |
1172 | { |
1173 | LIMITED_METHOD_CONTRACT; |
1174 | // Shutdown assemblies |
1175 | PathIterator i = IterateNativeDllSearchDirectories(); |
1176 | |
1177 | while (i.Next()) |
1178 | { |
1179 | delete i.GetPath(); |
1180 | } |
1181 | |
1182 | m_NativeDllSearchDirectories.Clear(); |
1183 | } |
1184 | |
1185 | void AppDomain::ReleaseDomainBoundInfo() |
1186 | { |
1187 | CONTRACTL |
1188 | { |
1189 | NOTHROW; |
1190 | GC_TRIGGERS; |
1191 | MODE_ANY; |
1192 | } |
1193 | CONTRACTL_END;; |
1194 | // Shutdown assemblies |
1195 | m_AssemblyCache.OnAppDomainUnload(); |
1196 | |
1197 | AssemblyIterator i = IterateAssembliesEx( (AssemblyIterationFlags)(kIncludeFailedToLoad) ); |
1198 | CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly; |
1199 | |
1200 | while (i.Next(pDomainAssembly.This())) |
1201 | { |
1202 | pDomainAssembly->ReleaseManagedData(); |
1203 | } |
1204 | } |
1205 | |
1206 | void AppDomain::ReleaseFiles() |
1207 | { |
1208 | STANDARD_VM_CONTRACT; |
1209 | |
1210 | // Shutdown assemblies |
1211 | AssemblyIterator i = IterateAssembliesEx((AssemblyIterationFlags)( |
1212 | kIncludeLoaded | kIncludeExecution | kIncludeFailedToLoad | kIncludeLoading)); |
1213 | CollectibleAssemblyHolder<DomainAssembly *> pAsm; |
1214 | |
1215 | while (i.Next(pAsm.This())) |
1216 | { |
1217 | if (pAsm->GetCurrentAssembly() == NULL) |
1218 | { |
1219 | // Might be domain neutral or not, but should have no live objects as it has not been |
1220 | // really loaded yet. Just reset it. |
1221 | _ASSERTE(FitsIn<DWORD>(i.GetIndex())); |
1222 | m_Assemblies.Set(this, static_cast<DWORD>(i.GetIndex()), NULL); |
1223 | delete pAsm.Extract(); |
1224 | } |
1225 | else |
1226 | { |
1227 | pAsm->ReleaseFiles(); |
1228 | } |
1229 | } |
1230 | } // AppDomain::ReleaseFiles |
1231 | |
1232 | |
1233 | OBJECTREF* BaseDomain::AllocateObjRefPtrsInLargeTable(int nRequested, OBJECTREF** ppLazyAllocate, BOOL bCrossAD) |
1234 | { |
1235 | CONTRACTL |
1236 | { |
1237 | THROWS; |
1238 | GC_TRIGGERS; |
1239 | MODE_ANY; |
1240 | PRECONDITION((nRequested > 0)); |
1241 | INJECT_FAULT(COMPlusThrowOM();); |
1242 | } |
1243 | CONTRACTL_END; |
1244 | |
1245 | if (ppLazyAllocate && *ppLazyAllocate) |
1246 | { |
1247 | // Allocation already happened |
1248 | return *ppLazyAllocate; |
1249 | } |
1250 | |
1251 | // Enter preemptive state, take the lock and go back to cooperative mode. |
1252 | { |
1253 | CrstHolder ch(&m_LargeHeapHandleTableCrst); |
1254 | GCX_COOP(); |
1255 | |
1256 | if (ppLazyAllocate && *ppLazyAllocate) |
1257 | { |
1258 | // Allocation already happened |
1259 | return *ppLazyAllocate; |
1260 | } |
1261 | |
1262 | // Make sure the large heap handle table is initialized. |
1263 | if (!m_pLargeHeapHandleTable) |
1264 | InitLargeHeapHandleTable(); |
1265 | |
1266 | // Allocate the handles. |
1267 | OBJECTREF* result = m_pLargeHeapHandleTable->AllocateHandles(nRequested, bCrossAD); |
1268 | |
1269 | if (ppLazyAllocate) |
1270 | { |
1271 | *ppLazyAllocate = result; |
1272 | } |
1273 | |
1274 | return result; |
1275 | } |
1276 | } |
1277 | #endif // CROSSGEN_COMPILE |
1278 | |
1279 | #endif // !DACCESS_COMPILE |
1280 | |
1281 | #ifndef DACCESS_COMPILE |
1282 | |
1283 | // Insert class in the hash table |
1284 | void AppDomain::InsertClassForCLSID(MethodTable* pMT, BOOL fForceInsert /*=FALSE*/) |
1285 | { |
1286 | CONTRACTL |
1287 | { |
1288 | GC_TRIGGERS; |
1289 | MODE_ANY; |
1290 | THROWS; |
1291 | INJECT_FAULT(COMPlusThrowOM();); |
1292 | } |
1293 | CONTRACTL_END; |
1294 | |
1295 | CVID cvid; |
1296 | |
1297 | // Ensure that registered classes are activated for allocation |
1298 | pMT->EnsureInstanceActive(); |
1299 | |
1300 | // Note that it is possible for multiple classes to claim the same CLSID, and in such a |
1301 | // case it is arbitrary which one we will return for a future query for a given app domain. |
1302 | |
1303 | pMT->GetGuid(&cvid, fForceInsert); |
1304 | |
1305 | if (!IsEqualIID(cvid, GUID_NULL)) |
1306 | { |
1307 | //<TODO>@todo get a better key</TODO> |
1308 | LPVOID val = (LPVOID)pMT; |
1309 | { |
1310 | LockHolder lh(this); |
1311 | |
1312 | if (LookupClass(cvid) != pMT) |
1313 | { |
1314 | m_clsidHash.InsertValue(GetKeyFromGUID(&cvid), val); |
1315 | } |
1316 | } |
1317 | } |
1318 | } |
1319 | |
1320 | void AppDomain::InsertClassForCLSID(MethodTable* pMT, GUID *pGuid) |
1321 | { |
1322 | CONTRACT_VOID |
1323 | { |
1324 | NOTHROW; |
1325 | PRECONDITION(CheckPointer(pMT)); |
1326 | PRECONDITION(CheckPointer(pGuid)); |
1327 | } |
1328 | CONTRACT_END; |
1329 | |
1330 | LPVOID val = (LPVOID)pMT; |
1331 | { |
1332 | LockHolder lh(this); |
1333 | |
1334 | CVID* cvid = pGuid; |
1335 | if (LookupClass(*cvid) != pMT) |
1336 | { |
1337 | m_clsidHash.InsertValue(GetKeyFromGUID(pGuid), val); |
1338 | } |
1339 | } |
1340 | |
1341 | RETURN; |
1342 | } |
1343 | #endif // DACCESS_COMPILE |
1344 | |
1345 | #ifdef FEATURE_COMINTEROP |
1346 | |
1347 | #ifndef DACCESS_COMPILE |
1348 | void AppDomain::CacheTypeByName(const SString &ssClassName, const UINT vCacheVersion, TypeHandle typeHandle, BYTE bFlags, BOOL bReplaceExisting /*= FALSE*/) |
1349 | { |
1350 | WRAPPER_NO_CONTRACT; |
1351 | LockHolder lh(this); |
1352 | CacheTypeByNameWorker(ssClassName, vCacheVersion, typeHandle, bFlags, bReplaceExisting); |
1353 | } |
1354 | |
1355 | void AppDomain::CacheTypeByNameWorker(const SString &ssClassName, const UINT vCacheVersion, TypeHandle typeHandle, BYTE bFlags, BOOL bReplaceExisting /*= FALSE*/) |
1356 | { |
1357 | CONTRACTL |
1358 | { |
1359 | THROWS; |
1360 | GC_TRIGGERS; |
1361 | PRECONDITION(!typeHandle.IsNull()); |
1362 | } |
1363 | CONTRACTL_END; |
1364 | |
1365 | NewArrayHolder<WCHAR> wzClassName(DuplicateStringThrowing(ssClassName.GetUnicode())); |
1366 | |
1367 | if (m_vNameToTypeMapVersion != vCacheVersion) |
1368 | return; |
1369 | |
1370 | if (m_pNameToTypeMap == nullptr) |
1371 | { |
1372 | m_pNameToTypeMap = new NameToTypeMapTable(); |
1373 | } |
1374 | |
1375 | NameToTypeMapEntry e; |
1376 | e.m_key.m_wzName = wzClassName; |
1377 | e.m_key.m_cchName = ssClassName.GetCount(); |
1378 | e.m_typeHandle = typeHandle; |
1379 | e.m_nEpoch = this->m_nEpoch; |
1380 | e.m_bFlags = bFlags; |
1381 | if (!bReplaceExisting) |
1382 | m_pNameToTypeMap->Add(e); |
1383 | else |
1384 | m_pNameToTypeMap->AddOrReplace(e); |
1385 | |
1386 | wzClassName.SuppressRelease(); |
1387 | } |
1388 | #endif // DACCESS_COMPILE |
1389 | |
1390 | TypeHandle AppDomain::LookupTypeByName(const SString &ssClassName, UINT* pvCacheVersion, BYTE *pbFlags) |
1391 | { |
1392 | WRAPPER_NO_CONTRACT; |
1393 | LockHolder lh(this); |
1394 | return LookupTypeByNameWorker(ssClassName, pvCacheVersion, pbFlags); |
1395 | } |
1396 | |
1397 | TypeHandle AppDomain::LookupTypeByNameWorker(const SString &ssClassName, UINT* pvCacheVersion, BYTE *pbFlags) |
1398 | { |
1399 | CONTRACTL |
1400 | { |
1401 | THROWS; |
1402 | GC_TRIGGERS; |
1403 | SUPPORTS_DAC; |
1404 | PRECONDITION(CheckPointer(pbFlags, NULL_OK)); |
1405 | } |
1406 | CONTRACTL_END; |
1407 | |
1408 | *pvCacheVersion = m_vNameToTypeMapVersion; |
1409 | |
1410 | if (m_pNameToTypeMap == nullptr) |
1411 | return TypeHandle(); // a null TypeHandle |
1412 | |
1413 | NameToTypeMapEntry::Key key; |
1414 | key.m_cchName = ssClassName.GetCount(); |
1415 | key.m_wzName = ssClassName.GetUnicode(); |
1416 | |
1417 | const NameToTypeMapEntry * pEntry = m_pNameToTypeMap->LookupPtr(key); |
1418 | if (pEntry == NULL) |
1419 | return TypeHandle(); // a null TypeHandle |
1420 | |
1421 | if (pbFlags != NULL) |
1422 | *pbFlags = pEntry->m_bFlags; |
1423 | |
1424 | return pEntry->m_typeHandle; |
1425 | } |
1426 | |
1427 | PTR_MethodTable AppDomain::LookupTypeByGuid(const GUID & guid) |
1428 | { |
1429 | CONTRACTL |
1430 | { |
1431 | THROWS; |
1432 | GC_TRIGGERS; |
1433 | MODE_ANY; |
1434 | SUPPORTS_DAC; |
1435 | } |
1436 | CONTRACTL_END; |
1437 | |
1438 | SString sGuid; |
1439 | { |
1440 | WCHAR wszGuid[64]; |
1441 | GuidToLPWSTR(guid, wszGuid, _countof(wszGuid)); |
1442 | sGuid.Append(wszGuid); |
1443 | } |
1444 | UINT ver; |
1445 | TypeHandle th = LookupTypeByName(sGuid, &ver, NULL); |
1446 | |
1447 | if (!th.IsNull()) |
1448 | { |
1449 | _ASSERTE(!th.IsTypeDesc()); |
1450 | return th.AsMethodTable(); |
1451 | } |
1452 | |
1453 | #ifdef FEATURE_PREJIT |
1454 | else |
1455 | { |
1456 | // Next look in each ngen'ed image in turn |
1457 | AssemblyIterator assemblyIterator = IterateAssembliesEx((AssemblyIterationFlags)( |
1458 | kIncludeLoaded | kIncludeExecution)); |
1459 | CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly; |
1460 | while (assemblyIterator.Next(pDomainAssembly.This())) |
1461 | { |
1462 | CollectibleAssemblyHolder<Assembly *> pAssembly = pDomainAssembly->GetLoadedAssembly(); |
1463 | |
1464 | DomainAssembly::ModuleIterator i = pDomainAssembly->IterateModules(kModIterIncludeLoaded); |
1465 | while (i.Next()) |
1466 | { |
1467 | Module * pModule = i.GetLoadedModule(); |
1468 | if (!pModule->HasNativeImage()) |
1469 | continue; |
1470 | _ASSERTE(!pModule->IsCollectible()); |
1471 | PTR_MethodTable pMT = pModule->LookupTypeByGuid(guid); |
1472 | if (pMT != NULL) |
1473 | { |
1474 | return pMT; |
1475 | } |
1476 | } |
1477 | } |
1478 | } |
1479 | #endif // FEATURE_PREJIT |
1480 | return NULL; |
1481 | } |
1482 | |
1483 | #ifndef DACCESS_COMPILE |
1484 | void AppDomain::CacheWinRTTypeByGuid(TypeHandle typeHandle) |
1485 | { |
1486 | CONTRACTL |
1487 | { |
1488 | THROWS; |
1489 | GC_TRIGGERS; |
1490 | MODE_ANY; |
1491 | PRECONDITION(!typeHandle.IsTypeDesc()); |
1492 | PRECONDITION(CanCacheWinRTTypeByGuid(typeHandle)); |
1493 | } |
1494 | CONTRACTL_END; |
1495 | |
1496 | PTR_MethodTable pMT = typeHandle.AsMethodTable(); |
1497 | |
1498 | GUID guid; |
1499 | if (pMT->GetGuidForWinRT(&guid)) |
1500 | { |
1501 | SString sGuid; |
1502 | |
1503 | { |
1504 | WCHAR wszGuid[64]; |
1505 | GuidToLPWSTR(guid, wszGuid, _countof(wszGuid)); |
1506 | sGuid.Append(wszGuid); |
1507 | } |
1508 | |
1509 | BYTE bFlags = 0x80; |
1510 | TypeHandle th; |
1511 | UINT vCacheVersion; |
1512 | { |
1513 | LockHolder lh(this); |
1514 | th = LookupTypeByNameWorker(sGuid, &vCacheVersion, &bFlags); |
1515 | |
1516 | if (th.IsNull()) |
1517 | { |
1518 | // no other entry with the same GUID exists in the cache |
1519 | CacheTypeByNameWorker(sGuid, vCacheVersion, typeHandle, bFlags); |
1520 | } |
1521 | else if (typeHandle.AsMethodTable() != th.AsMethodTable() && th.IsProjectedFromWinRT()) |
1522 | { |
1523 | // If we found a native WinRT type cached with the same GUID, replace it. |
1524 | // Otherwise simply add the new mapping to the cache. |
1525 | CacheTypeByNameWorker(sGuid, vCacheVersion, typeHandle, bFlags, TRUE); |
1526 | } |
1527 | } |
1528 | } |
1529 | } |
1530 | #endif // DACCESS_COMPILE |
1531 | |
1532 | void AppDomain::GetCachedWinRTTypes( |
1533 | SArray<PTR_MethodTable> * pTypes, |
1534 | SArray<GUID> * pGuids, |
1535 | UINT minEpoch, |
1536 | UINT * pCurEpoch) |
1537 | { |
1538 | CONTRACTL |
1539 | { |
1540 | THROWS; |
1541 | GC_TRIGGERS; |
1542 | MODE_ANY; |
1543 | SUPPORTS_DAC; |
1544 | } |
1545 | CONTRACTL_END; |
1546 | |
1547 | LockHolder lh(this); |
1548 | |
1549 | for (auto it = m_pNameToTypeMap->Begin(), end = m_pNameToTypeMap->End(); |
1550 | it != end; |
1551 | ++it) |
1552 | { |
1553 | NameToTypeMapEntry entry = (NameToTypeMapEntry)(*it); |
1554 | TypeHandle th = entry.m_typeHandle; |
1555 | if (th.AsMethodTable() != NULL && |
1556 | entry.m_key.m_wzName[0] == W('{') && |
1557 | entry.m_nEpoch >= minEpoch) |
1558 | { |
1559 | _ASSERTE(!th.IsTypeDesc()); |
1560 | PTR_MethodTable pMT = th.AsMethodTable(); |
1561 | // we're parsing the GUID value from the cache, because projected types do not cache the |
1562 | // COM GUID in their GetGuid() but rather the legacy GUID |
1563 | GUID iid; |
1564 | if (LPWSTRToGuid(&iid, entry.m_key.m_wzName, 38) && iid != GUID_NULL) |
1565 | { |
1566 | pTypes->Append(pMT); |
1567 | pGuids->Append(iid); |
1568 | } |
1569 | } |
1570 | } |
1571 | |
1572 | #ifdef FEATURE_PREJIT |
1573 | // Next look in each ngen'ed image in turn |
1574 | AssemblyIterator assemblyIterator = IterateAssembliesEx((AssemblyIterationFlags)( |
1575 | kIncludeLoaded | kIncludeExecution)); |
1576 | CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly; |
1577 | while (assemblyIterator.Next(pDomainAssembly.This())) |
1578 | { |
1579 | CollectibleAssemblyHolder<Assembly *> pAssembly = pDomainAssembly->GetLoadedAssembly(); |
1580 | |
1581 | DomainAssembly::ModuleIterator i = pDomainAssembly->IterateModules(kModIterIncludeLoaded); |
1582 | while (i.Next()) |
1583 | { |
1584 | Module * pModule = i.GetLoadedModule(); |
1585 | if (!pModule->HasNativeImage()) |
1586 | continue; |
1587 | _ASSERTE(!pModule->IsCollectible()); |
1588 | |
1589 | pModule->GetCachedWinRTTypes(pTypes, pGuids); |
1590 | } |
1591 | } |
1592 | #endif // FEATURE_PREJIT |
1593 | |
1594 | if (pCurEpoch != NULL) |
1595 | *pCurEpoch = m_nEpoch; |
1596 | ++m_nEpoch; |
1597 | } |
1598 | |
1599 | #ifndef CROSSGEN_COMPILE |
1600 | #ifndef DACCESS_COMPILE |
1601 | // static |
1602 | void WinRTFactoryCacheTraits::OnDestructPerEntryCleanupAction(const WinRTFactoryCacheEntry& e) |
1603 | { |
1604 | WRAPPER_NO_CONTRACT; |
1605 | if (e.m_pCtxEntry != NULL) |
1606 | { |
1607 | e.m_pCtxEntry->Release(); |
1608 | } |
1609 | // the AD is going away, no need to destroy the OBJECTHANDLE |
1610 | } |
1611 | |
1612 | void AppDomain::CacheWinRTFactoryObject(MethodTable *pClassMT, OBJECTREF *refFactory, LPVOID lpCtxCookie) |
1613 | { |
1614 | CONTRACTL |
1615 | { |
1616 | THROWS; |
1617 | GC_TRIGGERS; |
1618 | MODE_COOPERATIVE; |
1619 | PRECONDITION(CheckPointer(pClassMT)); |
1620 | } |
1621 | CONTRACTL_END; |
1622 | |
1623 | CtxEntryHolder pNewCtxEntry; |
1624 | if (lpCtxCookie != NULL) |
1625 | { |
1626 | // We don't want to insert the context cookie in the cache because it's just an address |
1627 | // of an internal COM data structure which will be freed when the apartment is torn down. |
1628 | // What's worse, if another apartment is later created, its context cookie may have exactly |
1629 | // the same value leading to incorrect cache hits. We'll use our CtxEntry instead which |
1630 | // is ref-counted and keeps the COM data structure alive even after the apartment ceases |
1631 | // to exist. |
1632 | pNewCtxEntry = CtxEntryCache::GetCtxEntryCache()->FindCtxEntry(lpCtxCookie, GetThread()); |
1633 | } |
1634 | |
1635 | WinRTFactoryCacheLockHolder lh(this); |
1636 | |
1637 | if (m_pWinRTFactoryCache == nullptr) |
1638 | { |
1639 | m_pWinRTFactoryCache = new WinRTFactoryCache(); |
1640 | } |
1641 | |
1642 | WinRTFactoryCacheEntry *pEntry = const_cast<WinRTFactoryCacheEntry*>(m_pWinRTFactoryCache->LookupPtr(pClassMT)); |
1643 | if (!pEntry) |
1644 | { |
1645 | // |
1646 | // No existing entry for this cache |
1647 | // Create a new one |
1648 | // |
1649 | WinRTFactoryCacheEntry e; |
1650 | |
1651 | OBJECTHANDLEHolder ohNewHandle(CreateHandle(*refFactory)); |
1652 | |
1653 | e.key = pClassMT; |
1654 | e.m_pCtxEntry = pNewCtxEntry; |
1655 | e.m_ohFactoryObject = ohNewHandle; |
1656 | |
1657 | m_pWinRTFactoryCache->Add(e); |
1658 | |
1659 | // suppress release of the CtxEntry and handle after we successfully inserted the new entry |
1660 | pNewCtxEntry.SuppressRelease(); |
1661 | ohNewHandle.SuppressRelease(); |
1662 | } |
1663 | else |
1664 | { |
1665 | // |
1666 | // Existing entry |
1667 | // |
1668 | // release the old CtxEntry and update the entry |
1669 | CtxEntry *pTemp = pNewCtxEntry.Extract(); |
1670 | pNewCtxEntry = pEntry->m_pCtxEntry; |
1671 | pEntry->m_pCtxEntry = pTemp; |
1672 | |
1673 | IGCHandleManager *mgr = GCHandleUtilities::GetGCHandleManager(); |
1674 | mgr->StoreObjectInHandle(pEntry->m_ohFactoryObject, OBJECTREFToObject(*refFactory)); |
1675 | } |
1676 | } |
1677 | |
1678 | OBJECTREF AppDomain::LookupWinRTFactoryObject(MethodTable *pClassMT, LPVOID lpCtxCookie) |
1679 | { |
1680 | CONTRACTL |
1681 | { |
1682 | THROWS; |
1683 | GC_NOTRIGGER; |
1684 | MODE_COOPERATIVE; |
1685 | PRECONDITION(CheckPointer(pClassMT)); |
1686 | PRECONDITION(CheckPointer(m_pWinRTFactoryCache, NULL_OK)); |
1687 | } |
1688 | CONTRACTL_END; |
1689 | |
1690 | |
1691 | if (m_pWinRTFactoryCache == nullptr) |
1692 | return NULL; |
1693 | |
1694 | // |
1695 | // Retrieve cached factory |
1696 | // |
1697 | WinRTFactoryCacheLockHolder lh(this); |
1698 | |
1699 | const WinRTFactoryCacheEntry *pEntry = m_pWinRTFactoryCache->LookupPtr(pClassMT); |
1700 | if (pEntry == NULL) |
1701 | return NULL; |
1702 | |
1703 | // |
1704 | // Ignore factories from a different context, unless lpCtxCookie == NULL, |
1705 | // which means the factory is free-threaded |
1706 | // Note that we cannot touch the RCW to retrieve cookie at this point |
1707 | // because the RCW might belong to a STA thread and that STA thread might die |
1708 | // and take the RCW with it. Therefore we have to save cookie in this cache |
1709 | // |
1710 | if (pEntry->m_pCtxEntry == NULL || pEntry->m_pCtxEntry->GetCtxCookie() == lpCtxCookie) |
1711 | return ObjectFromHandle(pEntry->m_ohFactoryObject); |
1712 | |
1713 | return NULL; |
1714 | } |
1715 | |
1716 | void AppDomain::RemoveWinRTFactoryObjects(LPVOID pCtxCookie) |
1717 | { |
1718 | CONTRACTL |
1719 | { |
1720 | THROWS; |
1721 | GC_TRIGGERS; |
1722 | MODE_ANY; |
1723 | } |
1724 | CONTRACTL_END; |
1725 | |
1726 | if (m_pWinRTFactoryCache == nullptr) |
1727 | return; |
1728 | |
1729 | // helper class for delayed CtxEntry cleanup |
1730 | class CtxEntryListReleaseHolder |
1731 | { |
1732 | public: |
1733 | CQuickArrayList<CtxEntry *> m_list; |
1734 | |
1735 | ~CtxEntryListReleaseHolder() |
1736 | { |
1737 | CONTRACTL |
1738 | { |
1739 | NOTHROW; |
1740 | GC_TRIGGERS; |
1741 | MODE_ANY; |
1742 | } |
1743 | CONTRACTL_END; |
1744 | |
1745 | for (SIZE_T i = 0; i < m_list.Size(); i++) |
1746 | { |
1747 | m_list[i]->Release(); |
1748 | } |
1749 | } |
1750 | } ctxEntryListReleaseHolder; |
1751 | |
1752 | GCX_COOP(); |
1753 | { |
1754 | WinRTFactoryCacheLockHolder lh(this); |
1755 | |
1756 | // Go through the hash table and remove items in the given context |
1757 | for (WinRTFactoryCache::Iterator it = m_pWinRTFactoryCache->Begin(); it != m_pWinRTFactoryCache->End(); it++) |
1758 | { |
1759 | if (it->m_pCtxEntry != NULL && it->m_pCtxEntry->GetCtxCookie() == pCtxCookie) |
1760 | { |
1761 | // Releasing the CtxEntry may trigger GC which we can't do under the lock so we push |
1762 | // it on our local list and release them all after we're done iterating the hashtable. |
1763 | ctxEntryListReleaseHolder.m_list.Push(it->m_pCtxEntry); |
1764 | |
1765 | DestroyHandle(it->m_ohFactoryObject); |
1766 | m_pWinRTFactoryCache->Remove(it); |
1767 | } |
1768 | } |
1769 | } |
1770 | } |
1771 | |
1772 | OBJECTREF AppDomain::GetMissingObject() |
1773 | { |
1774 | CONTRACTL |
1775 | { |
1776 | THROWS; |
1777 | GC_TRIGGERS; |
1778 | MODE_COOPERATIVE; |
1779 | } |
1780 | CONTRACTL_END; |
1781 | |
1782 | if (!m_hndMissing) |
1783 | { |
1784 | // Get the field |
1785 | FieldDesc *pValueFD = MscorlibBinder::GetField(FIELD__MISSING__VALUE); |
1786 | |
1787 | pValueFD->CheckRunClassInitThrowing(); |
1788 | |
1789 | // Retrieve the value static field and store it. |
1790 | OBJECTHANDLE hndMissing = CreateHandle(pValueFD->GetStaticOBJECTREF()); |
1791 | |
1792 | if (FastInterlockCompareExchangePointer(&m_hndMissing, hndMissing, NULL) != NULL) |
1793 | { |
1794 | // Exchanged failed. The m_hndMissing did not equal NULL and was returned. |
1795 | DestroyHandle(hndMissing); |
1796 | } |
1797 | } |
1798 | |
1799 | return ObjectFromHandle(m_hndMissing); |
1800 | } |
1801 | |
1802 | #endif // DACCESS_COMPILE |
1803 | #endif //CROSSGEN_COMPILE |
1804 | #endif // FEATURE_COMINTEROP |
1805 | |
1806 | #ifndef DACCESS_COMPILE |
1807 | |
1808 | EEMarshalingData *BaseDomain::GetMarshalingData() |
1809 | { |
1810 | CONTRACT (EEMarshalingData*) |
1811 | { |
1812 | THROWS; |
1813 | GC_TRIGGERS; |
1814 | MODE_ANY; |
1815 | INJECT_FAULT(COMPlusThrowOM()); |
1816 | POSTCONDITION(CheckPointer(m_pMarshalingData)); |
1817 | } |
1818 | CONTRACT_END; |
1819 | |
1820 | if (!m_pMarshalingData) |
1821 | { |
1822 | // Take the lock |
1823 | CrstHolder holder(&m_InteropDataCrst); |
1824 | |
1825 | if (!m_pMarshalingData) |
1826 | { |
1827 | LoaderHeap* pHeap = GetLoaderAllocator()->GetLowFrequencyHeap(); |
1828 | m_pMarshalingData = new (pHeap) EEMarshalingData(this, pHeap, &m_DomainCrst); |
1829 | } |
1830 | } |
1831 | |
1832 | RETURN m_pMarshalingData; |
1833 | } |
1834 | |
1835 | void BaseDomain::DeleteMarshalingData() |
1836 | { |
1837 | CONTRACTL |
1838 | { |
1839 | NOTHROW; |
1840 | GC_TRIGGERS; |
1841 | MODE_ANY; |
1842 | } |
1843 | CONTRACTL_END; |
1844 | |
1845 | // We are in shutdown - no need to take any lock |
1846 | if (m_pMarshalingData) |
1847 | { |
1848 | delete m_pMarshalingData; |
1849 | m_pMarshalingData = NULL; |
1850 | } |
1851 | } |
1852 | |
1853 | #ifndef CROSSGEN_COMPILE |
1854 | |
1855 | STRINGREF *BaseDomain::IsStringInterned(STRINGREF *pString) |
1856 | { |
1857 | CONTRACTL |
1858 | { |
1859 | GC_TRIGGERS; |
1860 | THROWS; |
1861 | MODE_COOPERATIVE; |
1862 | PRECONDITION(CheckPointer(pString)); |
1863 | INJECT_FAULT(COMPlusThrowOM();); |
1864 | } |
1865 | CONTRACTL_END; |
1866 | |
1867 | return GetLoaderAllocator()->IsStringInterned(pString); |
1868 | } |
1869 | |
1870 | STRINGREF *BaseDomain::GetOrInternString(STRINGREF *pString) |
1871 | { |
1872 | CONTRACTL |
1873 | { |
1874 | GC_TRIGGERS; |
1875 | THROWS; |
1876 | MODE_COOPERATIVE; |
1877 | PRECONDITION(CheckPointer(pString)); |
1878 | INJECT_FAULT(COMPlusThrowOM();); |
1879 | } |
1880 | CONTRACTL_END; |
1881 | |
1882 | return GetLoaderAllocator()->GetOrInternString(pString); |
1883 | } |
1884 | |
1885 | void BaseDomain::InitLargeHeapHandleTable() |
1886 | { |
1887 | CONTRACTL |
1888 | { |
1889 | THROWS; |
1890 | GC_TRIGGERS; |
1891 | MODE_ANY; |
1892 | PRECONDITION(m_pLargeHeapHandleTable==NULL); |
1893 | INJECT_FAULT(COMPlusThrowOM();); |
1894 | } |
1895 | CONTRACTL_END; |
1896 | |
1897 | m_pLargeHeapHandleTable = new LargeHeapHandleTable(this, STATIC_OBJECT_TABLE_BUCKET_SIZE); |
1898 | |
1899 | #ifdef _DEBUG |
1900 | m_pLargeHeapHandleTable->RegisterCrstDebug(&m_LargeHeapHandleTableCrst); |
1901 | #endif |
1902 | } |
1903 | |
1904 | #ifdef FEATURE_COMINTEROP |
1905 | MethodTable* AppDomain::GetLicenseInteropHelperMethodTable() |
1906 | { |
1907 | CONTRACTL |
1908 | { |
1909 | THROWS; |
1910 | GC_TRIGGERS; |
1911 | } |
1912 | CONTRACTL_END; |
1913 | |
1914 | if(m_pLicenseInteropHelperMT == NULL) |
1915 | { |
1916 | // Do this work outside of the lock so we don't have an unbreakable lock condition |
1917 | |
1918 | TypeHandle licenseMgrTypeHnd; |
1919 | MethodDescCallSite loadLM(METHOD__MARSHAL__LOAD_LICENSE_MANAGER); |
1920 | |
1921 | licenseMgrTypeHnd = (MethodTable*) loadLM.Call_RetLPVOID((ARG_SLOT*)NULL); |
1922 | |
1923 | // |
1924 | // Look up this method by name, because the type is actually declared in System.dll. <TODO>@todo: why?</TODO> |
1925 | // |
1926 | |
1927 | MethodDesc *pGetLIHMD = MemberLoader::FindMethod(licenseMgrTypeHnd.AsMethodTable(), |
1928 | "GetLicenseInteropHelperType" , &gsig_SM_Void_RetIntPtr); |
1929 | _ASSERTE(pGetLIHMD); |
1930 | |
1931 | TypeHandle lihTypeHnd; |
1932 | |
1933 | MethodDescCallSite getLIH(pGetLIHMD); |
1934 | lihTypeHnd = (MethodTable*) getLIH.Call_RetLPVOID((ARG_SLOT*)NULL); |
1935 | |
1936 | BaseDomain::LockHolder lh(this); |
1937 | |
1938 | if(m_pLicenseInteropHelperMT == NULL) |
1939 | m_pLicenseInteropHelperMT = lihTypeHnd.AsMethodTable(); |
1940 | } |
1941 | return m_pLicenseInteropHelperMT; |
1942 | } |
1943 | #endif // FEATURE_COMINTEROP |
1944 | |
1945 | #endif // CROSSGEN_COMPILE |
1946 | |
1947 | //***************************************************************************** |
1948 | //***************************************************************************** |
1949 | //***************************************************************************** |
1950 | |
1951 | void *SystemDomain::operator new(size_t size, void *pInPlace) |
1952 | { |
1953 | LIMITED_METHOD_CONTRACT; |
1954 | return pInPlace; |
1955 | } |
1956 | |
1957 | |
1958 | void SystemDomain::operator delete(void *pMem) |
1959 | { |
1960 | LIMITED_METHOD_CONTRACT; |
1961 | // Do nothing - new() was in-place |
1962 | } |
1963 | |
1964 | |
1965 | void SystemDomain::SetCompilationOverrides(BOOL fForceDebug, |
1966 | BOOL fForceProfiling, |
1967 | BOOL fForceInstrument) |
1968 | { |
1969 | LIMITED_METHOD_CONTRACT; |
1970 | s_fForceDebug = fForceDebug; |
1971 | s_fForceProfiling = fForceProfiling; |
1972 | s_fForceInstrument = fForceInstrument; |
1973 | } |
1974 | |
1975 | #endif //!DACCESS_COMPILE |
1976 | |
1977 | void SystemDomain::GetCompilationOverrides(BOOL * fForceDebug, |
1978 | BOOL * fForceProfiling, |
1979 | BOOL * fForceInstrument) |
1980 | { |
1981 | LIMITED_METHOD_DAC_CONTRACT; |
1982 | *fForceDebug = s_fForceDebug; |
1983 | *fForceProfiling = s_fForceProfiling; |
1984 | *fForceInstrument = s_fForceInstrument; |
1985 | } |
1986 | |
1987 | #ifndef DACCESS_COMPILE |
1988 | |
1989 | void SystemDomain::Attach() |
1990 | { |
1991 | CONTRACTL |
1992 | { |
1993 | THROWS; |
1994 | GC_TRIGGERS; |
1995 | MODE_ANY; |
1996 | PRECONDITION(m_pSystemDomain == NULL); |
1997 | INJECT_FAULT(COMPlusThrowOM();); |
1998 | } |
1999 | CONTRACTL_END; |
2000 | |
2001 | #ifndef CROSSGEN_COMPILE |
2002 | // Initialize stub managers |
2003 | PrecodeStubManager::Init(); |
2004 | DelegateInvokeStubManager::Init(); |
2005 | JumpStubStubManager::Init(); |
2006 | RangeSectionStubManager::Init(); |
2007 | ILStubManager::Init(); |
2008 | InteropDispatchStubManager::Init(); |
2009 | StubLinkStubManager::Init(); |
2010 | |
2011 | ThunkHeapStubManager::Init(); |
2012 | |
2013 | TailCallStubManager::Init(); |
2014 | |
2015 | PerAppDomainTPCountList::InitAppDomainIndexList(); |
2016 | #endif // CROSSGEN_COMPILE |
2017 | |
2018 | m_appDomainIndexList.Init(); |
2019 | m_appDomainIdList.Init(); |
2020 | |
2021 | m_SystemDomainCrst.Init(CrstSystemDomain, (CrstFlags)(CRST_REENTRANCY | CRST_TAKEN_DURING_SHUTDOWN)); |
2022 | m_DelayedUnloadCrst.Init(CrstSystemDomainDelayedUnloadList, CRST_UNSAFE_COOPGC); |
2023 | |
2024 | // Initialize the ID dispenser that is used for domain neutral module IDs |
2025 | g_pModuleIndexDispenser = new IdDispenser(); |
2026 | |
2027 | // Create the global SystemDomain and initialize it. |
2028 | m_pSystemDomain = new (&g_pSystemDomainMemory[0]) SystemDomain(); |
2029 | // No way it can fail since g_pSystemDomainMemory is a static array. |
2030 | CONSISTENCY_CHECK(CheckPointer(m_pSystemDomain)); |
2031 | |
2032 | LOG((LF_CLASSLOADER, |
2033 | LL_INFO10, |
2034 | "Created system domain at %p\n" , |
2035 | m_pSystemDomain)); |
2036 | |
2037 | // We need to initialize the memory pools etc. for the system domain. |
2038 | m_pSystemDomain->BaseDomain::Init(); // Setup the memory heaps |
2039 | |
2040 | // Create the one and only app domain |
2041 | AppDomain::Create(); |
2042 | |
2043 | // Each domain gets its own ReJitManager, and ReJitManager has its own static |
2044 | // initialization to run |
2045 | ReJitManager::InitStatic(); |
2046 | } |
2047 | |
2048 | #ifndef CROSSGEN_COMPILE |
2049 | |
2050 | void SystemDomain::DetachBegin() |
2051 | { |
2052 | WRAPPER_NO_CONTRACT; |
2053 | // Shut down the domain and its children (but don't deallocate anything just |
2054 | // yet). |
2055 | |
2056 | // TODO: we should really not running managed DLLMain during process detach. |
2057 | if (GetThread() == NULL) |
2058 | { |
2059 | return; |
2060 | } |
2061 | |
2062 | if(m_pSystemDomain) |
2063 | m_pSystemDomain->Stop(); |
2064 | } |
2065 | |
2066 | void SystemDomain::DetachEnd() |
2067 | { |
2068 | CONTRACTL |
2069 | { |
2070 | NOTHROW; |
2071 | GC_TRIGGERS; |
2072 | MODE_ANY; |
2073 | } |
2074 | CONTRACTL_END; |
2075 | // Shut down the domain and its children (but don't deallocate anything just |
2076 | // yet). |
2077 | if(m_pSystemDomain) |
2078 | { |
2079 | GCX_PREEMP(); |
2080 | m_pSystemDomain->ClearFusionContext(); |
2081 | AppDomain* pAppDomain = GetAppDomain(); |
2082 | if (pAppDomain) |
2083 | pAppDomain->ClearFusionContext(); |
2084 | } |
2085 | } |
2086 | |
2087 | void SystemDomain::Stop() |
2088 | { |
2089 | WRAPPER_NO_CONTRACT; |
2090 | AppDomainIterator i(TRUE); |
2091 | |
2092 | while (i.Next()) |
2093 | i.GetDomain()->Stop(); |
2094 | } |
2095 | |
2096 | |
2097 | void SystemDomain::Terminate() // bNotifyProfiler is ignored |
2098 | { |
2099 | CONTRACTL |
2100 | { |
2101 | NOTHROW; |
2102 | GC_TRIGGERS; |
2103 | MODE_ANY; |
2104 | } |
2105 | CONTRACTL_END; |
2106 | |
2107 | // This ignores the refences and terminates the appdomains |
2108 | AppDomainIterator i(FALSE); |
2109 | |
2110 | while (i.Next()) |
2111 | { |
2112 | delete i.GetDomain(); |
2113 | // Keep the iterator from Releasing the current domain |
2114 | i.m_pCurrent = NULL; |
2115 | } |
2116 | |
2117 | if (m_pSystemFile != NULL) { |
2118 | m_pSystemFile->Release(); |
2119 | m_pSystemFile = NULL; |
2120 | } |
2121 | |
2122 | m_pSystemAssembly = NULL; |
2123 | |
2124 | if (m_pGlobalStringLiteralMap) { |
2125 | delete m_pGlobalStringLiteralMap; |
2126 | m_pGlobalStringLiteralMap = NULL; |
2127 | } |
2128 | |
2129 | BaseDomain::Terminate(); |
2130 | |
2131 | #ifdef FEATURE_COMINTEROP |
2132 | if (g_pRCWCleanupList != NULL) |
2133 | delete g_pRCWCleanupList; |
2134 | #endif // FEATURE_COMINTEROP |
2135 | m_GlobalAllocator.Terminate(); |
2136 | } |
2137 | |
2138 | |
2139 | void SystemDomain::PreallocateSpecialObjects() |
2140 | { |
2141 | CONTRACTL |
2142 | { |
2143 | THROWS; |
2144 | GC_TRIGGERS; |
2145 | MODE_COOPERATIVE; |
2146 | INJECT_FAULT(COMPlusThrowOM();); |
2147 | } |
2148 | CONTRACTL_END; |
2149 | |
2150 | _ASSERTE(g_pPreallocatedSentinelObject == NULL); |
2151 | |
2152 | OBJECTREF pPreallocatedSentinalObject = AllocateObject(g_pObjectClass); |
2153 | g_pPreallocatedSentinelObject = CreatePinningHandle( pPreallocatedSentinalObject ); |
2154 | |
2155 | #ifdef FEATURE_PREJIT |
2156 | if (SystemModule()->HasNativeImage()) |
2157 | { |
2158 | CORCOMPILE_EE_INFO_TABLE *pEEInfo = SystemModule()->GetNativeImage()->GetNativeEEInfoTable(); |
2159 | pEEInfo->emptyString = (CORINFO_Object **)StringObject::GetEmptyStringRefPtr(); |
2160 | } |
2161 | #endif |
2162 | } |
2163 | |
2164 | void SystemDomain::CreatePreallocatedExceptions() |
2165 | { |
2166 | CONTRACTL |
2167 | { |
2168 | THROWS; |
2169 | GC_TRIGGERS; |
2170 | MODE_COOPERATIVE; |
2171 | INJECT_FAULT(COMPlusThrowOM();); |
2172 | } |
2173 | CONTRACTL_END; |
2174 | |
2175 | EXCEPTIONREF pBaseException = (EXCEPTIONREF)AllocateObject(g_pExceptionClass); |
2176 | pBaseException->SetHResult(COR_E_EXCEPTION); |
2177 | pBaseException->SetXCode(EXCEPTION_COMPLUS); |
2178 | _ASSERTE(g_pPreallocatedBaseException == NULL); |
2179 | g_pPreallocatedBaseException = CreateHandle(pBaseException); |
2180 | |
2181 | |
2182 | EXCEPTIONREF pOutOfMemory = (EXCEPTIONREF)AllocateObject(g_pOutOfMemoryExceptionClass); |
2183 | pOutOfMemory->SetHResult(COR_E_OUTOFMEMORY); |
2184 | pOutOfMemory->SetXCode(EXCEPTION_COMPLUS); |
2185 | _ASSERTE(g_pPreallocatedOutOfMemoryException == NULL); |
2186 | g_pPreallocatedOutOfMemoryException = CreateHandle(pOutOfMemory); |
2187 | |
2188 | |
2189 | EXCEPTIONREF pStackOverflow = (EXCEPTIONREF)AllocateObject(g_pStackOverflowExceptionClass); |
2190 | pStackOverflow->SetHResult(COR_E_STACKOVERFLOW); |
2191 | pStackOverflow->SetXCode(EXCEPTION_COMPLUS); |
2192 | _ASSERTE(g_pPreallocatedStackOverflowException == NULL); |
2193 | g_pPreallocatedStackOverflowException = CreateHandle(pStackOverflow); |
2194 | |
2195 | |
2196 | EXCEPTIONREF pExecutionEngine = (EXCEPTIONREF)AllocateObject(g_pExecutionEngineExceptionClass); |
2197 | pExecutionEngine->SetHResult(COR_E_EXECUTIONENGINE); |
2198 | pExecutionEngine->SetXCode(EXCEPTION_COMPLUS); |
2199 | _ASSERTE(g_pPreallocatedExecutionEngineException == NULL); |
2200 | g_pPreallocatedExecutionEngineException = CreateHandle(pExecutionEngine); |
2201 | |
2202 | |
2203 | EXCEPTIONREF pRudeAbortException = (EXCEPTIONREF)AllocateObject(g_pThreadAbortExceptionClass); |
2204 | pRudeAbortException->SetHResult(COR_E_THREADABORTED); |
2205 | pRudeAbortException->SetXCode(EXCEPTION_COMPLUS); |
2206 | _ASSERTE(g_pPreallocatedRudeThreadAbortException == NULL); |
2207 | g_pPreallocatedRudeThreadAbortException = CreateHandle(pRudeAbortException); |
2208 | |
2209 | |
2210 | EXCEPTIONREF pAbortException = (EXCEPTIONREF)AllocateObject(g_pThreadAbortExceptionClass); |
2211 | pAbortException->SetHResult(COR_E_THREADABORTED); |
2212 | pAbortException->SetXCode(EXCEPTION_COMPLUS); |
2213 | _ASSERTE(g_pPreallocatedThreadAbortException == NULL); |
2214 | g_pPreallocatedThreadAbortException = CreateHandle( pAbortException ); |
2215 | } |
2216 | #endif // CROSSGEN_COMPILE |
2217 | |
2218 | void SystemDomain::Init() |
2219 | { |
2220 | STANDARD_VM_CONTRACT; |
2221 | |
2222 | HRESULT hr = S_OK; |
2223 | |
2224 | #ifdef _DEBUG |
2225 | LOG(( |
2226 | LF_EEMEM, |
2227 | LL_INFO10, |
2228 | "sizeof(EEClass) = %d\n" |
2229 | "sizeof(MethodTable) = %d\n" |
2230 | "sizeof(MethodDesc)= %d\n" |
2231 | "sizeof(FieldDesc) = %d\n" |
2232 | "sizeof(Module) = %d\n" , |
2233 | sizeof(EEClass), |
2234 | sizeof(MethodTable), |
2235 | sizeof(MethodDesc), |
2236 | sizeof(FieldDesc), |
2237 | sizeof(Module) |
2238 | )); |
2239 | #endif // _DEBUG |
2240 | |
2241 | // The base domain is initialized in SystemDomain::Attach() |
2242 | // to allow stub caches to use the memory pool. Do not |
2243 | // initialze it here! |
2244 | |
2245 | #ifdef FEATURE_PREJIT |
2246 | if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_ZapDisable) != 0) |
2247 | g_fAllowNativeImages = false; |
2248 | #endif |
2249 | |
2250 | m_pSystemFile = NULL; |
2251 | m_pSystemAssembly = NULL; |
2252 | |
2253 | DWORD size = 0; |
2254 | |
2255 | |
2256 | // Get the install directory so we can find mscorlib |
2257 | hr = GetInternalSystemDirectory(NULL, &size); |
2258 | if (hr != HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) |
2259 | ThrowHR(hr); |
2260 | |
2261 | // GetInternalSystemDirectory returns a size, including the null! |
2262 | WCHAR *buffer = m_SystemDirectory.OpenUnicodeBuffer(size-1); |
2263 | IfFailThrow(GetInternalSystemDirectory(buffer, &size)); |
2264 | m_SystemDirectory.CloseBuffer(); |
2265 | m_SystemDirectory.Normalize(); |
2266 | |
2267 | // At this point m_SystemDirectory should already be canonicalized |
2268 | |
2269 | |
2270 | m_BaseLibrary.Append(m_SystemDirectory); |
2271 | if (!m_BaseLibrary.EndsWith(DIRECTORY_SEPARATOR_CHAR_W)) |
2272 | { |
2273 | m_BaseLibrary.Append(DIRECTORY_SEPARATOR_CHAR_W); |
2274 | } |
2275 | m_BaseLibrary.Append(g_pwBaseLibrary); |
2276 | m_BaseLibrary.Normalize(); |
2277 | |
2278 | LoadBaseSystemClasses(); |
2279 | |
2280 | { |
2281 | // We are about to start allocating objects, so we must be in cooperative mode. |
2282 | // However, many of the entrypoints to the system (DllGetClassObject and all |
2283 | // N/Direct exports) get called multiple times. Sometimes they initialize the EE, |
2284 | // but generally they remain in preemptive mode. So we really want to push/pop |
2285 | // the state here: |
2286 | GCX_COOP(); |
2287 | |
2288 | #ifndef CROSSGEN_COMPILE |
2289 | if (!NingenEnabled()) |
2290 | { |
2291 | CreatePreallocatedExceptions(); |
2292 | |
2293 | PreallocateSpecialObjects(); |
2294 | } |
2295 | #endif |
2296 | |
2297 | // Finish loading mscorlib now. |
2298 | m_pSystemAssembly->GetDomainAssembly()->EnsureActive(); |
2299 | } |
2300 | |
2301 | #ifdef _DEBUG |
2302 | BOOL fPause = EEConfig::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_PauseOnLoad, FALSE); |
2303 | |
2304 | while(fPause) |
2305 | { |
2306 | ClrSleepEx(20, TRUE); |
2307 | } |
2308 | #endif // _DEBUG |
2309 | } |
2310 | |
2311 | #ifndef CROSSGEN_COMPILE |
2312 | void SystemDomain::LazyInitGlobalStringLiteralMap() |
2313 | { |
2314 | CONTRACTL |
2315 | { |
2316 | THROWS; |
2317 | GC_TRIGGERS; |
2318 | MODE_ANY; |
2319 | INJECT_FAULT(COMPlusThrowOM();); |
2320 | } |
2321 | CONTRACTL_END; |
2322 | |
2323 | // Allocate the global string literal map. |
2324 | NewHolder<GlobalStringLiteralMap> pGlobalStringLiteralMap(new GlobalStringLiteralMap()); |
2325 | |
2326 | // Initialize the global string literal map. |
2327 | pGlobalStringLiteralMap->Init(); |
2328 | |
2329 | if (InterlockedCompareExchangeT<GlobalStringLiteralMap *>(&m_pGlobalStringLiteralMap, pGlobalStringLiteralMap, NULL) == NULL) |
2330 | { |
2331 | pGlobalStringLiteralMap.SuppressRelease(); |
2332 | } |
2333 | } |
2334 | |
2335 | /*static*/ void SystemDomain::EnumAllStaticGCRefs(promote_func* fn, ScanContext* sc) |
2336 | { |
2337 | CONTRACT_VOID |
2338 | { |
2339 | NOTHROW; |
2340 | GC_NOTRIGGER; |
2341 | MODE_COOPERATIVE; |
2342 | } |
2343 | CONTRACT_END; |
2344 | |
2345 | // We don't do a normal AppDomainIterator because we can't take the SystemDomain lock from |
2346 | // here. |
2347 | // We're only supposed to call this from a Server GC. We're walking here m_appDomainIdList |
2348 | // m_appDomainIdList will have an AppDomain* or will be NULL. So the only danger is if we |
2349 | // Fetch an AppDomain and then in some other thread the AppDomain is deleted. |
2350 | // |
2351 | // If the thread deleting the AppDomain (AppDomain::~AppDomain)was in Preemptive mode |
2352 | // while doing SystemDomain::EnumAllStaticGCRefs we will issue a GCX_COOP(), which will wait |
2353 | // for the GC to finish, so we are safe |
2354 | // |
2355 | // If the thread is in cooperative mode, it must have been suspended for the GC so a delete |
2356 | // can't happen. |
2357 | |
2358 | _ASSERTE(GCHeapUtilities::IsGCInProgress() && |
2359 | GCHeapUtilities::IsServerHeap() && |
2360 | IsGCSpecialThread()); |
2361 | |
2362 | SystemDomain* sysDomain = SystemDomain::System(); |
2363 | if (sysDomain) |
2364 | { |
2365 | DWORD i; |
2366 | DWORD count = (DWORD) m_appDomainIdList.GetCount(); |
2367 | for (i = 0 ; i < count ; i++) |
2368 | { |
2369 | AppDomain* pAppDomain = (AppDomain *)m_appDomainIdList.Get(i); |
2370 | if (pAppDomain && pAppDomain->IsActive()) |
2371 | { |
2372 | #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING |
2373 | if (g_fEnableARM) |
2374 | { |
2375 | sc->pCurrentDomain = pAppDomain; |
2376 | } |
2377 | #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING |
2378 | pAppDomain->EnumStaticGCRefs(fn, sc); |
2379 | } |
2380 | } |
2381 | } |
2382 | |
2383 | RETURN; |
2384 | } |
2385 | |
2386 | #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING |
2387 | void SystemDomain::ResetADSurvivedBytes() |
2388 | { |
2389 | CONTRACT_VOID |
2390 | { |
2391 | NOTHROW; |
2392 | GC_NOTRIGGER; |
2393 | MODE_ANY; |
2394 | } |
2395 | CONTRACT_END; |
2396 | |
2397 | _ASSERTE(GCHeapUtilities::IsGCInProgress()); |
2398 | |
2399 | SystemDomain* sysDomain = SystemDomain::System(); |
2400 | if (sysDomain) |
2401 | { |
2402 | DWORD i; |
2403 | DWORD count = (DWORD) m_appDomainIdList.GetCount(); |
2404 | for (i = 0 ; i < count ; i++) |
2405 | { |
2406 | AppDomain* pAppDomain = (AppDomain *)m_appDomainIdList.Get(i); |
2407 | if (pAppDomain && pAppDomain->IsUserActive()) |
2408 | { |
2409 | pAppDomain->ResetSurvivedBytes(); |
2410 | } |
2411 | } |
2412 | } |
2413 | |
2414 | RETURN; |
2415 | } |
2416 | |
2417 | ULONGLONG SystemDomain::GetADSurvivedBytes() |
2418 | { |
2419 | CONTRACTL |
2420 | { |
2421 | NOTHROW; |
2422 | GC_NOTRIGGER; |
2423 | MODE_ANY; |
2424 | } |
2425 | CONTRACTL_END; |
2426 | |
2427 | SystemDomain* sysDomain = SystemDomain::System(); |
2428 | ULONGLONG ullTotalADSurvived = 0; |
2429 | if (sysDomain) |
2430 | { |
2431 | DWORD i; |
2432 | DWORD count = (DWORD) m_appDomainIdList.GetCount(); |
2433 | for (i = 0 ; i < count ; i++) |
2434 | { |
2435 | AppDomain* pAppDomain = (AppDomain *)m_appDomainIdList.Get(i); |
2436 | if (pAppDomain && pAppDomain->IsUserActive()) |
2437 | { |
2438 | ULONGLONG ullSurvived = pAppDomain->GetSurvivedBytes(); |
2439 | ullTotalADSurvived += ullSurvived; |
2440 | } |
2441 | } |
2442 | } |
2443 | |
2444 | return ullTotalADSurvived; |
2445 | } |
2446 | |
2447 | void SystemDomain::RecordTotalSurvivedBytes(size_t totalSurvivedBytes) |
2448 | { |
2449 | CONTRACT_VOID |
2450 | { |
2451 | NOTHROW; |
2452 | GC_NOTRIGGER; |
2453 | MODE_ANY; |
2454 | } |
2455 | CONTRACT_END; |
2456 | |
2457 | m_totalSurvivedBytes = totalSurvivedBytes; |
2458 | |
2459 | SystemDomain* sysDomain = SystemDomain::System(); |
2460 | if (sysDomain) |
2461 | { |
2462 | DWORD i; |
2463 | DWORD count = (DWORD) m_appDomainIdList.GetCount(); |
2464 | for (i = 0 ; i < count ; i++) |
2465 | { |
2466 | AppDomain* pAppDomain = (AppDomain *)m_appDomainIdList.Get(i); |
2467 | if (pAppDomain && pAppDomain->IsUserActive()) |
2468 | { |
2469 | FireEtwAppDomainMemSurvived((ULONGLONG)pAppDomain, pAppDomain->GetSurvivedBytes(), totalSurvivedBytes, GetClrInstanceId()); |
2470 | } |
2471 | } |
2472 | } |
2473 | |
2474 | RETURN; |
2475 | } |
2476 | #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING |
2477 | |
2478 | // Only called when EE is suspended. |
2479 | DWORD SystemDomain::GetTotalNumSizedRefHandles() |
2480 | { |
2481 | CONTRACTL |
2482 | { |
2483 | NOTHROW; |
2484 | GC_NOTRIGGER; |
2485 | MODE_ANY; |
2486 | } |
2487 | CONTRACTL_END; |
2488 | |
2489 | SystemDomain* sysDomain = SystemDomain::System(); |
2490 | DWORD dwTotalNumSizedRefHandles = 0; |
2491 | if (sysDomain) |
2492 | { |
2493 | DWORD i; |
2494 | DWORD count = (DWORD) m_appDomainIdList.GetCount(); |
2495 | for (i = 0 ; i < count ; i++) |
2496 | { |
2497 | AppDomain* pAppDomain = (AppDomain *)m_appDomainIdList.Get(i); |
2498 | if (pAppDomain && pAppDomain->IsActive()) |
2499 | { |
2500 | dwTotalNumSizedRefHandles += pAppDomain->GetNumSizedRefHandles(); |
2501 | } |
2502 | } |
2503 | } |
2504 | |
2505 | return dwTotalNumSizedRefHandles; |
2506 | } |
2507 | #endif // CROSSGEN_COMPILE |
2508 | |
2509 | void SystemDomain::LoadBaseSystemClasses() |
2510 | { |
2511 | STANDARD_VM_CONTRACT; |
2512 | |
2513 | ETWOnStartup(LdSysBases_V1, LdSysBasesEnd_V1); |
2514 | |
2515 | { |
2516 | m_pSystemFile = PEAssembly::OpenSystem(NULL); |
2517 | } |
2518 | // Only partially load the system assembly. Other parts of the code will want to access |
2519 | // the globals in this function before finishing the load. |
2520 | m_pSystemAssembly = DefaultDomain()->LoadDomainAssembly(NULL, m_pSystemFile, FILE_LOAD_POST_LOADLIBRARY)->GetCurrentAssembly(); |
2521 | |
2522 | // Set up binder for mscorlib |
2523 | MscorlibBinder::AttachModule(m_pSystemAssembly->GetManifestModule()); |
2524 | |
2525 | // Load Object |
2526 | g_pObjectClass = MscorlibBinder::GetClass(CLASS__OBJECT); |
2527 | |
2528 | // Now that ObjectClass is loaded, we can set up |
2529 | // the system for finalizers. There is no point in deferring this, since we need |
2530 | // to know this before we allocate our first object. |
2531 | g_pObjectFinalizerMD = MscorlibBinder::GetMethod(METHOD__OBJECT__FINALIZE); |
2532 | |
2533 | |
2534 | g_pCanonMethodTableClass = MscorlibBinder::GetClass(CLASS____CANON); |
2535 | |
2536 | // NOTE: !!!IMPORTANT!!! ValueType and Enum MUST be loaded one immediately after |
2537 | // the other, because we have coded MethodTable::IsChildValueType |
2538 | // in such a way that it depends on this behaviour. |
2539 | // Load the ValueType class |
2540 | g_pValueTypeClass = MscorlibBinder::GetClass(CLASS__VALUE_TYPE); |
2541 | |
2542 | // Load the enum class |
2543 | g_pEnumClass = MscorlibBinder::GetClass(CLASS__ENUM); |
2544 | _ASSERTE(!g_pEnumClass->IsValueType()); |
2545 | |
2546 | // Load System.RuntimeType |
2547 | g_pRuntimeTypeClass = MscorlibBinder::GetClass(CLASS__CLASS); |
2548 | _ASSERTE(g_pRuntimeTypeClass->IsFullyLoaded()); |
2549 | |
2550 | // Load Array class |
2551 | g_pArrayClass = MscorlibBinder::GetClass(CLASS__ARRAY); |
2552 | |
2553 | // Calling a method on IList<T> for an array requires redirection to a method on |
2554 | // the SZArrayHelper class. Retrieving such methods means calling |
2555 | // GetActualImplementationForArrayGenericIListMethod, which calls FetchMethod for |
2556 | // the corresponding method on SZArrayHelper. This basically results in a class |
2557 | // load due to a method call, which the debugger cannot handle, so we pre-load |
2558 | // the SZArrayHelper class here. |
2559 | g_pSZArrayHelperClass = MscorlibBinder::GetClass(CLASS__SZARRAYHELPER); |
2560 | |
2561 | // Load ByReference class |
2562 | // |
2563 | // NOTE: ByReference<T> must be the first by-ref-like system type to be loaded, |
2564 | // because MethodTable::ClassifyEightBytesWithManagedLayout depends on it. |
2565 | g_pByReferenceClass = MscorlibBinder::GetClass(CLASS__BYREFERENCE); |
2566 | |
2567 | // Load Nullable class |
2568 | g_pNullableClass = MscorlibBinder::GetClass(CLASS__NULLABLE); |
2569 | |
2570 | // Load the Object array class. |
2571 | g_pPredefinedArrayTypes[ELEMENT_TYPE_OBJECT] = ClassLoader::LoadArrayTypeThrowing(TypeHandle(g_pObjectClass)).AsArray(); |
2572 | |
2573 | // We have delayed allocation of mscorlib's static handles until we load the object class |
2574 | MscorlibBinder::GetModule()->AllocateRegularStaticHandles(DefaultDomain()); |
2575 | |
2576 | g_TypedReferenceMT = MscorlibBinder::GetClass(CLASS__TYPED_REFERENCE); |
2577 | |
2578 | // Make sure all primitive types are loaded |
2579 | for (int et = ELEMENT_TYPE_VOID; et <= ELEMENT_TYPE_R8; et++) |
2580 | MscorlibBinder::LoadPrimitiveType((CorElementType)et); |
2581 | |
2582 | MscorlibBinder::LoadPrimitiveType(ELEMENT_TYPE_I); |
2583 | MscorlibBinder::LoadPrimitiveType(ELEMENT_TYPE_U); |
2584 | |
2585 | // unfortunately, the following cannot be delay loaded since the jit |
2586 | // uses it to compute method attributes within a function that cannot |
2587 | // handle Complus exception and the following call goes through a path |
2588 | // where a complus exception can be thrown. It is unfortunate, because |
2589 | // we know that the delegate class and multidelegate class are always |
2590 | // guaranteed to be found. |
2591 | g_pDelegateClass = MscorlibBinder::GetClass(CLASS__DELEGATE); |
2592 | g_pMulticastDelegateClass = MscorlibBinder::GetClass(CLASS__MULTICAST_DELEGATE); |
2593 | |
2594 | // used by IsImplicitInterfaceOfSZArray |
2595 | MscorlibBinder::GetClass(CLASS__IENUMERABLEGENERIC); |
2596 | MscorlibBinder::GetClass(CLASS__ICOLLECTIONGENERIC); |
2597 | MscorlibBinder::GetClass(CLASS__ILISTGENERIC); |
2598 | MscorlibBinder::GetClass(CLASS__IREADONLYCOLLECTIONGENERIC); |
2599 | MscorlibBinder::GetClass(CLASS__IREADONLYLISTGENERIC); |
2600 | |
2601 | // Load String |
2602 | g_pStringClass = MscorlibBinder::LoadPrimitiveType(ELEMENT_TYPE_STRING); |
2603 | |
2604 | // Used by Buffer::BlockCopy |
2605 | g_pByteArrayMT = ClassLoader::LoadArrayTypeThrowing( |
2606 | TypeHandle(MscorlibBinder::GetElementType(ELEMENT_TYPE_U1))).AsArray()->GetMethodTable(); |
2607 | |
2608 | #ifndef CROSSGEN_COMPILE |
2609 | ECall::PopulateManagedStringConstructors(); |
2610 | #endif // CROSSGEN_COMPILE |
2611 | |
2612 | g_pExceptionClass = MscorlibBinder::GetClass(CLASS__EXCEPTION); |
2613 | g_pOutOfMemoryExceptionClass = MscorlibBinder::GetException(kOutOfMemoryException); |
2614 | g_pStackOverflowExceptionClass = MscorlibBinder::GetException(kStackOverflowException); |
2615 | g_pExecutionEngineExceptionClass = MscorlibBinder::GetException(kExecutionEngineException); |
2616 | g_pThreadAbortExceptionClass = MscorlibBinder::GetException(kThreadAbortException); |
2617 | |
2618 | g_pThreadClass = MscorlibBinder::GetClass(CLASS__THREAD); |
2619 | |
2620 | #ifdef FEATURE_COMINTEROP |
2621 | g_pBaseCOMObject = MscorlibBinder::GetClass(CLASS__COM_OBJECT); |
2622 | g_pBaseRuntimeClass = MscorlibBinder::GetClass(CLASS__RUNTIME_CLASS); |
2623 | |
2624 | MscorlibBinder::GetClass(CLASS__IDICTIONARYGENERIC); |
2625 | MscorlibBinder::GetClass(CLASS__IREADONLYDICTIONARYGENERIC); |
2626 | MscorlibBinder::GetClass(CLASS__ATTRIBUTE); |
2627 | MscorlibBinder::GetClass(CLASS__EVENT_HANDLERGENERIC); |
2628 | |
2629 | MscorlibBinder::GetClass(CLASS__IENUMERABLE); |
2630 | MscorlibBinder::GetClass(CLASS__ICOLLECTION); |
2631 | MscorlibBinder::GetClass(CLASS__ILIST); |
2632 | MscorlibBinder::GetClass(CLASS__IDISPOSABLE); |
2633 | |
2634 | #ifdef _DEBUG |
2635 | WinRTInterfaceRedirector::VerifyRedirectedInterfaceStubs(); |
2636 | #endif // _DEBUG |
2637 | #endif |
2638 | |
2639 | #ifdef FEATURE_ICASTABLE |
2640 | g_pICastableInterface = MscorlibBinder::GetClass(CLASS__ICASTABLE); |
2641 | #endif // FEATURE_ICASTABLE |
2642 | |
2643 | // Load a special marker method used to detect Constrained Execution Regions |
2644 | // at jit time. |
2645 | g_pExecuteBackoutCodeHelperMethod = MscorlibBinder::GetMethod(METHOD__RUNTIME_HELPERS__EXECUTE_BACKOUT_CODE_HELPER); |
2646 | |
2647 | // Make sure that FCall mapping for Monitor.Enter is initialized. We need it in case Monitor.Enter is used only as JIT helper. |
2648 | // For more details, see comment in code:JITutil_MonEnterWorker around "__me = GetEEFuncEntryPointMacro(JIT_MonEnter)". |
2649 | ECall::GetFCallImpl(MscorlibBinder::GetMethod(METHOD__MONITOR__ENTER)); |
2650 | |
2651 | #ifdef PROFILING_SUPPORTED |
2652 | // Note that g_profControlBlock.fBaseSystemClassesLoaded must be set to TRUE only after |
2653 | // all base system classes are loaded. Profilers are not allowed to call any type-loading |
2654 | // APIs until g_profControlBlock.fBaseSystemClassesLoaded is TRUE. It is important that |
2655 | // all base system classes need to be loaded before profilers can trigger the type loading. |
2656 | g_profControlBlock.fBaseSystemClassesLoaded = TRUE; |
2657 | #endif // PROFILING_SUPPORTED |
2658 | |
2659 | #if defined(_DEBUG) && !defined(CROSSGEN_COMPILE) |
2660 | if (!NingenEnabled()) |
2661 | { |
2662 | g_Mscorlib.Check(); |
2663 | } |
2664 | #endif |
2665 | |
2666 | #if defined(HAVE_GCCOVER) && defined(FEATURE_PREJIT) |
2667 | if (GCStress<cfg_instr_ngen>::IsEnabled()) |
2668 | { |
2669 | // Setting up gc coverage requires the base system classes |
2670 | // to be initialized. So we have deferred it until now for mscorlib. |
2671 | Module *pModule = MscorlibBinder::GetModule(); |
2672 | _ASSERTE(pModule->IsSystem()); |
2673 | if(pModule->HasNativeImage()) |
2674 | { |
2675 | SetupGcCoverageForNativeImage(pModule); |
2676 | } |
2677 | } |
2678 | #endif // defined(HAVE_GCCOVER) && !defined(FEATURE_PREJIT) |
2679 | } |
2680 | |
2681 | /*static*/ |
2682 | void SystemDomain::LoadDomain(AppDomain *pDomain) |
2683 | { |
2684 | CONTRACTL |
2685 | { |
2686 | THROWS; |
2687 | GC_TRIGGERS; |
2688 | MODE_ANY; |
2689 | PRECONDITION(CheckPointer(System())); |
2690 | INJECT_FAULT(COMPlusThrowOM();); |
2691 | } |
2692 | CONTRACTL_END; |
2693 | |
2694 | SystemDomain::System()->AddDomain(pDomain); |
2695 | } |
2696 | |
2697 | ADIndex SystemDomain::GetNewAppDomainIndex(AppDomain *pAppDomain) |
2698 | { |
2699 | STANDARD_VM_CONTRACT; |
2700 | |
2701 | DWORD count = m_appDomainIndexList.GetCount(); |
2702 | DWORD i; |
2703 | |
2704 | #ifdef _DEBUG |
2705 | if (count < 2000) |
2706 | { |
2707 | // So that we can keep AD index inside object header. |
2708 | // We do not want to create syncblock unless needed. |
2709 | i = count; |
2710 | } |
2711 | else |
2712 | { |
2713 | #endif // _DEBUG |
2714 | // |
2715 | // Look for an unused index. Note that in a checked build, |
2716 | // we never reuse indexes - this makes it easier to tell |
2717 | // when we are looking at a stale app domain. |
2718 | // |
2719 | |
2720 | i = m_appDomainIndexList.FindElement(m_dwLowestFreeIndex, NULL); |
2721 | if (i == (DWORD) ArrayList::NOT_FOUND) |
2722 | i = count; |
2723 | m_dwLowestFreeIndex = i+1; |
2724 | #ifdef _DEBUG |
2725 | if (m_dwLowestFreeIndex >= 2000) |
2726 | { |
2727 | m_dwLowestFreeIndex = 0; |
2728 | } |
2729 | } |
2730 | #endif // _DEBUG |
2731 | |
2732 | if (i == count) |
2733 | IfFailThrow(m_appDomainIndexList.Append(pAppDomain)); |
2734 | else |
2735 | m_appDomainIndexList.Set(i, pAppDomain); |
2736 | |
2737 | _ASSERTE(i < m_appDomainIndexList.GetCount()); |
2738 | |
2739 | // Note that index 0 means domain agile. |
2740 | return ADIndex(i+1); |
2741 | } |
2742 | |
2743 | void SystemDomain::ReleaseAppDomainIndex(ADIndex index) |
2744 | { |
2745 | WRAPPER_NO_CONTRACT; |
2746 | SystemDomain::LockHolder lh; |
2747 | // Note that index 0 means domain agile. |
2748 | index.m_dwIndex--; |
2749 | |
2750 | _ASSERTE(m_appDomainIndexList.Get(index.m_dwIndex) != NULL); |
2751 | |
2752 | m_appDomainIndexList.Set(index.m_dwIndex, NULL); |
2753 | |
2754 | #ifndef _DEBUG |
2755 | if (index.m_dwIndex < m_dwLowestFreeIndex) |
2756 | m_dwLowestFreeIndex = index.m_dwIndex; |
2757 | #endif // !_DEBUG |
2758 | } |
2759 | |
2760 | #endif // !DACCESS_COMPILE |
2761 | |
2762 | PTR_AppDomain SystemDomain::GetAppDomainAtIndex(ADIndex index) |
2763 | { |
2764 | LIMITED_METHOD_CONTRACT; |
2765 | SUPPORTS_DAC; |
2766 | _ASSERTE(index.m_dwIndex != 0); |
2767 | |
2768 | PTR_AppDomain pAppDomain = TestGetAppDomainAtIndex(index); |
2769 | |
2770 | _ASSERTE(pAppDomain || !"Attempt to access unloaded app domain" ); |
2771 | |
2772 | return pAppDomain; |
2773 | } |
2774 | |
2775 | PTR_AppDomain SystemDomain::TestGetAppDomainAtIndex(ADIndex index) |
2776 | { |
2777 | LIMITED_METHOD_CONTRACT; |
2778 | SUPPORTS_DAC; |
2779 | _ASSERTE(index.m_dwIndex != 0); |
2780 | index.m_dwIndex--; |
2781 | |
2782 | #ifndef DACCESS_COMPILE |
2783 | _ASSERTE(index.m_dwIndex < (DWORD)m_appDomainIndexList.GetCount()); |
2784 | AppDomain *pAppDomain = (AppDomain*) m_appDomainIndexList.Get(index.m_dwIndex); |
2785 | #else // DACCESS_COMPILE |
2786 | PTR_ArrayListStatic pList = &m_appDomainIndexList; |
2787 | AppDomain *pAppDomain = dac_cast<PTR_AppDomain>(pList->Get(index.m_dwIndex)); |
2788 | #endif // DACCESS_COMPILE |
2789 | return PTR_AppDomain(pAppDomain); |
2790 | } |
2791 | |
2792 | #ifndef DACCESS_COMPILE |
2793 | |
2794 | // See also code:SystemDomain::ReleaseAppDomainId |
2795 | ADID SystemDomain::GetNewAppDomainId(AppDomain *pAppDomain) |
2796 | { |
2797 | CONTRACTL |
2798 | { |
2799 | THROWS; |
2800 | GC_TRIGGERS; |
2801 | MODE_ANY; |
2802 | INJECT_FAULT(COMPlusThrowOM();); |
2803 | } |
2804 | CONTRACTL_END; |
2805 | |
2806 | DWORD i = m_appDomainIdList.GetCount(); |
2807 | |
2808 | IfFailThrow(m_appDomainIdList.Append(pAppDomain)); |
2809 | |
2810 | _ASSERTE(i < m_appDomainIdList.GetCount()); |
2811 | |
2812 | return ADID(i+1); |
2813 | } |
2814 | |
2815 | AppDomain *SystemDomain::GetAppDomainAtId(ADID index) |
2816 | { |
2817 | CONTRACTL |
2818 | { |
2819 | #ifdef _DEBUG |
2820 | if (!SystemDomain::IsUnderDomainLock() && !IsGCThread()) { MODE_COOPERATIVE;} else { DISABLED(MODE_ANY);} |
2821 | #endif |
2822 | GC_NOTRIGGER; |
2823 | SO_TOLERANT; |
2824 | NOTHROW; |
2825 | } |
2826 | CONTRACTL_END; |
2827 | |
2828 | if(index.m_dwId == 0) |
2829 | return NULL; |
2830 | DWORD requestedID = index.m_dwId - 1; |
2831 | |
2832 | if(requestedID >= (DWORD)m_appDomainIdList.GetCount()) |
2833 | return NULL; |
2834 | |
2835 | AppDomain * result = (AppDomain *)m_appDomainIdList.Get(requestedID); |
2836 | |
2837 | #ifndef CROSSGEN_COMPILE |
2838 | // If the current thread can't enter the AppDomain, then don't return it. |
2839 | if (!result) |
2840 | return NULL; |
2841 | #endif // CROSSGEN_COMPILE |
2842 | |
2843 | return result; |
2844 | } |
2845 | |
2846 | // Releases an appdomain index. Note that today we have code that depends on these |
2847 | // indexes not being recycled, so we don't actually shrink m_appDomainIdList, but |
2848 | // simply zero out an entry. THus we 'leak' the memory associated the slot in |
2849 | // m_appDomainIdList. |
2850 | // |
2851 | // TODO make this a sparse structure so that we avoid that leak. |
2852 | // |
2853 | void SystemDomain::ReleaseAppDomainId(ADID index) |
2854 | { |
2855 | LIMITED_METHOD_CONTRACT; |
2856 | index.m_dwId--; |
2857 | |
2858 | _ASSERTE(index.m_dwId < (DWORD)m_appDomainIdList.GetCount()); |
2859 | |
2860 | m_appDomainIdList.Set(index.m_dwId, NULL); |
2861 | } |
2862 | |
2863 | #if defined(FEATURE_COMINTEROP_APARTMENT_SUPPORT) && !defined(CROSSGEN_COMPILE) |
2864 | |
2865 | Thread::ApartmentState SystemDomain::GetEntryPointThreadAptState(IMDInternalImport* pScope, mdMethodDef mdMethod) |
2866 | { |
2867 | STANDARD_VM_CONTRACT; |
2868 | |
2869 | HRESULT hr; |
2870 | IfFailThrow(hr = pScope->GetCustomAttributeByName(mdMethod, |
2871 | DEFAULTDOMAIN_MTA_TYPE, |
2872 | NULL, |
2873 | NULL)); |
2874 | BOOL fIsMTA = FALSE; |
2875 | if(hr == S_OK) |
2876 | fIsMTA = TRUE; |
2877 | |
2878 | IfFailThrow(hr = pScope->GetCustomAttributeByName(mdMethod, |
2879 | DEFAULTDOMAIN_STA_TYPE, |
2880 | NULL, |
2881 | NULL)); |
2882 | BOOL fIsSTA = FALSE; |
2883 | if (hr == S_OK) |
2884 | fIsSTA = TRUE; |
2885 | |
2886 | if (fIsSTA && fIsMTA) |
2887 | COMPlusThrowHR(COR_E_CUSTOMATTRIBUTEFORMAT); |
2888 | |
2889 | if (fIsSTA) |
2890 | return Thread::AS_InSTA; |
2891 | else if (fIsMTA) |
2892 | return Thread::AS_InMTA; |
2893 | |
2894 | return Thread::AS_Unknown; |
2895 | } |
2896 | |
2897 | void SystemDomain::SetThreadAptState (Thread::ApartmentState state) |
2898 | { |
2899 | STANDARD_VM_CONTRACT; |
2900 | |
2901 | Thread* pThread = GetThread(); |
2902 | _ASSERTE(pThread); |
2903 | |
2904 | if(state == Thread::AS_InSTA) |
2905 | { |
2906 | Thread::ApartmentState pState = pThread->SetApartment(Thread::AS_InSTA, TRUE); |
2907 | _ASSERTE(pState == Thread::AS_InSTA); |
2908 | } |
2909 | else |
2910 | { |
2911 | // If an apartment state was not explicitly requested, default to MTA |
2912 | Thread::ApartmentState pState = pThread->SetApartment(Thread::AS_InMTA, TRUE); |
2913 | _ASSERTE(pState == Thread::AS_InMTA); |
2914 | } |
2915 | } |
2916 | #endif // defined(FEATURE_COMINTEROP_APARTMENT_SUPPORT) && !defined(CROSSGEN_COMPILE) |
2917 | |
2918 | // Helper function to load an assembly. This is called from LoadCOMClass. |
2919 | /* static */ |
2920 | |
2921 | Assembly *AppDomain::LoadAssemblyHelper(LPCWSTR wszAssembly, |
2922 | LPCWSTR wszCodeBase) |
2923 | { |
2924 | CONTRACT(Assembly *) |
2925 | { |
2926 | THROWS; |
2927 | POSTCONDITION(CheckPointer(RETVAL)); |
2928 | PRECONDITION(wszAssembly || wszCodeBase); |
2929 | INJECT_FAULT(COMPlusThrowOM();); |
2930 | } |
2931 | CONTRACT_END; |
2932 | |
2933 | AssemblySpec spec; |
2934 | if(wszAssembly) { |
2935 | #define MAKE_TRANSLATIONFAILED { ThrowOutOfMemory(); } |
2936 | MAKE_UTF8PTR_FROMWIDE(szAssembly,wszAssembly); |
2937 | #undef MAKE_TRANSLATIONFAILED |
2938 | |
2939 | IfFailThrow(spec.Init(szAssembly)); |
2940 | } |
2941 | |
2942 | if (wszCodeBase) { |
2943 | spec.SetCodeBase(wszCodeBase); |
2944 | } |
2945 | RETURN spec.LoadAssembly(FILE_LOADED); |
2946 | } |
2947 | |
2948 | #if defined(FEATURE_CLASSIC_COMINTEROP) && !defined(CROSSGEN_COMPILE) |
2949 | |
2950 | MethodTable *AppDomain::LoadCOMClass(GUID clsid, |
2951 | BOOL bLoadRecord/*=FALSE*/, |
2952 | BOOL* pfAssemblyInReg/*=NULL*/) |
2953 | { |
2954 | // @CORESYSTODO: what to do here? |
2955 | return NULL; |
2956 | } |
2957 | |
2958 | #endif // FEATURE_CLASSIC_COMINTEROP && !CROSSGEN_COMPILE |
2959 | |
2960 | |
2961 | /*static*/ |
2962 | bool SystemDomain::IsReflectionInvocationMethod(MethodDesc* pMeth) |
2963 | { |
2964 | CONTRACTL |
2965 | { |
2966 | THROWS; |
2967 | GC_TRIGGERS; |
2968 | MODE_ANY; |
2969 | } |
2970 | CONTRACTL_END; |
2971 | |
2972 | MethodTable* pCaller = pMeth->GetMethodTable(); |
2973 | |
2974 | // All Reflection Invocation methods are defined in mscorlib.dll |
2975 | if (!pCaller->GetModule()->IsSystem()) |
2976 | return false; |
2977 | |
2978 | /* List of types that should be skipped to identify true caller */ |
2979 | static const BinderClassID reflectionInvocationTypes[] = { |
2980 | CLASS__METHOD, |
2981 | CLASS__METHOD_BASE, |
2982 | CLASS__METHOD_INFO, |
2983 | CLASS__CONSTRUCTOR, |
2984 | CLASS__CONSTRUCTOR_INFO, |
2985 | CLASS__CLASS, |
2986 | CLASS__TYPE_HANDLE, |
2987 | CLASS__METHOD_HANDLE, |
2988 | CLASS__FIELD_HANDLE, |
2989 | CLASS__TYPE, |
2990 | CLASS__FIELD, |
2991 | CLASS__RT_FIELD_INFO, |
2992 | CLASS__FIELD_INFO, |
2993 | CLASS__EVENT, |
2994 | CLASS__EVENT_INFO, |
2995 | CLASS__PROPERTY, |
2996 | CLASS__PROPERTY_INFO, |
2997 | CLASS__ACTIVATOR, |
2998 | CLASS__ARRAY, |
2999 | CLASS__ASSEMBLYBASE, |
3000 | CLASS__ASSEMBLY, |
3001 | CLASS__TYPE_DELEGATOR, |
3002 | CLASS__RUNTIME_HELPERS, |
3003 | CLASS__LAZY_INITIALIZER, |
3004 | CLASS__DYNAMICMETHOD, |
3005 | CLASS__DELEGATE, |
3006 | CLASS__MULTICAST_DELEGATE |
3007 | }; |
3008 | |
3009 | static const BinderClassID genericReflectionInvocationTypes[] = { |
3010 | CLASS__LAZY |
3011 | }; |
3012 | |
3013 | static mdTypeDef genericReflectionInvocationTypeDefs[NumItems(genericReflectionInvocationTypes)]; |
3014 | |
3015 | static bool fInited = false; |
3016 | |
3017 | if (!VolatileLoad(&fInited)) |
3018 | { |
3019 | // Make sure all types are loaded so that we can use faster GetExistingClass() |
3020 | for (unsigned i = 0; i < NumItems(reflectionInvocationTypes); i++) |
3021 | { |
3022 | MscorlibBinder::GetClass(reflectionInvocationTypes[i]); |
3023 | } |
3024 | |
3025 | // Make sure all types are loaded so that we can use faster GetExistingClass() |
3026 | for (unsigned i = 0; i < NumItems(genericReflectionInvocationTypes); i++) |
3027 | { |
3028 | genericReflectionInvocationTypeDefs[i] = MscorlibBinder::GetClass(genericReflectionInvocationTypes[i])->GetCl(); |
3029 | } |
3030 | |
3031 | VolatileStore(&fInited, true); |
3032 | } |
3033 | |
3034 | if (pCaller->HasInstantiation()) |
3035 | { |
3036 | // For generic types, pCaller will be an instantiated type and never equal to the type definition. |
3037 | // So we compare their TypeDef tokens instead. |
3038 | for (unsigned i = 0; i < NumItems(genericReflectionInvocationTypeDefs); i++) |
3039 | { |
3040 | if (pCaller->GetCl() == genericReflectionInvocationTypeDefs[i]) |
3041 | return true; |
3042 | } |
3043 | } |
3044 | else |
3045 | { |
3046 | for (unsigned i = 0; i < NumItems(reflectionInvocationTypes); i++) |
3047 | { |
3048 | if (MscorlibBinder::GetExistingClass(reflectionInvocationTypes[i]) == pCaller) |
3049 | return true; |
3050 | } |
3051 | } |
3052 | |
3053 | return false; |
3054 | } |
3055 | |
3056 | #ifndef CROSSGEN_COMPILE |
3057 | struct CallersDataWithStackMark |
3058 | { |
3059 | StackCrawlMark* stackMark; |
3060 | BOOL foundMe; |
3061 | MethodDesc* pFoundMethod; |
3062 | MethodDesc* pPrevMethod; |
3063 | AppDomain* pAppDomain; |
3064 | }; |
3065 | |
3066 | /*static*/ |
3067 | MethodDesc* SystemDomain::GetCallersMethod(StackCrawlMark* stackMark, |
3068 | AppDomain **ppAppDomain/*=NULL*/) |
3069 | |
3070 | { |
3071 | CONTRACTL |
3072 | { |
3073 | THROWS; |
3074 | GC_TRIGGERS; |
3075 | MODE_ANY; |
3076 | INJECT_FAULT(COMPlusThrowOM();); |
3077 | } |
3078 | CONTRACTL_END; |
3079 | |
3080 | GCX_COOP(); |
3081 | |
3082 | CallersDataWithStackMark cdata; |
3083 | ZeroMemory(&cdata, sizeof(CallersDataWithStackMark)); |
3084 | cdata.stackMark = stackMark; |
3085 | |
3086 | GetThread()->StackWalkFrames(CallersMethodCallbackWithStackMark, &cdata, FUNCTIONSONLY | LIGHTUNWIND); |
3087 | |
3088 | if(cdata.pFoundMethod) { |
3089 | if (ppAppDomain) |
3090 | *ppAppDomain = cdata.pAppDomain; |
3091 | return cdata.pFoundMethod; |
3092 | } else |
3093 | return NULL; |
3094 | } |
3095 | |
3096 | /*static*/ |
3097 | MethodTable* SystemDomain::GetCallersType(StackCrawlMark* stackMark, |
3098 | AppDomain **ppAppDomain/*=NULL*/) |
3099 | |
3100 | { |
3101 | CONTRACTL |
3102 | { |
3103 | THROWS; |
3104 | GC_TRIGGERS; |
3105 | MODE_COOPERATIVE; |
3106 | INJECT_FAULT(COMPlusThrowOM();); |
3107 | } |
3108 | CONTRACTL_END; |
3109 | |
3110 | CallersDataWithStackMark cdata; |
3111 | ZeroMemory(&cdata, sizeof(CallersDataWithStackMark)); |
3112 | cdata.stackMark = stackMark; |
3113 | |
3114 | GetThread()->StackWalkFrames(CallersMethodCallbackWithStackMark, &cdata, FUNCTIONSONLY | LIGHTUNWIND); |
3115 | |
3116 | if(cdata.pFoundMethod) { |
3117 | if (ppAppDomain) |
3118 | *ppAppDomain = cdata.pAppDomain; |
3119 | return cdata.pFoundMethod->GetMethodTable(); |
3120 | } else |
3121 | return NULL; |
3122 | } |
3123 | |
3124 | /*static*/ |
3125 | Module* SystemDomain::GetCallersModule(StackCrawlMark* stackMark, |
3126 | AppDomain **ppAppDomain/*=NULL*/) |
3127 | |
3128 | { |
3129 | CONTRACTL |
3130 | { |
3131 | THROWS; |
3132 | GC_TRIGGERS; |
3133 | MODE_ANY; |
3134 | INJECT_FAULT(COMPlusThrowOM();); |
3135 | } |
3136 | CONTRACTL_END; |
3137 | |
3138 | GCX_COOP(); |
3139 | |
3140 | CallersDataWithStackMark cdata; |
3141 | ZeroMemory(&cdata, sizeof(CallersDataWithStackMark)); |
3142 | cdata.stackMark = stackMark; |
3143 | |
3144 | GetThread()->StackWalkFrames(CallersMethodCallbackWithStackMark, &cdata, FUNCTIONSONLY | LIGHTUNWIND); |
3145 | |
3146 | if(cdata.pFoundMethod) { |
3147 | if (ppAppDomain) |
3148 | *ppAppDomain = cdata.pAppDomain; |
3149 | return cdata.pFoundMethod->GetModule(); |
3150 | } else |
3151 | return NULL; |
3152 | } |
3153 | |
3154 | struct CallersData |
3155 | { |
3156 | int skip; |
3157 | MethodDesc* pMethod; |
3158 | }; |
3159 | |
3160 | /*static*/ |
3161 | Assembly* SystemDomain::GetCallersAssembly(StackCrawlMark *stackMark, |
3162 | AppDomain **ppAppDomain/*=NULL*/) |
3163 | { |
3164 | WRAPPER_NO_CONTRACT; |
3165 | Module* mod = GetCallersModule(stackMark, ppAppDomain); |
3166 | if (mod) |
3167 | return mod->GetAssembly(); |
3168 | return NULL; |
3169 | } |
3170 | |
3171 | /*static*/ |
3172 | Module* SystemDomain::GetCallersModule(int skip) |
3173 | { |
3174 | CONTRACTL |
3175 | { |
3176 | THROWS; |
3177 | GC_TRIGGERS; |
3178 | MODE_ANY; |
3179 | INJECT_FAULT(COMPlusThrowOM();); |
3180 | } |
3181 | CONTRACTL_END; |
3182 | |
3183 | GCX_COOP(); |
3184 | |
3185 | CallersData cdata; |
3186 | ZeroMemory(&cdata, sizeof(CallersData)); |
3187 | cdata.skip = skip; |
3188 | |
3189 | StackWalkFunctions(GetThread(), CallersMethodCallback, &cdata); |
3190 | |
3191 | if(cdata.pMethod) |
3192 | return cdata.pMethod->GetModule(); |
3193 | else |
3194 | return NULL; |
3195 | } |
3196 | |
3197 | /*private static*/ |
3198 | StackWalkAction SystemDomain::CallersMethodCallbackWithStackMark(CrawlFrame* pCf, VOID* data) |
3199 | { |
3200 | CONTRACTL |
3201 | { |
3202 | THROWS; |
3203 | GC_TRIGGERS; |
3204 | MODE_COOPERATIVE; |
3205 | SO_INTOLERANT; |
3206 | INJECT_FAULT(COMPlusThrowOM();); |
3207 | } |
3208 | CONTRACTL_END; |
3209 | |
3210 | |
3211 | MethodDesc *pFunc = pCf->GetFunction(); |
3212 | |
3213 | /* We asked to be called back only for functions */ |
3214 | _ASSERTE(pFunc); |
3215 | |
3216 | CallersDataWithStackMark* pCaller = (CallersDataWithStackMark*) data; |
3217 | if (pCaller->stackMark) |
3218 | { |
3219 | if (!pCf->IsInCalleesFrames(pCaller->stackMark)) |
3220 | { |
3221 | // save the current in case it is the one we want |
3222 | pCaller->pPrevMethod = pFunc; |
3223 | pCaller->pAppDomain = pCf->GetAppDomain(); |
3224 | return SWA_CONTINUE; |
3225 | } |
3226 | |
3227 | // LookForMe stack crawl marks needn't worry about reflection or |
3228 | // remoting frames on the stack. Each frame above (newer than) the |
3229 | // target will be captured by the logic above. Once we transition to |
3230 | // finding the stack mark below the AofRA, we know that we hit the |
3231 | // target last time round and immediately exit with the cached result. |
3232 | |
3233 | if (*(pCaller->stackMark) == LookForMe) |
3234 | { |
3235 | pCaller->pFoundMethod = pCaller->pPrevMethod; |
3236 | return SWA_ABORT; |
3237 | } |
3238 | } |
3239 | |
3240 | // Skip reflection and remoting frames that could lie between a stack marked |
3241 | // method and its true caller (or that caller and its own caller). These |
3242 | // frames are infrastructure and logically transparent to the stack crawling |
3243 | // algorithm. |
3244 | |
3245 | // Skipping remoting frames. We always skip entire client to server spans |
3246 | // (though we see them in the order server then client during a stack crawl |
3247 | // obviously). |
3248 | |
3249 | // We spot the server dispatcher end because all calls are dispatched |
3250 | // through a single method: StackBuilderSink._PrivateProcessMessage. |
3251 | |
3252 | Frame* frame = pCf->GetFrame(); |
3253 | _ASSERTE(pCf->IsFrameless() || frame); |
3254 | |
3255 | |
3256 | |
3257 | // Skipping reflection frames. We don't need to be quite as exhaustive here |
3258 | // as the security or reflection stack walking code since we know this logic |
3259 | // is only invoked for selected methods in mscorlib itself. So we're |
3260 | // reasonably sure we won't have any sensitive methods late bound invoked on |
3261 | // constructors, properties or events. This leaves being invoked via |
3262 | // MethodInfo, Type or Delegate (and depending on which invoke overload is |
3263 | // being used, several different reflection classes may be involved). |
3264 | |
3265 | g_IBCLogger.LogMethodDescAccess(pFunc); |
3266 | |
3267 | if (SystemDomain::IsReflectionInvocationMethod(pFunc)) |
3268 | return SWA_CONTINUE; |
3269 | |
3270 | if (frame && frame->GetFrameType() == Frame::TYPE_MULTICAST) |
3271 | { |
3272 | // This must be either a secure delegate frame or a true multicast delegate invocation. |
3273 | |
3274 | _ASSERTE(pFunc->GetMethodTable()->IsDelegate()); |
3275 | |
3276 | DELEGATEREF del = (DELEGATEREF)((SecureDelegateFrame*)frame)->GetThis(); // This can throw. |
3277 | |
3278 | if (COMDelegate::IsSecureDelegate(del)) |
3279 | { |
3280 | if (del->IsWrapperDelegate()) |
3281 | { |
3282 | // On ARM, we use secure delegate infrastructure to preserve R4 register. |
3283 | return SWA_CONTINUE; |
3284 | } |
3285 | // For a secure delegate frame, we should return the delegate creator instead |
3286 | // of the delegate method itself. |
3287 | pFunc = (MethodDesc*) del->GetMethodPtrAux(); |
3288 | } |
3289 | else |
3290 | { |
3291 | _ASSERTE(COMDelegate::IsTrueMulticastDelegate(del)); |
3292 | return SWA_CONTINUE; |
3293 | } |
3294 | } |
3295 | |
3296 | // Return the first non-reflection/remoting frame if no stack mark was |
3297 | // supplied. |
3298 | if (!pCaller->stackMark) |
3299 | { |
3300 | pCaller->pFoundMethod = pFunc; |
3301 | pCaller->pAppDomain = pCf->GetAppDomain(); |
3302 | return SWA_ABORT; |
3303 | } |
3304 | |
3305 | // If we got here, we must already be in the frame containing the stack mark and we are not looking for "me". |
3306 | _ASSERTE(pCaller->stackMark && |
3307 | pCf->IsInCalleesFrames(pCaller->stackMark) && |
3308 | *(pCaller->stackMark) != LookForMe); |
3309 | |
3310 | // When looking for caller's caller, we delay returning results for another |
3311 | // round (the way this is structured, we will still be able to skip |
3312 | // reflection and remoting frames between the caller and the caller's |
3313 | // caller). |
3314 | |
3315 | if ((*(pCaller->stackMark) == LookForMyCallersCaller) && |
3316 | (pCaller->pFoundMethod == NULL)) |
3317 | { |
3318 | pCaller->pFoundMethod = pFunc; |
3319 | return SWA_CONTINUE; |
3320 | } |
3321 | |
3322 | // If remoting is not available, we only set the caller if the crawlframe is from the same domain. |
3323 | // Why? Because if the callerdomain is different from current domain, |
3324 | // there have to be interop/native frames in between. |
3325 | // For example, in the CORECLR, if we find the caller to be in a different domain, then the |
3326 | // call into reflection is due to an unmanaged call into mscorlib. For that |
3327 | // case, the caller really is an INTEROP method. |
3328 | // In general, if the caller is INTEROP, we set the caller/callerdomain to be NULL |
3329 | // (To be precise: they are already NULL and we don't change them). |
3330 | if (pCf->GetAppDomain() == GetAppDomain()) |
3331 | // We must either be looking for the caller, or the caller's caller when |
3332 | // we've already found the caller (we used a non-null value in pFoundMethod |
3333 | // simply as a flag, the correct method to return in both case is the |
3334 | // current method). |
3335 | { |
3336 | pCaller->pFoundMethod = pFunc; |
3337 | pCaller->pAppDomain = pCf->GetAppDomain(); |
3338 | } |
3339 | |
3340 | return SWA_ABORT; |
3341 | } |
3342 | |
3343 | /*private static*/ |
3344 | StackWalkAction SystemDomain::CallersMethodCallback(CrawlFrame* pCf, VOID* data) |
3345 | { |
3346 | LIMITED_METHOD_CONTRACT; |
3347 | STATIC_CONTRACT_SO_TOLERANT; |
3348 | MethodDesc *pFunc = pCf->GetFunction(); |
3349 | |
3350 | /* We asked to be called back only for functions */ |
3351 | _ASSERTE(pFunc); |
3352 | |
3353 | CallersData* pCaller = (CallersData*) data; |
3354 | if(pCaller->skip == 0) { |
3355 | pCaller->pMethod = pFunc; |
3356 | return SWA_ABORT; |
3357 | } |
3358 | else { |
3359 | pCaller->skip--; |
3360 | return SWA_CONTINUE; |
3361 | } |
3362 | |
3363 | } |
3364 | #endif // CROSSGEN_COMPILE |
3365 | |
3366 | #ifdef CROSSGEN_COMPILE |
3367 | // defined in compile.cpp |
3368 | extern CompilationDomain * theDomain; |
3369 | #endif |
3370 | |
3371 | void AppDomain::Create() |
3372 | { |
3373 | STANDARD_VM_CONTRACT; |
3374 | |
3375 | #ifdef CROSSGEN_COMPILE |
3376 | AppDomainRefHolder pDomain(theDomain); |
3377 | #else |
3378 | AppDomainRefHolder pDomain(new AppDomain()); |
3379 | #endif |
3380 | |
3381 | pDomain->Init(); |
3382 | |
3383 | // need to make this assignment here since we'll be releasing |
3384 | // the lock before calling AddDomain. So any other thread |
3385 | // grabbing this lock after we release it will find that |
3386 | // the COM Domain has already been created |
3387 | _ASSERTE (pDomain->GetId().m_dwId == DefaultADID); |
3388 | |
3389 | // allocate a Virtual Call Stub Manager for the default domain |
3390 | pDomain->InitVSD(); |
3391 | |
3392 | pDomain->SetStage(AppDomain::STAGE_OPEN); |
3393 | pDomain.SuppressRelease(); |
3394 | |
3395 | m_pTheAppDomain = pDomain; |
3396 | |
3397 | LOG((LF_CLASSLOADER | LF_CORDB, |
3398 | LL_INFO10, |
3399 | "Created the app domain at %p\n" , m_pTheAppDomain)); |
3400 | } |
3401 | |
3402 | #ifdef DEBUGGING_SUPPORTED |
3403 | |
3404 | void SystemDomain::PublishAppDomainAndInformDebugger (AppDomain *pDomain) |
3405 | { |
3406 | CONTRACTL |
3407 | { |
3408 | if(!g_fEEInit) {THROWS;} else {DISABLED(NOTHROW);}; |
3409 | if(!g_fEEInit) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);}; |
3410 | MODE_ANY; |
3411 | } |
3412 | CONTRACTL_END; |
3413 | |
3414 | LOG((LF_CORDB, LL_INFO100, "SD::PADAID: Adding 0x%x\n" , pDomain)); |
3415 | |
3416 | // Call the publisher API to add this appdomain entry to the list |
3417 | // The publisher will handle failures, so we don't care if this succeeds or fails. |
3418 | if (g_pDebugInterface != NULL) |
3419 | { |
3420 | g_pDebugInterface->AddAppDomainToIPC(pDomain); |
3421 | } |
3422 | } |
3423 | |
3424 | #endif // DEBUGGING_SUPPORTED |
3425 | |
3426 | void SystemDomain::AddDomain(AppDomain* pDomain) |
3427 | { |
3428 | CONTRACTL |
3429 | { |
3430 | NOTHROW; |
3431 | MODE_ANY; |
3432 | GC_TRIGGERS; |
3433 | PRECONDITION(CheckPointer((pDomain))); |
3434 | } |
3435 | CONTRACTL_END; |
3436 | |
3437 | { |
3438 | LockHolder lh; |
3439 | |
3440 | _ASSERTE (pDomain->m_Stage != AppDomain::STAGE_CREATING); |
3441 | if (pDomain->m_Stage == AppDomain::STAGE_READYFORMANAGEDCODE || |
3442 | pDomain->m_Stage == AppDomain::STAGE_ACTIVE) |
3443 | { |
3444 | pDomain->SetStage(AppDomain::STAGE_OPEN); |
3445 | IncrementNumAppDomains(); // Maintain a count of app domains added to the list. |
3446 | } |
3447 | } |
3448 | |
3449 | // Note that if you add another path that can reach here without calling |
3450 | // PublishAppDomainAndInformDebugger, then you should go back & make sure |
3451 | // that PADAID gets called. Right after this call, if not sooner. |
3452 | LOG((LF_CORDB, LL_INFO1000, "SD::AD:Would have added domain here! 0x%x\n" , |
3453 | pDomain)); |
3454 | } |
3455 | |
3456 | BOOL SystemDomain::RemoveDomain(AppDomain* pDomain) |
3457 | { |
3458 | CONTRACTL |
3459 | { |
3460 | NOTHROW; |
3461 | GC_TRIGGERS; |
3462 | MODE_ANY; |
3463 | PRECONDITION(CheckPointer(pDomain)); |
3464 | PRECONDITION(!pDomain->IsDefaultDomain()); |
3465 | } |
3466 | CONTRACTL_END; |
3467 | |
3468 | // You can not remove the default domain. |
3469 | |
3470 | |
3471 | if (!pDomain->IsActive()) |
3472 | return FALSE; |
3473 | |
3474 | pDomain->Release(); |
3475 | |
3476 | return TRUE; |
3477 | } |
3478 | |
3479 | |
3480 | #ifdef PROFILING_SUPPORTED |
3481 | void SystemDomain::NotifyProfilerStartup() |
3482 | { |
3483 | CONTRACTL |
3484 | { |
3485 | NOTHROW; |
3486 | GC_TRIGGERS; |
3487 | MODE_PREEMPTIVE; |
3488 | } |
3489 | CONTRACTL_END; |
3490 | |
3491 | { |
3492 | BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads()); |
3493 | _ASSERTE(System()); |
3494 | g_profControlBlock.pProfInterface->AppDomainCreationStarted((AppDomainID) System()); |
3495 | END_PIN_PROFILER(); |
3496 | } |
3497 | |
3498 | { |
3499 | BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads()); |
3500 | _ASSERTE(System()); |
3501 | g_profControlBlock.pProfInterface->AppDomainCreationFinished((AppDomainID) System(), S_OK); |
3502 | END_PIN_PROFILER(); |
3503 | } |
3504 | |
3505 | { |
3506 | BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads()); |
3507 | _ASSERTE(System()->DefaultDomain()); |
3508 | g_profControlBlock.pProfInterface->AppDomainCreationStarted((AppDomainID) System()->DefaultDomain()); |
3509 | END_PIN_PROFILER(); |
3510 | } |
3511 | |
3512 | { |
3513 | BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads()); |
3514 | _ASSERTE(System()->DefaultDomain()); |
3515 | g_profControlBlock.pProfInterface->AppDomainCreationFinished((AppDomainID) System()->DefaultDomain(), S_OK); |
3516 | END_PIN_PROFILER(); |
3517 | } |
3518 | } |
3519 | |
3520 | HRESULT SystemDomain::NotifyProfilerShutdown() |
3521 | { |
3522 | CONTRACTL |
3523 | { |
3524 | NOTHROW; |
3525 | GC_TRIGGERS; |
3526 | MODE_PREEMPTIVE; |
3527 | } |
3528 | CONTRACTL_END; |
3529 | |
3530 | { |
3531 | BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads()); |
3532 | _ASSERTE(System()); |
3533 | g_profControlBlock.pProfInterface->AppDomainShutdownStarted((AppDomainID) System()); |
3534 | END_PIN_PROFILER(); |
3535 | } |
3536 | |
3537 | { |
3538 | BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads()); |
3539 | _ASSERTE(System()); |
3540 | g_profControlBlock.pProfInterface->AppDomainShutdownFinished((AppDomainID) System(), S_OK); |
3541 | END_PIN_PROFILER(); |
3542 | } |
3543 | |
3544 | { |
3545 | BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads()); |
3546 | _ASSERTE(System()->DefaultDomain()); |
3547 | g_profControlBlock.pProfInterface->AppDomainShutdownStarted((AppDomainID) System()->DefaultDomain()); |
3548 | END_PIN_PROFILER(); |
3549 | } |
3550 | |
3551 | { |
3552 | BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads()); |
3553 | _ASSERTE(System()->DefaultDomain()); |
3554 | g_profControlBlock.pProfInterface->AppDomainShutdownFinished((AppDomainID) System()->DefaultDomain(), S_OK); |
3555 | END_PIN_PROFILER(); |
3556 | } |
3557 | return (S_OK); |
3558 | } |
3559 | #endif // PROFILING_SUPPORTED |
3560 | |
3561 | |
3562 | #ifdef _DEBUG |
3563 | struct AppDomain::ThreadTrackInfo { |
3564 | Thread *pThread; |
3565 | CDynArray<Frame *> frameStack; |
3566 | }; |
3567 | #endif // _DEBUG |
3568 | |
3569 | AppDomain::AppDomain() |
3570 | { |
3571 | // initialize fields so the appdomain can be safely destructed |
3572 | // shouldn't call anything that can fail here - use ::Init instead |
3573 | CONTRACTL |
3574 | { |
3575 | THROWS; |
3576 | GC_TRIGGERS; |
3577 | MODE_ANY; |
3578 | FORBID_FAULT; |
3579 | } |
3580 | CONTRACTL_END; |
3581 | |
3582 | m_cRef=1; |
3583 | |
3584 | m_pRootAssembly = NULL; |
3585 | |
3586 | m_dwFlags = 0; |
3587 | #ifdef FEATURE_COMINTEROP |
3588 | m_pRCWCache = NULL; |
3589 | m_pRCWRefCache = NULL; |
3590 | m_pLicenseInteropHelperMT = NULL; |
3591 | memset(m_rpCLRTypes, 0, sizeof(m_rpCLRTypes)); |
3592 | #endif // FEATURE_COMINTEROP |
3593 | |
3594 | m_handleStore = NULL; |
3595 | |
3596 | #ifdef _DEBUG |
3597 | m_pThreadTrackInfoList = NULL; |
3598 | m_TrackSpinLock = 0; |
3599 | m_Assemblies.Debug_SetAppDomain(this); |
3600 | #endif // _DEBUG |
3601 | |
3602 | m_dwThreadEnterCount = 0; |
3603 | m_dwThreadsStillInAppDomain = (ULONG)-1; |
3604 | |
3605 | #ifdef FEATURE_COMINTEROP |
3606 | m_pRefDispIDCache = NULL; |
3607 | m_hndMissing = NULL; |
3608 | #endif |
3609 | |
3610 | m_pRefClassFactHash = NULL; |
3611 | |
3612 | m_ReversePInvokeCanEnter=TRUE; |
3613 | m_ForceTrivialWaitOperations = false; |
3614 | m_Stage=STAGE_CREATING; |
3615 | |
3616 | #ifdef _DEBUG |
3617 | m_dwIterHolders=0; |
3618 | m_dwRefTakers=0; |
3619 | m_dwCreationHolders=0; |
3620 | #endif |
3621 | |
3622 | #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING |
3623 | m_ullTotalProcessorUsage = 0; |
3624 | m_pullAllocBytes = NULL; |
3625 | m_pullSurvivedBytes = NULL; |
3626 | #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING |
3627 | |
3628 | #ifdef FEATURE_TYPEEQUIVALENCE |
3629 | m_pTypeEquivalenceTable = NULL; |
3630 | #endif // FEATURE_TYPEEQUIVALENCE |
3631 | |
3632 | #ifdef FEATURE_COMINTEROP |
3633 | m_pNameToTypeMap = NULL; |
3634 | m_vNameToTypeMapVersion = 0; |
3635 | m_nEpoch = 0; |
3636 | m_pWinRTFactoryCache = NULL; |
3637 | #endif // FEATURE_COMINTEROP |
3638 | |
3639 | #ifdef FEATURE_PREJIT |
3640 | m_pDomainFileWithNativeImageList = NULL; |
3641 | #endif |
3642 | |
3643 | } // AppDomain::AppDomain |
3644 | |
3645 | AppDomain::~AppDomain() |
3646 | { |
3647 | CONTRACTL |
3648 | { |
3649 | NOTHROW; |
3650 | GC_TRIGGERS; |
3651 | MODE_ANY; |
3652 | } |
3653 | CONTRACTL_END; |
3654 | |
3655 | #ifndef CROSSGEN_COMPILE |
3656 | |
3657 | _ASSERTE(m_dwCreationHolders == 0); |
3658 | |
3659 | // release the TPIndex. note that since TPIndex values are recycled the TPIndex |
3660 | // can only be released once all threads in the AppDomain have exited. |
3661 | if (GetTPIndex().m_dwIndex != 0) |
3662 | PerAppDomainTPCountList::ResetAppDomainIndex(GetTPIndex()); |
3663 | |
3664 | if (m_dwId.m_dwId!=0) |
3665 | SystemDomain::ReleaseAppDomainId(m_dwId); |
3666 | |
3667 | m_AssemblyCache.Clear(); |
3668 | |
3669 | if(!g_fEEInit) |
3670 | Terminate(); |
3671 | |
3672 | |
3673 | |
3674 | |
3675 | #ifdef FEATURE_COMINTEROP |
3676 | if (m_pNameToTypeMap != nullptr) |
3677 | { |
3678 | delete m_pNameToTypeMap; |
3679 | m_pNameToTypeMap = nullptr; |
3680 | } |
3681 | if (m_pWinRTFactoryCache != nullptr) |
3682 | { |
3683 | delete m_pWinRTFactoryCache; |
3684 | m_pWinRTFactoryCache = nullptr; |
3685 | } |
3686 | #endif //FEATURE_COMINTEROP |
3687 | |
3688 | #ifdef _DEBUG |
3689 | // If we were tracking thread AD transitions, cleanup the list on shutdown |
3690 | if (m_pThreadTrackInfoList) |
3691 | { |
3692 | while (m_pThreadTrackInfoList->Count() > 0) |
3693 | { |
3694 | // Get the very last element |
3695 | ThreadTrackInfo *pElem = *(m_pThreadTrackInfoList->Get(m_pThreadTrackInfoList->Count() - 1)); |
3696 | _ASSERTE(pElem); |
3697 | |
3698 | // Free the memory |
3699 | delete pElem; |
3700 | |
3701 | // Remove pointer entry from the list |
3702 | m_pThreadTrackInfoList->Delete(m_pThreadTrackInfoList->Count() - 1); |
3703 | } |
3704 | |
3705 | // Now delete the list itself |
3706 | delete m_pThreadTrackInfoList; |
3707 | m_pThreadTrackInfoList = NULL; |
3708 | } |
3709 | #endif // _DEBUG |
3710 | |
3711 | #endif // CROSSGEN_COMPILE |
3712 | } |
3713 | |
3714 | //***************************************************************************** |
3715 | //***************************************************************************** |
3716 | //***************************************************************************** |
3717 | void AppDomain::Init() |
3718 | { |
3719 | CONTRACTL |
3720 | { |
3721 | STANDARD_VM_CHECK; |
3722 | } |
3723 | CONTRACTL_END; |
3724 | |
3725 | m_pDelayedLoaderAllocatorUnloadList = NULL; |
3726 | |
3727 | SetStage( STAGE_CREATING); |
3728 | |
3729 | |
3730 | // The lock is taken also during stack walking (GC or profiler) |
3731 | // - To prevent deadlock with GC thread, we cannot trigger GC while holding the lock |
3732 | // - To prevent deadlock with profiler thread, we cannot allow thread suspension |
3733 | m_crstHostAssemblyMap.Init( |
3734 | CrstHostAssemblyMap, |
3735 | (CrstFlags)(CRST_GC_NOTRIGGER_WHEN_TAKEN |
3736 | | CRST_DEBUGGER_THREAD |
3737 | INDEBUG(| CRST_DEBUG_ONLY_CHECK_FORBID_SUSPEND_THREAD))); |
3738 | m_crstHostAssemblyMapAdd.Init(CrstHostAssemblyMapAdd); |
3739 | |
3740 | m_dwId = SystemDomain::GetNewAppDomainId(this); |
3741 | |
3742 | #ifndef CROSSGEN_COMPILE |
3743 | //Allocate the threadpool entry before the appdomain id list. Otherwise, |
3744 | //the thread pool list will be out of sync if insertion of id in |
3745 | //the appdomain fails. |
3746 | m_tpIndex = PerAppDomainTPCountList::AddNewTPIndex(); |
3747 | #endif // CROSSGEN_COMPILE |
3748 | |
3749 | m_dwIndex = SystemDomain::GetNewAppDomainIndex(this); |
3750 | |
3751 | #ifndef CROSSGEN_COMPILE |
3752 | PerAppDomainTPCountList::SetAppDomainId(m_tpIndex, m_dwId); |
3753 | #endif |
3754 | |
3755 | BaseDomain::Init(); |
3756 | |
3757 | // Set up the binding caches |
3758 | m_AssemblyCache.Init(&m_DomainCacheCrst, GetHighFrequencyHeap()); |
3759 | m_UnmanagedCache.InitializeTable(this, &m_DomainCacheCrst); |
3760 | |
3761 | m_MemoryPressure = 0; |
3762 | |
3763 | m_sDomainLocalBlock.Init(this); |
3764 | |
3765 | #ifndef CROSSGEN_COMPILE |
3766 | |
3767 | #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING |
3768 | // NOTE: it's important that we initialize ARM data structures before calling |
3769 | // IGCHandleManager::CreateHandleStore, this is because AD::Init() can race with GC |
3770 | // and once we add ourselves to the handle table map the GC can start walking |
3771 | // our handles and calling AD::RecordSurvivedBytes() which touches ARM data. |
3772 | if (GCHeapUtilities::IsServerHeap()) |
3773 | m_dwNumHeaps = CPUGroupInfo::CanEnableGCCPUGroups() ? |
3774 | CPUGroupInfo::GetNumActiveProcessors() : |
3775 | GetCurrentProcessCpuCount(); |
3776 | else |
3777 | m_dwNumHeaps = 1; |
3778 | m_pullAllocBytes = new ULONGLONG [m_dwNumHeaps * ARM_CACHE_LINE_SIZE_ULL]; |
3779 | m_pullSurvivedBytes = new ULONGLONG [m_dwNumHeaps * ARM_CACHE_LINE_SIZE_ULL]; |
3780 | for (DWORD i = 0; i < m_dwNumHeaps; i++) |
3781 | { |
3782 | m_pullAllocBytes[i * ARM_CACHE_LINE_SIZE_ULL] = 0; |
3783 | m_pullSurvivedBytes[i * ARM_CACHE_LINE_SIZE_ULL] = 0; |
3784 | } |
3785 | m_ullLastEtwAllocBytes = 0; |
3786 | #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING |
3787 | |
3788 | // Default domain reuses the handletablemap that was created during EEStartup since |
3789 | // default domain cannot be unloaded. |
3790 | if (GetId().m_dwId == DefaultADID) |
3791 | { |
3792 | m_handleStore = GCHandleUtilities::GetGCHandleManager()->GetGlobalHandleStore(); |
3793 | } |
3794 | else |
3795 | { |
3796 | m_handleStore = GCHandleUtilities::GetGCHandleManager()->CreateHandleStore((void*)(uintptr_t)m_dwIndex.m_dwIndex); |
3797 | } |
3798 | |
3799 | if (!m_handleStore) |
3800 | { |
3801 | COMPlusThrowOM(); |
3802 | } |
3803 | |
3804 | #endif // CROSSGEN_COMPILE |
3805 | |
3806 | #ifdef FEATURE_TYPEEQUIVALENCE |
3807 | m_TypeEquivalenceCrst.Init(CrstTypeEquivalenceMap); |
3808 | #endif |
3809 | |
3810 | m_ReflectionCrst.Init(CrstReflection, CRST_UNSAFE_ANYMODE); |
3811 | m_RefClassFactCrst.Init(CrstClassFactInfoHash); |
3812 | |
3813 | { |
3814 | LockOwner lock = {&m_DomainCrst, IsOwnerOfCrst}; |
3815 | m_clsidHash.Init(0,&CompareCLSID,true, &lock); // init hash table |
3816 | } |
3817 | |
3818 | SetStage(STAGE_READYFORMANAGEDCODE); |
3819 | |
3820 | #ifndef CROSSGEN_COMPILE |
3821 | COUNTER_ONLY(GetPerfCounters().m_Loading.cAppDomains++); |
3822 | |
3823 | #ifdef FEATURE_TIERED_COMPILATION |
3824 | m_tieredCompilationManager.Init(GetId()); |
3825 | #endif |
3826 | #endif // CROSSGEN_COMPILE |
3827 | } // AppDomain::Init |
3828 | |
3829 | |
3830 | /*********************************************************************/ |
3831 | |
3832 | BOOL AppDomain::IsCompilationDomain() |
3833 | { |
3834 | LIMITED_METHOD_CONTRACT; |
3835 | |
3836 | BOOL isCompilationDomain = (m_dwFlags & COMPILATION_DOMAIN) != 0; |
3837 | #ifdef FEATURE_PREJIT |
3838 | _ASSERTE(!isCompilationDomain || IsCompilationProcess()); |
3839 | #endif // FEATURE_PREJIT |
3840 | return isCompilationDomain; |
3841 | } |
3842 | |
3843 | #ifndef CROSSGEN_COMPILE |
3844 | |
3845 | void AppDomain::Stop() |
3846 | { |
3847 | CONTRACTL |
3848 | { |
3849 | NOTHROW; |
3850 | MODE_ANY; |
3851 | GC_TRIGGERS; |
3852 | } |
3853 | CONTRACTL_END; |
3854 | |
3855 | #ifdef FEATURE_MULTICOREJIT |
3856 | GetMulticoreJitManager().StopProfile(true); |
3857 | #endif |
3858 | |
3859 | // Set the unloaded flag before notifying the debugger |
3860 | GetLoaderAllocator()->SetIsUnloaded(); |
3861 | |
3862 | #ifdef DEBUGGING_SUPPORTED |
3863 | if (IsDebuggerAttached()) |
3864 | NotifyDebuggerUnload(); |
3865 | #endif // DEBUGGING_SUPPORTED |
3866 | |
3867 | m_pRootAssembly = NULL; // This assembly is in the assembly list; |
3868 | |
3869 | #ifdef DEBUGGING_SUPPORTED |
3870 | if (NULL != g_pDebugInterface) |
3871 | { |
3872 | // Call the publisher API to delete this appdomain entry from the list |
3873 | CONTRACT_VIOLATION(ThrowsViolation); |
3874 | g_pDebugInterface->RemoveAppDomainFromIPC (this); |
3875 | } |
3876 | #endif // DEBUGGING_SUPPORTED |
3877 | } |
3878 | |
3879 | void AppDomain::Terminate() |
3880 | { |
3881 | CONTRACTL |
3882 | { |
3883 | NOTHROW; |
3884 | GC_TRIGGERS; |
3885 | MODE_ANY; |
3886 | } |
3887 | CONTRACTL_END; |
3888 | |
3889 | GCX_PREEMP(); |
3890 | |
3891 | _ASSERTE(m_dwThreadEnterCount == 0 || IsDefaultDomain()); |
3892 | |
3893 | #ifdef FEATURE_COMINTEROP |
3894 | if (m_pRCWCache) |
3895 | { |
3896 | delete m_pRCWCache; |
3897 | m_pRCWCache = NULL; |
3898 | } |
3899 | |
3900 | if (m_pRCWRefCache) |
3901 | { |
3902 | delete m_pRCWRefCache; |
3903 | m_pRCWRefCache = NULL; |
3904 | } |
3905 | #endif // FEATURE_COMINTEROP |
3906 | |
3907 | |
3908 | if (!IsAtProcessExit()) |
3909 | { |
3910 | // if we're not shutting down everything then clean up the string literals associated |
3911 | // with this appdomain -- note that is no longer needs to happen while suspended |
3912 | // because the appropriate locks are taken in the GlobalStringLiteralMap |
3913 | // this is important as this locks have a higher lock number than does the |
3914 | // thread-store lock which is taken when we suspend. |
3915 | GetLoaderAllocator()->CleanupStringLiteralMap(); |
3916 | |
3917 | // Suspend the EE to do some clean up that can only occur |
3918 | // while no threads are running. |
3919 | GCX_COOP (); // SuspendEE may require current thread to be in Coop mode |
3920 | ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_APPDOMAIN_SHUTDOWN); |
3921 | } |
3922 | |
3923 | // Note that this must be performed before restarting the EE. It will clean |
3924 | // the cache and prevent others from using stale cache entries. |
3925 | //@TODO: Would be nice to get this back to BaseDomain, but need larger fix for that. |
3926 | // NOTE: Must have the runtime suspended to unlink managers |
3927 | // NOTE: May be NULL due to OOM during initialization. Can skip in that case. |
3928 | GetLoaderAllocator()->UninitVirtualCallStubManager(); |
3929 | MethodTable::ClearMethodDataCache(); |
3930 | ClearJitGenericHandleCache(this); |
3931 | |
3932 | // @TODO s_TPMethodTableCrst prevents us from from keeping the whole |
3933 | // assembly shutdown logic here. See if we can do better in the next milestone |
3934 | #ifdef FEATURE_PREJIT |
3935 | DeleteNativeCodeRanges(); |
3936 | #endif |
3937 | |
3938 | if (!IsAtProcessExit()) |
3939 | { |
3940 | // Resume the EE. |
3941 | ThreadSuspend::RestartEE(FALSE, TRUE); |
3942 | } |
3943 | |
3944 | ShutdownAssemblies(); |
3945 | ShutdownNativeDllSearchDirectories(); |
3946 | |
3947 | if (m_pRefClassFactHash) |
3948 | { |
3949 | m_pRefClassFactHash->Destroy(); |
3950 | // storage for m_pRefClassFactHash itself is allocated on the loader heap |
3951 | } |
3952 | |
3953 | #ifdef FEATURE_TYPEEQUIVALENCE |
3954 | m_TypeEquivalenceCrst.Destroy(); |
3955 | #endif |
3956 | |
3957 | m_ReflectionCrst.Destroy(); |
3958 | m_RefClassFactCrst.Destroy(); |
3959 | |
3960 | BaseDomain::Terminate(); |
3961 | |
3962 | if (m_handleStore) |
3963 | { |
3964 | GCHandleUtilities::GetGCHandleManager()->DestroyHandleStore(m_handleStore); |
3965 | m_handleStore = NULL; |
3966 | } |
3967 | |
3968 | #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING |
3969 | if (m_pullAllocBytes) |
3970 | { |
3971 | delete [] m_pullAllocBytes; |
3972 | } |
3973 | if (m_pullSurvivedBytes) |
3974 | { |
3975 | delete [] m_pullSurvivedBytes; |
3976 | } |
3977 | #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING |
3978 | |
3979 | if(m_dwIndex.m_dwIndex != 0) |
3980 | SystemDomain::ReleaseAppDomainIndex(m_dwIndex); |
3981 | } // AppDomain::Terminate |
3982 | |
3983 | void AppDomain::CloseDomain() |
3984 | { |
3985 | CONTRACTL |
3986 | { |
3987 | NOTHROW; |
3988 | GC_TRIGGERS; |
3989 | MODE_ANY; |
3990 | } |
3991 | CONTRACTL_END; |
3992 | |
3993 | |
3994 | BOOL bADRemoved=FALSE;; |
3995 | |
3996 | AddRef(); // Hold a reference |
3997 | AppDomainRefHolder AdHolder(this); |
3998 | { |
3999 | SystemDomain::LockHolder lh; |
4000 | |
4001 | SystemDomain::System()->DecrementNumAppDomains(); // Maintain a count of app domains added to the list. |
4002 | bADRemoved = SystemDomain::System()->RemoveDomain(this); |
4003 | } |
4004 | |
4005 | if(bADRemoved) |
4006 | Stop(); |
4007 | } |
4008 | |
4009 | #endif // !CROSSGEN_COMPILE |
4010 | |
4011 | #ifdef FEATURE_COMINTEROP |
4012 | MethodTable *AppDomain::GetRedirectedType(WinMDAdapter::RedirectedTypeIndex index) |
4013 | { |
4014 | CONTRACTL |
4015 | { |
4016 | THROWS; |
4017 | GC_TRIGGERS; |
4018 | MODE_ANY; |
4019 | } |
4020 | CONTRACTL_END; |
4021 | |
4022 | // If we have the type loaded already, use that |
4023 | if (m_rpCLRTypes[index] != nullptr) |
4024 | { |
4025 | return m_rpCLRTypes[index]; |
4026 | } |
4027 | |
4028 | WinMDAdapter::FrameworkAssemblyIndex frameworkAssemblyIndex; |
4029 | WinMDAdapter::GetRedirectedTypeInfo(index, nullptr, nullptr, nullptr, &frameworkAssemblyIndex, nullptr, nullptr); |
4030 | MethodTable * pMT = LoadRedirectedType(index, frameworkAssemblyIndex); |
4031 | m_rpCLRTypes[index] = pMT; |
4032 | return pMT; |
4033 | } |
4034 | |
4035 | MethodTable* AppDomain::LoadRedirectedType(WinMDAdapter::RedirectedTypeIndex index, WinMDAdapter::FrameworkAssemblyIndex assembly) |
4036 | { |
4037 | CONTRACTL |
4038 | { |
4039 | THROWS; |
4040 | GC_TRIGGERS; |
4041 | MODE_ANY; |
4042 | PRECONDITION(index < WinMDAdapter::RedirectedTypeIndex_Count); |
4043 | } |
4044 | CONTRACTL_END; |
4045 | |
4046 | LPCSTR szClrNamespace; |
4047 | LPCSTR szClrName; |
4048 | LPCSTR szFullWinRTName; |
4049 | WinMDAdapter::FrameworkAssemblyIndex nFrameworkAssemblyIndex; |
4050 | |
4051 | WinMDAdapter::GetRedirectedTypeInfo(index, &szClrNamespace, &szClrName, &szFullWinRTName, &nFrameworkAssemblyIndex, nullptr, nullptr); |
4052 | |
4053 | _ASSERTE(nFrameworkAssemblyIndex >= WinMDAdapter::FrameworkAssembly_Mscorlib && |
4054 | nFrameworkAssemblyIndex < WinMDAdapter::FrameworkAssembly_Count); |
4055 | |
4056 | if (assembly != nFrameworkAssemblyIndex) |
4057 | { |
4058 | // The framework type does not live in the assembly we were requested to load redirected types from |
4059 | return nullptr; |
4060 | } |
4061 | else if (nFrameworkAssemblyIndex == WinMDAdapter::FrameworkAssembly_Mscorlib) |
4062 | { |
4063 | return ClassLoader::LoadTypeByNameThrowing(MscorlibBinder::GetModule()->GetAssembly(), |
4064 | szClrNamespace, |
4065 | szClrName, |
4066 | ClassLoader::ThrowIfNotFound, |
4067 | ClassLoader::LoadTypes, |
4068 | CLASS_LOAD_EXACTPARENTS).GetMethodTable(); |
4069 | } |
4070 | else |
4071 | { |
4072 | LPCSTR pSimpleName; |
4073 | AssemblyMetaDataInternal context; |
4074 | const BYTE * pbKeyToken; |
4075 | DWORD cbKeyTokenLength; |
4076 | DWORD dwFlags; |
4077 | |
4078 | WinMDAdapter::GetExtraAssemblyRefProps(nFrameworkAssemblyIndex, |
4079 | &pSimpleName, |
4080 | &context, |
4081 | &pbKeyToken, |
4082 | &cbKeyTokenLength, |
4083 | &dwFlags); |
4084 | |
4085 | Assembly* pAssembly = AssemblySpec::LoadAssembly(pSimpleName, |
4086 | &context, |
4087 | pbKeyToken, |
4088 | cbKeyTokenLength, |
4089 | dwFlags); |
4090 | |
4091 | return ClassLoader::LoadTypeByNameThrowing( |
4092 | pAssembly, |
4093 | szClrNamespace, |
4094 | szClrName, |
4095 | ClassLoader::ThrowIfNotFound, |
4096 | ClassLoader::LoadTypes, |
4097 | CLASS_LOAD_EXACTPARENTS).GetMethodTable(); |
4098 | } |
4099 | } |
4100 | #endif //FEATURE_COMINTEROP |
4101 | |
4102 | #endif //!DACCESS_COMPILE |
4103 | |
4104 | #ifndef DACCESS_COMPILE |
4105 | |
4106 | bool IsPlatformAssembly(LPCSTR szName, DomainAssembly *pDomainAssembly) |
4107 | { |
4108 | CONTRACTL |
4109 | { |
4110 | THROWS; |
4111 | GC_TRIGGERS; |
4112 | MODE_ANY; |
4113 | PRECONDITION(CheckPointer(szName)); |
4114 | PRECONDITION(CheckPointer(pDomainAssembly)); |
4115 | } |
4116 | CONTRACTL_END; |
4117 | |
4118 | PEAssembly *pPEAssembly = pDomainAssembly->GetFile(); |
4119 | |
4120 | if (strcmp(szName, pPEAssembly->GetSimpleName()) != 0) |
4121 | { |
4122 | return false; |
4123 | } |
4124 | |
4125 | DWORD cbPublicKey; |
4126 | const BYTE *pbPublicKey = static_cast<const BYTE *>(pPEAssembly->GetPublicKey(&cbPublicKey)); |
4127 | if (pbPublicKey == nullptr) |
4128 | { |
4129 | return false; |
4130 | } |
4131 | |
4132 | return StrongNameIsSilverlightPlatformKey(pbPublicKey, cbPublicKey); |
4133 | } |
4134 | |
4135 | void AppDomain::AddAssembly(DomainAssembly * assem) |
4136 | { |
4137 | CONTRACTL |
4138 | { |
4139 | THROWS; |
4140 | GC_TRIGGERS; |
4141 | MODE_ANY; |
4142 | INJECT_FAULT(COMPlusThrowOM();); |
4143 | } |
4144 | CONTRACTL_END; |
4145 | |
4146 | { |
4147 | CrstHolder ch(GetAssemblyListLock()); |
4148 | |
4149 | // Attempt to find empty space in assemblies list |
4150 | DWORD asmCount = m_Assemblies.GetCount_Unlocked(); |
4151 | for (DWORD i = 0; i < asmCount; ++i) |
4152 | { |
4153 | if (m_Assemblies.Get_UnlockedNoReference(i) == NULL) |
4154 | { |
4155 | m_Assemblies.Set_Unlocked(i, assem); |
4156 | return; |
4157 | } |
4158 | } |
4159 | |
4160 | // If empty space not found, simply add to end of list |
4161 | IfFailThrow(m_Assemblies.Append_Unlocked(assem)); |
4162 | } |
4163 | } |
4164 | |
4165 | void AppDomain::RemoveAssembly(DomainAssembly * pAsm) |
4166 | { |
4167 | CONTRACTL |
4168 | { |
4169 | NOTHROW; |
4170 | GC_NOTRIGGER; |
4171 | } |
4172 | CONTRACTL_END; |
4173 | |
4174 | CrstHolder ch(GetAssemblyListLock()); |
4175 | DWORD asmCount = m_Assemblies.GetCount_Unlocked(); |
4176 | for (DWORD i = 0; i < asmCount; ++i) |
4177 | { |
4178 | if (m_Assemblies.Get_UnlockedNoReference(i) == pAsm) |
4179 | { |
4180 | m_Assemblies.Set_Unlocked(i, NULL); |
4181 | return; |
4182 | } |
4183 | } |
4184 | |
4185 | _ASSERTE(!"Unreachable" ); |
4186 | } |
4187 | |
4188 | BOOL AppDomain::ContainsAssembly(Assembly * assem) |
4189 | { |
4190 | WRAPPER_NO_CONTRACT; |
4191 | AssemblyIterator i = IterateAssembliesEx((AssemblyIterationFlags)( |
4192 | kIncludeLoaded | kIncludeExecution)); |
4193 | CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly; |
4194 | |
4195 | while (i.Next(pDomainAssembly.This())) |
4196 | { |
4197 | CollectibleAssemblyHolder<Assembly *> pAssembly = pDomainAssembly->GetLoadedAssembly(); |
4198 | if (pAssembly == assem) |
4199 | return TRUE; |
4200 | } |
4201 | |
4202 | return FALSE; |
4203 | } |
4204 | |
4205 | EEClassFactoryInfoHashTable* AppDomain::SetupClassFactHash() |
4206 | { |
4207 | CONTRACTL |
4208 | { |
4209 | THROWS; |
4210 | GC_TRIGGERS; |
4211 | MODE_ANY; |
4212 | INJECT_FAULT(COMPlusThrowOM();); |
4213 | } |
4214 | CONTRACTL_END; |
4215 | |
4216 | CrstHolder ch(&m_ReflectionCrst); |
4217 | |
4218 | if (m_pRefClassFactHash == NULL) |
4219 | { |
4220 | AllocMemHolder<void> pCache(GetLowFrequencyHeap()->AllocMem(S_SIZE_T(sizeof (EEClassFactoryInfoHashTable)))); |
4221 | EEClassFactoryInfoHashTable *tmp = new (pCache) EEClassFactoryInfoHashTable; |
4222 | LockOwner lock = {&m_RefClassFactCrst,IsOwnerOfCrst}; |
4223 | if (!tmp->Init(20, &lock)) |
4224 | COMPlusThrowOM(); |
4225 | pCache.SuppressRelease(); |
4226 | m_pRefClassFactHash = tmp; |
4227 | } |
4228 | |
4229 | return m_pRefClassFactHash; |
4230 | } |
4231 | |
4232 | #ifdef FEATURE_COMINTEROP |
4233 | DispIDCache* AppDomain::SetupRefDispIDCache() |
4234 | { |
4235 | CONTRACTL |
4236 | { |
4237 | THROWS; |
4238 | GC_TRIGGERS; |
4239 | MODE_ANY; |
4240 | INJECT_FAULT(COMPlusThrowOM();); |
4241 | } |
4242 | CONTRACTL_END; |
4243 | |
4244 | CrstHolder ch(&m_ReflectionCrst); |
4245 | |
4246 | if (m_pRefDispIDCache == NULL) |
4247 | { |
4248 | AllocMemHolder<void> pCache = GetLowFrequencyHeap()->AllocMem(S_SIZE_T(sizeof (DispIDCache))); |
4249 | |
4250 | DispIDCache *tmp = new (pCache) DispIDCache; |
4251 | tmp->Init(); |
4252 | |
4253 | pCache.SuppressRelease(); |
4254 | m_pRefDispIDCache = tmp; |
4255 | } |
4256 | |
4257 | return m_pRefDispIDCache; |
4258 | } |
4259 | |
4260 | #endif // FEATURE_COMINTEROP |
4261 | |
4262 | FileLoadLock *FileLoadLock::Create(PEFileListLock *pLock, PEFile *pFile, DomainFile *pDomainFile) |
4263 | { |
4264 | CONTRACTL |
4265 | { |
4266 | THROWS; |
4267 | GC_TRIGGERS; |
4268 | MODE_ANY; |
4269 | PRECONDITION(pLock->HasLock()); |
4270 | PRECONDITION(pLock->FindFileLock(pFile) == NULL); |
4271 | INJECT_FAULT(COMPlusThrowOM();); |
4272 | } |
4273 | CONTRACTL_END; |
4274 | |
4275 | NewHolder<FileLoadLock> result(new FileLoadLock(pLock, pFile, pDomainFile)); |
4276 | |
4277 | pLock->AddElement(result); |
4278 | result->AddRef(); // Add one ref on behalf of the ListLock's reference. The corresponding Release() happens in FileLoadLock::CompleteLoadLevel. |
4279 | return result.Extract(); |
4280 | } |
4281 | |
4282 | FileLoadLock::~FileLoadLock() |
4283 | { |
4284 | CONTRACTL |
4285 | { |
4286 | DESTRUCTOR_CHECK; |
4287 | NOTHROW; |
4288 | GC_TRIGGERS; |
4289 | MODE_ANY; |
4290 | } |
4291 | CONTRACTL_END; |
4292 | ((PEFile *) m_data)->Release(); |
4293 | } |
4294 | |
4295 | DomainFile *FileLoadLock::GetDomainFile() |
4296 | { |
4297 | LIMITED_METHOD_CONTRACT; |
4298 | return m_pDomainFile; |
4299 | } |
4300 | |
4301 | FileLoadLevel FileLoadLock::GetLoadLevel() |
4302 | { |
4303 | LIMITED_METHOD_CONTRACT; |
4304 | return m_level; |
4305 | } |
4306 | |
4307 | ADID FileLoadLock::GetAppDomainId() |
4308 | { |
4309 | LIMITED_METHOD_CONTRACT; |
4310 | return m_AppDomainId; |
4311 | } |
4312 | |
4313 | // Acquire will return FALSE and not take the lock if the file |
4314 | // has already been loaded to the target level. Otherwise, |
4315 | // it will return TRUE and take the lock. |
4316 | // |
4317 | // Note that the taker must release the lock via IncrementLoadLevel. |
4318 | |
4319 | BOOL FileLoadLock::Acquire(FileLoadLevel targetLevel) |
4320 | { |
4321 | WRAPPER_NO_CONTRACT; |
4322 | |
4323 | // If we are already loaded to the desired level, the lock is "free". |
4324 | if (m_level >= targetLevel) |
4325 | return FALSE; |
4326 | |
4327 | if (!DeadlockAwareEnter()) |
4328 | { |
4329 | // We failed to get the lock due to a deadlock. |
4330 | return FALSE; |
4331 | } |
4332 | |
4333 | if (m_level >= targetLevel) |
4334 | { |
4335 | Leave(); |
4336 | return FALSE; |
4337 | } |
4338 | |
4339 | return TRUE; |
4340 | } |
4341 | |
4342 | BOOL FileLoadLock::CanAcquire(FileLoadLevel targetLevel) |
4343 | { |
4344 | // If we are already loaded to the desired level, the lock is "free". |
4345 | if (m_level >= targetLevel) |
4346 | return FALSE; |
4347 | |
4348 | return CanDeadlockAwareEnter(); |
4349 | } |
4350 | |
4351 | #if !defined(DACCESS_COMPILE) && (defined(LOGGING) || defined(STRESS_LOG)) |
4352 | static const char *fileLoadLevelName[] = |
4353 | { |
4354 | "CREATE" , // FILE_LOAD_CREATE |
4355 | "BEGIN" , // FILE_LOAD_BEGIN |
4356 | "FIND_NATIVE_IMAGE" , // FILE_LOAD_FIND_NATIVE_IMAGE |
4357 | "VERIFY_NATIVE_IMAGE_DEPENDENCIES" , // FILE_LOAD_VERIFY_NATIVE_IMAGE_DEPENDENCIES |
4358 | "ALLOCATE" , // FILE_LOAD_ALLOCATE |
4359 | "ADD_DEPENDENCIES" , // FILE_LOAD_ADD_DEPENDENCIES |
4360 | "PRE_LOADLIBRARY" , // FILE_LOAD_PRE_LOADLIBRARY |
4361 | "LOADLIBRARY" , // FILE_LOAD_LOADLIBRARY |
4362 | "POST_LOADLIBRARY" , // FILE_LOAD_POST_LOADLIBRARY |
4363 | "EAGER_FIXUPS" , // FILE_LOAD_EAGER_FIXUPS |
4364 | "VTABLE FIXUPS" , // FILE_LOAD_VTABLE_FIXUPS |
4365 | "DELIVER_EVENTS" , // FILE_LOAD_DELIVER_EVENTS |
4366 | "LOADED" , // FILE_LOADED |
4367 | "VERIFY_EXECUTION" , // FILE_LOAD_VERIFY_EXECUTION |
4368 | "ACTIVE" , // FILE_ACTIVE |
4369 | }; |
4370 | #endif // !DACCESS_COMPILE && (LOGGING || STRESS_LOG) |
4371 | |
4372 | BOOL FileLoadLock::CompleteLoadLevel(FileLoadLevel level, BOOL success) |
4373 | { |
4374 | CONTRACTL |
4375 | { |
4376 | MODE_ANY; |
4377 | GC_TRIGGERS; |
4378 | THROWS; |
4379 | PRECONDITION(HasLock()); |
4380 | } |
4381 | CONTRACTL_END; |
4382 | |
4383 | // Increment may happen more than once if reentrancy occurs (e.g. LoadLibrary) |
4384 | if (level > m_level) |
4385 | { |
4386 | // Must complete each level in turn, unless we have an error |
4387 | CONSISTENCY_CHECK(m_pDomainFile->IsError() || (level == (m_level+1))); |
4388 | // Remove the lock from the list if the load is completed |
4389 | if (level >= FILE_ACTIVE) |
4390 | { |
4391 | { |
4392 | GCX_COOP(); |
4393 | PEFileListLockHolder lock((PEFileListLock*)m_pList); |
4394 | |
4395 | #if _DEBUG |
4396 | BOOL fDbgOnly_SuccessfulUnlink = |
4397 | #endif |
4398 | m_pList->Unlink(this); |
4399 | _ASSERTE(fDbgOnly_SuccessfulUnlink); |
4400 | |
4401 | m_pDomainFile->ClearLoading(); |
4402 | |
4403 | CONSISTENCY_CHECK(m_dwRefCount >= 2); // Caller (LoadDomainFile) should have 1 refcount and m_pList should have another which was acquired in FileLoadLock::Create. |
4404 | |
4405 | m_level = (FileLoadLevel)level; |
4406 | |
4407 | // Dev11 bug 236344 |
4408 | // In AppDomain::IsLoading, if the lock is taken on m_pList and then FindFileLock returns NULL, |
4409 | // we depend on the DomainFile's load level being up to date. Hence we must update the load |
4410 | // level while the m_pList lock is held. |
4411 | if (success) |
4412 | m_pDomainFile->SetLoadLevel(level); |
4413 | } |
4414 | |
4415 | |
4416 | Release(); // Release m_pList's refcount on this lock, which was acquired in FileLoadLock::Create |
4417 | |
4418 | } |
4419 | else |
4420 | { |
4421 | m_level = (FileLoadLevel)level; |
4422 | |
4423 | if (success) |
4424 | m_pDomainFile->SetLoadLevel(level); |
4425 | } |
4426 | |
4427 | #ifndef DACCESS_COMPILE |
4428 | switch(level) |
4429 | { |
4430 | case FILE_LOAD_ALLOCATE: |
4431 | case FILE_LOAD_ADD_DEPENDENCIES: |
4432 | case FILE_LOAD_DELIVER_EVENTS: |
4433 | case FILE_LOADED: |
4434 | case FILE_ACTIVE: // The timing of stress logs is not critical, so even for the FILE_ACTIVE stage we need not do it while the m_pList lock is held. |
4435 | STRESS_LOG4(LF_CLASSLOADER, LL_INFO100, "Completed Load Level %s for DomainFile %p in AD %i - success = %i\n" , fileLoadLevelName[level], m_pDomainFile, m_AppDomainId.m_dwId, success); |
4436 | break; |
4437 | default: |
4438 | break; |
4439 | } |
4440 | #endif |
4441 | |
4442 | return TRUE; |
4443 | } |
4444 | else |
4445 | return FALSE; |
4446 | } |
4447 | |
4448 | void FileLoadLock::SetError(Exception *ex) |
4449 | { |
4450 | CONTRACTL |
4451 | { |
4452 | MODE_ANY; |
4453 | GC_TRIGGERS; |
4454 | THROWS; |
4455 | PRECONDITION(CheckPointer(ex)); |
4456 | PRECONDITION(HasLock()); |
4457 | INJECT_FAULT(COMPlusThrowOM();); |
4458 | } |
4459 | CONTRACTL_END; |
4460 | |
4461 | m_cachedHR = ex->GetHR(); |
4462 | |
4463 | LOG((LF_LOADER, LL_WARNING, "LOADER: %x:***%s*\t!!!Non-transient error 0x%x\n" , |
4464 | m_pDomainFile->GetAppDomain(), m_pDomainFile->GetSimpleName(), m_cachedHR)); |
4465 | |
4466 | m_pDomainFile->SetError(ex); |
4467 | |
4468 | CompleteLoadLevel(FILE_ACTIVE, FALSE); |
4469 | } |
4470 | |
4471 | void FileLoadLock::AddRef() |
4472 | { |
4473 | LIMITED_METHOD_CONTRACT; |
4474 | FastInterlockIncrement((LONG *) &m_dwRefCount); |
4475 | } |
4476 | |
4477 | UINT32 FileLoadLock::Release() |
4478 | { |
4479 | CONTRACTL |
4480 | { |
4481 | NOTHROW; |
4482 | GC_TRIGGERS; |
4483 | MODE_ANY; |
4484 | } |
4485 | CONTRACTL_END; |
4486 | |
4487 | LONG count = FastInterlockDecrement((LONG *) &m_dwRefCount); |
4488 | if (count == 0) |
4489 | delete this; |
4490 | |
4491 | return count; |
4492 | } |
4493 | |
4494 | FileLoadLock::FileLoadLock(PEFileListLock *pLock, PEFile *pFile, DomainFile *pDomainFile) |
4495 | : ListLockEntry(pLock, pFile, "File load lock" ), |
4496 | m_level((FileLoadLevel) (FILE_LOAD_CREATE)), |
4497 | m_pDomainFile(pDomainFile), |
4498 | m_cachedHR(S_OK), |
4499 | m_AppDomainId(pDomainFile->GetAppDomain()->GetId()) |
4500 | { |
4501 | WRAPPER_NO_CONTRACT; |
4502 | pFile->AddRef(); |
4503 | } |
4504 | |
4505 | void FileLoadLock::HolderLeave(FileLoadLock *pThis) |
4506 | { |
4507 | LIMITED_METHOD_CONTRACT; |
4508 | pThis->Leave(); |
4509 | } |
4510 | |
4511 | |
4512 | |
4513 | |
4514 | |
4515 | |
4516 | // |
4517 | // Assembly loading: |
4518 | // |
4519 | // Assembly loading is carefully layered to avoid deadlocks in the |
4520 | // presence of circular loading dependencies. |
4521 | // A LoadLevel is associated with each assembly as it is being loaded. During the |
4522 | // act of loading (abstractly, increasing its load level), its lock is |
4523 | // held, and the current load level is stored on the thread. Any |
4524 | // recursive loads during that period are automatically restricted to |
4525 | // only partially load the dependent assembly to the same level as the |
4526 | // caller (or to one short of that level in the presence of a deadlock |
4527 | // loop.) |
4528 | // |
4529 | // Each loading stage must be carfully constructed so that |
4530 | // this constraint is expected and can be dealt with. |
4531 | // |
4532 | // Note that there is one case where this still doesn't handle recursion, and that is the |
4533 | // security subsytem. The security system runs managed code, and thus must typically fully |
4534 | // initialize assemblies of permission sets it is trying to use. (And of course, these may be used |
4535 | // while those assemblies are initializing.) This is dealt with in the historical manner - namely |
4536 | // the security system passes in a special flag which says that it will deal with null return values |
4537 | // in the case where a load cannot be safely completed due to such issues. |
4538 | // |
4539 | |
4540 | void AppDomain::LoadSystemAssemblies() |
4541 | { |
4542 | STANDARD_VM_CONTRACT; |
4543 | |
4544 | // The only reason to make an assembly a "system assembly" is if the EE is caching |
4545 | // pointers to stuff in the assembly. Because this is going on, we need to preserve |
4546 | // the invariant that the assembly is loaded into every app domain. |
4547 | // |
4548 | // Right now we have only one system assembly. We shouldn't need to add any more. |
4549 | |
4550 | LoadAssembly(NULL, SystemDomain::System()->SystemFile(), FILE_ACTIVE); |
4551 | } |
4552 | |
4553 | FileLoadLevel AppDomain::GetDomainFileLoadLevel(DomainFile *pFile) |
4554 | { |
4555 | CONTRACTL |
4556 | { |
4557 | THROWS; |
4558 | GC_TRIGGERS; |
4559 | MODE_ANY; |
4560 | } |
4561 | CONTRACTL_END |
4562 | |
4563 | LoadLockHolder lock(this); |
4564 | |
4565 | FileLoadLock* pLockEntry = (FileLoadLock *) lock->FindFileLock(pFile->GetFile()); |
4566 | |
4567 | if (pLockEntry == NULL) |
4568 | return pFile->GetLoadLevel(); |
4569 | else |
4570 | return pLockEntry->GetLoadLevel(); |
4571 | } |
4572 | |
4573 | // This checks if the thread has initiated (or completed) loading at the given level. A false guarantees that |
4574 | // (a) The current thread (or a thread blocking on the current thread) has not started loading the file |
4575 | // at the given level, and |
4576 | // (b) No other thread had started loading the file at this level at the start of this function call. |
4577 | |
4578 | // Note that another thread may start loading the file at that level in a race with the completion of |
4579 | // this function. However, the caller still has the guarantee that such a load started after this |
4580 | // function was called (and e.g. any state in place before the function call will be seen by the other thread.) |
4581 | // |
4582 | // Conversely, a true guarantees that either the current thread has started the load step, or another |
4583 | // thread has completed the load step. |
4584 | // |
4585 | |
4586 | BOOL AppDomain::IsLoading(DomainFile *pFile, FileLoadLevel level) |
4587 | { |
4588 | // Cheap out |
4589 | if (pFile->GetLoadLevel() < level) |
4590 | { |
4591 | FileLoadLock *pLock = NULL; |
4592 | { |
4593 | LoadLockHolder lock(this); |
4594 | |
4595 | pLock = (FileLoadLock *) lock->FindFileLock(pFile->GetFile()); |
4596 | |
4597 | if (pLock == NULL) |
4598 | { |
4599 | // No thread involved with loading |
4600 | return pFile->GetLoadLevel() >= level; |
4601 | } |
4602 | |
4603 | pLock->AddRef(); |
4604 | } |
4605 | |
4606 | FileLoadLockRefHolder lockRef(pLock); |
4607 | |
4608 | if (pLock->Acquire(level)) |
4609 | { |
4610 | // We got the lock - therefore no other thread has started this loading step yet. |
4611 | pLock->Leave(); |
4612 | return FALSE; |
4613 | } |
4614 | |
4615 | // We didn't get the lock - either this thread is already doing the load, |
4616 | // or else the load has already finished. |
4617 | } |
4618 | return TRUE; |
4619 | } |
4620 | |
4621 | // CheckLoading is a weaker form of IsLoading, which will not block on |
4622 | // other threads waiting for their status. This is appropriate for asserts. |
4623 | CHECK AppDomain::CheckLoading(DomainFile *pFile, FileLoadLevel level) |
4624 | { |
4625 | // Cheap out |
4626 | if (pFile->GetLoadLevel() < level) |
4627 | { |
4628 | FileLoadLock *pLock = NULL; |
4629 | |
4630 | LoadLockHolder lock(this); |
4631 | |
4632 | pLock = (FileLoadLock *) lock->FindFileLock(pFile->GetFile()); |
4633 | |
4634 | if (pLock != NULL |
4635 | && pLock->CanAcquire(level)) |
4636 | { |
4637 | // We can get the lock - therefore no other thread has started this loading step yet. |
4638 | CHECK_FAILF(("Loading step %d has not been initiated yet" , level)); |
4639 | } |
4640 | |
4641 | // We didn't get the lock - either this thread is already doing the load, |
4642 | // or else the load has already finished. |
4643 | } |
4644 | |
4645 | CHECK_OK; |
4646 | } |
4647 | |
4648 | CHECK AppDomain::CheckCanLoadTypes(Assembly *pAssembly) |
4649 | { |
4650 | CONTRACTL |
4651 | { |
4652 | THROWS; |
4653 | GC_TRIGGERS; |
4654 | MODE_ANY; |
4655 | } |
4656 | CONTRACTL_END; |
4657 | CHECK_MSG(CheckValidModule(pAssembly->GetManifestModule()), |
4658 | "Type loading can occur only when executing in the assembly's app domain" ); |
4659 | CHECK_OK; |
4660 | } |
4661 | |
4662 | CHECK AppDomain::CheckCanExecuteManagedCode(MethodDesc* pMD) |
4663 | { |
4664 | CONTRACTL |
4665 | { |
4666 | THROWS; |
4667 | GC_TRIGGERS; |
4668 | MODE_ANY; |
4669 | } |
4670 | CONTRACTL_END; |
4671 | |
4672 | Module* pModule=pMD->GetModule(); |
4673 | |
4674 | CHECK_MSG(CheckValidModule(pModule), |
4675 | "Managed code can only run when executing in the module's app domain" ); |
4676 | |
4677 | if (!pMD->IsInterface() || pMD->IsStatic()) //interfaces require no activation for instance methods |
4678 | { |
4679 | //cctor could have been interupted by ADU |
4680 | CHECK_MSG(pModule->CheckActivated(), |
4681 | "Managed code can only run when its module has been activated in the current app domain" ); |
4682 | } |
4683 | |
4684 | CHECK_OK; |
4685 | } |
4686 | |
4687 | #endif // !DACCESS_COMPILE |
4688 | |
4689 | void AppDomain::LoadDomainFile(DomainFile *pFile, |
4690 | FileLoadLevel targetLevel) |
4691 | { |
4692 | CONTRACTL |
4693 | { |
4694 | if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS; |
4695 | if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS; |
4696 | if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM();); } |
4697 | INJECT_FAULT(COMPlusThrowOM();); |
4698 | } |
4699 | CONTRACTL_END; |
4700 | |
4701 | // Quick exit if finished |
4702 | if (pFile->GetLoadLevel() >= targetLevel) |
4703 | return; |
4704 | |
4705 | // Handle the error case |
4706 | pFile->ThrowIfError(targetLevel); |
4707 | |
4708 | |
4709 | #ifndef DACCESS_COMPILE |
4710 | |
4711 | if (pFile->IsLoading()) |
4712 | { |
4713 | GCX_PREEMP(); |
4714 | |
4715 | // Load some more if appropriate |
4716 | LoadLockHolder lock(this); |
4717 | |
4718 | FileLoadLock* pLockEntry = (FileLoadLock *) lock->FindFileLock(pFile->GetFile()); |
4719 | if (pLockEntry == NULL) |
4720 | { |
4721 | _ASSERTE (!pFile->IsLoading()); |
4722 | return; |
4723 | } |
4724 | |
4725 | pLockEntry->AddRef(); |
4726 | |
4727 | lock.Release(); |
4728 | |
4729 | LoadDomainFile(pLockEntry, targetLevel); |
4730 | } |
4731 | |
4732 | #else // DACCESS_COMPILE |
4733 | DacNotImpl(); |
4734 | #endif // DACCESS_COMPILE |
4735 | } |
4736 | |
4737 | #ifndef DACCESS_COMPILE |
4738 | |
4739 | FileLoadLevel AppDomain::GetThreadFileLoadLevel() |
4740 | { |
4741 | WRAPPER_NO_CONTRACT; |
4742 | if (GetThread()->GetLoadLevelLimiter() == NULL) |
4743 | return FILE_ACTIVE; |
4744 | else |
4745 | return (FileLoadLevel)(GetThread()->GetLoadLevelLimiter()->GetLoadLevel()-1); |
4746 | } |
4747 | |
4748 | |
4749 | Assembly *AppDomain::LoadAssembly(AssemblySpec* pIdentity, |
4750 | PEAssembly *pFile, |
4751 | FileLoadLevel targetLevel) |
4752 | { |
4753 | CONTRACT(Assembly *) |
4754 | { |
4755 | GC_TRIGGERS; |
4756 | THROWS; |
4757 | MODE_ANY; |
4758 | PRECONDITION(CheckPointer(pFile)); |
4759 | POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); // May be NULL in recursive load case |
4760 | INJECT_FAULT(COMPlusThrowOM();); |
4761 | } |
4762 | CONTRACT_END; |
4763 | |
4764 | DomainAssembly *pAssembly = LoadDomainAssembly(pIdentity, pFile, targetLevel); |
4765 | PREFIX_ASSUME(pAssembly != NULL); |
4766 | |
4767 | RETURN pAssembly->GetAssembly(); |
4768 | } |
4769 | |
4770 | #ifndef CROSSGEN_COMPILE |
4771 | // Thread stress |
4772 | class LoadDomainAssemblyStress : APIThreadStress |
4773 | { |
4774 | public: |
4775 | AppDomain *pThis; |
4776 | AssemblySpec* pSpec; |
4777 | PEAssembly *pFile; |
4778 | FileLoadLevel targetLevel; |
4779 | |
4780 | LoadDomainAssemblyStress(AppDomain *pThis, AssemblySpec* pSpec, PEAssembly *pFile, FileLoadLevel targetLevel) |
4781 | : pThis(pThis), pSpec(pSpec), pFile(pFile), targetLevel(targetLevel) {LIMITED_METHOD_CONTRACT;} |
4782 | |
4783 | void Invoke() |
4784 | { |
4785 | WRAPPER_NO_CONTRACT; |
4786 | STATIC_CONTRACT_SO_INTOLERANT; |
4787 | SetupThread(); |
4788 | pThis->LoadDomainAssembly(pSpec, pFile, targetLevel); |
4789 | } |
4790 | }; |
4791 | #endif // CROSSGEN_COMPILE |
4792 | |
4793 | extern BOOL AreSameBinderInstance(ICLRPrivBinder *pBinderA, ICLRPrivBinder *pBinderB); |
4794 | |
4795 | DomainAssembly* AppDomain::LoadDomainAssembly( AssemblySpec* pSpec, |
4796 | PEAssembly *pFile, |
4797 | FileLoadLevel targetLevel) |
4798 | { |
4799 | STATIC_CONTRACT_THROWS; |
4800 | |
4801 | if (pSpec == nullptr) |
4802 | { |
4803 | // skip caching, since we don't have anything to base it on |
4804 | return LoadDomainAssemblyInternal(pSpec, pFile, targetLevel); |
4805 | } |
4806 | |
4807 | DomainAssembly* pRetVal = NULL; |
4808 | EX_TRY |
4809 | { |
4810 | pRetVal = LoadDomainAssemblyInternal(pSpec, pFile, targetLevel); |
4811 | } |
4812 | EX_HOOK |
4813 | { |
4814 | Exception* pEx=GET_EXCEPTION(); |
4815 | if (!pEx->IsTransient()) |
4816 | { |
4817 | // Setup the binder reference in AssemblySpec from the PEAssembly if one is not already set. |
4818 | ICLRPrivBinder* pCurrentBindingContext = pSpec->GetBindingContext(); |
4819 | ICLRPrivBinder* pBindingContextFromPEAssembly = pFile->GetBindingContext(); |
4820 | |
4821 | if (pCurrentBindingContext == NULL) |
4822 | { |
4823 | // Set the binding context we got from the PEAssembly if AssemblySpec does not |
4824 | // have that information |
4825 | _ASSERTE(pBindingContextFromPEAssembly != NULL); |
4826 | pSpec->SetBindingContext(pBindingContextFromPEAssembly); |
4827 | } |
4828 | #if defined(_DEBUG) |
4829 | else |
4830 | { |
4831 | // Binding context in the spec should be the same as the binding context in the PEAssembly |
4832 | _ASSERTE(AreSameBinderInstance(pCurrentBindingContext, pBindingContextFromPEAssembly)); |
4833 | } |
4834 | #endif // _DEBUG |
4835 | |
4836 | if (!EEFileLoadException::CheckType(pEx)) |
4837 | { |
4838 | StackSString name; |
4839 | pSpec->GetFileOrDisplayName(0, name); |
4840 | pEx=new EEFileLoadException(name, pEx->GetHR(), NULL, pEx); |
4841 | AddExceptionToCache(pSpec, pEx); |
4842 | PAL_CPP_THROW(Exception *, pEx); |
4843 | } |
4844 | else |
4845 | AddExceptionToCache(pSpec, pEx); |
4846 | } |
4847 | } |
4848 | EX_END_HOOK; |
4849 | |
4850 | return pRetVal; |
4851 | } |
4852 | |
4853 | |
4854 | DomainAssembly *AppDomain::LoadDomainAssemblyInternal(AssemblySpec* pIdentity, |
4855 | PEAssembly *pFile, |
4856 | FileLoadLevel targetLevel) |
4857 | { |
4858 | CONTRACT(DomainAssembly *) |
4859 | { |
4860 | GC_TRIGGERS; |
4861 | THROWS; |
4862 | MODE_ANY; |
4863 | PRECONDITION(CheckPointer(pFile)); |
4864 | PRECONDITION(pFile->IsSystem() || ::GetAppDomain()==this); |
4865 | POSTCONDITION(CheckPointer(RETVAL)); |
4866 | POSTCONDITION(RETVAL->GetLoadLevel() >= GetThreadFileLoadLevel() |
4867 | || RETVAL->GetLoadLevel() >= targetLevel); |
4868 | POSTCONDITION(RETVAL->CheckNoError(targetLevel)); |
4869 | INJECT_FAULT(COMPlusThrowOM();); |
4870 | } |
4871 | CONTRACT_END; |
4872 | |
4873 | |
4874 | DomainAssembly * result; |
4875 | |
4876 | #ifndef CROSSGEN_COMPILE |
4877 | LoadDomainAssemblyStress ts (this, pIdentity, pFile, targetLevel); |
4878 | #endif |
4879 | |
4880 | // Go into preemptive mode since this may take a while. |
4881 | GCX_PREEMP(); |
4882 | |
4883 | // Check for existing fully loaded assembly, or for an assembly which has failed during the loading process. |
4884 | result = FindAssembly(pFile, FindAssemblyOptions_IncludeFailedToLoad); |
4885 | |
4886 | if (result == NULL) |
4887 | { |
4888 | LoaderAllocator *pLoaderAllocator = NULL; |
4889 | |
4890 | #ifndef CROSSGEN_COMPILE |
4891 | ICLRPrivBinder *pFileBinder = pFile->GetBindingContext(); |
4892 | if (pFileBinder != NULL) |
4893 | { |
4894 | // Assemblies loaded with AssemblyLoadContext need to use a different LoaderAllocator if |
4895 | // marked as collectible |
4896 | pFileBinder->GetLoaderAllocator((LPVOID*)&pLoaderAllocator); |
4897 | } |
4898 | #endif // !CROSSGEN_COMPILE |
4899 | |
4900 | if (pLoaderAllocator == NULL) |
4901 | { |
4902 | pLoaderAllocator = this->GetLoaderAllocator(); |
4903 | } |
4904 | |
4905 | // Allocate the DomainAssembly a bit early to avoid GC mode problems. We could potentially avoid |
4906 | // a rare redundant allocation by moving this closer to FileLoadLock::Create, but it's not worth it. |
4907 | NewHolder<DomainAssembly> pDomainAssembly = new DomainAssembly(this, pFile, pLoaderAllocator); |
4908 | |
4909 | LoadLockHolder lock(this); |
4910 | |
4911 | // Find the list lock entry |
4912 | FileLoadLock * fileLock = (FileLoadLock *)lock->FindFileLock(pFile); |
4913 | if (fileLock == NULL) |
4914 | { |
4915 | // Check again in case we were racing |
4916 | result = FindAssembly(pFile, FindAssemblyOptions_IncludeFailedToLoad); |
4917 | if (result == NULL) |
4918 | { |
4919 | // We are the first one in - create the DomainAssembly |
4920 | fileLock = FileLoadLock::Create(lock, pFile, pDomainAssembly); |
4921 | pDomainAssembly.SuppressRelease(); |
4922 | #ifndef CROSSGEN_COMPILE |
4923 | if (pDomainAssembly->IsCollectible()) |
4924 | { |
4925 | // We add the assembly to the LoaderAllocator only when we are sure that it can be added |
4926 | // and won't be deleted in case of a concurrent load from the same ALC |
4927 | ((AssemblyLoaderAllocator *)pLoaderAllocator)->AddDomainAssembly(pDomainAssembly); |
4928 | } |
4929 | #endif // !CROSSGEN_COMPILE |
4930 | } |
4931 | } |
4932 | else |
4933 | { |
4934 | fileLock->AddRef(); |
4935 | } |
4936 | |
4937 | lock.Release(); |
4938 | |
4939 | if (result == NULL) |
4940 | { |
4941 | // We pass our ref on fileLock to LoadDomainFile to release. |
4942 | |
4943 | // Note that if we throw here, we will poison fileLock with an error condition, |
4944 | // so it will not be removed until app domain unload. So there is no need |
4945 | // to release our ref count. |
4946 | result = (DomainAssembly *)LoadDomainFile(fileLock, targetLevel); |
4947 | } |
4948 | else |
4949 | { |
4950 | result->EnsureLoadLevel(targetLevel); |
4951 | } |
4952 | } |
4953 | else |
4954 | result->EnsureLoadLevel(targetLevel); |
4955 | |
4956 | // Malformed metadata may contain a Module reference to what is actually |
4957 | // an Assembly. In this case we need to throw an exception, since returning |
4958 | // a DomainModule as a DomainAssembly is a type safety violation. |
4959 | if (!result->IsAssembly()) |
4960 | { |
4961 | ThrowHR(COR_E_ASSEMBLYEXPECTED); |
4962 | } |
4963 | |
4964 | // Cache result in all cases, since found pFile could be from a different AssemblyRef than pIdentity |
4965 | // Do not cache WindowsRuntime assemblies, they are cached in code:CLRPrivTypeCacheWinRT |
4966 | if ((pIdentity != NULL) && (pIdentity->CanUseWithBindingCache()) && (result->CanUseWithBindingCache())) |
4967 | GetAppDomain()->AddAssemblyToCache(pIdentity, result); |
4968 | |
4969 | RETURN result; |
4970 | } // AppDomain::LoadDomainAssembly |
4971 | |
4972 | |
4973 | struct LoadFileArgs |
4974 | { |
4975 | FileLoadLock *pLock; |
4976 | FileLoadLevel targetLevel; |
4977 | DomainFile *result; |
4978 | }; |
4979 | |
4980 | DomainFile *AppDomain::LoadDomainFile(FileLoadLock *pLock, FileLoadLevel targetLevel) |
4981 | { |
4982 | CONTRACT(DomainFile *) |
4983 | { |
4984 | STANDARD_VM_CHECK; |
4985 | PRECONDITION(CheckPointer(pLock)); |
4986 | PRECONDITION(pLock->GetDomainFile()->GetAppDomain() == this); |
4987 | POSTCONDITION(RETVAL->GetLoadLevel() >= GetThreadFileLoadLevel() |
4988 | || RETVAL->GetLoadLevel() >= targetLevel); |
4989 | POSTCONDITION(RETVAL->CheckNoError(targetLevel)); |
4990 | } |
4991 | CONTRACT_END; |
4992 | |
4993 | // Thread stress |
4994 | APIThreadStress::SyncThreadStress(); |
4995 | |
4996 | DomainFile *pFile = pLock->GetDomainFile(); |
4997 | |
4998 | // Make sure we release the lock on exit |
4999 | FileLoadLockRefHolder lockRef(pLock); |
5000 | |
5001 | // We need to perform the early steps of loading mscorlib without a domain transition. This is |
5002 | // important for bootstrapping purposes - we need to get mscorlib at least partially loaded |
5003 | // into a domain before we can run serialization code to do the transition. |
5004 | // |
5005 | // Note that we cannot do this in general for all assemblies, because some of the security computations |
5006 | // require the managed exposed object, which must be created in the correct app domain. |
5007 | |
5008 | if (this != GetAppDomain() |
5009 | && pFile->GetFile()->IsSystem() |
5010 | && targetLevel > FILE_LOAD_ALLOCATE) |
5011 | { |
5012 | // Re-call the routine with a limited load level. This will cause the first part of the load to |
5013 | // get performed in the current app domain. |
5014 | |
5015 | pLock->AddRef(); |
5016 | LoadDomainFile(pLock, targetLevel > FILE_LOAD_ALLOCATE ? FILE_LOAD_ALLOCATE : targetLevel); |
5017 | |
5018 | // Now continue on to complete the rest of the load, if any. |
5019 | } |
5020 | |
5021 | // Do a quick out check for the already loaded case. |
5022 | if (pLock->GetLoadLevel() >= targetLevel) |
5023 | { |
5024 | pFile->ThrowIfError(targetLevel); |
5025 | |
5026 | RETURN pFile; |
5027 | } |
5028 | |
5029 | // Initialize a loading queue. This will hold any loads which are triggered recursively but |
5030 | // which cannot be immediately satisfied due to anti-deadlock constraints. |
5031 | |
5032 | // PendingLoadQueues are allocated on the stack during a load, and |
5033 | // shared with all nested loads on the same thread. (Note that we won't use |
5034 | // "candidate" if we are in a recursive load; that's OK since they are cheap to |
5035 | // construct.) |
5036 | FileLoadLevel immediateTargetLevel = targetLevel; |
5037 | { |
5038 | LoadLevelLimiter limit; |
5039 | limit.Activate(); |
5040 | |
5041 | // We cannot set a target level higher than that allowed by the limiter currently. |
5042 | // This is because of anti-deadlock constraints. |
5043 | if (immediateTargetLevel > limit.GetLoadLevel()) |
5044 | immediateTargetLevel = limit.GetLoadLevel(); |
5045 | |
5046 | LOG((LF_LOADER, LL_INFO100, "LOADER: %x:***%s*\t>>>Load initiated, %s/%s\n" , |
5047 | pFile->GetAppDomain(), pFile->GetSimpleName(), |
5048 | fileLoadLevelName[immediateTargetLevel], fileLoadLevelName[targetLevel])); |
5049 | |
5050 | // Now loop and do the load incrementally to the target level. |
5051 | if (pLock->GetLoadLevel() < immediateTargetLevel) |
5052 | { |
5053 | // Thread stress |
5054 | APIThreadStress::SyncThreadStress(); |
5055 | |
5056 | while (pLock->Acquire(immediateTargetLevel)) |
5057 | { |
5058 | FileLoadLevel workLevel; |
5059 | { |
5060 | FileLoadLockHolder fileLock(pLock); |
5061 | |
5062 | // Work level is next step to do |
5063 | workLevel = (FileLoadLevel)(fileLock->GetLoadLevel()+1); |
5064 | |
5065 | // Set up the anti-deadlock constraint: we cannot safely recursively load any assemblies |
5066 | // on this thread to a higher level than this assembly is being loaded now. |
5067 | // Note that we do allow work at a parallel level; any deadlocks caused here will |
5068 | // be resolved by the deadlock detection in the FileLoadLocks. |
5069 | limit.SetLoadLevel(workLevel); |
5070 | |
5071 | LOG((LF_LOADER, |
5072 | (workLevel == FILE_LOAD_BEGIN |
5073 | || workLevel == FILE_LOADED |
5074 | || workLevel == FILE_ACTIVE) |
5075 | ? LL_INFO10 : LL_INFO1000, |
5076 | "LOADER: %p:***%s*\t loading at level %s\n" , |
5077 | this, pFile->GetSimpleName(), fileLoadLevelName[workLevel])); |
5078 | |
5079 | TryIncrementalLoad(pFile, workLevel, fileLock); |
5080 | } |
5081 | TESTHOOKCALL(CompletedFileLoadLevel(GetId().m_dwId,pFile,workLevel)); |
5082 | } |
5083 | |
5084 | if (pLock->GetLoadLevel() == immediateTargetLevel-1) |
5085 | { |
5086 | LOG((LF_LOADER, LL_INFO100, "LOADER: %x:***%s*\t<<<Load limited due to detected deadlock, %s\n" , |
5087 | pFile->GetAppDomain(), pFile->GetSimpleName(), |
5088 | fileLoadLevelName[immediateTargetLevel-1])); |
5089 | } |
5090 | } |
5091 | |
5092 | LOG((LF_LOADER, LL_INFO100, "LOADER: %x:***%s*\t<<<Load completed, %s\n" , |
5093 | pFile->GetAppDomain(), pFile->GetSimpleName(), |
5094 | fileLoadLevelName[pLock->GetLoadLevel()])); |
5095 | |
5096 | } |
5097 | |
5098 | // There may have been an error stored on the domain file by another thread, or from a previous load |
5099 | pFile->ThrowIfError(targetLevel); |
5100 | |
5101 | // There are two normal results from the above loop. |
5102 | // |
5103 | // 1. We succeeded in loading the file to the current thread's load level. |
5104 | // 2. We succeeded in loading the file to the current thread's load level - 1, due |
5105 | // to deadlock condition with another thread loading the same assembly. |
5106 | // |
5107 | // Either of these are considered satisfactory results, as code inside a load must expect |
5108 | // a parial load result. |
5109 | // |
5110 | // However, if load level elevation has occurred, then it is possible for a deadlock to |
5111 | // prevent us from loading an assembly which was loading before the elevation at a radically |
5112 | // lower level. In such a case, we throw an exception which transiently fails the current |
5113 | // load, since it is likely we have not satisfied the caller. |
5114 | // (An alternate, and possibly preferable, strategy here would be for all callers to explicitly |
5115 | // identify the minimum load level acceptable via CheckLoadDomainFile and throw from there.) |
5116 | |
5117 | pFile->RequireLoadLevel((FileLoadLevel)(immediateTargetLevel-1)); |
5118 | |
5119 | |
5120 | RETURN pFile; |
5121 | } |
5122 | |
5123 | void AppDomain::TryIncrementalLoad(DomainFile *pFile, FileLoadLevel workLevel, FileLoadLockHolder &lockHolder) |
5124 | { |
5125 | STANDARD_VM_CONTRACT; |
5126 | |
5127 | // This is factored out so we don't call EX_TRY in a loop (EX_TRY can _alloca) |
5128 | |
5129 | BOOL released = FALSE; |
5130 | FileLoadLock* pLoadLock = lockHolder.GetValue(); |
5131 | |
5132 | EX_TRY |
5133 | { |
5134 | |
5135 | // Special case: for LoadLibrary, we cannot hold the lock during the |
5136 | // actual LoadLibrary call, because we might get a callback from _CorDllMain on any |
5137 | // other thread. (Note that this requires DomainFile's LoadLibrary to be independently threadsafe.) |
5138 | |
5139 | if (workLevel == FILE_LOAD_LOADLIBRARY) |
5140 | { |
5141 | lockHolder.Release(); |
5142 | released = TRUE; |
5143 | } |
5144 | |
5145 | // Do the work |
5146 | TESTHOOKCALL(NextFileLoadLevel(GetId().m_dwId,pFile,workLevel)); |
5147 | BOOL success = pFile->DoIncrementalLoad(workLevel); |
5148 | TESTHOOKCALL(CompletingFileLoadLevel(GetId().m_dwId,pFile,workLevel)); |
5149 | if (released) |
5150 | { |
5151 | // Reobtain lock to increment level. (Note that another thread may |
5152 | // have already done it which is OK. |
5153 | if (pLoadLock->Acquire(workLevel)) |
5154 | { |
5155 | // note lockHolder.Acquire isn't wired up to actually take the lock |
5156 | lockHolder = pLoadLock; |
5157 | released = FALSE; |
5158 | } |
5159 | } |
5160 | |
5161 | if (!released) |
5162 | { |
5163 | // Complete the level. |
5164 | if (pLoadLock->CompleteLoadLevel(workLevel, success) && |
5165 | pLoadLock->GetLoadLevel()==FILE_LOAD_DELIVER_EVENTS) |
5166 | { |
5167 | lockHolder.Release(); |
5168 | released = TRUE; |
5169 | pFile->DeliverAsyncEvents(); |
5170 | }; |
5171 | } |
5172 | } |
5173 | EX_HOOK |
5174 | { |
5175 | Exception *pEx = GET_EXCEPTION(); |
5176 | |
5177 | |
5178 | //We will cache this error and wire this load to forever fail, |
5179 | // unless the exception is transient or the file is loaded OK but just cannot execute |
5180 | if (!pEx->IsTransient() && !pFile->IsLoaded()) |
5181 | { |
5182 | |
5183 | if (released) |
5184 | { |
5185 | // Reobtain lock to increment level. (Note that another thread may |
5186 | // have already done it which is OK. |
5187 | if (pLoadLock->Acquire(workLevel)) // note pLockHolder->Acquire isn't wired up to actually take the lock |
5188 | { |
5189 | // note lockHolder.Acquire isn't wired up to actually take the lock |
5190 | lockHolder = pLoadLock; |
5191 | released = FALSE; |
5192 | } |
5193 | } |
5194 | |
5195 | if (!released) |
5196 | { |
5197 | // Report the error in the lock |
5198 | pLoadLock->SetError(pEx); |
5199 | } |
5200 | |
5201 | if (!EEFileLoadException::CheckType(pEx)) |
5202 | EEFileLoadException::Throw(pFile->GetFile(), pEx->GetHR(), pEx); |
5203 | } |
5204 | |
5205 | // Otherwise, we simply abort this load, and can retry later on. |
5206 | // @todo cleanup: make sure that each level is restartable after an exception, and |
5207 | // leaves no bad side effects |
5208 | } |
5209 | EX_END_HOOK; |
5210 | } |
5211 | |
5212 | // Checks whether the module is valid to be in the given app domain (need not be yet loaded) |
5213 | CHECK AppDomain::CheckValidModule(Module * pModule) |
5214 | { |
5215 | CONTRACTL |
5216 | { |
5217 | THROWS; |
5218 | GC_TRIGGERS; |
5219 | MODE_ANY; |
5220 | } |
5221 | CONTRACTL_END; |
5222 | |
5223 | if (pModule->FindDomainFile(this) != NULL) |
5224 | CHECK_OK; |
5225 | |
5226 | CHECK_OK; |
5227 | } |
5228 | |
5229 | static void NormalizeAssemblySpecForNativeDependencies(AssemblySpec * pSpec) |
5230 | { |
5231 | CONTRACTL |
5232 | { |
5233 | THROWS; |
5234 | GC_NOTRIGGER; |
5235 | MODE_ANY; |
5236 | } |
5237 | CONTRACTL_END; |
5238 | |
5239 | if (pSpec->IsStrongNamed() && pSpec->HasPublicKey()) |
5240 | { |
5241 | pSpec->ConvertPublicKeyToToken(); |
5242 | } |
5243 | |
5244 | // |
5245 | // CoreCLR binder unifies assembly versions. Ignore assembly version here to |
5246 | // detect more types of potential mismatches. |
5247 | // |
5248 | AssemblyMetaDataInternal * pContext = pSpec->GetContext(); |
5249 | pContext->usMajorVersion = (USHORT)-1; |
5250 | pContext->usMinorVersion = (USHORT)-1; |
5251 | pContext->usBuildNumber = (USHORT)-1; |
5252 | pContext->usRevisionNumber = (USHORT)-1; |
5253 | |
5254 | // Ignore the WinRT type while considering if two assemblies have the same identity. |
5255 | pSpec->SetWindowsRuntimeType(NULL, NULL); |
5256 | } |
5257 | |
5258 | void AppDomain::CheckForMismatchedNativeImages(AssemblySpec * pSpec, const GUID * pGuid) |
5259 | { |
5260 | STANDARD_VM_CONTRACT; |
5261 | |
5262 | // |
5263 | // The native images are ever used only for trusted images in CoreCLR. |
5264 | // We don't wish to open the IL file at runtime so we just forgo any |
5265 | // eager consistency checking. But we still want to prevent mistmatched |
5266 | // NGen images from being used. We record all mappings between assembly |
5267 | // names and MVID, and fail once we detect mismatch. |
5268 | // |
5269 | NormalizeAssemblySpecForNativeDependencies(pSpec); |
5270 | |
5271 | CrstHolder ch(&m_DomainCrst); |
5272 | |
5273 | const NativeImageDependenciesEntry * pEntry = m_NativeImageDependencies.Lookup(pSpec); |
5274 | |
5275 | if (pEntry != NULL) |
5276 | { |
5277 | if (*pGuid != pEntry->m_guidMVID) |
5278 | { |
5279 | SString msg; |
5280 | msg.Printf("ERROR: Native images generated against multiple versions of assembly %s. " , pSpec->GetName()); |
5281 | WszOutputDebugString(msg.GetUnicode()); |
5282 | COMPlusThrowNonLocalized(kFileLoadException, msg.GetUnicode()); |
5283 | } |
5284 | } |
5285 | else |
5286 | { |
5287 | // |
5288 | // No entry yet - create one |
5289 | // |
5290 | NativeImageDependenciesEntry * pNewEntry = new NativeImageDependenciesEntry(); |
5291 | pNewEntry->m_AssemblySpec.CopyFrom(pSpec); |
5292 | pNewEntry->m_AssemblySpec.CloneFields(AssemblySpec::ALL_OWNED); |
5293 | pNewEntry->m_guidMVID = *pGuid; |
5294 | m_NativeImageDependencies.Add(pNewEntry); |
5295 | } |
5296 | } |
5297 | |
5298 | BOOL AppDomain::RemoveNativeImageDependency(AssemblySpec * pSpec) |
5299 | { |
5300 | CONTRACTL |
5301 | { |
5302 | GC_NOTRIGGER; |
5303 | PRECONDITION(CheckPointer(pSpec)); |
5304 | } |
5305 | CONTRACTL_END; |
5306 | |
5307 | BOOL result = FALSE; |
5308 | NormalizeAssemblySpecForNativeDependencies(pSpec); |
5309 | |
5310 | CrstHolder ch(&m_DomainCrst); |
5311 | |
5312 | const NativeImageDependenciesEntry * pEntry = m_NativeImageDependencies.Lookup(pSpec); |
5313 | |
5314 | if (pEntry != NULL) |
5315 | { |
5316 | m_NativeImageDependencies.Remove(pSpec); |
5317 | delete pEntry; |
5318 | result = TRUE; |
5319 | } |
5320 | |
5321 | return result; |
5322 | } |
5323 | |
5324 | void AppDomain::SetupSharedStatics() |
5325 | { |
5326 | CONTRACTL |
5327 | { |
5328 | THROWS; |
5329 | GC_TRIGGERS; |
5330 | MODE_ANY; |
5331 | INJECT_FAULT(COMPlusThrowOM();); |
5332 | } |
5333 | CONTRACTL_END; |
5334 | |
5335 | #ifndef CROSSGEN_COMPILE |
5336 | if (NingenEnabled()) |
5337 | return; |
5338 | |
5339 | LOG((LF_CLASSLOADER, LL_INFO10000, "STATICS: SetupSharedStatics()" )); |
5340 | |
5341 | // don't do any work in init stage. If not init only do work in non-shared case if are default domain |
5342 | _ASSERTE(!g_fEEInit); |
5343 | |
5344 | // Because we are allocating/referencing objects, need to be in cooperative mode |
5345 | GCX_COOP(); |
5346 | |
5347 | DomainLocalModule *pLocalModule = MscorlibBinder::GetModule()->GetDomainLocalModule(); |
5348 | |
5349 | // This is a convenient place to initialize String.Empty. |
5350 | // It is treated as intrinsic by the JIT as so the static constructor would never run. |
5351 | // Leaving it uninitialized would confuse debuggers. |
5352 | |
5353 | // String should not have any static constructors. |
5354 | _ASSERTE(g_pStringClass->IsClassPreInited()); |
5355 | |
5356 | FieldDesc * pEmptyStringFD = MscorlibBinder::GetField(FIELD__STRING__EMPTY); |
5357 | OBJECTREF* pEmptyStringHandle = (OBJECTREF*) |
5358 | ((TADDR)pLocalModule->GetPrecomputedGCStaticsBasePointer()+pEmptyStringFD->GetOffset()); |
5359 | SetObjectReference( pEmptyStringHandle, StringObject::GetEmptyString(), this ); |
5360 | #endif // CROSSGEN_COMPILE |
5361 | } |
5362 | |
5363 | DomainAssembly * AppDomain::FindAssembly(PEAssembly * pFile, FindAssemblyOptions options/* = FindAssemblyOptions_None*/) |
5364 | { |
5365 | CONTRACTL |
5366 | { |
5367 | THROWS; |
5368 | GC_TRIGGERS; |
5369 | MODE_ANY; |
5370 | INJECT_FAULT(COMPlusThrowOM();); |
5371 | } |
5372 | CONTRACTL_END; |
5373 | |
5374 | const bool includeFailedToLoad = (options & FindAssemblyOptions_IncludeFailedToLoad) != 0; |
5375 | |
5376 | if (pFile->HasHostAssembly()) |
5377 | { |
5378 | DomainAssembly * pDA = FindAssembly(pFile->GetHostAssembly()); |
5379 | if (pDA != nullptr && (pDA->IsLoaded() || (includeFailedToLoad && pDA->IsError()))) |
5380 | { |
5381 | return pDA; |
5382 | } |
5383 | return nullptr; |
5384 | } |
5385 | |
5386 | AssemblyIterator i = IterateAssembliesEx((AssemblyIterationFlags)( |
5387 | kIncludeLoaded | |
5388 | (includeFailedToLoad ? kIncludeFailedToLoad : 0) | |
5389 | kIncludeExecution)); |
5390 | CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly; |
5391 | |
5392 | while (i.Next(pDomainAssembly.This())) |
5393 | { |
5394 | PEFile * pManifestFile = pDomainAssembly->GetFile(); |
5395 | if (pManifestFile && |
5396 | !pManifestFile->IsResource() && |
5397 | pManifestFile->Equals(pFile)) |
5398 | { |
5399 | // Caller already has PEAssembly, so we can give DomainAssembly away freely without AddRef |
5400 | return pDomainAssembly.Extract(); |
5401 | } |
5402 | } |
5403 | return NULL; |
5404 | } |
5405 | |
5406 | static const AssemblyIterationFlags STANDARD_IJW_ITERATOR_FLAGS = |
5407 | (AssemblyIterationFlags)(kIncludeLoaded | kIncludeLoading | kIncludeExecution | kExcludeCollectible); |
5408 | |
5409 | |
5410 | void AppDomain::SetFriendlyName(LPCWSTR pwzFriendlyName, BOOL fDebuggerCares/*=TRUE*/) |
5411 | { |
5412 | CONTRACTL |
5413 | { |
5414 | THROWS; |
5415 | if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} |
5416 | MODE_ANY; |
5417 | INJECT_FAULT(COMPlusThrowOM();); |
5418 | } |
5419 | CONTRACTL_END; |
5420 | |
5421 | // Do all computations into a temporary until we're ensured of success |
5422 | SString tmpFriendlyName; |
5423 | |
5424 | |
5425 | if (pwzFriendlyName) |
5426 | tmpFriendlyName.Set(pwzFriendlyName); |
5427 | else |
5428 | { |
5429 | // If there is an assembly, try to get the name from it. |
5430 | // If no assembly, but if it's the DefaultDomain, then give it a name |
5431 | |
5432 | if (m_pRootAssembly) |
5433 | { |
5434 | tmpFriendlyName.SetUTF8(m_pRootAssembly->GetSimpleName()); |
5435 | |
5436 | SString::Iterator i = tmpFriendlyName.End(); |
5437 | if (tmpFriendlyName.FindBack(i, '.')) |
5438 | tmpFriendlyName.Truncate(i); |
5439 | } |
5440 | else |
5441 | { |
5442 | if (IsDefaultDomain()) |
5443 | tmpFriendlyName.Set(DEFAULT_DOMAIN_FRIENDLY_NAME); |
5444 | |
5445 | // This is for the profiler - if they call GetFriendlyName on an AppdomainCreateStarted |
5446 | // event, then we want to give them a temporary name they can use. |
5447 | else if (GetId().m_dwId != 0) |
5448 | { |
5449 | tmpFriendlyName.Clear(); |
5450 | tmpFriendlyName.Printf(W("%s %d" ), OTHER_DOMAIN_FRIENDLY_NAME_PREFIX, GetId().m_dwId); |
5451 | } |
5452 | } |
5453 | |
5454 | } |
5455 | |
5456 | tmpFriendlyName.Normalize(); |
5457 | |
5458 | |
5459 | m_friendlyName = tmpFriendlyName; |
5460 | m_friendlyName.Normalize(); |
5461 | |
5462 | if(g_pDebugInterface) |
5463 | { |
5464 | // update the name in the IPC publishing block |
5465 | if (SUCCEEDED(g_pDebugInterface->UpdateAppDomainEntryInIPC(this))) |
5466 | { |
5467 | // inform the attached debugger that the name of this appdomain has changed. |
5468 | if (IsDebuggerAttached() && fDebuggerCares) |
5469 | g_pDebugInterface->NameChangeEvent(this, NULL); |
5470 | } |
5471 | } |
5472 | } |
5473 | |
5474 | LPCWSTR AppDomain::GetFriendlyName(BOOL fDebuggerCares/*=TRUE*/) |
5475 | { |
5476 | CONTRACT (LPCWSTR) |
5477 | { |
5478 | THROWS; |
5479 | if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} |
5480 | MODE_ANY; |
5481 | POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); |
5482 | INJECT_FAULT(COMPlusThrowOM();); |
5483 | } |
5484 | CONTRACT_END; |
5485 | |
5486 | #if _DEBUG |
5487 | // Handle NULL this pointer - this happens sometimes when printing log messages |
5488 | // but in general shouldn't occur in real code |
5489 | if (this == NULL) |
5490 | RETURN NULL; |
5491 | #endif // _DEBUG |
5492 | |
5493 | if (m_friendlyName.IsEmpty()) |
5494 | SetFriendlyName(NULL, fDebuggerCares); |
5495 | |
5496 | RETURN m_friendlyName; |
5497 | } |
5498 | |
5499 | LPCWSTR AppDomain::GetFriendlyNameForLogging() |
5500 | { |
5501 | CONTRACT(LPCWSTR) |
5502 | { |
5503 | NOTHROW; |
5504 | GC_NOTRIGGER; |
5505 | MODE_ANY; |
5506 | POSTCONDITION(CheckPointer(RETVAL,NULL_OK)); |
5507 | } |
5508 | CONTRACT_END; |
5509 | #if _DEBUG |
5510 | // Handle NULL this pointer - this happens sometimes when printing log messages |
5511 | // but in general shouldn't occur in real code |
5512 | if (this == NULL) |
5513 | RETURN NULL; |
5514 | #endif // _DEBUG |
5515 | RETURN (m_friendlyName.IsEmpty() ?W("" ):(LPCWSTR)m_friendlyName); |
5516 | } |
5517 | |
5518 | LPCWSTR AppDomain::GetFriendlyNameForDebugger() |
5519 | { |
5520 | CONTRACT (LPCWSTR) |
5521 | { |
5522 | NOTHROW; |
5523 | if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} |
5524 | MODE_ANY; |
5525 | POSTCONDITION(CheckPointer(RETVAL)); |
5526 | } |
5527 | CONTRACT_END; |
5528 | |
5529 | |
5530 | if (m_friendlyName.IsEmpty()) |
5531 | { |
5532 | BOOL fSuccess = FALSE; |
5533 | |
5534 | EX_TRY |
5535 | { |
5536 | SetFriendlyName(NULL); |
5537 | |
5538 | fSuccess = TRUE; |
5539 | } |
5540 | EX_CATCH |
5541 | { |
5542 | // Gobble all exceptions. |
5543 | } |
5544 | EX_END_CATCH(SwallowAllExceptions); |
5545 | |
5546 | if (!fSuccess) |
5547 | { |
5548 | RETURN W("" ); |
5549 | } |
5550 | } |
5551 | |
5552 | RETURN m_friendlyName; |
5553 | } |
5554 | |
5555 | |
5556 | #endif // !DACCESS_COMPILE |
5557 | |
5558 | #ifdef DACCESS_COMPILE |
5559 | |
5560 | PVOID AppDomain::GetFriendlyNameNoSet(bool* isUtf8) |
5561 | { |
5562 | SUPPORTS_DAC; |
5563 | |
5564 | if (!m_friendlyName.IsEmpty()) |
5565 | { |
5566 | *isUtf8 = false; |
5567 | return m_friendlyName.DacGetRawContent(); |
5568 | } |
5569 | else if (m_pRootAssembly) |
5570 | { |
5571 | *isUtf8 = true; |
5572 | return (PVOID)m_pRootAssembly->GetSimpleName(); |
5573 | } |
5574 | else if (dac_cast<TADDR>(this) == |
5575 | dac_cast<TADDR>(SystemDomain::System()->DefaultDomain())) |
5576 | { |
5577 | *isUtf8 = false; |
5578 | return (PVOID)DEFAULT_DOMAIN_FRIENDLY_NAME; |
5579 | } |
5580 | else |
5581 | { |
5582 | return NULL; |
5583 | } |
5584 | } |
5585 | |
5586 | #endif // DACCESS_COMPILE |
5587 | |
5588 | #ifndef DACCESS_COMPILE |
5589 | |
5590 | BOOL AppDomain::AddFileToCache(AssemblySpec* pSpec, PEAssembly *pFile, BOOL fAllowFailure) |
5591 | { |
5592 | CONTRACTL |
5593 | { |
5594 | THROWS; |
5595 | GC_TRIGGERS; |
5596 | MODE_ANY; |
5597 | PRECONDITION(CheckPointer(pSpec)); |
5598 | // Hosted fusion binder makes an exception here, so we cannot assert. |
5599 | //PRECONDITION(pSpec->CanUseWithBindingCache()); |
5600 | //PRECONDITION(pFile->CanUseWithBindingCache()); |
5601 | INJECT_FAULT(COMPlusThrowOM();); |
5602 | } |
5603 | CONTRACTL_END; |
5604 | |
5605 | CrstHolder holder(&m_DomainCacheCrst); |
5606 | // !!! suppress exceptions |
5607 | if(!m_AssemblyCache.StoreFile(pSpec, pFile) && !fAllowFailure) |
5608 | { |
5609 | // TODO: Disabling the below assertion as currently we experience |
5610 | // inconsistency on resolving the Microsoft.Office.Interop.MSProject.dll |
5611 | // This causes below assertion to fire and crashes the VS. This issue |
5612 | // is being tracked with Dev10 Bug 658555. Brought back it when this bug |
5613 | // is fixed. |
5614 | // _ASSERTE(FALSE); |
5615 | |
5616 | EEFileLoadException::Throw(pSpec, FUSION_E_CACHEFILE_FAILED, NULL); |
5617 | } |
5618 | |
5619 | return TRUE; |
5620 | } |
5621 | |
5622 | BOOL AppDomain::AddAssemblyToCache(AssemblySpec* pSpec, DomainAssembly *pAssembly) |
5623 | { |
5624 | CONTRACTL |
5625 | { |
5626 | THROWS; |
5627 | GC_TRIGGERS; |
5628 | MODE_ANY; |
5629 | PRECONDITION(CheckPointer(pSpec)); |
5630 | PRECONDITION(CheckPointer(pAssembly)); |
5631 | PRECONDITION(pSpec->CanUseWithBindingCache()); |
5632 | PRECONDITION(pAssembly->CanUseWithBindingCache()); |
5633 | INJECT_FAULT(COMPlusThrowOM();); |
5634 | } |
5635 | CONTRACTL_END; |
5636 | |
5637 | CrstHolder holder(&m_DomainCacheCrst); |
5638 | // !!! suppress exceptions |
5639 | BOOL bRetVal = m_AssemblyCache.StoreAssembly(pSpec, pAssembly); |
5640 | return bRetVal; |
5641 | } |
5642 | |
5643 | BOOL AppDomain::AddExceptionToCache(AssemblySpec* pSpec, Exception *ex) |
5644 | { |
5645 | CONTRACTL |
5646 | { |
5647 | THROWS; |
5648 | GC_TRIGGERS; |
5649 | MODE_ANY; |
5650 | PRECONDITION(CheckPointer(pSpec)); |
5651 | PRECONDITION(pSpec->CanUseWithBindingCache()); |
5652 | INJECT_FAULT(COMPlusThrowOM();); |
5653 | } |
5654 | CONTRACTL_END; |
5655 | |
5656 | if (ex->IsTransient()) |
5657 | return TRUE; |
5658 | |
5659 | CrstHolder holder(&m_DomainCacheCrst); |
5660 | // !!! suppress exceptions |
5661 | return m_AssemblyCache.StoreException(pSpec, ex); |
5662 | } |
5663 | |
5664 | void AppDomain::AddUnmanagedImageToCache(LPCWSTR libraryName, HMODULE hMod) |
5665 | { |
5666 | CONTRACTL |
5667 | { |
5668 | THROWS; |
5669 | GC_TRIGGERS; |
5670 | MODE_ANY; |
5671 | PRECONDITION(CheckPointer(libraryName)); |
5672 | INJECT_FAULT(COMPlusThrowOM();); |
5673 | } |
5674 | CONTRACTL_END; |
5675 | if (libraryName) |
5676 | { |
5677 | AssemblySpec spec; |
5678 | spec.SetCodeBase(libraryName); |
5679 | m_UnmanagedCache.InsertEntry(&spec, hMod); |
5680 | } |
5681 | return ; |
5682 | } |
5683 | |
5684 | |
5685 | HMODULE AppDomain::FindUnmanagedImageInCache(LPCWSTR libraryName) |
5686 | { |
5687 | CONTRACT(HMODULE) |
5688 | { |
5689 | THROWS; |
5690 | GC_TRIGGERS; |
5691 | MODE_ANY; |
5692 | PRECONDITION(CheckPointer(libraryName,NULL_OK)); |
5693 | POSTCONDITION(CheckPointer(RETVAL,NULL_OK)); |
5694 | INJECT_FAULT(COMPlusThrowOM();); |
5695 | } |
5696 | CONTRACT_END; |
5697 | if(libraryName == NULL) RETURN NULL; |
5698 | |
5699 | AssemblySpec spec; |
5700 | spec.SetCodeBase(libraryName); |
5701 | RETURN (HMODULE) m_UnmanagedCache.LookupEntry(&spec, 0); |
5702 | } |
5703 | |
5704 | BOOL AppDomain::RemoveFileFromCache(PEAssembly *pFile) |
5705 | { |
5706 | CONTRACTL |
5707 | { |
5708 | GC_TRIGGERS; |
5709 | PRECONDITION(CheckPointer(pFile)); |
5710 | } |
5711 | CONTRACTL_END; |
5712 | |
5713 | LoadLockHolder lock(this); |
5714 | FileLoadLock *fileLock = (FileLoadLock *)lock->FindFileLock(pFile); |
5715 | |
5716 | if (fileLock == NULL) |
5717 | return FALSE; |
5718 | |
5719 | VERIFY(lock->Unlink(fileLock)); |
5720 | |
5721 | fileLock->Release(); |
5722 | |
5723 | return TRUE; |
5724 | } |
5725 | |
5726 | BOOL AppDomain::RemoveAssemblyFromCache(DomainAssembly* pAssembly) |
5727 | { |
5728 | CONTRACTL |
5729 | { |
5730 | THROWS; |
5731 | GC_TRIGGERS; |
5732 | MODE_ANY; |
5733 | PRECONDITION(CheckPointer(pAssembly)); |
5734 | INJECT_FAULT(COMPlusThrowOM();); |
5735 | } |
5736 | CONTRACTL_END; |
5737 | |
5738 | CrstHolder holder(&m_DomainCacheCrst); |
5739 | |
5740 | return m_AssemblyCache.RemoveAssembly(pAssembly); |
5741 | } |
5742 | |
5743 | BOOL AppDomain::IsCached(AssemblySpec *pSpec) |
5744 | { |
5745 | WRAPPER_NO_CONTRACT; |
5746 | |
5747 | // Check to see if this fits our rather loose idea of a reference to mscorlib. |
5748 | // If so, don't use fusion to bind it - do it ourselves. |
5749 | if (pSpec->IsMscorlib()) |
5750 | return TRUE; |
5751 | |
5752 | return m_AssemblyCache.Contains(pSpec); |
5753 | } |
5754 | |
5755 | void AppDomain::GetCacheAssemblyList(SetSHash<PTR_DomainAssembly>& assemblyList) |
5756 | { |
5757 | CrstHolder holder(&m_DomainCacheCrst); |
5758 | m_AssemblyCache.GetAllAssemblies(assemblyList); |
5759 | } |
5760 | |
5761 | PEAssembly* AppDomain::FindCachedFile(AssemblySpec* pSpec, BOOL fThrow /*=TRUE*/) |
5762 | { |
5763 | CONTRACTL |
5764 | { |
5765 | if (fThrow) { |
5766 | GC_TRIGGERS; |
5767 | THROWS; |
5768 | } |
5769 | else { |
5770 | GC_NOTRIGGER; |
5771 | NOTHROW; |
5772 | } |
5773 | MODE_ANY; |
5774 | } |
5775 | CONTRACTL_END; |
5776 | |
5777 | // Check to see if this fits our rather loose idea of a reference to mscorlib. |
5778 | // If so, don't use fusion to bind it - do it ourselves. |
5779 | if (fThrow && pSpec->IsMscorlib()) |
5780 | { |
5781 | CONSISTENCY_CHECK(SystemDomain::System()->SystemAssembly() != NULL); |
5782 | PEAssembly *pFile = SystemDomain::System()->SystemFile(); |
5783 | pFile->AddRef(); |
5784 | return pFile; |
5785 | } |
5786 | |
5787 | return m_AssemblyCache.LookupFile(pSpec, fThrow); |
5788 | } |
5789 | |
5790 | |
5791 | BOOL AppDomain::PostBindResolveAssembly(AssemblySpec *pPrePolicySpec, |
5792 | AssemblySpec *pPostPolicySpec, |
5793 | HRESULT hrBindResult, |
5794 | AssemblySpec **ppFailedSpec) |
5795 | { |
5796 | STATIC_CONTRACT_THROWS; |
5797 | STATIC_CONTRACT_GC_TRIGGERS; |
5798 | PRECONDITION(CheckPointer(pPrePolicySpec)); |
5799 | PRECONDITION(CheckPointer(pPostPolicySpec)); |
5800 | PRECONDITION(CheckPointer(ppFailedSpec)); |
5801 | |
5802 | BOOL fFailure = TRUE; |
5803 | *ppFailedSpec = pPrePolicySpec; |
5804 | |
5805 | |
5806 | PEAssemblyHolder result; |
5807 | |
5808 | if ((EEFileLoadException::GetFileLoadKind(hrBindResult) == kFileNotFoundException) || |
5809 | (hrBindResult == FUSION_E_REF_DEF_MISMATCH) || |
5810 | (hrBindResult == FUSION_E_INVALID_NAME)) |
5811 | { |
5812 | result = TryResolveAssembly(*ppFailedSpec); |
5813 | |
5814 | if (result != NULL && pPrePolicySpec->CanUseWithBindingCache() && result->CanUseWithBindingCache()) |
5815 | { |
5816 | fFailure = FALSE; |
5817 | |
5818 | // Given the post-policy resolve event construction of the CLR binder, |
5819 | // chained managed resolve events can race with each other, therefore we do allow |
5820 | // the adding of the result to fail. Checking for already chached specs |
5821 | // is not an option as it would introduce another race window. |
5822 | // The binder does a re-fetch of the |
5823 | // original binding spec and therefore will not cause inconsistency here. |
5824 | // For the purposes of the resolve event, failure to add to the cache still is a success. |
5825 | AddFileToCache(pPrePolicySpec, result, TRUE /* fAllowFailure */); |
5826 | if (*ppFailedSpec != pPrePolicySpec && pPostPolicySpec->CanUseWithBindingCache()) |
5827 | { |
5828 | AddFileToCache(pPostPolicySpec, result, TRUE /* fAllowFailure */ ); |
5829 | } |
5830 | } |
5831 | } |
5832 | |
5833 | return fFailure; |
5834 | } |
5835 | |
5836 | //---------------------------------------------------------------------------------------- |
5837 | // Helper class for hosted binder |
5838 | |
5839 | class PEAssemblyAsPrivAssemblyInfo : public IUnknownCommon<ICLRPrivAssemblyInfo> |
5840 | { |
5841 | public: |
5842 | //------------------------------------------------------------------------------------ |
5843 | // Ctor |
5844 | |
5845 | PEAssemblyAsPrivAssemblyInfo(PEAssembly *pPEAssembly) |
5846 | { |
5847 | LIMITED_METHOD_CONTRACT; |
5848 | STATIC_CONTRACT_THROWS; |
5849 | |
5850 | if (pPEAssembly == nullptr) |
5851 | ThrowHR(E_UNEXPECTED); |
5852 | |
5853 | pPEAssembly->AddRef(); |
5854 | m_pPEAssembly = pPEAssembly; |
5855 | } |
5856 | |
5857 | //------------------------------------------------------------------------------------ |
5858 | // ICLRPrivAssemblyInfo methods |
5859 | |
5860 | //------------------------------------------------------------------------------------ |
5861 | STDMETHOD(GetAssemblyName)( |
5862 | __in DWORD cchBuffer, |
5863 | __out_opt LPDWORD pcchBuffer, |
5864 | __out_ecount_part_opt(cchBuffer, *pcchBuffer) LPWSTR wzBuffer) |
5865 | { |
5866 | CONTRACTL |
5867 | { |
5868 | NOTHROW; |
5869 | GC_TRIGGERS; |
5870 | MODE_ANY; |
5871 | } |
5872 | CONTRACTL_END; |
5873 | |
5874 | HRESULT hr = S_OK; |
5875 | |
5876 | if ((cchBuffer == 0) != (wzBuffer == nullptr)) |
5877 | { |
5878 | return E_INVALIDARG; |
5879 | } |
5880 | |
5881 | LPCUTF8 szName = m_pPEAssembly->GetSimpleName(); |
5882 | |
5883 | bool bIsAscii; |
5884 | DWORD cchName; |
5885 | IfFailRet(FString::Utf8_Unicode_Length(szName, &bIsAscii, &cchName)); |
5886 | |
5887 | if (cchBuffer < cchName + 1) |
5888 | { |
5889 | if (pcchBuffer != nullptr) |
5890 | { |
5891 | *pcchBuffer = cchName + 1; |
5892 | } |
5893 | return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); |
5894 | } |
5895 | else |
5896 | { |
5897 | IfFailRet(FString::Utf8_Unicode(szName, bIsAscii, wzBuffer, cchName)); |
5898 | if (pcchBuffer != nullptr) |
5899 | { |
5900 | *pcchBuffer = cchName; |
5901 | } |
5902 | return S_OK; |
5903 | } |
5904 | } |
5905 | |
5906 | //------------------------------------------------------------------------------------ |
5907 | STDMETHOD(GetAssemblyVersion)( |
5908 | USHORT *pMajor, |
5909 | USHORT *pMinor, |
5910 | USHORT *pBuild, |
5911 | USHORT *pRevision) |
5912 | { |
5913 | WRAPPER_NO_CONTRACT; |
5914 | return m_pPEAssembly->GetVersion(pMajor, pMinor, pBuild, pRevision); |
5915 | } |
5916 | |
5917 | //------------------------------------------------------------------------------------ |
5918 | STDMETHOD(GetAssemblyPublicKey)( |
5919 | DWORD cbBuffer, |
5920 | LPDWORD pcbBuffer, |
5921 | BYTE *pbBuffer) |
5922 | { |
5923 | STATIC_CONTRACT_LIMITED_METHOD; |
5924 | STATIC_CONTRACT_CAN_TAKE_LOCK; |
5925 | |
5926 | VALIDATE_PTR_RET(pcbBuffer); |
5927 | VALIDATE_CONDITION((pbBuffer == nullptr) == (cbBuffer == 0), return E_INVALIDARG); |
5928 | |
5929 | HRESULT hr = S_OK; |
5930 | |
5931 | EX_TRY |
5932 | { |
5933 | // Note: PEAssembly::GetPublicKey will return bogus data pointer when *pcbBuffer == 0 |
5934 | LPCVOID pbKey = m_pPEAssembly->GetPublicKey(pcbBuffer); |
5935 | |
5936 | if (*pcbBuffer != 0) |
5937 | { |
5938 | if (pbBuffer != nullptr && cbBuffer >= *pcbBuffer) |
5939 | { |
5940 | memcpy(pbBuffer, pbKey, *pcbBuffer); |
5941 | hr = S_OK; |
5942 | } |
5943 | else |
5944 | { |
5945 | hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); |
5946 | } |
5947 | } |
5948 | else |
5949 | { |
5950 | hr = S_FALSE; // ==> No public key |
5951 | } |
5952 | } |
5953 | EX_CATCH_HRESULT(hr); |
5954 | |
5955 | return hr; |
5956 | } |
5957 | |
5958 | private: |
5959 | ReleaseHolder<PEAssembly> m_pPEAssembly; |
5960 | }; |
5961 | |
5962 | //----------------------------------------------------------------------------------------------------------------- |
5963 | HRESULT AppDomain::BindAssemblySpecForHostedBinder( |
5964 | AssemblySpec * pSpec, |
5965 | IAssemblyName * pAssemblyName, |
5966 | ICLRPrivBinder * pBinder, |
5967 | PEAssembly ** ppAssembly) |
5968 | { |
5969 | STANDARD_VM_CONTRACT; |
5970 | |
5971 | PRECONDITION(CheckPointer(pSpec)); |
5972 | PRECONDITION(pSpec->GetAppDomain() == this); |
5973 | PRECONDITION(CheckPointer(ppAssembly)); |
5974 | PRECONDITION(pSpec->GetCodeBase() == nullptr); |
5975 | |
5976 | HRESULT hr = S_OK; |
5977 | |
5978 | |
5979 | // The Fusion binder can throw (to preserve compat, since it will actually perform an assembly |
5980 | // load as part of it's bind), so we need to be careful here to catch any FileNotFoundException |
5981 | // objects if fThrowIfNotFound is false. |
5982 | ReleaseHolder<ICLRPrivAssembly> pPrivAssembly; |
5983 | |
5984 | // We return HRESULTs here on failure instead of throwing as failures here are not necessarily indicative |
5985 | // of an actual application problem. Returning an error code is substantially faster than throwing, and |
5986 | // should be used when possible. |
5987 | IfFailRet(pBinder->BindAssemblyByName(pAssemblyName, &pPrivAssembly)); |
5988 | |
5989 | IfFailRet(BindHostedPrivAssembly(nullptr, pPrivAssembly, pAssemblyName, ppAssembly)); |
5990 | |
5991 | |
5992 | return S_OK; |
5993 | } |
5994 | |
5995 | //----------------------------------------------------------------------------------------------------------------- |
5996 | HRESULT |
5997 | AppDomain::BindHostedPrivAssembly( |
5998 | PEAssembly * pParentAssembly, |
5999 | ICLRPrivAssembly * pPrivAssembly, |
6000 | IAssemblyName * pAssemblyName, |
6001 | PEAssembly ** ppAssembly) |
6002 | { |
6003 | STANDARD_VM_CONTRACT; |
6004 | |
6005 | PRECONDITION(CheckPointer(pPrivAssembly)); |
6006 | PRECONDITION(CheckPointer(ppAssembly)); |
6007 | |
6008 | HRESULT hr = S_OK; |
6009 | |
6010 | *ppAssembly = nullptr; |
6011 | |
6012 | // See if result has been previously loaded. |
6013 | { |
6014 | DomainAssembly* pDomainAssembly = FindAssembly(pPrivAssembly); |
6015 | if (pDomainAssembly != nullptr) |
6016 | { |
6017 | *ppAssembly = clr::SafeAddRef(pDomainAssembly->GetFile()); |
6018 | } |
6019 | } |
6020 | |
6021 | if (*ppAssembly != nullptr) |
6022 | { // Already exists: return the assembly. |
6023 | return S_OK; |
6024 | } |
6025 | |
6026 | // Get the IL PEFile. |
6027 | PEImageHolder pPEImageIL; |
6028 | { |
6029 | // Does not already exist, so get the resource for the assembly and load it. |
6030 | DWORD dwImageType; |
6031 | ReleaseHolder<ICLRPrivResource> pIResourceIL; |
6032 | |
6033 | IfFailRet(pPrivAssembly->GetImageResource(ASSEMBLY_IMAGE_TYPE_IL, &dwImageType, &pIResourceIL)); |
6034 | _ASSERTE(dwImageType == ASSEMBLY_IMAGE_TYPE_IL); |
6035 | |
6036 | pPEImageIL = PEImage::OpenImage(pIResourceIL, MDInternalImport_Default); |
6037 | } |
6038 | |
6039 | // See if an NI is available. |
6040 | DWORD dwAvailableImages; |
6041 | IfFailRet(pPrivAssembly->GetAvailableImageTypes(&dwAvailableImages)); |
6042 | _ASSERTE(dwAvailableImages & ASSEMBLY_IMAGE_TYPE_IL); // Just double checking that IL bit is always set. |
6043 | |
6044 | // Get the NI PEFile if available. |
6045 | PEImageHolder pPEImageNI; |
6046 | if (dwAvailableImages & ASSEMBLY_IMAGE_TYPE_NATIVE) |
6047 | { |
6048 | DWORD dwImageType; |
6049 | ReleaseHolder<ICLRPrivResource> pIResourceNI; |
6050 | |
6051 | IfFailRet(pPrivAssembly->GetImageResource(ASSEMBLY_IMAGE_TYPE_NATIVE, &dwImageType, &pIResourceNI)); |
6052 | _ASSERTE(dwImageType == ASSEMBLY_IMAGE_TYPE_NATIVE || FAILED(hr)); |
6053 | |
6054 | pPEImageNI = PEImage::OpenImage(pIResourceNI, MDInternalImport_TrustedNativeImage); |
6055 | } |
6056 | _ASSERTE(pPEImageIL != nullptr); |
6057 | |
6058 | // Create a PEAssembly using the IL and NI images. |
6059 | PEAssemblyHolder pPEAssembly = PEAssembly::Open(pParentAssembly, pPEImageIL, pPEImageNI, pPrivAssembly); |
6060 | |
6061 | // The result. |
6062 | *ppAssembly = pPEAssembly.Extract(); |
6063 | |
6064 | return S_OK; |
6065 | } // AppDomain::BindHostedPrivAssembly |
6066 | |
6067 | //--------------------------------------------------------------------------------------------------------------------- |
6068 | PEAssembly * AppDomain::BindAssemblySpec( |
6069 | AssemblySpec * pSpec, |
6070 | BOOL fThrowOnFileNotFound, |
6071 | BOOL fUseHostBinderIfAvailable) |
6072 | { |
6073 | STATIC_CONTRACT_THROWS; |
6074 | STATIC_CONTRACT_GC_TRIGGERS; |
6075 | PRECONDITION(CheckPointer(pSpec)); |
6076 | PRECONDITION(pSpec->GetAppDomain() == this); |
6077 | PRECONDITION(this==::GetAppDomain()); |
6078 | |
6079 | GCX_PREEMP(); |
6080 | |
6081 | BOOL fForceReThrow = FALSE; |
6082 | |
6083 | #if defined(FEATURE_COMINTEROP) |
6084 | // Handle WinRT assemblies in the classic/hybrid scenario. If this is an AppX process, |
6085 | // then this case will be handled by the previous block as part of the full set of |
6086 | // available binding hosts. |
6087 | if (pSpec->IsContentType_WindowsRuntime()) |
6088 | { |
6089 | HRESULT hr = S_OK; |
6090 | |
6091 | // Get the assembly display name. |
6092 | ReleaseHolder<IAssemblyName> pAssemblyName; |
6093 | |
6094 | IfFailThrow(pSpec->CreateFusionName(&pAssemblyName, TRUE, TRUE)); |
6095 | |
6096 | |
6097 | PEAssemblyHolder pAssembly; |
6098 | |
6099 | EX_TRY |
6100 | { |
6101 | hr = BindAssemblySpecForHostedBinder(pSpec, pAssemblyName, m_pWinRtBinder, &pAssembly); |
6102 | if (FAILED(hr)) |
6103 | goto EndTry2; // Goto end of try block. |
6104 | EndTry2:; |
6105 | } |
6106 | // The combination of this conditional catch/ the following if statement which will throw reduces the count of exceptions |
6107 | // thrown in scenarios where the exception does not escape the method. We cannot get rid of the try/catch block, as |
6108 | // there are cases within some of the clrpriv binder's which throw. |
6109 | // Note: In theory, FileNotFound should always come here as HRESULT, never as exception. |
6110 | EX_CATCH_HRESULT_IF(hr, |
6111 | !fThrowOnFileNotFound && Assembly::FileNotFound(hr)) |
6112 | |
6113 | if (FAILED(hr) && (fThrowOnFileNotFound || !Assembly::FileNotFound(hr))) |
6114 | { |
6115 | if (Assembly::FileNotFound(hr)) |
6116 | { |
6117 | _ASSERTE(fThrowOnFileNotFound); |
6118 | // Uses defaultScope |
6119 | EEFileLoadException::Throw(pSpec, hr); |
6120 | } |
6121 | |
6122 | // WinRT type bind failures |
6123 | _ASSERTE(pSpec->IsContentType_WindowsRuntime()); |
6124 | if (hr == HRESULT_FROM_WIN32(APPMODEL_ERROR_NO_PACKAGE)) // Returned by RoResolveNamespace when using 3rd party WinRT types in classic process |
6125 | { |
6126 | if (fThrowOnFileNotFound) |
6127 | { // Throw NotSupportedException (with custom message) wrapped by TypeLoadException to give user type name for diagnostics |
6128 | // Note: TypeLoadException is equivalent of FileNotFound in WinRT world |
6129 | EEMessageException ex(kNotSupportedException, IDS_EE_WINRT_THIRDPARTY_NOTSUPPORTED); |
6130 | EX_THROW_WITH_INNER(EETypeLoadException, (pSpec->GetWinRtTypeNamespace(), pSpec->GetWinRtTypeClassName(), nullptr, nullptr, IDS_EE_WINRT_LOADFAILURE), &ex); |
6131 | } |
6132 | } |
6133 | else if ((hr == CLR_E_BIND_UNRECOGNIZED_IDENTITY_FORMAT) || // Returned e.g. for WinRT type name without namespace |
6134 | (hr == COR_E_PLATFORMNOTSUPPORTED)) // Using WinRT on pre-Win8 OS |
6135 | { |
6136 | if (fThrowOnFileNotFound) |
6137 | { // Throw ArgumentException/PlatformNotSupportedException wrapped by TypeLoadException to give user type name for diagnostics |
6138 | // Note: TypeLoadException is equivalent of FileNotFound in WinRT world |
6139 | EEMessageException ex(hr); |
6140 | EX_THROW_WITH_INNER(EETypeLoadException, (pSpec->GetWinRtTypeNamespace(), pSpec->GetWinRtTypeClassName(), nullptr, nullptr, IDS_EE_WINRT_LOADFAILURE), &ex); |
6141 | } |
6142 | } |
6143 | else |
6144 | { |
6145 | IfFailThrow(hr); |
6146 | } |
6147 | } |
6148 | _ASSERTE((FAILED(hr) && !fThrowOnFileNotFound) || pAssembly != nullptr); |
6149 | |
6150 | return pAssembly.Extract(); |
6151 | } |
6152 | else |
6153 | #endif // FEATURE_COMINTEROP |
6154 | if (pSpec->HasUniqueIdentity()) |
6155 | { |
6156 | HRESULT hrBindResult = S_OK; |
6157 | PEAssemblyHolder result; |
6158 | |
6159 | |
6160 | EX_TRY |
6161 | { |
6162 | if (!IsCached(pSpec)) |
6163 | { |
6164 | |
6165 | { |
6166 | bool fAddFileToCache = false; |
6167 | |
6168 | // Use CoreClr's fusion alternative |
6169 | CoreBindResult bindResult; |
6170 | |
6171 | pSpec->Bind(this, fThrowOnFileNotFound, &bindResult, FALSE /* fNgenExplicitBind */, FALSE /* fExplicitBindToNativeImage */); |
6172 | hrBindResult = bindResult.GetHRBindResult(); |
6173 | |
6174 | if (bindResult.Found()) |
6175 | { |
6176 | if (SystemDomain::SystemFile() && bindResult.IsMscorlib()) |
6177 | { |
6178 | // Avoid rebinding to another copy of mscorlib |
6179 | result = SystemDomain::SystemFile(); |
6180 | result.SuppressRelease(); // Didn't get a refcount |
6181 | } |
6182 | else |
6183 | { |
6184 | // IsSystem on the PEFile should be false, even for mscorlib satellites |
6185 | result = PEAssembly::Open(&bindResult, |
6186 | FALSE); |
6187 | } |
6188 | fAddFileToCache = true; |
6189 | |
6190 | // Setup the reference to the binder, which performed the bind, into the AssemblySpec |
6191 | ICLRPrivBinder* pBinder = result->GetBindingContext(); |
6192 | _ASSERTE(pBinder != NULL); |
6193 | pSpec->SetBindingContext(pBinder); |
6194 | } |
6195 | |
6196 | |
6197 | if (fAddFileToCache) |
6198 | { |
6199 | |
6200 | |
6201 | if (pSpec->CanUseWithBindingCache() && result->CanUseWithBindingCache()) |
6202 | { |
6203 | // Failure to add simply means someone else beat us to it. In that case |
6204 | // the FindCachedFile call below (after catch block) will update result |
6205 | // to the cached value. |
6206 | AddFileToCache(pSpec, result, TRUE /*fAllowFailure*/); |
6207 | } |
6208 | } |
6209 | else |
6210 | { |
6211 | _ASSERTE(fThrowOnFileNotFound == FALSE); |
6212 | |
6213 | // Don't trigger the resolve event for the CoreLib satellite assembly. A misbehaving resolve event may |
6214 | // return an assembly that does not match, and this can cause recursive resource lookups during error |
6215 | // reporting. The CoreLib satellite assembly is loaded from relative locations based on the culture, see |
6216 | // AssemblySpec::Bind(). |
6217 | if (!pSpec->IsMscorlibSatellite()) |
6218 | { |
6219 | // Trigger the resolve event also for non-throw situation. |
6220 | // However, this code path will behave as if the resolve handler has thrown, |
6221 | // that is, not trigger an MDA. |
6222 | |
6223 | AssemblySpec NewSpec(this); |
6224 | AssemblySpec *pFailedSpec = NULL; |
6225 | |
6226 | fForceReThrow = TRUE; // Managed resolve event handler can throw |
6227 | |
6228 | // Purposly ignore return value |
6229 | PostBindResolveAssembly(pSpec, &NewSpec, hrBindResult, &pFailedSpec); |
6230 | } |
6231 | } |
6232 | } |
6233 | } |
6234 | } |
6235 | EX_CATCH |
6236 | { |
6237 | Exception *ex = GET_EXCEPTION(); |
6238 | |
6239 | AssemblySpec NewSpec(this); |
6240 | AssemblySpec *pFailedSpec = NULL; |
6241 | |
6242 | // Let transient exceptions or managed resolve event handler exceptions propagate |
6243 | if (ex->IsTransient() || fForceReThrow) |
6244 | { |
6245 | EX_RETHROW; |
6246 | } |
6247 | |
6248 | { |
6249 | // This is not executed for SO exceptions so we need to disable the backout |
6250 | // stack validation to prevent false violations from being reported. |
6251 | DISABLE_BACKOUT_STACK_VALIDATION; |
6252 | |
6253 | BOOL fFailure = PostBindResolveAssembly(pSpec, &NewSpec, ex->GetHR(), &pFailedSpec); |
6254 | if (fFailure) |
6255 | { |
6256 | BOOL bFileNotFoundException = |
6257 | (EEFileLoadException::GetFileLoadKind(ex->GetHR()) == kFileNotFoundException); |
6258 | |
6259 | if (!bFileNotFoundException) |
6260 | { |
6261 | fFailure = AddExceptionToCache(pFailedSpec, ex); |
6262 | } // else, fFailure stays TRUE |
6263 | // Effectively, fFailure == bFileNotFoundException || AddExceptionToCache(pFailedSpec, ex) |
6264 | |
6265 | // Only throw this exception if we are the first in the cache |
6266 | if (fFailure) |
6267 | { |
6268 | // |
6269 | // If the BindingFailure MDA is enabled, trigger one for this failure |
6270 | // Note: TryResolveAssembly() can also throw if an AssemblyResolve event subscriber throws |
6271 | // and the MDA isn't sent in this case (or for transient failure cases) |
6272 | // |
6273 | #ifdef MDA_SUPPORTED |
6274 | MdaBindingFailure* pProbe = MDA_GET_ASSISTANT(BindingFailure); |
6275 | if (pProbe) |
6276 | { |
6277 | // Transition to cooperative GC mode before using any OBJECTREFs. |
6278 | GCX_COOP(); |
6279 | |
6280 | OBJECTREF exceptionObj = GET_THROWABLE(); |
6281 | GCPROTECT_BEGIN(exceptionObj) |
6282 | { |
6283 | pProbe->BindFailed(pFailedSpec, &exceptionObj); |
6284 | } |
6285 | GCPROTECT_END(); |
6286 | } |
6287 | #endif |
6288 | |
6289 | // In the same cases as for the MDA, store the failure information for DAC to read |
6290 | if (IsDebuggerAttached()) { |
6291 | FailedAssembly *pFailed = new FailedAssembly(); |
6292 | pFailed->Initialize(pFailedSpec, ex); |
6293 | IfFailThrow(m_failedAssemblies.Append(pFailed)); |
6294 | } |
6295 | |
6296 | if (!bFileNotFoundException || fThrowOnFileNotFound) |
6297 | { |
6298 | |
6299 | // V1.1 App-compatibility workaround. See VSW530166 if you want to whine about it. |
6300 | // |
6301 | // In Everett, if we failed to download an assembly because of a broken network cable, |
6302 | // we returned a FileNotFoundException with a COR_E_FILENOTFOUND hr embedded inside |
6303 | // (which would be exposed when marshaled to native.) |
6304 | // |
6305 | // In Whidbey, we now set the more appropriate INET_E_RESOURCE_NOT_FOUND hr. But |
6306 | // the online/offline switch code in VSTO for Everett hardcoded a check for |
6307 | // COR_E_FILENOTFOUND. |
6308 | // |
6309 | // So now, to keep that code from breaking, we have to remap INET_E_RESOURCE_NOT_FOUND |
6310 | // back to COR_E_FILENOTFOUND. We're doing it here rather down in Fusion so as to affect |
6311 | // the least number of callers. |
6312 | |
6313 | if (ex->GetHR() == INET_E_RESOURCE_NOT_FOUND) |
6314 | { |
6315 | EEFileLoadException::Throw(pFailedSpec, COR_E_FILENOTFOUND, ex); |
6316 | } |
6317 | |
6318 | if (EEFileLoadException::CheckType(ex)) |
6319 | { |
6320 | if (pFailedSpec == pSpec) |
6321 | { |
6322 | EX_RETHROW; //preserve the information |
6323 | } |
6324 | else |
6325 | { |
6326 | StackSString exceptionDisplayName, failedSpecDisplayName; |
6327 | |
6328 | ((EEFileLoadException*)ex)->GetName(exceptionDisplayName); |
6329 | pFailedSpec->GetFileOrDisplayName(0, failedSpecDisplayName); |
6330 | |
6331 | if (exceptionDisplayName.CompareCaseInsensitive(failedSpecDisplayName) == 0) |
6332 | { |
6333 | EX_RETHROW; // Throw the original exception. Otherwise, we'd throw an exception that contains the same message twice. |
6334 | } |
6335 | } |
6336 | } |
6337 | |
6338 | EEFileLoadException::Throw(pFailedSpec, ex->GetHR(), ex); |
6339 | } |
6340 | |
6341 | } |
6342 | } |
6343 | } |
6344 | } |
6345 | EX_END_CATCH(RethrowTerminalExceptions); |
6346 | |
6347 | // Now, if it's a cacheable bind we need to re-fetch the result from the cache, as we may have been racing with another |
6348 | // thread to store our result. Note that we may throw from here, if there is a cached exception. |
6349 | // This will release the refcount of the current result holder (if any), and will replace |
6350 | // it with a non-addref'ed result |
6351 | if (pSpec->CanUseWithBindingCache() && (result== NULL || result->CanUseWithBindingCache())) |
6352 | { |
6353 | result = FindCachedFile(pSpec); |
6354 | |
6355 | if (result != NULL) |
6356 | result->AddRef(); |
6357 | } |
6358 | |
6359 | return result.Extract(); |
6360 | } |
6361 | else |
6362 | { |
6363 | // Unsupported content type |
6364 | if (fThrowOnFileNotFound) |
6365 | { |
6366 | ThrowHR(COR_E_BADIMAGEFORMAT); |
6367 | } |
6368 | return nullptr; |
6369 | } |
6370 | } // AppDomain::BindAssemblySpec |
6371 | |
6372 | |
6373 | |
6374 | PEAssembly *AppDomain::TryResolveAssembly(AssemblySpec *pSpec) |
6375 | { |
6376 | STATIC_CONTRACT_THROWS; |
6377 | STATIC_CONTRACT_GC_TRIGGERS; |
6378 | STATIC_CONTRACT_MODE_ANY; |
6379 | |
6380 | PEAssembly *result = NULL; |
6381 | |
6382 | EX_TRY |
6383 | { |
6384 | result = pSpec->ResolveAssemblyFile(this); |
6385 | } |
6386 | EX_HOOK |
6387 | { |
6388 | Exception *pEx = GET_EXCEPTION(); |
6389 | |
6390 | if (!pEx->IsTransient()) |
6391 | { |
6392 | AddExceptionToCache(pSpec, pEx); |
6393 | if (!EEFileLoadException::CheckType(pEx)) |
6394 | EEFileLoadException::Throw(pSpec, pEx->GetHR(), pEx); |
6395 | } |
6396 | } |
6397 | EX_END_HOOK; |
6398 | |
6399 | return result; |
6400 | } |
6401 | |
6402 | |
6403 | ULONG AppDomain::AddRef() |
6404 | { |
6405 | LIMITED_METHOD_CONTRACT; |
6406 | return InterlockedIncrement(&m_cRef); |
6407 | } |
6408 | |
6409 | ULONG AppDomain::Release() |
6410 | { |
6411 | CONTRACTL |
6412 | { |
6413 | NOTHROW; |
6414 | GC_TRIGGERS; |
6415 | MODE_ANY; |
6416 | PRECONDITION(m_cRef > 0); |
6417 | } |
6418 | CONTRACTL_END; |
6419 | |
6420 | ULONG cRef = InterlockedDecrement(&m_cRef); |
6421 | if (!cRef) |
6422 | { |
6423 | _ASSERTE (m_Stage == STAGE_CREATING); |
6424 | ADID adid=GetId(); |
6425 | delete this; |
6426 | TESTHOOKCALL(AppDomainDestroyed(adid.m_dwId)); |
6427 | } |
6428 | return (cRef); |
6429 | } |
6430 | |
6431 | |
6432 | #ifndef CROSSGEN_COMPILE |
6433 | |
6434 | void AppDomain::RaiseLoadingAssemblyEvent(DomainAssembly *pAssembly) |
6435 | { |
6436 | CONTRACTL |
6437 | { |
6438 | NOTHROW; |
6439 | GC_TRIGGERS; |
6440 | PRECONDITION(this == GetAppDomain()); |
6441 | MODE_ANY; |
6442 | } |
6443 | CONTRACTL_END; |
6444 | |
6445 | if (pAssembly->GetFile()->IsSystem()) |
6446 | { |
6447 | return; |
6448 | } |
6449 | |
6450 | GCX_COOP(); |
6451 | FAULT_NOT_FATAL(); |
6452 | OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED); |
6453 | |
6454 | EX_TRY |
6455 | { |
6456 | if (MscorlibBinder::GetField(FIELD__ASSEMBLYLOADCONTEXT__ASSEMBLY_LOAD)->GetStaticOBJECTREF() != NULL) |
6457 | { |
6458 | struct _gc { |
6459 | OBJECTREF orThis; |
6460 | } gc; |
6461 | ZeroMemory(&gc, sizeof(gc)); |
6462 | |
6463 | ARG_SLOT args[1]; |
6464 | GCPROTECT_BEGIN(gc); |
6465 | |
6466 | gc.orThis = pAssembly->GetExposedAssemblyObject(); |
6467 | |
6468 | MethodDescCallSite onAssemblyLoad(METHOD__ASSEMBLYLOADCONTEXT__ON_ASSEMBLY_LOAD); |
6469 | |
6470 | // GetExposedAssemblyObject may cause a gc, so call this before filling args[0] |
6471 | args[0] = ObjToArgSlot(gc.orThis); |
6472 | |
6473 | onAssemblyLoad.Call(args); |
6474 | |
6475 | GCPROTECT_END(); |
6476 | } |
6477 | } |
6478 | EX_CATCH |
6479 | { |
6480 | } |
6481 | EX_END_CATCH(SwallowAllExceptions); |
6482 | } |
6483 | |
6484 | BOOL AppDomain::OnUnhandledException(OBJECTREF *pThrowable, BOOL isTerminating/*=TRUE*/) |
6485 | { |
6486 | STATIC_CONTRACT_NOTHROW; |
6487 | STATIC_CONTRACT_GC_TRIGGERS; |
6488 | STATIC_CONTRACT_MODE_ANY; |
6489 | |
6490 | BOOL retVal = FALSE; |
6491 | |
6492 | GCX_COOP(); |
6493 | |
6494 | EX_TRY |
6495 | { |
6496 | retVal = GetAppDomain()->RaiseUnhandledExceptionEvent(pThrowable, isTerminating); |
6497 | } |
6498 | EX_CATCH |
6499 | { |
6500 | } |
6501 | EX_END_CATCH(SwallowAllExceptions) // Swallow any errors. |
6502 | |
6503 | return retVal; |
6504 | } |
6505 | |
6506 | static LONG s_ProcessedExitProcessEventCount = 0; |
6507 | |
6508 | LONG GetProcessedExitProcessEventCount() |
6509 | { |
6510 | LIMITED_METHOD_CONTRACT; |
6511 | return s_ProcessedExitProcessEventCount; |
6512 | } |
6513 | |
6514 | void AppDomain::RaiseExitProcessEvent() |
6515 | { |
6516 | if (!g_fEEStarted) |
6517 | return; |
6518 | |
6519 | STATIC_CONTRACT_MODE_COOPERATIVE; |
6520 | STATIC_CONTRACT_THROWS; |
6521 | STATIC_CONTRACT_GC_TRIGGERS; |
6522 | |
6523 | // Only finalizer thread during shutdown can call this function. |
6524 | _ASSERTE ((g_fEEShutDown&ShutDown_Finalize1) && GetThread() == FinalizerThread::GetFinalizerThread()); |
6525 | |
6526 | _ASSERTE (GetThread()->PreemptiveGCDisabled()); |
6527 | |
6528 | MethodDescCallSite onProcessExit(METHOD__APPCONTEXT__ON_PROCESS_EXIT); |
6529 | onProcessExit.Call(NULL); |
6530 | } |
6531 | |
6532 | BOOL |
6533 | AppDomain::RaiseUnhandledExceptionEvent(OBJECTREF *pThrowable, BOOL isTerminating) |
6534 | { |
6535 | CONTRACTL |
6536 | { |
6537 | THROWS; |
6538 | GC_TRIGGERS; |
6539 | MODE_COOPERATIVE; |
6540 | INJECT_FAULT(COMPlusThrowOM();); |
6541 | } |
6542 | CONTRACTL_END; |
6543 | |
6544 | _ASSERTE(pThrowable != NULL && IsProtectedByGCFrame(pThrowable)); |
6545 | |
6546 | _ASSERTE(this == GetThread()->GetDomain()); |
6547 | |
6548 | OBJECTREF orDelegate = MscorlibBinder::GetField(FIELD__APPCONTEXT__UNHANDLED_EXCEPTION)->GetStaticOBJECTREF(); |
6549 | if (orDelegate == NULL) |
6550 | return FALSE; |
6551 | |
6552 | struct _gc { |
6553 | OBJECTREF Delegate; |
6554 | OBJECTREF Sender; |
6555 | } gc; |
6556 | ZeroMemory(&gc, sizeof(gc)); |
6557 | |
6558 | GCPROTECT_BEGIN(gc); |
6559 | gc.Delegate = orDelegate; |
6560 | if (orDelegate != NULL) |
6561 | { |
6562 | DistributeUnhandledExceptionReliably(&gc.Delegate, &gc.Sender, pThrowable, isTerminating); |
6563 | } |
6564 | GCPROTECT_END(); |
6565 | return TRUE; |
6566 | } |
6567 | |
6568 | #endif // CROSSGEN_COMPILE |
6569 | |
6570 | IUnknown *AppDomain::CreateFusionContext() |
6571 | { |
6572 | CONTRACT(IUnknown *) |
6573 | { |
6574 | GC_TRIGGERS; |
6575 | THROWS; |
6576 | MODE_ANY; |
6577 | POSTCONDITION(CheckPointer(RETVAL)); |
6578 | INJECT_FAULT(COMPlusThrowOM();); |
6579 | } |
6580 | CONTRACT_END; |
6581 | |
6582 | if (!m_pFusionContext) |
6583 | { |
6584 | ETWOnStartup (FusionAppCtx_V1, FusionAppCtxEnd_V1); |
6585 | CLRPrivBinderCoreCLR *pTPABinder = NULL; |
6586 | |
6587 | GCX_PREEMP(); |
6588 | |
6589 | // Initialize the assembly binder for the default context loads for CoreCLR. |
6590 | IfFailThrow(CCoreCLRBinderHelper::DefaultBinderSetupContext(GetId().m_dwId, &pTPABinder)); |
6591 | m_pFusionContext = reinterpret_cast<IUnknown *>(pTPABinder); |
6592 | |
6593 | // By default, initial binding context setup for CoreCLR is also the TPABinding context |
6594 | (m_pTPABinderContext = pTPABinder)->AddRef(); |
6595 | |
6596 | } |
6597 | |
6598 | RETURN m_pFusionContext; |
6599 | } |
6600 | |
6601 | |
6602 | |
6603 | //--------------------------------------------------------------------------------------- |
6604 | // |
6605 | // AppDomain::IsDebuggerAttached - is a debugger attached to this process |
6606 | // |
6607 | // Arguments: |
6608 | // None |
6609 | // |
6610 | // Return Value: |
6611 | // TRUE if a debugger is attached to this process, FALSE otherwise. |
6612 | // |
6613 | // Notes: |
6614 | // This is identical to CORDebuggerAttached. This exists idependantly for legacy reasons - we used to |
6615 | // support attaching to individual AppDomains. This should probably go away eventually. |
6616 | // |
6617 | |
6618 | BOOL AppDomain::IsDebuggerAttached() |
6619 | { |
6620 | LIMITED_METHOD_CONTRACT; |
6621 | |
6622 | if (CORDebuggerAttached()) |
6623 | { |
6624 | return TRUE; |
6625 | } |
6626 | else |
6627 | { |
6628 | return FALSE; |
6629 | } |
6630 | } |
6631 | |
6632 | #ifdef DEBUGGING_SUPPORTED |
6633 | |
6634 | // This is called from the debugger to request notification events from |
6635 | // Assemblies, Modules, Types in this appdomain. |
6636 | BOOL AppDomain::NotifyDebuggerLoad(int flags, BOOL attaching) |
6637 | { |
6638 | WRAPPER_NO_CONTRACT; |
6639 | BOOL result = FALSE; |
6640 | |
6641 | if (!attaching && !IsDebuggerAttached()) |
6642 | return FALSE; |
6643 | |
6644 | AssemblyIterator i; |
6645 | |
6646 | // Attach to our assemblies |
6647 | LOG((LF_CORDB, LL_INFO100, "AD::NDA: Iterating assemblies\n" )); |
6648 | i = IterateAssembliesEx((AssemblyIterationFlags)(kIncludeLoaded | kIncludeLoading | kIncludeExecution)); |
6649 | CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly; |
6650 | while (i.Next(pDomainAssembly.This())) |
6651 | { |
6652 | result = (pDomainAssembly->NotifyDebuggerLoad(flags, attaching) || |
6653 | result); |
6654 | } |
6655 | |
6656 | return result; |
6657 | } |
6658 | |
6659 | void AppDomain::NotifyDebuggerUnload() |
6660 | { |
6661 | WRAPPER_NO_CONTRACT; |
6662 | if (!IsDebuggerAttached()) |
6663 | return; |
6664 | |
6665 | LOG((LF_CORDB, LL_INFO10, "AD::NDD domain [%d] %#08x %ls\n" , |
6666 | GetId().m_dwId, this, GetFriendlyNameForLogging())); |
6667 | |
6668 | LOG((LF_CORDB, LL_INFO100, "AD::NDD: Interating domain bound assemblies\n" )); |
6669 | AssemblyIterator i = IterateAssembliesEx((AssemblyIterationFlags)(kIncludeLoaded | kIncludeLoading | kIncludeExecution)); |
6670 | CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly; |
6671 | |
6672 | // Detach from our assemblies |
6673 | while (i.Next(pDomainAssembly.This())) |
6674 | { |
6675 | LOG((LF_CORDB, LL_INFO100, "AD::NDD: Iterating assemblies\n" )); |
6676 | pDomainAssembly->NotifyDebuggerUnload(); |
6677 | } |
6678 | } |
6679 | #endif // DEBUGGING_SUPPORTED |
6680 | |
6681 | void AppDomain::SetSystemAssemblyLoadEventSent(BOOL fFlag) |
6682 | { |
6683 | LIMITED_METHOD_CONTRACT; |
6684 | if (fFlag == TRUE) |
6685 | m_dwFlags |= LOAD_SYSTEM_ASSEMBLY_EVENT_SENT; |
6686 | else |
6687 | m_dwFlags &= ~LOAD_SYSTEM_ASSEMBLY_EVENT_SENT; |
6688 | } |
6689 | |
6690 | BOOL AppDomain::WasSystemAssemblyLoadEventSent(void) |
6691 | { |
6692 | LIMITED_METHOD_CONTRACT; |
6693 | return ((m_dwFlags & LOAD_SYSTEM_ASSEMBLY_EVENT_SENT) == 0) ? FALSE : TRUE; |
6694 | } |
6695 | |
6696 | #ifndef CROSSGEN_COMPILE |
6697 | |
6698 | #ifdef FEATURE_COMINTEROP |
6699 | |
6700 | RCWRefCache *AppDomain::GetRCWRefCache() |
6701 | { |
6702 | CONTRACT(RCWRefCache*) |
6703 | { |
6704 | THROWS; |
6705 | GC_NOTRIGGER; |
6706 | MODE_ANY; |
6707 | POSTCONDITION(CheckPointer(RETVAL)); |
6708 | } |
6709 | CONTRACT_END; |
6710 | |
6711 | if (!m_pRCWRefCache) { |
6712 | NewHolder<RCWRefCache> pRCWRefCache = new RCWRefCache(this); |
6713 | if (FastInterlockCompareExchangePointer(&m_pRCWRefCache, (RCWRefCache *)pRCWRefCache, NULL) == NULL) |
6714 | { |
6715 | pRCWRefCache.SuppressRelease(); |
6716 | } |
6717 | } |
6718 | RETURN m_pRCWRefCache; |
6719 | } |
6720 | |
6721 | RCWCache *AppDomain::CreateRCWCache() |
6722 | { |
6723 | CONTRACT(RCWCache*) |
6724 | { |
6725 | THROWS; |
6726 | GC_TRIGGERS; |
6727 | MODE_ANY; |
6728 | INJECT_FAULT(COMPlusThrowOM();); |
6729 | POSTCONDITION(CheckPointer(RETVAL)); |
6730 | } |
6731 | CONTRACT_END; |
6732 | |
6733 | // Initialize the global RCW cleanup list here as well. This is so that it |
6734 | // it guaranteed to exist if any RCW's are created, but it is not created |
6735 | // unconditionally. |
6736 | if (!g_pRCWCleanupList) |
6737 | { |
6738 | SystemDomain::LockHolder lh; |
6739 | |
6740 | if (!g_pRCWCleanupList) |
6741 | g_pRCWCleanupList = new RCWCleanupList(); |
6742 | } |
6743 | _ASSERTE(g_pRCWCleanupList); |
6744 | |
6745 | { |
6746 | BaseDomain::LockHolder lh(this); |
6747 | |
6748 | if (!m_pRCWCache) |
6749 | m_pRCWCache = new RCWCache(this); |
6750 | } |
6751 | |
6752 | RETURN m_pRCWCache; |
6753 | } |
6754 | |
6755 | void AppDomain::ReleaseRCWs(LPVOID pCtxCookie) |
6756 | { |
6757 | WRAPPER_NO_CONTRACT; |
6758 | if (m_pRCWCache) |
6759 | m_pRCWCache->ReleaseWrappersWorker(pCtxCookie); |
6760 | |
6761 | RemoveWinRTFactoryObjects(pCtxCookie); |
6762 | } |
6763 | |
6764 | void AppDomain::DetachRCWs() |
6765 | { |
6766 | WRAPPER_NO_CONTRACT; |
6767 | if (m_pRCWCache) |
6768 | m_pRCWCache->DetachWrappersWorker(); |
6769 | } |
6770 | |
6771 | #endif // FEATURE_COMINTEROP |
6772 | |
6773 | void AppDomain::ExceptionUnwind(Frame *pFrame) |
6774 | { |
6775 | CONTRACTL |
6776 | { |
6777 | DISABLED(GC_TRIGGERS); // EEResourceException |
6778 | DISABLED(THROWS); // EEResourceException |
6779 | MODE_ANY; |
6780 | } |
6781 | CONTRACTL_END; |
6782 | |
6783 | LOG((LF_APPDOMAIN, LL_INFO10, "AppDomain::ExceptionUnwind for %8.8x\n" , pFrame)); |
6784 | #if _DEBUG_ADUNLOAD |
6785 | printf("%x AppDomain::ExceptionUnwind for %8.8p\n" , GetThread()->GetThreadId(), pFrame); |
6786 | #endif |
6787 | Thread *pThread = GetThread(); |
6788 | _ASSERTE(pThread); |
6789 | |
6790 | LOG((LF_APPDOMAIN, LL_INFO10, "AppDomain::ExceptionUnwind: not first transition or abort\n" )); |
6791 | } |
6792 | |
6793 | BOOL AppDomain::StopEEAndUnwindThreads(unsigned int retryCount, BOOL *pFMarkUnloadRequestThread) |
6794 | { |
6795 | CONTRACTL |
6796 | { |
6797 | THROWS; |
6798 | GC_TRIGGERS; |
6799 | MODE_ANY; |
6800 | SO_INTOLERANT; |
6801 | } |
6802 | CONTRACTL_END; |
6803 | |
6804 | Thread *pThread = NULL; |
6805 | DWORD nThreadsNeedMoreWork=0; |
6806 | if (retryCount != (unsigned int)-1 && retryCount < g_pConfig->AppDomainUnloadRetryCount()) |
6807 | { |
6808 | Thread *pCurThread = GetThread(); |
6809 | if (pCurThread->CatchAtSafePoint()) |
6810 | pCurThread->PulseGCMode(); |
6811 | |
6812 | m_dwThreadsStillInAppDomain=nThreadsNeedMoreWork; |
6813 | return !nThreadsNeedMoreWork; |
6814 | } |
6815 | |
6816 | // For now piggyback on the GC's suspend EE mechanism |
6817 | ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_APPDOMAIN_SHUTDOWN); |
6818 | #ifdef _DEBUG |
6819 | // <TODO>@todo: what to do with any threads that didn't stop?</TODO> |
6820 | _ASSERTE(ThreadStore::s_pThreadStore->DbgBackgroundThreadCount() > 0); |
6821 | #endif // _DEBUG |
6822 | |
6823 | int totalADCount = 0; |
6824 | int finalizerADCount = 0; |
6825 | pThread = NULL; |
6826 | |
6827 | RuntimeExceptionKind reKind = kLastException; |
6828 | UINT resId = 0; |
6829 | SmallStackSString ssThreadId; |
6830 | |
6831 | while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL) |
6832 | { |
6833 | // we already checked that we're not running in the unload domain |
6834 | if (pThread == GetThread()) |
6835 | { |
6836 | continue; |
6837 | } |
6838 | |
6839 | #ifdef _DEBUG |
6840 | void PrintStackTraceWithADToLog(Thread *pThread); |
6841 | if (LoggingOn(LF_APPDOMAIN, LL_INFO100)) { |
6842 | LOG((LF_APPDOMAIN, LL_INFO100, "\nStackTrace for %x\n" , pThread->GetThreadId())); |
6843 | PrintStackTraceWithADToLog(pThread); |
6844 | } |
6845 | #endif // _DEBUG |
6846 | int count = 0; |
6847 | Frame *pFrame = pThread->GetFirstTransitionInto(this, &count); |
6848 | if (! pFrame) { |
6849 | _ASSERTE(count == 0); |
6850 | continue; |
6851 | } |
6852 | |
6853 | if (pThread != FinalizerThread::GetFinalizerThread()) |
6854 | { |
6855 | totalADCount += count; |
6856 | nThreadsNeedMoreWork++; |
6857 | } |
6858 | else |
6859 | { |
6860 | finalizerADCount = count; |
6861 | } |
6862 | |
6863 | // don't setup the exception info for the unloading thread unless it's the last one in |
6864 | if (retryCount != ((unsigned int) -1) && retryCount > g_pConfig->AppDomainUnloadRetryCount() && reKind == kLastException) |
6865 | { |
6866 | #ifdef AD_BREAK_ON_CANNOT_UNLOAD |
6867 | static int breakOnCannotUnload = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ADBreakOnCannotUnload); |
6868 | if (breakOnCannotUnload) |
6869 | _ASSERTE(!"Cannot unload AD" ); |
6870 | #endif // AD_BREAK_ON_CANNOT_UNLOAD |
6871 | reKind = kCannotUnloadAppDomainException; |
6872 | resId = IDS_EE_ADUNLOAD_CANT_UNWIND_THREAD; |
6873 | ssThreadId.Printf(W("%x" ), pThread->GetThreadId()); |
6874 | STRESS_LOG2(LF_APPDOMAIN, LL_INFO10, "AppDomain::UnwindThreads cannot stop thread %x with %d transitions\n" , pThread->GetThreadId(), count); |
6875 | // don't break out of this early or the assert totalADCount == (int)m_dwThreadEnterCount below will fire |
6876 | // it's better to chew a little extra time here and make sure our counts are consistent |
6877 | } |
6878 | // only abort the thread requesting the unload if it's the last one in, that way it will get |
6879 | // notification that the unload failed for some other thread not being aborted. And don't abort |
6880 | // the finalizer thread - let it finish it's work as it's allowed to be in there. If it won't finish, |
6881 | // then we will eventually get a CannotUnloadException on it. |
6882 | |
6883 | if (pThread != FinalizerThread::GetFinalizerThread()) |
6884 | { |
6885 | |
6886 | STRESS_LOG2(LF_APPDOMAIN, LL_INFO100, "AppDomain::UnwindThreads stopping %x with %d transitions\n" , pThread->GetThreadId(), count); |
6887 | LOG((LF_APPDOMAIN, LL_INFO100, "AppDomain::UnwindThreads stopping %x with %d transitions\n" , pThread->GetThreadId(), count)); |
6888 | #if _DEBUG_ADUNLOAD |
6889 | printf("AppDomain::UnwindThreads %x stopping %x with first frame %8.8p\n" , GetThread()->GetThreadId(), pThread->GetThreadId(), pFrame); |
6890 | #endif |
6891 | pThread->SetAbortRequest(EEPolicy::TA_V1Compatible); |
6892 | } |
6893 | TESTHOOKCALL(UnwindingThreads(GetId().m_dwId)) ; |
6894 | } |
6895 | _ASSERTE(totalADCount + finalizerADCount == (int)m_dwThreadEnterCount); |
6896 | |
6897 | //@TODO: This is intended to catch a stress bug. Remove when no longer needed. |
6898 | if (totalADCount + finalizerADCount != (int)m_dwThreadEnterCount) |
6899 | FreeBuildDebugBreak(); |
6900 | |
6901 | // if our count did get messed up, set it to whatever count we actually found in the domain to avoid looping |
6902 | // or other problems related to incorrect count. This is very much a bug if this happens - a thread should always |
6903 | // exit the domain gracefully. |
6904 | // m_dwThreadEnterCount = totalADCount; |
6905 | |
6906 | // CommonTripThread will handle the abort for any threads that we've marked |
6907 | ThreadSuspend::RestartEE(FALSE, TRUE); |
6908 | if (reKind != kLastException) |
6909 | COMPlusThrow(reKind, resId, ssThreadId.GetUnicode()); |
6910 | |
6911 | _ASSERTE((totalADCount==0 && nThreadsNeedMoreWork==0) ||(totalADCount!=0 && nThreadsNeedMoreWork!=0)); |
6912 | |
6913 | m_dwThreadsStillInAppDomain=nThreadsNeedMoreWork; |
6914 | return (totalADCount == 0); |
6915 | } |
6916 | |
6917 | void AppDomain::UnwindThreads() |
6918 | { |
6919 | // This function should guarantee appdomain |
6920 | // consistency even if it fails. Everything that is going |
6921 | // to make the appdomain impossible to reenter |
6922 | // should be factored out |
6923 | |
6924 | // <TODO>@todo: need real synchronization here!!!</TODO> |
6925 | CONTRACTL |
6926 | { |
6927 | MODE_COOPERATIVE; |
6928 | THROWS; |
6929 | GC_TRIGGERS; |
6930 | } |
6931 | CONTRACTL_END; |
6932 | |
6933 | int retryCount = -1; |
6934 | m_dwThreadsStillInAppDomain=(ULONG)-1; |
6935 | ULONGLONG startTime = CLRGetTickCount64(); |
6936 | |
6937 | // Force threads to go through slow path during AD unload. |
6938 | TSSuspendHolder shTrap; |
6939 | |
6940 | BOOL fMarkUnloadRequestThread = TRUE; |
6941 | |
6942 | // now wait for all the threads running in our AD to get out |
6943 | do |
6944 | { |
6945 | DWORD timeout = GetEEPolicy()->GetTimeout(OPR_AppDomainUnload); |
6946 | EPolicyAction action = GetEEPolicy()->GetActionOnTimeout(OPR_AppDomainUnload, NULL); |
6947 | if (timeout != INFINITE && action >= eExitProcess) { |
6948 | // Escalation policy specified. |
6949 | ULONGLONG curTime = CLRGetTickCount64(); |
6950 | ULONGLONG elapseTime = curTime - startTime; |
6951 | if (elapseTime > timeout) |
6952 | { |
6953 | // Escalate |
6954 | switch (action) |
6955 | { |
6956 | case eExitProcess: |
6957 | case eFastExitProcess: |
6958 | case eRudeExitProcess: |
6959 | case eDisableRuntime: |
6960 | GetEEPolicy()->NotifyHostOnTimeout(OPR_AppDomainUnload, action); |
6961 | EEPolicy::HandleExitProcessFromEscalation(action, HOST_E_EXITPROCESS_TIMEOUT); |
6962 | _ASSERTE (!"Should not reach here" ); |
6963 | break; |
6964 | default: |
6965 | break; |
6966 | } |
6967 | } |
6968 | } |
6969 | #ifdef _DEBUG |
6970 | if (LoggingOn(LF_APPDOMAIN, LL_INFO100)) |
6971 | DumpADThreadTrack(); |
6972 | #endif // _DEBUG |
6973 | if (StopEEAndUnwindThreads(retryCount, &fMarkUnloadRequestThread)) |
6974 | break; |
6975 | if (timeout != INFINITE) |
6976 | { |
6977 | // Turn off the timeout used by AD. |
6978 | retryCount = 1; |
6979 | } |
6980 | else |
6981 | { |
6982 | // GCStress takes a long time to unwind, due to expensive creation of |
6983 | // a threadabort exception. |
6984 | if (!GCStress<cfg_any>::IsEnabled()) |
6985 | ++retryCount; |
6986 | LOG((LF_APPDOMAIN, LL_INFO10, "AppDomain::UnwindThreads iteration %d waiting on thread count %d\n" , retryCount, m_dwThreadEnterCount)); |
6987 | #if _DEBUG_ADUNLOAD |
6988 | printf("AppDomain::UnwindThreads iteration %d waiting on thread count %d\n" , retryCount, m_dwThreadEnterCount); |
6989 | #endif |
6990 | } |
6991 | |
6992 | if (m_dwThreadEnterCount != 0) |
6993 | { |
6994 | #ifdef _DEBUG |
6995 | GetThread()->UserSleep(20); |
6996 | #else // !_DEBUG |
6997 | GetThread()->UserSleep(10); |
6998 | #endif // !_DEBUG |
6999 | } |
7000 | } |
7001 | while (TRUE) ; |
7002 | } |
7003 | |
7004 | #ifdef _DEBUG |
7005 | |
7006 | void AppDomain::TrackADThreadEnter(Thread *pThread, Frame *pFrame) |
7007 | { |
7008 | CONTRACTL |
7009 | { |
7010 | NOTHROW; |
7011 | GC_NOTRIGGER; |
7012 | // REENTRANT |
7013 | PRECONDITION(CheckPointer(pThread)); |
7014 | PRECONDITION(pFrame != (Frame*)(size_t) INVALID_POINTER_CD); |
7015 | } |
7016 | CONTRACTL_END; |
7017 | |
7018 | while (FastInterlockCompareExchange((LONG*)&m_TrackSpinLock, 1, 0) != 0) |
7019 | ; |
7020 | if (m_pThreadTrackInfoList == NULL) |
7021 | m_pThreadTrackInfoList = new (nothrow) ThreadTrackInfoList; |
7022 | // If we don't assert here, we will AV in the for loop below |
7023 | _ASSERTE(m_pThreadTrackInfoList); |
7024 | |
7025 | ThreadTrackInfoList *pTrackList= m_pThreadTrackInfoList; |
7026 | |
7027 | ThreadTrackInfo *pTrack = NULL; |
7028 | int i; |
7029 | for (i=0; i < pTrackList->Count(); i++) { |
7030 | if ((*(pTrackList->Get(i)))->pThread == pThread) { |
7031 | pTrack = *(pTrackList->Get(i)); |
7032 | break; |
7033 | } |
7034 | } |
7035 | if (! pTrack) { |
7036 | pTrack = new (nothrow) ThreadTrackInfo; |
7037 | // If we don't assert here, we will AV in the for loop below. |
7038 | _ASSERTE(pTrack); |
7039 | pTrack->pThread = pThread; |
7040 | ThreadTrackInfo **pSlot = pTrackList->Append(); |
7041 | *pSlot = pTrack; |
7042 | } |
7043 | |
7044 | InterlockedIncrement((LONG*)&m_dwThreadEnterCount); |
7045 | Frame **pSlot; |
7046 | if (pTrack) |
7047 | { |
7048 | pSlot = pTrack->frameStack.Insert(0); |
7049 | *pSlot = pFrame; |
7050 | } |
7051 | int totThreads = 0; |
7052 | for (i=0; i < pTrackList->Count(); i++) |
7053 | totThreads += (*(pTrackList->Get(i)))->frameStack.Count(); |
7054 | _ASSERTE(totThreads == (int)m_dwThreadEnterCount); |
7055 | |
7056 | InterlockedExchange((LONG*)&m_TrackSpinLock, 0); |
7057 | } |
7058 | |
7059 | |
7060 | void AppDomain::TrackADThreadExit(Thread *pThread, Frame *pFrame) |
7061 | { |
7062 | CONTRACTL |
7063 | { |
7064 | if (GetThread()) {MODE_COOPERATIVE;} |
7065 | NOTHROW; |
7066 | GC_NOTRIGGER; |
7067 | } |
7068 | CONTRACTL_END; |
7069 | |
7070 | while (FastInterlockCompareExchange((LONG*)&m_TrackSpinLock, 1, 0) != 0) |
7071 | ; |
7072 | ThreadTrackInfoList *pTrackList= m_pThreadTrackInfoList; |
7073 | _ASSERTE(pTrackList); |
7074 | ThreadTrackInfo *pTrack = NULL; |
7075 | int i; |
7076 | for (i=0; i < pTrackList->Count(); i++) |
7077 | { |
7078 | if ((*(pTrackList->Get(i)))->pThread == pThread) |
7079 | { |
7080 | pTrack = *(pTrackList->Get(i)); |
7081 | break; |
7082 | } |
7083 | } |
7084 | _ASSERTE(pTrack); |
7085 | _ASSERTE(*(pTrack->frameStack.Get(0)) == pFrame); |
7086 | pTrack->frameStack.Delete(0); |
7087 | InterlockedDecrement((LONG*)&m_dwThreadEnterCount); |
7088 | |
7089 | int totThreads = 0; |
7090 | for (i=0; i < pTrackList->Count(); i++) |
7091 | totThreads += (*(pTrackList->Get(i)))->frameStack.Count(); |
7092 | _ASSERTE(totThreads == (int)m_dwThreadEnterCount); |
7093 | |
7094 | InterlockedExchange((LONG*)&m_TrackSpinLock, 0); |
7095 | } |
7096 | |
7097 | void AppDomain::DumpADThreadTrack() |
7098 | { |
7099 | CONTRACTL |
7100 | { |
7101 | NOTHROW; |
7102 | GC_NOTRIGGER; |
7103 | MODE_COOPERATIVE; |
7104 | } |
7105 | CONTRACTL_END; |
7106 | |
7107 | while (FastInterlockCompareExchange((LONG*)&m_TrackSpinLock, 1, 0) != 0) |
7108 | ; |
7109 | ThreadTrackInfoList *pTrackList= m_pThreadTrackInfoList; |
7110 | if (!pTrackList) |
7111 | goto end; |
7112 | |
7113 | { |
7114 | LOG((LF_APPDOMAIN, LL_INFO10000, "\nThread dump of %d threads for [%d] %#08x %S\n" , |
7115 | m_dwThreadEnterCount, GetId().m_dwId, this, GetFriendlyNameForLogging())); |
7116 | int totThreads = 0; |
7117 | for (int i=0; i < pTrackList->Count(); i++) |
7118 | { |
7119 | ThreadTrackInfo *pTrack = *(pTrackList->Get(i)); |
7120 | if (pTrack->frameStack.Count()==0) |
7121 | continue; |
7122 | LOG((LF_APPDOMAIN, LL_INFO100, " ADEnterCount for %x is %d\n" , pTrack->pThread->GetThreadId(), pTrack->frameStack.Count())); |
7123 | totThreads += pTrack->frameStack.Count(); |
7124 | for (int j=0; j < pTrack->frameStack.Count(); j++) |
7125 | LOG((LF_APPDOMAIN, LL_INFO100, " frame %8.8x\n" , *(pTrack->frameStack.Get(j)))); |
7126 | } |
7127 | _ASSERTE(totThreads == (int)m_dwThreadEnterCount); |
7128 | } |
7129 | end: |
7130 | InterlockedExchange((LONG*)&m_TrackSpinLock, 0); |
7131 | } |
7132 | #endif // _DEBUG |
7133 | |
7134 | #endif // CROSSGEN_COMPILE |
7135 | |
7136 | #endif // !DACCESS_COMPILE |
7137 | |
7138 | DWORD DomainLocalModule::GetClassFlags(MethodTable* pMT, DWORD iClassIndex /*=(DWORD)-1*/) |
7139 | { |
7140 | CONTRACTL { |
7141 | NOTHROW; |
7142 | GC_NOTRIGGER; |
7143 | SO_TOLERANT; |
7144 | } CONTRACTL_END; |
7145 | |
7146 | { // SO tolerance exception for debug-only assertion. |
7147 | CONTRACT_VIOLATION(SOToleranceViolation); |
7148 | CONSISTENCY_CHECK(GetDomainFile()->GetModule() == pMT->GetModuleForStatics()); |
7149 | } |
7150 | |
7151 | if (pMT->IsDynamicStatics()) |
7152 | { |
7153 | _ASSERTE(!pMT->ContainsGenericVariables()); |
7154 | DWORD dynamicClassID = pMT->GetModuleDynamicEntryID(); |
7155 | if(m_aDynamicEntries <= dynamicClassID) |
7156 | return FALSE; |
7157 | return (m_pDynamicClassTable[dynamicClassID].m_dwFlags); |
7158 | } |
7159 | else |
7160 | { |
7161 | if (iClassIndex == (DWORD)-1) |
7162 | iClassIndex = pMT->GetClassIndex(); |
7163 | return GetPrecomputedStaticsClassData()[iClassIndex]; |
7164 | } |
7165 | } |
7166 | |
7167 | #ifndef DACCESS_COMPILE |
7168 | |
7169 | void DomainLocalModule::SetClassInitialized(MethodTable* pMT) |
7170 | { |
7171 | CONTRACTL |
7172 | { |
7173 | THROWS; |
7174 | GC_TRIGGERS; |
7175 | MODE_ANY; |
7176 | } |
7177 | CONTRACTL_END; |
7178 | |
7179 | BaseDomain::DomainLocalBlockLockHolder lh(GetDomainFile()->GetAppDomain()); |
7180 | |
7181 | _ASSERTE(!IsClassInitialized(pMT)); |
7182 | _ASSERTE(!IsClassInitError(pMT)); |
7183 | |
7184 | SetClassFlags(pMT, ClassInitFlags::INITIALIZED_FLAG); |
7185 | } |
7186 | |
7187 | void DomainLocalModule::SetClassInitError(MethodTable* pMT) |
7188 | { |
7189 | WRAPPER_NO_CONTRACT; |
7190 | |
7191 | BaseDomain::DomainLocalBlockLockHolder lh(GetDomainFile()->GetAppDomain()); |
7192 | |
7193 | SetClassFlags(pMT, ClassInitFlags::ERROR_FLAG); |
7194 | } |
7195 | |
7196 | void DomainLocalModule::SetClassFlags(MethodTable* pMT, DWORD dwFlags) |
7197 | { |
7198 | CONTRACTL { |
7199 | THROWS; |
7200 | GC_TRIGGERS; |
7201 | PRECONDITION(GetDomainFile()->GetModule() == pMT->GetModuleForStatics()); |
7202 | // Assumes BaseDomain::DomainLocalBlockLockHolder is taken |
7203 | PRECONDITION(GetDomainFile()->GetAppDomain()->OwnDomainLocalBlockLock()); |
7204 | } CONTRACTL_END; |
7205 | |
7206 | if (pMT->IsDynamicStatics()) |
7207 | { |
7208 | _ASSERTE(!pMT->ContainsGenericVariables()); |
7209 | DWORD dwID = pMT->GetModuleDynamicEntryID(); |
7210 | EnsureDynamicClassIndex(dwID); |
7211 | m_pDynamicClassTable[dwID].m_dwFlags |= dwFlags; |
7212 | } |
7213 | else |
7214 | { |
7215 | GetPrecomputedStaticsClassData()[pMT->GetClassIndex()] |= dwFlags; |
7216 | } |
7217 | } |
7218 | |
7219 | void DomainLocalModule::EnsureDynamicClassIndex(DWORD dwID) |
7220 | { |
7221 | CONTRACTL |
7222 | { |
7223 | THROWS; |
7224 | GC_TRIGGERS; |
7225 | MODE_ANY; |
7226 | INJECT_FAULT(COMPlusThrowOM();); |
7227 | // Assumes BaseDomain::DomainLocalBlockLockHolder is taken |
7228 | PRECONDITION(GetDomainFile()->GetAppDomain()->OwnDomainLocalBlockLock()); |
7229 | } |
7230 | CONTRACTL_END; |
7231 | |
7232 | if (dwID < m_aDynamicEntries) |
7233 | { |
7234 | _ASSERTE(m_pDynamicClassTable.Load() != NULL); |
7235 | return; |
7236 | } |
7237 | |
7238 | SIZE_T aDynamicEntries = max(16, m_aDynamicEntries.Load()); |
7239 | while (aDynamicEntries <= dwID) |
7240 | { |
7241 | aDynamicEntries *= 2; |
7242 | } |
7243 | |
7244 | DynamicClassInfo* pNewDynamicClassTable; |
7245 | pNewDynamicClassTable = (DynamicClassInfo*) |
7246 | (void*)GetDomainFile()->GetLoaderAllocator()->GetHighFrequencyHeap()->AllocMem( |
7247 | S_SIZE_T(sizeof(DynamicClassInfo)) * S_SIZE_T(aDynamicEntries)); |
7248 | |
7249 | memcpy(pNewDynamicClassTable, m_pDynamicClassTable, sizeof(DynamicClassInfo) * m_aDynamicEntries); |
7250 | |
7251 | // Note: Memory allocated on loader heap is zero filled |
7252 | // memset(pNewDynamicClassTable + m_aDynamicEntries, 0, (aDynamicEntries - m_aDynamicEntries) * sizeof(DynamicClassInfo)); |
7253 | |
7254 | _ASSERTE(m_aDynamicEntries%2 == 0); |
7255 | |
7256 | // Commit new dynamic table. The lock-free helpers depend on the order. |
7257 | MemoryBarrier(); |
7258 | m_pDynamicClassTable = pNewDynamicClassTable; |
7259 | MemoryBarrier(); |
7260 | m_aDynamicEntries = aDynamicEntries; |
7261 | } |
7262 | |
7263 | #ifndef CROSSGEN_COMPILE |
7264 | void DomainLocalModule::AllocateDynamicClass(MethodTable *pMT) |
7265 | { |
7266 | CONTRACTL |
7267 | { |
7268 | THROWS; |
7269 | GC_TRIGGERS; |
7270 | // Assumes BaseDomain::DomainLocalBlockLockHolder is taken |
7271 | PRECONDITION(GetDomainFile()->GetAppDomain()->OwnDomainLocalBlockLock()); |
7272 | } |
7273 | CONTRACTL_END; |
7274 | |
7275 | _ASSERTE(!pMT->ContainsGenericVariables()); |
7276 | _ASSERTE(!pMT->IsSharedByGenericInstantiations()); |
7277 | _ASSERTE(GetDomainFile()->GetModule() == pMT->GetModuleForStatics()); |
7278 | _ASSERTE(pMT->IsDynamicStatics()); |
7279 | |
7280 | DWORD dynamicEntryIDIndex = pMT->GetModuleDynamicEntryID(); |
7281 | |
7282 | EnsureDynamicClassIndex(dynamicEntryIDIndex); |
7283 | |
7284 | _ASSERTE(m_aDynamicEntries > dynamicEntryIDIndex); |
7285 | |
7286 | EEClass *pClass = pMT->GetClass(); |
7287 | |
7288 | DWORD dwStaticBytes = pClass->GetNonGCRegularStaticFieldBytes(); |
7289 | DWORD dwNumHandleStatics = pClass->GetNumHandleRegularStatics(); |
7290 | |
7291 | _ASSERTE(!IsClassAllocated(pMT)); |
7292 | _ASSERTE(!IsClassInitialized(pMT)); |
7293 | _ASSERTE(!IsClassInitError(pMT)); |
7294 | |
7295 | DynamicEntry *pDynamicStatics = m_pDynamicClassTable[dynamicEntryIDIndex].m_pDynamicEntry; |
7296 | |
7297 | // We need this check because maybe a class had a cctor but no statics |
7298 | if (dwStaticBytes > 0 || dwNumHandleStatics > 0) |
7299 | { |
7300 | if (pDynamicStatics == NULL) |
7301 | { |
7302 | LoaderHeap * pLoaderAllocator = GetDomainFile()->GetLoaderAllocator()->GetHighFrequencyHeap(); |
7303 | |
7304 | if (pMT->Collectible()) |
7305 | { |
7306 | pDynamicStatics = (DynamicEntry*)(void*)pLoaderAllocator->AllocMem(S_SIZE_T(sizeof(CollectibleDynamicEntry))); |
7307 | } |
7308 | else |
7309 | { |
7310 | SIZE_T dynamicEntrySize = DynamicEntry::GetOffsetOfDataBlob() + dwStaticBytes; |
7311 | |
7312 | #ifdef FEATURE_64BIT_ALIGNMENT |
7313 | // Allocate memory with extra alignment only if it is really necessary |
7314 | if (dwStaticBytes >= MAX_PRIMITIVE_FIELD_SIZE) |
7315 | { |
7316 | static_assert_no_msg(sizeof(NormalDynamicEntry) % MAX_PRIMITIVE_FIELD_SIZE == 0); |
7317 | pDynamicStatics = (DynamicEntry*)(void*)pLoaderAllocator->AllocAlignedMem(dynamicEntrySize, MAX_PRIMITIVE_FIELD_SIZE); |
7318 | } |
7319 | else |
7320 | #endif |
7321 | pDynamicStatics = (DynamicEntry*)(void*)pLoaderAllocator->AllocMem(S_SIZE_T(dynamicEntrySize)); |
7322 | } |
7323 | |
7324 | // Note: Memory allocated on loader heap is zero filled |
7325 | |
7326 | m_pDynamicClassTable[dynamicEntryIDIndex].m_pDynamicEntry = pDynamicStatics; |
7327 | } |
7328 | |
7329 | if (pMT->Collectible() && (dwStaticBytes != 0)) |
7330 | { |
7331 | GCX_COOP(); |
7332 | OBJECTREF nongcStaticsArray = NULL; |
7333 | GCPROTECT_BEGIN(nongcStaticsArray); |
7334 | #ifdef FEATURE_64BIT_ALIGNMENT |
7335 | // Allocate memory with extra alignment only if it is really necessary |
7336 | if (dwStaticBytes >= MAX_PRIMITIVE_FIELD_SIZE) |
7337 | nongcStaticsArray = AllocatePrimitiveArray(ELEMENT_TYPE_I8, (dwStaticBytes + (sizeof(CLR_I8)-1)) / (sizeof(CLR_I8))); |
7338 | else |
7339 | #endif |
7340 | nongcStaticsArray = AllocatePrimitiveArray(ELEMENT_TYPE_U1, dwStaticBytes); |
7341 | ((CollectibleDynamicEntry *)pDynamicStatics)->m_hNonGCStatics = GetDomainFile()->GetModule()->GetLoaderAllocator()->AllocateHandle(nongcStaticsArray); |
7342 | GCPROTECT_END(); |
7343 | } |
7344 | if (dwNumHandleStatics > 0) |
7345 | { |
7346 | if (!pMT->Collectible()) |
7347 | { |
7348 | GetAppDomain()->AllocateStaticFieldObjRefPtrs(dwNumHandleStatics, |
7349 | &((NormalDynamicEntry *)pDynamicStatics)->m_pGCStatics); |
7350 | } |
7351 | else |
7352 | { |
7353 | GCX_COOP(); |
7354 | OBJECTREF gcStaticsArray = NULL; |
7355 | GCPROTECT_BEGIN(gcStaticsArray); |
7356 | gcStaticsArray = AllocateObjectArray(dwNumHandleStatics, g_pObjectClass); |
7357 | ((CollectibleDynamicEntry *)pDynamicStatics)->m_hGCStatics = GetDomainFile()->GetModule()->GetLoaderAllocator()->AllocateHandle(gcStaticsArray); |
7358 | GCPROTECT_END(); |
7359 | } |
7360 | } |
7361 | } |
7362 | } |
7363 | |
7364 | |
7365 | void DomainLocalModule::PopulateClass(MethodTable *pMT) |
7366 | { |
7367 | CONTRACTL |
7368 | { |
7369 | THROWS; |
7370 | GC_TRIGGERS; |
7371 | } |
7372 | CONTRACTL_END; |
7373 | |
7374 | _ASSERTE(!pMT->ContainsGenericVariables()); |
7375 | |
7376 | // <todo> the only work actually done here for non-dynamics is the freezing related work. |
7377 | // See if we can eliminate this and make this a dynamic-only path </todo> |
7378 | DWORD iClassIndex = pMT->GetClassIndex(); |
7379 | |
7380 | if (!IsClassAllocated(pMT, iClassIndex)) |
7381 | { |
7382 | BaseDomain::DomainLocalBlockLockHolder lh(GetDomainFile()->GetAppDomain()); |
7383 | |
7384 | if (!IsClassAllocated(pMT, iClassIndex)) |
7385 | { |
7386 | // Allocate dynamic space if necessary |
7387 | if (pMT->IsDynamicStatics()) |
7388 | AllocateDynamicClass(pMT); |
7389 | |
7390 | // determine flags to set on the statics block |
7391 | DWORD dwFlags = ClassInitFlags::ALLOCATECLASS_FLAG; |
7392 | |
7393 | if (!pMT->HasClassConstructor() && !pMT->HasBoxedRegularStatics()) |
7394 | { |
7395 | _ASSERTE(!IsClassInitialized(pMT)); |
7396 | _ASSERTE(!IsClassInitError(pMT)); |
7397 | dwFlags |= ClassInitFlags::INITIALIZED_FLAG; |
7398 | } |
7399 | |
7400 | if (pMT->Collectible()) |
7401 | { |
7402 | dwFlags |= ClassInitFlags::COLLECTIBLE_FLAG; |
7403 | } |
7404 | |
7405 | // Set all flags at the same time to avoid races |
7406 | SetClassFlags(pMT, dwFlags); |
7407 | } |
7408 | } |
7409 | |
7410 | return; |
7411 | } |
7412 | #endif // CROSSGEN_COMPILE |
7413 | |
7414 | void DomainLocalBlock::EnsureModuleIndex(ModuleIndex index) |
7415 | { |
7416 | CONTRACTL |
7417 | { |
7418 | THROWS; |
7419 | GC_TRIGGERS; |
7420 | MODE_ANY; |
7421 | INJECT_FAULT(COMPlusThrowOM();); |
7422 | // Assumes BaseDomain::DomainLocalBlockLockHolder is taken |
7423 | PRECONDITION(m_pDomain->OwnDomainLocalBlockLock()); |
7424 | } |
7425 | CONTRACTL_END; |
7426 | |
7427 | if (m_aModuleIndices > index.m_dwIndex) |
7428 | { |
7429 | _ASSERTE(m_pModuleSlots != NULL); |
7430 | return; |
7431 | } |
7432 | |
7433 | SIZE_T aModuleIndices = max(16, m_aModuleIndices); |
7434 | while (aModuleIndices <= index.m_dwIndex) |
7435 | { |
7436 | aModuleIndices *= 2; |
7437 | } |
7438 | |
7439 | PTR_DomainLocalModule* pNewModuleSlots = (PTR_DomainLocalModule*) (void*)m_pDomain->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(PTR_DomainLocalModule)) * S_SIZE_T(aModuleIndices)); |
7440 | |
7441 | memcpy(pNewModuleSlots, m_pModuleSlots, sizeof(SIZE_T)*m_aModuleIndices); |
7442 | |
7443 | // Note: Memory allocated on loader heap is zero filled |
7444 | // memset(pNewModuleSlots + m_aModuleIndices, 0 , (aModuleIndices - m_aModuleIndices)*sizeof(PTR_DomainLocalModule) ); |
7445 | |
7446 | // Commit new table. The lock-free helpers depend on the order. |
7447 | MemoryBarrier(); |
7448 | m_pModuleSlots = pNewModuleSlots; |
7449 | MemoryBarrier(); |
7450 | m_aModuleIndices = aModuleIndices; |
7451 | |
7452 | } |
7453 | |
7454 | void DomainLocalBlock::SetModuleSlot(ModuleIndex index, PTR_DomainLocalModule pLocalModule) |
7455 | { |
7456 | // Need to synchronize with table growth in this domain |
7457 | BaseDomain::DomainLocalBlockLockHolder lh(m_pDomain); |
7458 | |
7459 | EnsureModuleIndex(index); |
7460 | |
7461 | _ASSERTE(index.m_dwIndex < m_aModuleIndices); |
7462 | |
7463 | // We would like this assert here, unfortunately, loading a module in this appdomain can fail |
7464 | // after here and we will keep the module around and reuse the slot when we retry (if |
7465 | // the failure happened due to a transient error, such as OOM). In that case the slot wont |
7466 | // be null. |
7467 | //_ASSERTE(m_pModuleSlots[index.m_dwIndex] == 0); |
7468 | |
7469 | m_pModuleSlots[index.m_dwIndex] = pLocalModule; |
7470 | } |
7471 | |
7472 | #ifndef CROSSGEN_COMPILE |
7473 | |
7474 | DomainAssembly* AppDomain::RaiseTypeResolveEventThrowing(DomainAssembly* pAssembly, LPCSTR szName, ASSEMBLYREF *pResultingAssemblyRef) |
7475 | { |
7476 | CONTRACTL |
7477 | { |
7478 | MODE_ANY; |
7479 | GC_TRIGGERS; |
7480 | THROWS; |
7481 | INJECT_FAULT(COMPlusThrowOM();); |
7482 | } |
7483 | CONTRACTL_END; |
7484 | |
7485 | OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED); |
7486 | |
7487 | DomainAssembly* pResolvedAssembly = NULL; |
7488 | _ASSERTE(strcmp(szName, g_AppDomainClassName)); |
7489 | |
7490 | GCX_COOP(); |
7491 | |
7492 | struct _gc { |
7493 | OBJECTREF AssemblyRef; |
7494 | STRINGREF str; |
7495 | } gc; |
7496 | ZeroMemory(&gc, sizeof(gc)); |
7497 | |
7498 | GCPROTECT_BEGIN(gc); |
7499 | |
7500 | if (pAssembly != NULL) |
7501 | gc.AssemblyRef = pAssembly->GetExposedAssemblyObject(); |
7502 | |
7503 | MethodDescCallSite onTypeResolve(METHOD__ASSEMBLYLOADCONTEXT__ON_TYPE_RESOLVE); |
7504 | |
7505 | gc.str = StringObject::NewString(szName); |
7506 | ARG_SLOT args[2] = |
7507 | { |
7508 | ObjToArgSlot(gc.AssemblyRef), |
7509 | ObjToArgSlot(gc.str) |
7510 | }; |
7511 | ASSEMBLYREF ResultingAssemblyRef = (ASSEMBLYREF) onTypeResolve.Call_RetOBJECTREF(args); |
7512 | |
7513 | if (ResultingAssemblyRef != NULL) |
7514 | { |
7515 | pResolvedAssembly = ResultingAssemblyRef->GetDomainAssembly(); |
7516 | |
7517 | if (pResultingAssemblyRef) |
7518 | *pResultingAssemblyRef = ResultingAssemblyRef; |
7519 | else |
7520 | { |
7521 | if (pResolvedAssembly->IsCollectible()) |
7522 | { |
7523 | COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleBoundNonCollectible" )); |
7524 | } |
7525 | } |
7526 | } |
7527 | GCPROTECT_END(); |
7528 | |
7529 | return pResolvedAssembly; |
7530 | } |
7531 | |
7532 | |
7533 | Assembly* AppDomain::RaiseResourceResolveEvent(DomainAssembly* pAssembly, LPCSTR szName) |
7534 | { |
7535 | CONTRACT(Assembly*) |
7536 | { |
7537 | THROWS; |
7538 | GC_TRIGGERS; |
7539 | MODE_ANY; |
7540 | POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); |
7541 | INJECT_FAULT(COMPlusThrowOM();); |
7542 | } |
7543 | CONTRACT_END; |
7544 | |
7545 | Assembly* pResolvedAssembly = NULL; |
7546 | |
7547 | GCX_COOP(); |
7548 | |
7549 | struct _gc { |
7550 | OBJECTREF AssemblyRef; |
7551 | STRINGREF str; |
7552 | } gc; |
7553 | ZeroMemory(&gc, sizeof(gc)); |
7554 | |
7555 | GCPROTECT_BEGIN(gc); |
7556 | |
7557 | if (pAssembly != NULL) |
7558 | gc.AssemblyRef=pAssembly->GetExposedAssemblyObject(); |
7559 | |
7560 | MethodDescCallSite onResourceResolve(METHOD__ASSEMBLYLOADCONTEXT__ON_RESOURCE_RESOLVE); |
7561 | gc.str = StringObject::NewString(szName); |
7562 | ARG_SLOT args[2] = |
7563 | { |
7564 | ObjToArgSlot(gc.AssemblyRef), |
7565 | ObjToArgSlot(gc.str) |
7566 | }; |
7567 | ASSEMBLYREF ResultingAssemblyRef = (ASSEMBLYREF) onResourceResolve.Call_RetOBJECTREF(args); |
7568 | if (ResultingAssemblyRef != NULL) |
7569 | { |
7570 | pResolvedAssembly = ResultingAssemblyRef->GetAssembly(); |
7571 | if (pResolvedAssembly->IsCollectible()) |
7572 | { |
7573 | COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleAssemblyResolve" )); |
7574 | } |
7575 | } |
7576 | GCPROTECT_END(); |
7577 | |
7578 | RETURN pResolvedAssembly; |
7579 | } |
7580 | |
7581 | |
7582 | Assembly * |
7583 | AppDomain::RaiseAssemblyResolveEvent( |
7584 | AssemblySpec * pSpec) |
7585 | { |
7586 | CONTRACT(Assembly*) |
7587 | { |
7588 | THROWS; |
7589 | GC_TRIGGERS; |
7590 | MODE_ANY; |
7591 | POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); |
7592 | INJECT_FAULT(COMPlusThrowOM();); |
7593 | } |
7594 | CONTRACT_END; |
7595 | |
7596 | StackSString ssName; |
7597 | pSpec->GetFileOrDisplayName(0, ssName); |
7598 | |
7599 | // Elevate threads allowed loading level. This allows the host to load an assembly even in a restricted |
7600 | // condition. Note, however, that this exposes us to possible recursion failures, if the host tries to |
7601 | // load the assemblies currently being loaded. (Such cases would then throw an exception.) |
7602 | |
7603 | OVERRIDE_LOAD_LEVEL_LIMIT(FILE_ACTIVE); |
7604 | OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED); |
7605 | |
7606 | GCX_COOP(); |
7607 | |
7608 | Assembly* pAssembly = NULL; |
7609 | |
7610 | struct _gc { |
7611 | OBJECTREF AssemblyRef; |
7612 | STRINGREF str; |
7613 | } gc; |
7614 | ZeroMemory(&gc, sizeof(gc)); |
7615 | |
7616 | GCPROTECT_BEGIN(gc); |
7617 | { |
7618 | if (pSpec->GetParentAssembly() != NULL) |
7619 | { |
7620 | gc.AssemblyRef=pSpec->GetParentAssembly()->GetExposedAssemblyObject(); |
7621 | } |
7622 | |
7623 | MethodDescCallSite onAssemblyResolve(METHOD__ASSEMBLYLOADCONTEXT__ON_ASSEMBLY_RESOLVE); |
7624 | |
7625 | gc.str = StringObject::NewString(ssName); |
7626 | ARG_SLOT args[2] = { |
7627 | ObjToArgSlot(gc.AssemblyRef), |
7628 | ObjToArgSlot(gc.str) |
7629 | }; |
7630 | |
7631 | ASSEMBLYREF ResultingAssemblyRef = (ASSEMBLYREF) onAssemblyResolve.Call_RetOBJECTREF(args); |
7632 | |
7633 | if (ResultingAssemblyRef != NULL) |
7634 | { |
7635 | pAssembly = ResultingAssemblyRef->GetAssembly(); |
7636 | if (pAssembly->IsCollectible()) |
7637 | { |
7638 | COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleAssemblyResolve" )); |
7639 | } |
7640 | } |
7641 | } |
7642 | GCPROTECT_END(); |
7643 | |
7644 | if (pAssembly != NULL) |
7645 | { |
7646 | // Check that the public key token matches the one specified in the spec |
7647 | // MatchPublicKeys throws as appropriate |
7648 | pSpec->MatchPublicKeys(pAssembly); |
7649 | } |
7650 | |
7651 | RETURN pAssembly; |
7652 | } // AppDomain::RaiseAssemblyResolveEvent |
7653 | |
7654 | |
7655 | ULONGLONG g_ObjFinalizeStartTime = 0; |
7656 | Volatile<BOOL> g_FinalizerIsRunning = FALSE; |
7657 | Volatile<ULONG> g_FinalizerLoopCount = 0; |
7658 | |
7659 | ULONGLONG GetObjFinalizeStartTime() |
7660 | { |
7661 | LIMITED_METHOD_CONTRACT; |
7662 | return g_ObjFinalizeStartTime; |
7663 | } |
7664 | |
7665 | void FinalizerThreadAbortOnTimeout() |
7666 | { |
7667 | STATIC_CONTRACT_NOTHROW; |
7668 | STATIC_CONTRACT_MODE_COOPERATIVE; |
7669 | STATIC_CONTRACT_GC_TRIGGERS; |
7670 | |
7671 | { |
7672 | // If finalizer thread is blocked because scheduler is running another task, |
7673 | // or it is waiting for another thread, we first see if we get finalizer thread |
7674 | // running again. |
7675 | Thread::ThreadAbortWatchDog(); |
7676 | } |
7677 | |
7678 | EX_TRY |
7679 | { |
7680 | Thread *pFinalizerThread = FinalizerThread::GetFinalizerThread(); |
7681 | EPolicyAction action = GetEEPolicy()->GetActionOnTimeout(OPR_FinalizerRun, pFinalizerThread); |
7682 | switch (action) |
7683 | { |
7684 | case eAbortThread: |
7685 | GetEEPolicy()->NotifyHostOnTimeout(OPR_FinalizerRun, action); |
7686 | pFinalizerThread->UserAbort(Thread::TAR_Thread, |
7687 | EEPolicy::TA_Safe, |
7688 | INFINITE, |
7689 | Thread::UAC_FinalizerTimeout); |
7690 | break; |
7691 | case eRudeAbortThread: |
7692 | GetEEPolicy()->NotifyHostOnTimeout(OPR_FinalizerRun, action); |
7693 | pFinalizerThread->UserAbort(Thread::TAR_Thread, |
7694 | EEPolicy::TA_Rude, |
7695 | INFINITE, |
7696 | Thread::UAC_FinalizerTimeout); |
7697 | break; |
7698 | case eUnloadAppDomain: |
7699 | { |
7700 | AppDomain *pDomain = pFinalizerThread->GetDomain(); |
7701 | pFinalizerThread->UserAbort(Thread::TAR_Thread, |
7702 | EEPolicy::TA_Safe, |
7703 | INFINITE, |
7704 | Thread::UAC_FinalizerTimeout); |
7705 | } |
7706 | break; |
7707 | case eRudeUnloadAppDomain: |
7708 | { |
7709 | AppDomain *pDomain = pFinalizerThread->GetDomain(); |
7710 | pFinalizerThread->UserAbort(Thread::TAR_Thread, |
7711 | EEPolicy::TA_Rude, |
7712 | INFINITE, |
7713 | Thread::UAC_FinalizerTimeout); |
7714 | } |
7715 | break; |
7716 | case eExitProcess: |
7717 | case eFastExitProcess: |
7718 | case eRudeExitProcess: |
7719 | case eDisableRuntime: |
7720 | GetEEPolicy()->NotifyHostOnTimeout(OPR_FinalizerRun, action); |
7721 | EEPolicy::HandleExitProcessFromEscalation(action, HOST_E_EXITPROCESS_TIMEOUT); |
7722 | _ASSERTE (!"Should not get here" ); |
7723 | break; |
7724 | default: |
7725 | break; |
7726 | } |
7727 | } |
7728 | EX_CATCH |
7729 | { |
7730 | } |
7731 | EX_END_CATCH(SwallowAllExceptions); |
7732 | } |
7733 | |
7734 | enum WorkType |
7735 | { |
7736 | WT_UnloadDomain = 0x1, |
7737 | WT_ThreadAbort = 0x2, |
7738 | WT_FinalizerThread = 0x4 |
7739 | }; |
7740 | |
7741 | static Volatile<DWORD> s_WorkType = 0; |
7742 | |
7743 | void SystemDomain::ProcessDelayedUnloadLoaderAllocators() |
7744 | { |
7745 | CONTRACTL |
7746 | { |
7747 | NOTHROW; |
7748 | GC_TRIGGERS; |
7749 | MODE_COOPERATIVE; |
7750 | } |
7751 | CONTRACTL_END; |
7752 | |
7753 | int iGCRefPoint=GCHeapUtilities::GetGCHeap()->CollectionCount(GCHeapUtilities::GetGCHeap()->GetMaxGeneration()); |
7754 | if (GCHeapUtilities::GetGCHeap()->IsConcurrentGCInProgress()) |
7755 | iGCRefPoint--; |
7756 | |
7757 | LoaderAllocator * pAllocatorsToDelete = NULL; |
7758 | |
7759 | { |
7760 | CrstHolder lh(&m_DelayedUnloadCrst); |
7761 | |
7762 | LoaderAllocator ** ppAllocator=&m_pDelayedUnloadListOfLoaderAllocators; |
7763 | while (*ppAllocator!= NULL) |
7764 | { |
7765 | LoaderAllocator * pAllocator = *ppAllocator; |
7766 | if (0 < iGCRefPoint - pAllocator->GetGCRefPoint()) |
7767 | { |
7768 | *ppAllocator = pAllocator->m_pLoaderAllocatorDestroyNext; |
7769 | |
7770 | pAllocator->m_pLoaderAllocatorDestroyNext = pAllocatorsToDelete; |
7771 | pAllocatorsToDelete = pAllocator; |
7772 | } |
7773 | else |
7774 | { |
7775 | ppAllocator = &pAllocator->m_pLoaderAllocatorDestroyNext; |
7776 | } |
7777 | } |
7778 | } |
7779 | |
7780 | // Delete collected loader allocators on the finalizer thread. We cannot offload it to appdomain unload thread because of |
7781 | // there is not guaranteed to be one, and it is not that expensive operation anyway. |
7782 | while (pAllocatorsToDelete != NULL) |
7783 | { |
7784 | LoaderAllocator * pAllocator = pAllocatorsToDelete; |
7785 | pAllocatorsToDelete = pAllocator->m_pLoaderAllocatorDestroyNext; |
7786 | delete pAllocator; |
7787 | } |
7788 | } |
7789 | |
7790 | #endif // CROSSGEN_COMPILE |
7791 | |
7792 | void AppDomain::EnumStaticGCRefs(promote_func* fn, ScanContext* sc) |
7793 | { |
7794 | CONTRACT_VOID |
7795 | { |
7796 | NOTHROW; |
7797 | GC_NOTRIGGER; |
7798 | } |
7799 | CONTRACT_END; |
7800 | |
7801 | _ASSERTE(GCHeapUtilities::IsGCInProgress() && |
7802 | GCHeapUtilities::IsServerHeap() && |
7803 | IsGCSpecialThread()); |
7804 | |
7805 | AppDomain::AssemblyIterator asmIterator = IterateAssembliesEx((AssemblyIterationFlags)(kIncludeLoaded | kIncludeExecution)); |
7806 | CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly; |
7807 | while (asmIterator.Next(pDomainAssembly.This())) |
7808 | { |
7809 | // @TODO: Review when DomainAssemblies get added. |
7810 | _ASSERTE(pDomainAssembly != NULL); |
7811 | pDomainAssembly->EnumStaticGCRefs(fn, sc); |
7812 | } |
7813 | |
7814 | RETURN; |
7815 | } |
7816 | |
7817 | #endif // !DACCESS_COMPILE |
7818 | |
7819 | //------------------------------------------------------------------------ |
7820 | PTR_LoaderAllocator BaseDomain::GetLoaderAllocator() |
7821 | { |
7822 | WRAPPER_NO_CONTRACT; |
7823 | return SystemDomain::GetGlobalLoaderAllocator(); // The one and only domain is not unloadable |
7824 | } |
7825 | |
7826 | //------------------------------------------------------------------------ |
7827 | UINT32 BaseDomain::GetTypeID(PTR_MethodTable pMT) { |
7828 | CONTRACTL { |
7829 | THROWS; |
7830 | GC_TRIGGERS; |
7831 | PRECONDITION(pMT->GetDomain() == this); |
7832 | } CONTRACTL_END; |
7833 | |
7834 | return m_typeIDMap.GetTypeID(pMT); |
7835 | } |
7836 | |
7837 | //------------------------------------------------------------------------ |
7838 | // Returns the ID of the type if found. If not found, returns INVALID_TYPE_ID |
7839 | UINT32 BaseDomain::LookupTypeID(PTR_MethodTable pMT) |
7840 | { |
7841 | CONTRACTL { |
7842 | NOTHROW; |
7843 | SO_TOLERANT; |
7844 | WRAPPER(GC_TRIGGERS); |
7845 | PRECONDITION(pMT->GetDomain() == this); |
7846 | } CONTRACTL_END; |
7847 | |
7848 | return m_typeIDMap.LookupTypeID(pMT); |
7849 | } |
7850 | |
7851 | //------------------------------------------------------------------------ |
7852 | PTR_MethodTable BaseDomain::LookupType(UINT32 id) { |
7853 | CONTRACTL { |
7854 | NOTHROW; |
7855 | SO_TOLERANT; |
7856 | WRAPPER(GC_TRIGGERS); |
7857 | CONSISTENCY_CHECK(id != TYPE_ID_THIS_CLASS); |
7858 | } CONTRACTL_END; |
7859 | |
7860 | PTR_MethodTable pMT = m_typeIDMap.LookupType(id); |
7861 | |
7862 | CONSISTENCY_CHECK(CheckPointer(pMT)); |
7863 | CONSISTENCY_CHECK(pMT->IsInterface()); |
7864 | return pMT; |
7865 | } |
7866 | |
7867 | //--------------------------------------------------------------------------------------- |
7868 | // |
7869 | BOOL |
7870 | AppDomain::AssemblyIterator::Next( |
7871 | CollectibleAssemblyHolder<DomainAssembly *> * pDomainAssemblyHolder) |
7872 | { |
7873 | CONTRACTL { |
7874 | NOTHROW; |
7875 | WRAPPER(GC_TRIGGERS); // Triggers only in MODE_COOPERATIVE (by taking the lock) |
7876 | MODE_ANY; |
7877 | } CONTRACTL_END; |
7878 | |
7879 | CrstHolder ch(m_pAppDomain->GetAssemblyListLock()); |
7880 | return Next_Unlocked(pDomainAssemblyHolder); |
7881 | } |
7882 | |
7883 | //--------------------------------------------------------------------------------------- |
7884 | // |
7885 | // Note: Does not lock the assembly list, but locks collectible assemblies for adding references. |
7886 | // |
7887 | BOOL |
7888 | AppDomain::AssemblyIterator::Next_Unlocked( |
7889 | CollectibleAssemblyHolder<DomainAssembly *> * pDomainAssemblyHolder) |
7890 | { |
7891 | CONTRACTL { |
7892 | NOTHROW; |
7893 | GC_NOTRIGGER; |
7894 | MODE_ANY; |
7895 | } CONTRACTL_END; |
7896 | |
7897 | #ifndef DACCESS_COMPILE |
7898 | _ASSERTE(m_pAppDomain->GetAssemblyListLock()->OwnedByCurrentThread()); |
7899 | #endif |
7900 | |
7901 | while (m_Iterator.Next()) |
7902 | { |
7903 | // Get element from the list/iterator (without adding reference to the assembly) |
7904 | DomainAssembly * pDomainAssembly = dac_cast<PTR_DomainAssembly>(m_Iterator.GetElement()); |
7905 | if (pDomainAssembly == NULL) |
7906 | { |
7907 | continue; |
7908 | } |
7909 | |
7910 | if (pDomainAssembly->IsError()) |
7911 | { |
7912 | if (m_assemblyIterationFlags & kIncludeFailedToLoad) |
7913 | { |
7914 | *pDomainAssemblyHolder = pDomainAssembly; |
7915 | return TRUE; |
7916 | } |
7917 | continue; // reject |
7918 | } |
7919 | |
7920 | // First, reject DomainAssemblies whose load status is not to be included in |
7921 | // the enumeration |
7922 | |
7923 | if (pDomainAssembly->IsAvailableToProfilers() && |
7924 | (m_assemblyIterationFlags & kIncludeAvailableToProfilers)) |
7925 | { |
7926 | // The assembly has reached the state at which we would notify profilers, |
7927 | // and we're supposed to include such assemblies in the enumeration. So |
7928 | // don't reject it (i.e., noop here, and don't bother with the rest of |
7929 | // the load status checks). Check for this first, since |
7930 | // kIncludeAvailableToProfilers contains some loaded AND loading |
7931 | // assemblies. |
7932 | } |
7933 | else if (pDomainAssembly->IsLoaded()) |
7934 | { |
7935 | // A loaded assembly |
7936 | if (!(m_assemblyIterationFlags & kIncludeLoaded)) |
7937 | { |
7938 | continue; // reject |
7939 | } |
7940 | } |
7941 | else |
7942 | { |
7943 | // A loading assembly |
7944 | if (!(m_assemblyIterationFlags & kIncludeLoading)) |
7945 | { |
7946 | continue; // reject |
7947 | } |
7948 | } |
7949 | |
7950 | // Next, reject DomainAssemblies whose execution status is |
7951 | // not to be included in the enumeration |
7952 | |
7953 | // execution assembly |
7954 | if (!(m_assemblyIterationFlags & kIncludeExecution)) |
7955 | { |
7956 | continue; // reject |
7957 | } |
7958 | |
7959 | // Next, reject collectible assemblies |
7960 | if (pDomainAssembly->IsCollectible()) |
7961 | { |
7962 | if (m_assemblyIterationFlags & kExcludeCollectible) |
7963 | { |
7964 | _ASSERTE(!(m_assemblyIterationFlags & kIncludeCollected)); |
7965 | continue; // reject |
7966 | } |
7967 | |
7968 | // Un-tenured collectible assemblies should not be returned. (This can only happen in a brief |
7969 | // window during collectible assembly creation. No thread should need to have a pointer |
7970 | // to the just allocated DomainAssembly at this stage.) |
7971 | if (!pDomainAssembly->GetAssembly()->GetManifestModule()->IsTenured()) |
7972 | { |
7973 | continue; // reject |
7974 | } |
7975 | |
7976 | if (pDomainAssembly->GetLoaderAllocator()->AddReferenceIfAlive()) |
7977 | { // The assembly is alive |
7978 | |
7979 | // Set the holder value (incl. increasing ref-count) |
7980 | *pDomainAssemblyHolder = pDomainAssembly; |
7981 | |
7982 | // Now release the reference we took in the if-condition |
7983 | pDomainAssembly->GetLoaderAllocator()->Release(); |
7984 | return TRUE; |
7985 | } |
7986 | // The assembly is not alive anymore (and we didn't increase its ref-count in the |
7987 | // if-condition) |
7988 | |
7989 | if (!(m_assemblyIterationFlags & kIncludeCollected)) |
7990 | { |
7991 | continue; // reject |
7992 | } |
7993 | // Set the holder value to assembly with 0 ref-count without increasing the ref-count (won't |
7994 | // call Release either) |
7995 | pDomainAssemblyHolder->Assign(pDomainAssembly, FALSE); |
7996 | return TRUE; |
7997 | } |
7998 | |
7999 | *pDomainAssemblyHolder = pDomainAssembly; |
8000 | return TRUE; |
8001 | } |
8002 | |
8003 | *pDomainAssemblyHolder = NULL; |
8004 | return FALSE; |
8005 | } // AppDomain::AssemblyIterator::Next_Unlocked |
8006 | |
8007 | #ifndef DACCESS_COMPILE |
8008 | |
8009 | //--------------------------------------------------------------------------------------- |
8010 | // |
8011 | // Can be called only from AppDomain shutdown code:AppDomain::ShutdownAssemblies. |
8012 | // Does not add-ref collectible assemblies (as the LoaderAllocator might not be reachable from the |
8013 | // DomainAssembly anymore). |
8014 | // |
8015 | BOOL |
8016 | AppDomain::AssemblyIterator::Next_UnsafeNoAddRef( |
8017 | DomainAssembly ** ppDomainAssembly) |
8018 | { |
8019 | CONTRACTL { |
8020 | NOTHROW; |
8021 | GC_TRIGGERS; |
8022 | MODE_ANY; |
8023 | } CONTRACTL_END; |
8024 | |
8025 | // Make sure we are iterating all assemblies (see the only caller code:AppDomain::ShutdownAssemblies) |
8026 | _ASSERTE(m_assemblyIterationFlags == |
8027 | (kIncludeLoaded | kIncludeLoading | kIncludeExecution | kIncludeFailedToLoad | kIncludeCollected)); |
8028 | // It also means that we do not exclude anything |
8029 | _ASSERTE((m_assemblyIterationFlags & kExcludeCollectible) == 0); |
8030 | |
8031 | // We are on shutdown path, so lock shouldn't be neccessary, but all _Unlocked methods on AssemblyList |
8032 | // have asserts that the lock is held, so why not to take it ... |
8033 | CrstHolder ch(m_pAppDomain->GetAssemblyListLock()); |
8034 | |
8035 | while (m_Iterator.Next()) |
8036 | { |
8037 | // Get element from the list/iterator (without adding reference to the assembly) |
8038 | *ppDomainAssembly = dac_cast<PTR_DomainAssembly>(m_Iterator.GetElement()); |
8039 | if (*ppDomainAssembly == NULL) |
8040 | { |
8041 | continue; |
8042 | } |
8043 | |
8044 | return TRUE; |
8045 | } |
8046 | |
8047 | *ppDomainAssembly = NULL; |
8048 | return FALSE; |
8049 | } // AppDomain::AssemblyIterator::Next_UnsafeNoAddRef |
8050 | |
8051 | |
8052 | #endif //!DACCESS_COMPILE |
8053 | |
8054 | #if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) |
8055 | |
8056 | // Returns S_OK if the assembly was successfully loaded |
8057 | HRESULT RuntimeInvokeHostAssemblyResolver(INT_PTR pManagedAssemblyLoadContextToBindWithin, IAssemblyName *pIAssemblyName, CLRPrivBinderCoreCLR *pTPABinder, BINDER_SPACE::AssemblyName *pAssemblyName, ICLRPrivAssembly **ppLoadedAssembly) |
8058 | { |
8059 | CONTRACTL |
8060 | { |
8061 | THROWS; |
8062 | GC_TRIGGERS; |
8063 | MODE_ANY; |
8064 | PRECONDITION(ppLoadedAssembly != NULL); |
8065 | } |
8066 | CONTRACTL_END; |
8067 | |
8068 | HRESULT hr = E_FAIL; |
8069 | |
8070 | // DevDiv #933506: Exceptions thrown during AssemblyLoadContext.Load should propagate |
8071 | // EX_TRY |
8072 | { |
8073 | // Switch to COOP mode since we are going to work with managed references |
8074 | GCX_COOP(); |
8075 | |
8076 | struct |
8077 | { |
8078 | ASSEMBLYNAMEREF oRefAssemblyName; |
8079 | ASSEMBLYREF oRefLoadedAssembly; |
8080 | } _gcRefs; |
8081 | |
8082 | ZeroMemory(&_gcRefs, sizeof(_gcRefs)); |
8083 | |
8084 | GCPROTECT_BEGIN(_gcRefs); |
8085 | |
8086 | ICLRPrivAssembly *pAssemblyBindingContext = NULL; |
8087 | |
8088 | bool fInvokedForTPABinder = (pTPABinder == NULL)?true:false; |
8089 | |
8090 | // Prepare to invoke System.Runtime.Loader.AssemblyLoadContext.Resolve method. |
8091 | // |
8092 | // First, initialize an assembly spec for the requested assembly |
8093 | // |
8094 | AssemblySpec spec; |
8095 | hr = spec.Init(pIAssemblyName); |
8096 | if (SUCCEEDED(hr)) |
8097 | { |
8098 | bool fResolvedAssembly = false; |
8099 | bool fResolvedAssemblyViaTPALoadContext = false; |
8100 | |
8101 | // Allocate an AssemblyName managed object |
8102 | _gcRefs.oRefAssemblyName = (ASSEMBLYNAMEREF) AllocateObject(MscorlibBinder::GetClass(CLASS__ASSEMBLY_NAME)); |
8103 | |
8104 | // Initialize the AssemblyName object from the AssemblySpec |
8105 | spec.AssemblyNameInit(&_gcRefs.oRefAssemblyName, NULL); |
8106 | |
8107 | if (!fInvokedForTPABinder) |
8108 | { |
8109 | // Step 2 (of CLRPrivBinderAssemblyLoadContext::BindUsingAssemblyName) - Invoke Load method |
8110 | // This is not invoked for TPA Binder since it always returns NULL. |
8111 | |
8112 | // Finally, setup arguments for invocation |
8113 | BinderMethodID idHAR_Resolve = METHOD__ASSEMBLYLOADCONTEXT__RESOLVE; |
8114 | MethodDescCallSite methLoadAssembly(idHAR_Resolve); |
8115 | |
8116 | // Setup the arguments for the call |
8117 | ARG_SLOT args[2] = |
8118 | { |
8119 | PtrToArgSlot(pManagedAssemblyLoadContextToBindWithin), // IntPtr for managed assembly load context instance |
8120 | ObjToArgSlot(_gcRefs.oRefAssemblyName), // AssemblyName instance |
8121 | }; |
8122 | |
8123 | // Make the call |
8124 | _gcRefs.oRefLoadedAssembly = (ASSEMBLYREF) methLoadAssembly.Call_RetOBJECTREF(args); |
8125 | if (_gcRefs.oRefLoadedAssembly != NULL) |
8126 | { |
8127 | fResolvedAssembly = true; |
8128 | } |
8129 | |
8130 | // Step 3 (of CLRPrivBinderAssemblyLoadContext::BindUsingAssemblyName) |
8131 | if (!fResolvedAssembly) |
8132 | { |
8133 | // If we could not resolve the assembly using Load method, then attempt fallback with TPA Binder. |
8134 | // Since TPA binder cannot fallback to itself, this fallback does not happen for binds within TPA binder. |
8135 | // |
8136 | // Switch to pre-emp mode before calling into the binder |
8137 | GCX_PREEMP(); |
8138 | ICLRPrivAssembly *pCoreCLRFoundAssembly = NULL; |
8139 | hr = pTPABinder->BindAssemblyByName(pIAssemblyName, &pCoreCLRFoundAssembly); |
8140 | if (SUCCEEDED(hr)) |
8141 | { |
8142 | pAssemblyBindingContext = pCoreCLRFoundAssembly; |
8143 | fResolvedAssembly = true; |
8144 | fResolvedAssemblyViaTPALoadContext = true; |
8145 | } |
8146 | } |
8147 | } |
8148 | |
8149 | if (!fResolvedAssembly) |
8150 | { |
8151 | // Step 4 (of CLRPrivBinderAssemblyLoadContext::BindUsingAssemblyName) |
8152 | // |
8153 | // If we couldnt resolve the assembly using TPA LoadContext as well, then |
8154 | // attempt to resolve it using the Resolving event. |
8155 | // Finally, setup arguments for invocation |
8156 | BinderMethodID idHAR_ResolveUsingEvent = METHOD__ASSEMBLYLOADCONTEXT__RESOLVEUSINGEVENT; |
8157 | MethodDescCallSite methLoadAssembly(idHAR_ResolveUsingEvent); |
8158 | |
8159 | // Setup the arguments for the call |
8160 | ARG_SLOT args[2] = |
8161 | { |
8162 | PtrToArgSlot(pManagedAssemblyLoadContextToBindWithin), // IntPtr for managed assembly load context instance |
8163 | ObjToArgSlot(_gcRefs.oRefAssemblyName), // AssemblyName instance |
8164 | }; |
8165 | |
8166 | // Make the call |
8167 | _gcRefs.oRefLoadedAssembly = (ASSEMBLYREF) methLoadAssembly.Call_RetOBJECTREF(args); |
8168 | if (_gcRefs.oRefLoadedAssembly != NULL) |
8169 | { |
8170 | // Set the flag indicating we found the assembly |
8171 | fResolvedAssembly = true; |
8172 | } |
8173 | } |
8174 | |
8175 | if (fResolvedAssembly && !fResolvedAssemblyViaTPALoadContext) |
8176 | { |
8177 | // If we are here, assembly was successfully resolved via Load or Resolving events. |
8178 | _ASSERTE(_gcRefs.oRefLoadedAssembly != NULL); |
8179 | |
8180 | // We were able to get the assembly loaded. Now, get its name since the host could have |
8181 | // performed the resolution using an assembly with different name. |
8182 | DomainAssembly *pDomainAssembly = _gcRefs.oRefLoadedAssembly->GetDomainAssembly(); |
8183 | PEAssembly *pLoadedPEAssembly = NULL; |
8184 | bool fFailLoad = false; |
8185 | if (!pDomainAssembly) |
8186 | { |
8187 | // Reflection emitted assemblies will not have a domain assembly. |
8188 | fFailLoad = true; |
8189 | } |
8190 | else |
8191 | { |
8192 | pLoadedPEAssembly = pDomainAssembly->GetFile(); |
8193 | if (pLoadedPEAssembly->HasHostAssembly() != true) |
8194 | { |
8195 | // Reflection emitted assemblies will not have a domain assembly. |
8196 | fFailLoad = true; |
8197 | } |
8198 | } |
8199 | |
8200 | // The loaded assembly's ICLRPrivAssembly* is saved as HostAssembly in PEAssembly |
8201 | if (fFailLoad) |
8202 | { |
8203 | SString name; |
8204 | spec.GetFileOrDisplayName(0, name); |
8205 | COMPlusThrowHR(COR_E_INVALIDOPERATION, IDS_HOST_ASSEMBLY_RESOLVER_DYNAMICALLY_EMITTED_ASSEMBLIES_UNSUPPORTED, name); |
8206 | } |
8207 | |
8208 | // Is the assembly already bound using a binding context that will be incompatible? |
8209 | // An example is attempting to consume an assembly bound to WinRT binder. |
8210 | pAssemblyBindingContext = pLoadedPEAssembly->GetHostAssembly(); |
8211 | } |
8212 | |
8213 | #ifdef FEATURE_COMINTEROP |
8214 | if (AreSameBinderInstance(pAssemblyBindingContext, GetAppDomain()->GetWinRtBinder())) |
8215 | { |
8216 | // It is invalid to return an assembly bound to an incompatible binder |
8217 | *ppLoadedAssembly = NULL; |
8218 | SString name; |
8219 | spec.GetFileOrDisplayName(0, name); |
8220 | COMPlusThrowHR(COR_E_INVALIDOPERATION, IDS_HOST_ASSEMBLY_RESOLVER_INCOMPATIBLE_BINDING_CONTEXT, name); |
8221 | } |
8222 | #endif // FEATURE_COMINTEROP |
8223 | |
8224 | // Get the ICLRPrivAssembly reference to return back to. |
8225 | *ppLoadedAssembly = clr::SafeAddRef(pAssemblyBindingContext); |
8226 | hr = S_OK; |
8227 | } |
8228 | |
8229 | GCPROTECT_END(); |
8230 | } |
8231 | // EX_CATCH_HRESULT(hr); |
8232 | |
8233 | return hr; |
8234 | |
8235 | } |
8236 | #endif // !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) |
8237 | |
8238 | //approximate size of loader data |
8239 | //maintained for each assembly |
8240 | #define APPROX_LOADER_DATA_PER_ASSEMBLY 8196 |
8241 | |
8242 | size_t AppDomain::EstimateSize() |
8243 | { |
8244 | CONTRACTL |
8245 | { |
8246 | NOTHROW; |
8247 | GC_TRIGGERS; |
8248 | MODE_ANY; |
8249 | } |
8250 | CONTRACTL_END; |
8251 | |
8252 | size_t retval = sizeof(AppDomain); |
8253 | retval += GetLoaderAllocator()->EstimateSize(); |
8254 | //very rough estimate |
8255 | retval += GetAssemblyCount() * APPROX_LOADER_DATA_PER_ASSEMBLY; |
8256 | return retval; |
8257 | } |
8258 | |
8259 | #ifdef DACCESS_COMPILE |
8260 | |
8261 | void |
8262 | DomainLocalModule::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) |
8263 | { |
8264 | SUPPORTS_DAC; |
8265 | |
8266 | // Enumerate the DomainLocalModule itself. DLMs are allocated to be larger than |
8267 | // sizeof(DomainLocalModule) to make room for ClassInit flags and non-GC statics. |
8268 | // "DAC_ENUM_DTHIS()" probably does not account for this, so we might not enumerate |
8269 | // all of the ClassInit flags and non-GC statics. |
8270 | // sizeof(DomainLocalModule) == 0x28 |
8271 | DAC_ENUM_DTHIS(); |
8272 | |
8273 | if (m_pDomainFile.IsValid()) |
8274 | { |
8275 | m_pDomainFile->EnumMemoryRegions(flags); |
8276 | } |
8277 | |
8278 | if (m_pDynamicClassTable.Load().IsValid()) |
8279 | { |
8280 | DacEnumMemoryRegion(dac_cast<TADDR>(m_pDynamicClassTable.Load()), |
8281 | m_aDynamicEntries * sizeof(DynamicClassInfo)); |
8282 | |
8283 | for (SIZE_T i = 0; i < m_aDynamicEntries; i++) |
8284 | { |
8285 | PTR_DynamicEntry entry = dac_cast<PTR_DynamicEntry>(m_pDynamicClassTable[i].m_pDynamicEntry.Load()); |
8286 | if (entry.IsValid()) |
8287 | { |
8288 | // sizeof(DomainLocalModule::DynamicEntry) == 8 |
8289 | entry.EnumMem(); |
8290 | } |
8291 | } |
8292 | } |
8293 | } |
8294 | |
8295 | void |
8296 | DomainLocalBlock::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) |
8297 | { |
8298 | SUPPORTS_DAC; |
8299 | // Block is contained in AppDomain, don't enum this. |
8300 | |
8301 | if (m_pModuleSlots.IsValid()) |
8302 | { |
8303 | DacEnumMemoryRegion(dac_cast<TADDR>(m_pModuleSlots), |
8304 | m_aModuleIndices * sizeof(TADDR)); |
8305 | |
8306 | for (SIZE_T i = 0; i < m_aModuleIndices; i++) |
8307 | { |
8308 | PTR_DomainLocalModule domMod = m_pModuleSlots[i]; |
8309 | if (domMod.IsValid()) |
8310 | { |
8311 | domMod->EnumMemoryRegions(flags); |
8312 | } |
8313 | } |
8314 | } |
8315 | } |
8316 | |
8317 | void |
8318 | BaseDomain::EnumMemoryRegions(CLRDataEnumMemoryFlags flags, |
8319 | bool enumThis) |
8320 | { |
8321 | SUPPORTS_DAC; |
8322 | if (enumThis) |
8323 | { |
8324 | // This is wrong. Don't do it. |
8325 | // BaseDomain cannot be instantiated. |
8326 | // The only thing this code can hope to accomplish is to potentially break |
8327 | // memory enumeration walking through the derived class if we |
8328 | // explicitly call the base class enum first. |
8329 | // DAC_ENUM_VTHIS(); |
8330 | } |
8331 | |
8332 | EMEM_OUT(("MEM: %p BaseDomain\n" , dac_cast<TADDR>(this))); |
8333 | } |
8334 | |
8335 | void |
8336 | AppDomain::EnumMemoryRegions(CLRDataEnumMemoryFlags flags, |
8337 | bool enumThis) |
8338 | { |
8339 | SUPPORTS_DAC; |
8340 | |
8341 | if (enumThis) |
8342 | { |
8343 | //sizeof(AppDomain) == 0xeb0 |
8344 | DAC_ENUM_VTHIS(); |
8345 | } |
8346 | BaseDomain::EnumMemoryRegions(flags, false); |
8347 | |
8348 | // We don't need AppDomain name in triage dumps. |
8349 | if (flags != CLRDATA_ENUM_MEM_TRIAGE) |
8350 | { |
8351 | m_friendlyName.EnumMemoryRegions(flags); |
8352 | } |
8353 | |
8354 | m_Assemblies.EnumMemoryRegions(flags); |
8355 | AssemblyIterator assem = IterateAssembliesEx((AssemblyIterationFlags)(kIncludeLoaded | kIncludeExecution)); |
8356 | CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly; |
8357 | |
8358 | while (assem.Next(pDomainAssembly.This())) |
8359 | { |
8360 | pDomainAssembly->EnumMemoryRegions(flags); |
8361 | } |
8362 | |
8363 | m_sDomainLocalBlock.EnumMemoryRegions(flags); |
8364 | } |
8365 | |
8366 | void |
8367 | SystemDomain::EnumMemoryRegions(CLRDataEnumMemoryFlags flags, |
8368 | bool enumThis) |
8369 | { |
8370 | SUPPORTS_DAC; |
8371 | if (enumThis) |
8372 | { |
8373 | DAC_ENUM_VTHIS(); |
8374 | } |
8375 | BaseDomain::EnumMemoryRegions(flags, false); |
8376 | |
8377 | if (m_pSystemFile.IsValid()) |
8378 | { |
8379 | m_pSystemFile->EnumMemoryRegions(flags); |
8380 | } |
8381 | if (m_pSystemAssembly.IsValid()) |
8382 | { |
8383 | m_pSystemAssembly->EnumMemoryRegions(flags); |
8384 | } |
8385 | if (AppDomain::GetCurrentDomain()) |
8386 | { |
8387 | AppDomain::GetCurrentDomain()->EnumMemoryRegions(flags, true); |
8388 | } |
8389 | |
8390 | m_appDomainIndexList.EnumMem(); |
8391 | (&m_appDomainIndexList)->EnumMemoryRegions(flags); |
8392 | } |
8393 | |
8394 | #endif //DACCESS_COMPILE |
8395 | |
8396 | |
8397 | PTR_LoaderAllocator SystemDomain::GetGlobalLoaderAllocator() |
8398 | { |
8399 | return PTR_LoaderAllocator(PTR_HOST_MEMBER_TADDR(SystemDomain,System(),m_GlobalAllocator)); |
8400 | } |
8401 | |
8402 | #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING |
8403 | |
8404 | #ifndef CROSSGEN_COMPILE |
8405 | // Return the total processor time (user and kernel) used by threads executing in this AppDomain so far. The |
8406 | // result is in 100ns units. |
8407 | ULONGLONG AppDomain::QueryProcessorUsage() |
8408 | { |
8409 | CONTRACTL |
8410 | { |
8411 | NOTHROW; |
8412 | GC_TRIGGERS; |
8413 | MODE_ANY; |
8414 | } |
8415 | CONTRACTL_END; |
8416 | |
8417 | #ifndef DACCESS_COMPILE |
8418 | Thread *pThread = NULL; |
8419 | |
8420 | // Need to update our accumulated processor time count with current values from each thread that is |
8421 | // currently executing in this domain. |
8422 | |
8423 | // Take the thread store lock while we enumerate threads. |
8424 | ThreadStoreLockHolder tsl; |
8425 | while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL) |
8426 | { |
8427 | // Skip unstarted and dead threads and those that are currently executing in a different AppDomain. |
8428 | if (pThread->IsUnstarted() || pThread->IsDead() || pThread->GetDomain(INDEBUG(TRUE)) != this) |
8429 | continue; |
8430 | |
8431 | // Add the amount of time spent by the thread in the AppDomain since the last time we asked (calling |
8432 | // Thread::QueryThreadProcessorUsage() will reset the thread's counter). |
8433 | UpdateProcessorUsage(pThread->QueryThreadProcessorUsage()); |
8434 | } |
8435 | #endif // !DACCESS_COMPILE |
8436 | |
8437 | // Return the updated total. |
8438 | return m_ullTotalProcessorUsage; |
8439 | } |
8440 | |
8441 | // Add to the current count of processor time used by threads within this AppDomain. This API is called by |
8442 | // threads transitioning between AppDomains. |
8443 | void AppDomain::UpdateProcessorUsage(ULONGLONG ullAdditionalUsage) |
8444 | { |
8445 | LIMITED_METHOD_CONTRACT; |
8446 | |
8447 | // Need to be careful to synchronize here, multiple threads could be racing to update this count. |
8448 | ULONGLONG ullOldValue; |
8449 | ULONGLONG ullNewValue; |
8450 | do |
8451 | { |
8452 | ullOldValue = m_ullTotalProcessorUsage; |
8453 | ullNewValue = ullOldValue + ullAdditionalUsage; |
8454 | } while (InterlockedCompareExchange64((LONGLONG*)&m_ullTotalProcessorUsage, |
8455 | (LONGLONG)ullNewValue, |
8456 | (LONGLONG)ullOldValue) != (LONGLONG)ullOldValue); |
8457 | } |
8458 | #endif // CROSSGEN_COMPILE |
8459 | |
8460 | #endif // FEATURE_APPDOMAIN_RESOURCE_MONITORING |
8461 | |
8462 | #if defined(FEATURE_TYPEEQUIVALENCE) |
8463 | |
8464 | #ifndef DACCESS_COMPILE |
8465 | TypeEquivalenceHashTable * AppDomain::GetTypeEquivalenceCache() |
8466 | { |
8467 | CONTRACTL |
8468 | { |
8469 | THROWS; |
8470 | GC_TRIGGERS; |
8471 | INJECT_FAULT(COMPlusThrowOM()); |
8472 | MODE_ANY; |
8473 | } |
8474 | CONTRACTL_END; |
8475 | |
8476 | // Take the critical section all of the time in debug builds to ensure that it is safe to take |
8477 | // the critical section in the unusual times when it may actually be needed in retail builds |
8478 | #ifdef _DEBUG |
8479 | CrstHolder ch(&m_TypeEquivalenceCrst); |
8480 | #endif |
8481 | |
8482 | if (m_pTypeEquivalenceTable.Load() == NULL) |
8483 | { |
8484 | #ifndef _DEBUG |
8485 | CrstHolder ch(&m_TypeEquivalenceCrst); |
8486 | #endif |
8487 | if (m_pTypeEquivalenceTable.Load() == NULL) |
8488 | { |
8489 | m_pTypeEquivalenceTable = TypeEquivalenceHashTable::Create(this, /* bucket count */ 12, &m_TypeEquivalenceCrst); |
8490 | } |
8491 | } |
8492 | return m_pTypeEquivalenceTable; |
8493 | } |
8494 | #endif //!DACCESS_COMPILE |
8495 | |
8496 | #endif //FEATURE_TYPEEQUIVALENCE |
8497 | |
8498 | #if !defined(DACCESS_COMPILE) |
8499 | |
8500 | //--------------------------------------------------------------------------------------------------------------------- |
8501 | void AppDomain::PublishHostedAssembly( |
8502 | DomainAssembly * pDomainAssembly) |
8503 | { |
8504 | CONTRACTL |
8505 | { |
8506 | THROWS; |
8507 | GC_NOTRIGGER; |
8508 | MODE_ANY; |
8509 | } |
8510 | CONTRACTL_END |
8511 | |
8512 | if (pDomainAssembly->GetFile()->HasHostAssembly()) |
8513 | { |
8514 | // We have to serialize all Add operations |
8515 | CrstHolder lockAdd(&m_crstHostAssemblyMapAdd); |
8516 | _ASSERTE(m_hostAssemblyMap.Lookup(pDomainAssembly->GetFile()->GetHostAssembly()) == nullptr); |
8517 | |
8518 | // Wrapper for m_hostAssemblyMap.Add that avoids call out into host |
8519 | HostAssemblyMap::AddPhases addCall; |
8520 | |
8521 | // 1. Preallocate one element |
8522 | addCall.PreallocateForAdd(&m_hostAssemblyMap); |
8523 | { |
8524 | // 2. Take the reader lock which can be taken during stack walking |
8525 | // We cannot call out into host from ForbidSuspend region (i.e. no allocations/deallocations) |
8526 | ForbidSuspendThreadHolder suspend; |
8527 | { |
8528 | CrstHolder lock(&m_crstHostAssemblyMap); |
8529 | // 3. Add the element to the hash table (no call out into host) |
8530 | addCall.Add(pDomainAssembly); |
8531 | } |
8532 | } |
8533 | // 4. Cleanup the old memory (if any) |
8534 | addCall.DeleteOldTable(); |
8535 | } |
8536 | else |
8537 | { |
8538 | } |
8539 | } |
8540 | |
8541 | //--------------------------------------------------------------------------------------------------------------------- |
8542 | void AppDomain::UpdatePublishHostedAssembly( |
8543 | DomainAssembly * pAssembly, |
8544 | PTR_PEFile pFile) |
8545 | { |
8546 | CONTRACTL |
8547 | { |
8548 | THROWS; |
8549 | GC_NOTRIGGER; |
8550 | MODE_ANY; |
8551 | CAN_TAKE_LOCK; |
8552 | } |
8553 | CONTRACTL_END |
8554 | |
8555 | if (pAssembly->GetFile()->HasHostAssembly()) |
8556 | { |
8557 | // We have to serialize all Add operations |
8558 | CrstHolder lockAdd(&m_crstHostAssemblyMapAdd); |
8559 | { |
8560 | // Wrapper for m_hostAssemblyMap.Add that avoids call out into host |
8561 | OriginalFileHostAssemblyMap::AddPhases addCall; |
8562 | bool fAddOrigFile = false; |
8563 | |
8564 | // For cases where the pefile is being updated |
8565 | // 1. Preallocate one element |
8566 | if (pFile != pAssembly->GetFile()) |
8567 | { |
8568 | addCall.PreallocateForAdd(&m_hostAssemblyMapForOrigFile); |
8569 | fAddOrigFile = true; |
8570 | } |
8571 | |
8572 | { |
8573 | // We cannot call out into host from ForbidSuspend region (i.e. no allocations/deallocations) |
8574 | ForbidSuspendThreadHolder suspend; |
8575 | { |
8576 | CrstHolder lock(&m_crstHostAssemblyMap); |
8577 | |
8578 | // Remove from hash table. |
8579 | _ASSERTE(m_hostAssemblyMap.Lookup(pAssembly->GetFile()->GetHostAssembly()) != nullptr); |
8580 | m_hostAssemblyMap.Remove(pAssembly->GetFile()->GetHostAssembly()); |
8581 | |
8582 | // Update PEFile on DomainAssembly. (This may cause the key for the hash to change, which is why we need this function) |
8583 | pAssembly->UpdatePEFileWorker(pFile); |
8584 | |
8585 | _ASSERTE(fAddOrigFile == (pAssembly->GetOriginalFile() != pAssembly->GetFile())); |
8586 | if (fAddOrigFile) |
8587 | { |
8588 | // Add to the orig file hash table if we might be in a case where we've cached the original pefile and not the final pe file (for use during GetAssemblyIfLoaded) |
8589 | addCall.Add(pAssembly); |
8590 | } |
8591 | |
8592 | // Add back to the hashtable (the call to Remove above guarantees that we will not call into host for table reallocation) |
8593 | _ASSERTE(m_hostAssemblyMap.Lookup(pAssembly->GetFile()->GetHostAssembly()) == nullptr); |
8594 | m_hostAssemblyMap.Add(pAssembly); |
8595 | } |
8596 | } |
8597 | |
8598 | // 4. Cleanup the old memory (if any) |
8599 | if (fAddOrigFile) |
8600 | addCall.DeleteOldTable(); |
8601 | } |
8602 | } |
8603 | else |
8604 | { |
8605 | |
8606 | pAssembly->UpdatePEFileWorker(pFile); |
8607 | } |
8608 | } |
8609 | |
8610 | //--------------------------------------------------------------------------------------------------------------------- |
8611 | void AppDomain::UnPublishHostedAssembly( |
8612 | DomainAssembly * pAssembly) |
8613 | { |
8614 | CONTRACTL |
8615 | { |
8616 | NOTHROW; |
8617 | GC_NOTRIGGER; |
8618 | MODE_ANY; |
8619 | CAN_TAKE_LOCK; |
8620 | } |
8621 | CONTRACTL_END |
8622 | |
8623 | if (pAssembly->GetFile()->HasHostAssembly()) |
8624 | { |
8625 | ForbidSuspendThreadHolder suspend; |
8626 | { |
8627 | CrstHolder lock(&m_crstHostAssemblyMap); |
8628 | _ASSERTE(m_hostAssemblyMap.Lookup(pAssembly->GetFile()->GetHostAssembly()) != nullptr); |
8629 | m_hostAssemblyMap.Remove(pAssembly->GetFile()->GetHostAssembly()); |
8630 | |
8631 | // We also have an entry in m_hostAssemblyMapForOrigFile. Handle that case. |
8632 | if (pAssembly->GetOriginalFile() != pAssembly->GetFile()) |
8633 | { |
8634 | m_hostAssemblyMapForOrigFile.Remove(pAssembly->GetOriginalFile()->GetHostAssembly()); |
8635 | } |
8636 | } |
8637 | } |
8638 | else |
8639 | { |
8640 | // In AppX processes, all PEAssemblies that are reach this stage should have host binders. |
8641 | _ASSERTE(!AppX::IsAppXProcess()); |
8642 | } |
8643 | } |
8644 | |
8645 | #if defined(FEATURE_COMINTEROP) |
8646 | HRESULT AppDomain::SetWinrtApplicationContext(LPCWSTR pwzAppLocalWinMD) |
8647 | { |
8648 | STANDARD_VM_CONTRACT; |
8649 | |
8650 | _ASSERTE(WinRTSupported()); |
8651 | _ASSERTE(m_pWinRtBinder != nullptr); |
8652 | |
8653 | _ASSERTE(GetTPABinderContext() != NULL); |
8654 | BINDER_SPACE::ApplicationContext *pApplicationContext = GetTPABinderContext()->GetAppContext(); |
8655 | _ASSERTE(pApplicationContext != NULL); |
8656 | |
8657 | return m_pWinRtBinder->SetApplicationContext(pApplicationContext, pwzAppLocalWinMD); |
8658 | } |
8659 | |
8660 | #endif // FEATURE_COMINTEROP |
8661 | |
8662 | #endif //!DACCESS_COMPILE |
8663 | |
8664 | //--------------------------------------------------------------------------------------------------------------------- |
8665 | PTR_DomainAssembly AppDomain::FindAssembly(PTR_ICLRPrivAssembly pHostAssembly) |
8666 | { |
8667 | CONTRACTL |
8668 | { |
8669 | NOTHROW; |
8670 | GC_NOTRIGGER; |
8671 | MODE_ANY; |
8672 | SUPPORTS_DAC; |
8673 | } |
8674 | CONTRACTL_END |
8675 | |
8676 | if (pHostAssembly == nullptr) |
8677 | return NULL; |
8678 | |
8679 | { |
8680 | ForbidSuspendThreadHolder suspend; |
8681 | { |
8682 | CrstHolder lock(&m_crstHostAssemblyMap); |
8683 | PTR_DomainAssembly returnValue = m_hostAssemblyMap.Lookup(pHostAssembly); |
8684 | if (returnValue == NULL) |
8685 | { |
8686 | // If not found in the m_hostAssemblyMap, look in the m_hostAssemblyMapForOrigFile |
8687 | // This is necessary as it may happen during in a second AppDomain that the PEFile |
8688 | // first discovered in the AppDomain may not be used by the DomainFile, but the CLRPrivBinderFusion |
8689 | // will in some cases find the pHostAssembly associated with this no longer used PEFile |
8690 | // instead of the PEFile that was finally decided upon. |
8691 | returnValue = m_hostAssemblyMapForOrigFile.Lookup(pHostAssembly); |
8692 | } |
8693 | |
8694 | return returnValue; |
8695 | } |
8696 | } |
8697 | } |
8698 | |
8699 | #if !defined(DACCESS_COMPILE) && defined(FEATURE_NATIVE_IMAGE_GENERATION) |
8700 | |
8701 | void ZapperSetBindingPaths(ICorCompilationDomain *pDomain, SString &trustedPlatformAssemblies, SString &platformResourceRoots, SString &appPaths, SString &appNiPaths) |
8702 | { |
8703 | CLRPrivBinderCoreCLR *pBinder = static_cast<CLRPrivBinderCoreCLR*>(((CompilationDomain *)pDomain)->GetFusionContext()); |
8704 | _ASSERTE(pBinder != NULL); |
8705 | pBinder->SetupBindingPaths(trustedPlatformAssemblies, platformResourceRoots, appPaths, appNiPaths); |
8706 | #ifdef FEATURE_COMINTEROP |
8707 | ((CompilationDomain*)pDomain)->SetWinrtApplicationContext(NULL); |
8708 | #endif |
8709 | } |
8710 | |
8711 | #endif |
8712 | |