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 | // CRST.CPP |
6 | // |
7 | |
8 | // |
9 | |
10 | |
11 | #include "common.h" |
12 | |
13 | #include "crst.h" |
14 | #include "log.h" |
15 | #include "corhost.h" |
16 | |
17 | // We need to know if we're on the helper thread. We need this header for g_pDebugInterface. |
18 | #include "dbginterface.h" |
19 | #include "threadsuspend.h" |
20 | |
21 | #define __IN_CRST_CPP |
22 | #include <crsttypes.h> |
23 | #undef __IN_CRST_CPP |
24 | |
25 | #ifndef DACCESS_COMPILE |
26 | Volatile<LONG> g_ShutdownCrstUsageCount = 0; |
27 | |
28 | //----------------------------------------------------------------- |
29 | // Initialize critical section |
30 | //----------------------------------------------------------------- |
31 | VOID CrstBase::InitWorker(INDEBUG_COMMA(CrstType crstType) CrstFlags flags) |
32 | { |
33 | CONTRACTL { |
34 | THROWS; |
35 | WRAPPER(GC_TRIGGERS); |
36 | } CONTRACTL_END; |
37 | |
38 | _ASSERTE((flags & CRST_INITIALIZED) == 0); |
39 | |
40 | { |
41 | SetOSCritSec (); |
42 | } |
43 | |
44 | { |
45 | UnsafeInitializeCriticalSection(&m_criticalsection); |
46 | } |
47 | |
48 | SetFlags(flags); |
49 | SetCrstInitialized(); |
50 | |
51 | #ifdef _DEBUG |
52 | DebugInit(crstType, flags); |
53 | #endif |
54 | } |
55 | |
56 | //----------------------------------------------------------------- |
57 | // Clean up critical section |
58 | //----------------------------------------------------------------- |
59 | void CrstBase::Destroy() |
60 | { |
61 | WRAPPER_NO_CONTRACT; |
62 | |
63 | // nothing to do if not initialized |
64 | if (!IsCrstInitialized()) |
65 | return; |
66 | |
67 | // If this assert fired, a crst got deleted while some thread |
68 | // still owned it. This can happen if the process detaches from |
69 | // our DLL. |
70 | #ifdef _DEBUG |
71 | EEThreadId holderthreadid = m_holderthreadid; |
72 | _ASSERTE(holderthreadid.IsUnknown() || IsAtProcessExit() || g_fEEShutDown); |
73 | #endif |
74 | |
75 | // If a lock is host breakable, a host is required to block the release call until |
76 | // deadlock detection is finished. |
77 | GCPreemp __gcHolder((m_dwFlags & CRST_HOST_BREAKABLE) == CRST_HOST_BREAKABLE); |
78 | |
79 | { |
80 | UnsafeDeleteCriticalSection(&m_criticalsection); |
81 | } |
82 | |
83 | LOG((LF_SYNC, INFO3, "Deleting 0x%x\n" , this)); |
84 | #ifdef _DEBUG |
85 | DebugDestroy(); |
86 | #endif |
87 | |
88 | ResetFlags(); |
89 | } |
90 | |
91 | extern void WaitForEndOfShutdown(); |
92 | |
93 | //----------------------------------------------------------------- |
94 | // If we're in shutdown (as determined by caller since each lock needs its |
95 | // own shutdown flag) and this is a non-special thread (not helper/finalizer/shutdown), |
96 | // then release the crst and block forever. |
97 | // See the prototype for more details. |
98 | //----------------------------------------------------------------- |
99 | void CrstBase::ReleaseAndBlockForShutdownIfNotSpecialThread() |
100 | { |
101 | CONTRACTL { |
102 | NOTHROW; |
103 | |
104 | // We're almost always MODE_PREEMPTIVE, but if it's a thread suspending for GC, |
105 | // then we might be MODE_COOPERATIVE. Fortunately in that case, we don't block on shutdown. |
106 | // We assert this below. |
107 | MODE_ANY; |
108 | GC_NOTRIGGER; |
109 | |
110 | PRECONDITION(this->OwnedByCurrentThread()); |
111 | } |
112 | CONTRACTL_END; |
113 | |
114 | if ( |
115 | (((size_t)ClrFlsGetValue (TlsIdx_ThreadType)) & (ThreadType_Finalizer|ThreadType_DbgHelper|ThreadType_Shutdown|ThreadType_GC)) == 0) |
116 | { |
117 | // The process is shutting down. Release the lock and just block forever. |
118 | this->Leave(); |
119 | |
120 | // is this safe to use here since we never return? |
121 | GCX_ASSERT_PREEMP(); |
122 | |
123 | WaitForEndOfShutdown(); |
124 | __SwitchToThread(INFINITE, CALLER_LIMITS_SPINNING); |
125 | _ASSERTE (!"Can not reach here" ); |
126 | } |
127 | } |
128 | |
129 | #endif // DACCESS_COMPILE |
130 | |
131 | |
132 | //----------------------------------------------------------------- |
133 | // Acquire the lock. |
134 | //----------------------------------------------------------------- |
135 | #ifdef DACCESS_COMPILE |
136 | // In DAC builds, we will not actually take the lock. Instead, we just need to determine |
137 | // whether the LS holds the lock. If it does, we assume the locked data is in an inconsistent |
138 | // state and throw, rather than using erroneous values. |
139 | // Argument: |
140 | // input: noLevelCheckFlag - indicates whether to check the crst level |
141 | // Note: Throws |
142 | void CrstBase::Enter(INDEBUG(NoLevelCheckFlag noLevelCheckFlag/* = CRST_LEVEL_CHECK*/)) |
143 | { |
144 | #ifdef _DEBUG |
145 | if (m_entercount != 0) |
146 | { |
147 | ThrowHR(CORDBG_E_PROCESS_NOT_SYNCHRONIZED); |
148 | } |
149 | #endif |
150 | } |
151 | #else // !DACCESS_COMPILE |
152 | |
153 | |
154 | |
155 | |
156 | void CrstBase::Enter(INDEBUG(NoLevelCheckFlag noLevelCheckFlag/* = CRST_LEVEL_CHECK*/)) |
157 | { |
158 | //------------------------------------------------------------------------------------------- |
159 | // What, no CONTRACT? |
160 | // |
161 | // We can't put an actual CONTRACT here as PostEnter() makes unscoped changes to the GC_NoTrigger |
162 | // counter. But we do perform the equivalent checks manually. |
163 | // |
164 | // What's worse, the implied contract differs for different flavors of crst. |
165 | // |
166 | // THROWS/FAULT |
167 | // |
168 | // A crst can be HOST_BREAKBALE or not. A HOST_BREAKABLE crst can throw on an attempt to enter |
169 | // (due to deadlock breaking by the host.) A non-breakable crst will never |
170 | // throw or OOM or fail an enter. |
171 | // |
172 | // |
173 | // |
174 | // |
175 | // GC/MODE |
176 | // Orthogonally, a crst can be one of the following flavors. We only want to see the |
177 | // "normal" type used in new code. Other types, kept for legacy reasons, are listed in |
178 | // order from least objectionable to most objectionable. |
179 | // |
180 | // normal - This is the preferred type of crst. Enter() will force-switch your thread |
181 | // into preemptive mode if it isn't already. Thus, the effective contract is: |
182 | // |
183 | // MODE_ANY |
184 | // GC_TRIGGERS |
185 | // |
186 | // |
187 | // |
188 | // CRST_UNSAFE_COOPGC - You can only attempt to acquire this crst if you're already |
189 | // in coop mode. It is guaranteed no GC will occur while waiting to acquire the lock. |
190 | // While you hold the lock, your thread is in a GCFORBID state. |
191 | // |
192 | // MODE_COOP |
193 | // GC_NOTRIGGER |
194 | // |
195 | // |
196 | // |
197 | // CRST_UNSAFE_ANYMODE - You can attempt to acquire this in either mode. Entering the |
198 | // crst will not change your thread mode but it will increment the GCNoTrigger count. |
199 | // |
200 | // MODE_ANY |
201 | // GC_NOTRIGGER |
202 | //------------------------------------------------------------------------------------------------ |
203 | |
204 | #ifdef ENABLE_CONTRACTS_IMPL |
205 | ClrDebugState *pClrDebugState = CheckClrDebugState(); |
206 | if (pClrDebugState) |
207 | { |
208 | if (m_dwFlags & CRST_HOST_BREAKABLE) |
209 | { |
210 | if (pClrDebugState->IsFaultForbid() && |
211 | !(pClrDebugState->ViolationMask() & (FaultViolation|FaultNotFatal|BadDebugState))) |
212 | { |
213 | CONTRACT_ASSERT("You cannot enter a HOST_BREAKABLE lock in a FAULTFORBID region." , |
214 | Contract::FAULT_Forbid, |
215 | Contract::FAULT_Mask, |
216 | __FUNCTION__, |
217 | __FILE__, |
218 | __LINE__); |
219 | } |
220 | |
221 | if (!(pClrDebugState->CheckOkayToThrowNoAssert())) |
222 | { |
223 | CONTRACT_ASSERT("You cannot enter a HOST_BREAKABLE lock in a NOTHROW region." , |
224 | Contract::THROWS_No, |
225 | Contract::THROWS_Mask, |
226 | __FUNCTION__, |
227 | __FILE__, |
228 | __LINE__); |
229 | } |
230 | } |
231 | |
232 | // If we might want to toggle the GC mode, then we better not be in a GC_NOTRIGGERS region |
233 | if (!(m_dwFlags & (CRST_UNSAFE_COOPGC | CRST_UNSAFE_ANYMODE | CRST_GC_NOTRIGGER_WHEN_TAKEN))) |
234 | { |
235 | if (pClrDebugState->GetGCNoTriggerCount()) |
236 | { |
237 | // If we have no thread object, we won't be toggling the GC. This is the case, |
238 | // for example, on the debugger helper thread which is always GC_NOTRIGGERS. |
239 | if (GetThreadNULLOk() != NULL) |
240 | { |
241 | // Will we really need to change GC mode COOPERATIVE to PREEMPTIVE? |
242 | if (GetThreadNULLOk()->PreemptiveGCDisabled()) |
243 | { |
244 | if (!((GCViolation | BadDebugState) & pClrDebugState->ViolationMask())) |
245 | { |
246 | CONTRACT_ASSERT("You cannot enter a lock in a GC_NOTRIGGER + MODE_COOPERATIVE region." , |
247 | Contract::GC_NoTrigger, |
248 | Contract::GC_Mask, |
249 | __FUNCTION__, |
250 | __FILE__, |
251 | __LINE__); |
252 | } |
253 | } |
254 | } |
255 | } |
256 | } |
257 | |
258 | // The mode checks and enforcement of GC_NOTRIGGER during the lock are done in CrstBase::PostEnter(). |
259 | |
260 | } |
261 | #endif //ENABLE_CONTRACTS_IMPL |
262 | |
263 | |
264 | |
265 | SCAN_IGNORE_THROW; |
266 | SCAN_IGNORE_FAULT; |
267 | SCAN_IGNORE_TRIGGER; |
268 | STATIC_CONTRACT_CAN_TAKE_LOCK; |
269 | |
270 | _ASSERTE(IsCrstInitialized()); |
271 | |
272 | // Is Critical Section entered? |
273 | // We could have perhaps used m_criticalsection.LockCount, but |
274 | // while spinning, we want to fire the ETW event only once |
275 | BOOL fIsCriticalSectionEnteredAfterFailingOnce = FALSE; |
276 | |
277 | Thread * pThread; |
278 | BOOL fToggle; |
279 | |
280 | BEGIN_GETTHREAD_ALLOWED; |
281 | pThread = GetThread(); |
282 | fToggle = ((m_dwFlags & (CRST_UNSAFE_ANYMODE | CRST_UNSAFE_COOPGC | CRST_GC_NOTRIGGER_WHEN_TAKEN)) == 0) // condition normally false |
283 | && pThread && pThread->PreemptiveGCDisabled(); |
284 | |
285 | if (fToggle) { |
286 | pThread->EnablePreemptiveGC(); |
287 | } |
288 | END_GETTHREAD_ALLOWED; |
289 | |
290 | #ifdef _DEBUG |
291 | PreEnter (); |
292 | #endif |
293 | |
294 | _ASSERTE(noLevelCheckFlag == CRST_NO_LEVEL_CHECK || IsSafeToTake() || g_fEEShutDown); |
295 | |
296 | // Check for both rare case using one if-check |
297 | if (m_dwFlags & (CRST_TAKEN_DURING_SHUTDOWN | CRST_DEBUGGER_THREAD)) |
298 | { |
299 | if (m_dwFlags & CRST_TAKEN_DURING_SHUTDOWN) |
300 | { |
301 | // increment the usage count of locks that can be taken during shutdown |
302 | FastInterlockIncrement(&g_ShutdownCrstUsageCount); |
303 | } |
304 | |
305 | // If this is a debugger lock, bump up the "Can't-Stop" count. |
306 | // We'll bump it down when we release the lock. |
307 | if (m_dwFlags & CRST_DEBUGGER_THREAD) |
308 | { |
309 | IncCantStopCount(); |
310 | } |
311 | } |
312 | |
313 | UnsafeEnterCriticalSection(&m_criticalsection); |
314 | |
315 | #ifdef _DEBUG |
316 | PostEnter(); |
317 | #endif |
318 | |
319 | if (fToggle) |
320 | { |
321 | BEGIN_GETTHREAD_ALLOWED; |
322 | pThread->DisablePreemptiveGC(); |
323 | END_GETTHREAD_ALLOWED; |
324 | } |
325 | } |
326 | |
327 | //----------------------------------------------------------------- |
328 | // Release the lock. |
329 | //----------------------------------------------------------------- |
330 | void CrstBase::Leave() |
331 | { |
332 | STATIC_CONTRACT_MODE_ANY; |
333 | STATIC_CONTRACT_NOTHROW; |
334 | STATIC_CONTRACT_GC_NOTRIGGER; |
335 | |
336 | _ASSERTE(IsCrstInitialized()); |
337 | |
338 | #ifdef _DEBUG |
339 | PreLeave (); |
340 | #endif //_DEBUG |
341 | |
342 | #if defined(_DEBUG) |
343 | Thread * pThread = GetThread(); |
344 | #endif |
345 | |
346 | UnsafeLeaveCriticalSection(&m_criticalsection); |
347 | |
348 | // Check for both rare case using one if-check |
349 | if (m_dwFlags & (CRST_TAKEN_DURING_SHUTDOWN | CRST_DEBUGGER_THREAD)) |
350 | { |
351 | // If this is a debugger lock, restore the "Can't-Stop" count. |
352 | // We bumped it up when we Entered the lock. |
353 | if (m_dwFlags & CRST_DEBUGGER_THREAD) |
354 | { |
355 | DecCantStopCount(); |
356 | } |
357 | |
358 | if (m_dwFlags & CRST_TAKEN_DURING_SHUTDOWN) |
359 | { |
360 | // decrement the usage count of locks that can be taken during shutdown |
361 | _ASSERTE_MSG(g_ShutdownCrstUsageCount.Load() > 0, "Attempting to leave a lock that was never taken!" ); |
362 | FastInterlockDecrement(&g_ShutdownCrstUsageCount); |
363 | } |
364 | } |
365 | |
366 | #ifdef _DEBUG |
367 | //_ASSERTE(m_cannotLeave==0 || OwnedByCurrentThread()); |
368 | |
369 | if ((pThread != NULL) && |
370 | (m_dwFlags & CRST_DEBUG_ONLY_CHECK_FORBID_SUSPEND_THREAD)) |
371 | { // The lock requires ForbidSuspendRegion while it is taken |
372 | CONSISTENCY_CHECK_MSGF(pThread->IsInForbidSuspendRegion(), ("ForbidSuspend region was released before the lock:'%s'" , m_tag)); |
373 | } |
374 | #endif //_DEBUG |
375 | } // CrstBase::Leave |
376 | |
377 | |
378 | #ifdef _DEBUG |
379 | void CrstBase::PreEnter() |
380 | { |
381 | STATIC_CONTRACT_NOTHROW; |
382 | STATIC_CONTRACT_GC_NOTRIGGER; |
383 | |
384 | // Are we in the shutdown sequence and in phase 2 of it? |
385 | if (g_fProcessDetach && (g_fEEShutDown & ShutDown_Phase2)) |
386 | { |
387 | // Ensure that this lock has been flagged to be taken during shutdown |
388 | _ASSERTE_MSG(CanBeTakenDuringShutdown(), "Attempting to take a lock at shutdown that is not CRST_TAKEN_DURING_SHUTDOWN" ); |
389 | } |
390 | |
391 | Thread * pThread = GetThreadNULLOk(); |
392 | |
393 | if (pThread) |
394 | { |
395 | // If the thread has SpinLock, it can not take Crst. |
396 | _ASSERTE ((pThread->m_StateNC & Thread::TSNC_OwnsSpinLock) == 0); |
397 | } |
398 | |
399 | // If we're on the debugger helper thread, we can only take helper thread locks. |
400 | bool fIsHelperThread = (g_pDebugInterface == NULL) ? false : g_pDebugInterface->ThisIsHelperThread(); |
401 | bool fIsDebuggerLock = (m_dwFlags & CRST_DEBUGGER_THREAD) != 0; |
402 | |
403 | // don't enforce this check during regular process exit or fail fast |
404 | if (fIsHelperThread && !fIsDebuggerLock && !IsAtProcessExit() && !g_fFastExitProcess) |
405 | { |
406 | CONSISTENCY_CHECK_MSGF(false, ("Helper thread taking non-helper lock:'%s'" , m_tag)); |
407 | } |
408 | |
409 | // If a thread suspends another thread, it cannot acquire locks. |
410 | if ((pThread != NULL) && |
411 | (pThread->Debug_GetUnsafeSuspendeeCount() != 0)) |
412 | { |
413 | CONSISTENCY_CHECK_MSGF(false, ("Suspender thread taking non-suspender lock:'%s'" , m_tag)); |
414 | } |
415 | |
416 | if (ThreadStore::s_pThreadStore->IsCrstForThreadStore(this)) |
417 | return; |
418 | |
419 | if (m_dwFlags & CRST_UNSAFE_COOPGC) |
420 | { |
421 | CONSISTENCY_CHECK (IsGCThread () |
422 | || (pThread != NULL && pThread->PreemptiveGCDisabled()) |
423 | // If GC heap has not been initialized yet, there is no need to synchronize with GC. |
424 | // This check is mainly for code called from EEStartup. |
425 | || (pThread == NULL && !GCHeapUtilities::IsGCHeapInitialized()) ); |
426 | } |
427 | |
428 | if ((pThread != NULL) && |
429 | (m_dwFlags & CRST_DEBUG_ONLY_CHECK_FORBID_SUSPEND_THREAD)) |
430 | { |
431 | CONSISTENCY_CHECK_MSGF(pThread->IsInForbidSuspendRegion(), ("The lock '%s' can be taken only in ForbidSuspend region." , m_tag)); |
432 | } |
433 | } |
434 | |
435 | void CrstBase::PostEnter() |
436 | { |
437 | STATIC_CONTRACT_NOTHROW; |
438 | STATIC_CONTRACT_GC_NOTRIGGER; |
439 | |
440 | if ((m_dwFlags & CRST_HOST_BREAKABLE) != 0) |
441 | { |
442 | HOST_BREAKABLE_CRST_TAKEN(this); |
443 | } |
444 | else |
445 | { |
446 | EE_LOCK_TAKEN(this); |
447 | } |
448 | |
449 | _ASSERTE((m_entercount == 0 && m_holderthreadid.IsUnknown()) || |
450 | m_holderthreadid.IsCurrentThread() || |
451 | IsAtProcessExit()); |
452 | m_holderthreadid.SetToCurrentThread(); |
453 | m_entercount++; |
454 | |
455 | if (m_entercount == 1) |
456 | { |
457 | _ASSERTE((m_next == NULL) && (m_prev == NULL)); |
458 | |
459 | // Link this Crst into the Thread's chain of OwnedCrsts |
460 | CrstBase *pcrst = GetThreadsOwnedCrsts(); |
461 | if (pcrst == NULL) |
462 | { |
463 | SetThreadsOwnedCrsts (this); |
464 | } |
465 | else |
466 | { |
467 | while (pcrst->m_next != NULL) |
468 | pcrst = pcrst->m_next; |
469 | pcrst->m_next = this; |
470 | m_prev = pcrst; |
471 | } |
472 | } |
473 | |
474 | Thread * pThread = GetThreadNULLOk(); |
475 | if ((m_dwFlags & CRST_HOST_BREAKABLE) == 0) |
476 | { |
477 | if (pThread) |
478 | { |
479 | pThread->IncUnbreakableLockCount(); |
480 | } |
481 | } |
482 | |
483 | if (ThreadStore::s_pThreadStore->IsCrstForThreadStore(this)) |
484 | return; |
485 | |
486 | if (m_dwFlags & (CRST_UNSAFE_ANYMODE | CRST_UNSAFE_COOPGC | CRST_GC_NOTRIGGER_WHEN_TAKEN)) |
487 | { |
488 | if (pThread == NULL) |
489 | { |
490 | // Cannot set NoTrigger. This could conceivably turn into |
491 | // A GC hole if the thread is created and then a GC rendezvous happens |
492 | // while the lock is still held. |
493 | } |
494 | else |
495 | { |
496 | // Keep a count, since the thread may change from NULL to non-NULL and |
497 | // we don't want to have unbalanced NoTrigger calls |
498 | m_countNoTriggerGC++; |
499 | INCONTRACT(pThread->BeginNoTriggerGC(__FILE__, __LINE__)); |
500 | } |
501 | } |
502 | } |
503 | |
504 | void CrstBase::PreLeave() |
505 | { |
506 | LIMITED_METHOD_CONTRACT; |
507 | |
508 | _ASSERTE(OwnedByCurrentThread()); |
509 | _ASSERTE(m_entercount > 0); |
510 | m_entercount--; |
511 | if (!m_entercount) { |
512 | m_holderthreadid.Clear(); |
513 | |
514 | // Delink it from the Thread's chain of OwnedChain |
515 | if (m_prev) |
516 | m_prev->m_next = m_next; |
517 | else |
518 | SetThreadsOwnedCrsts(m_next); |
519 | |
520 | if (m_next) |
521 | m_next->m_prev = m_prev; |
522 | |
523 | m_next = NULL; |
524 | m_prev = NULL; |
525 | } |
526 | |
527 | Thread * pThread = GetThreadNULLOk(); |
528 | |
529 | if ((m_dwFlags & CRST_HOST_BREAKABLE) == 0) |
530 | { |
531 | if (pThread) |
532 | { |
533 | pThread->DecUnbreakableLockCount(); |
534 | } |
535 | } |
536 | |
537 | if (m_countNoTriggerGC > 0 && !ThreadStore::s_pThreadStore->IsCrstForThreadStore(this)) |
538 | { |
539 | m_countNoTriggerGC--; |
540 | if (pThread != NULL) |
541 | { |
542 | INCONTRACT(pThread->EndNoTriggerGC()); |
543 | } |
544 | } |
545 | |
546 | if ((m_dwFlags & CRST_HOST_BREAKABLE) != 0) |
547 | { |
548 | HOST_BREAKABLE_CRST_RELEASED(this); |
549 | } |
550 | else |
551 | { |
552 | EE_LOCK_RELEASED(this); |
553 | } |
554 | |
555 | // Are we in the shutdown sequence and in phase 2 of it? |
556 | if (g_fProcessDetach && (g_fEEShutDown & ShutDown_Phase2)) |
557 | { |
558 | // Ensure that this lock has been flagged to be taken during shutdown |
559 | _ASSERTE_MSG(CanBeTakenDuringShutdown(), "Attempting to leave a lock at shutdown that is not CRST_TAKEN_DURING_SHUTDOWN" ); |
560 | } |
561 | |
562 | } |
563 | |
564 | // We have seen several times that a Crst is not destroyed before its memory is freed. This corrupts |
565 | // our chain, and also causes memory leak. The following structure is to track what Crst exists. |
566 | // If our chain is broken, find out which Crst causes problem, then lookup this array. The problematic |
567 | // Crst can be identified with crstType. |
568 | struct CrstDebugInfo |
569 | { |
570 | CrstBase *pAddress; |
571 | CrstType crstType; |
572 | }; |
573 | const int crstDebugInfoCount = 4000; |
574 | CrstDebugInfo crstDebugInfo[crstDebugInfoCount]; |
575 | |
576 | CrstBase *CrstBase::GetThreadsOwnedCrsts() |
577 | { |
578 | return (CrstBase*)ClrFlsGetValue(TlsIdx_OwnedCrstsChain); |
579 | } |
580 | void CrstBase::SetThreadsOwnedCrsts(CrstBase *pCrst) |
581 | { |
582 | WRAPPER_NO_CONTRACT; |
583 | ClrFlsSetValue(TlsIdx_OwnedCrstsChain, (LPVOID) (pCrst)); |
584 | } |
585 | |
586 | void CrstBase::DebugInit(CrstType crstType, CrstFlags flags) |
587 | { |
588 | LIMITED_METHOD_CONTRACT; |
589 | |
590 | m_crstType = crstType; |
591 | m_tag = GetCrstName(crstType); |
592 | m_crstlevel = GetCrstLevel(crstType); |
593 | m_holderthreadid.Clear(); |
594 | m_entercount = 0; |
595 | m_next = NULL; |
596 | m_prev = NULL; |
597 | m_cannotLeave=0; |
598 | |
599 | _ASSERTE((m_dwFlags & ~(CRST_REENTRANCY | |
600 | CRST_UNSAFE_SAMELEVEL | |
601 | CRST_UNSAFE_COOPGC | |
602 | CRST_UNSAFE_ANYMODE | |
603 | CRST_DEBUGGER_THREAD | |
604 | CRST_HOST_BREAKABLE | |
605 | CRST_OS_CRIT_SEC | |
606 | CRST_INITIALIZED | |
607 | CRST_TAKEN_DURING_SHUTDOWN | |
608 | CRST_GC_NOTRIGGER_WHEN_TAKEN | |
609 | CRST_DEBUG_ONLY_CHECK_FORBID_SUSPEND_THREAD)) == 0); |
610 | |
611 | // @todo - Any Crst w/ CRST_DEBUGGER_THREAD must be on a special blessed list. Check that here. |
612 | |
613 | LOG((LF_SYNC, INFO3, "ConstructCrst with this:0x%x\n" , this)); |
614 | |
615 | for (int i = 0; i < crstDebugInfoCount; i++) |
616 | { |
617 | if (crstDebugInfo[i].pAddress == NULL) |
618 | { |
619 | crstDebugInfo[i].pAddress = this; |
620 | crstDebugInfo[i].crstType = crstType; |
621 | break; |
622 | } |
623 | } |
624 | |
625 | m_countNoTriggerGC = 0; |
626 | } |
627 | |
628 | void CrstBase::DebugDestroy() |
629 | { |
630 | LIMITED_METHOD_CONTRACT; |
631 | |
632 | // Ideally, when we destroy the crst, it wouldn't be held. |
633 | // This is violated if a thread holds a lock and is asynchronously killed |
634 | // (such as what happens on ExitProcess). |
635 | // Delink it from the Thread's chain of OwnedChain |
636 | if (IsAtProcessExit()) |
637 | { |
638 | // In shutdown scenario, crst may or may not be held. |
639 | if (m_prev == NULL) |
640 | { |
641 | if (!m_holderthreadid.IsUnknown()) // Crst taken! |
642 | { |
643 | if (m_next) |
644 | m_next->m_prev = NULL; // workaround: break up the chain |
645 | SetThreadsOwnedCrsts(NULL); |
646 | } |
647 | } |
648 | else |
649 | { |
650 | m_prev->m_next = m_next; |
651 | if (m_next) |
652 | m_next->m_prev = m_prev; |
653 | } |
654 | } |
655 | else |
656 | { |
657 | // Crst is destroyed while being held. |
658 | CONSISTENCY_CHECK_MSGF( |
659 | ((m_prev == NULL) && (m_next == NULL) && m_holderthreadid.IsUnknown()), |
660 | ("CRST '%s' is destroyed while being held in non-shutdown scenario.\n" |
661 | "this=0x%p, m_prev=0x%p. m_next=0x%p" , m_tag, this, this->m_prev, this->m_next)); |
662 | } |
663 | |
664 | FillMemory(&m_criticalsection, sizeof(m_criticalsection), 0xcc); |
665 | m_holderthreadid.Clear(); |
666 | m_entercount = 0xcccccccc; |
667 | |
668 | m_next = (CrstBase*)POISONC; |
669 | m_prev = (CrstBase*)POISONC; |
670 | |
671 | for (int i = 0; i < crstDebugInfoCount; i++) |
672 | { |
673 | if (crstDebugInfo[i].pAddress == this) |
674 | { |
675 | crstDebugInfo[i].pAddress = NULL; |
676 | crstDebugInfo[i].crstType = kNumberOfCrstTypes; |
677 | break; |
678 | } |
679 | } |
680 | } |
681 | |
682 | //----------------------------------------------------------------- |
683 | // Check if attempting to take the lock would violate level order. |
684 | //----------------------------------------------------------------- |
685 | BOOL CrstBase::IsSafeToTake() |
686 | { |
687 | CONTRACTL { |
688 | DEBUG_ONLY; |
689 | WRAPPER(THROWS); |
690 | WRAPPER(GC_TRIGGERS); |
691 | } CONTRACTL_END; |
692 | |
693 | // If mscoree.dll is being detached |
694 | if (IsAtProcessExit()) |
695 | return TRUE; |
696 | |
697 | // Cannot take a Crst in cooperative mode unless CRST_UNSAFE_COOPGC is set, in |
698 | // which case it must always be taken in this mode. |
699 | // If there is no thread object, we ignore the check since this thread isn't |
700 | // coordinated with the GC. |
701 | Thread * pThread; |
702 | BEGIN_GETTHREAD_ALLOWED; |
703 | pThread = GetThread(); |
704 | |
705 | _ASSERTE(pThread == NULL || |
706 | (pThread->PreemptiveGCDisabled() == ((m_dwFlags & CRST_UNSAFE_COOPGC) != 0)) || |
707 | ((m_dwFlags & (CRST_UNSAFE_ANYMODE | CRST_GC_NOTRIGGER_WHEN_TAKEN)) != 0) || |
708 | (GCHeapUtilities::IsGCInProgress() && pThread == ThreadSuspend::GetSuspensionThread())); |
709 | END_GETTHREAD_ALLOWED; |
710 | |
711 | if (m_holderthreadid.IsCurrentThread()) |
712 | { |
713 | // If we already hold it, we can't violate level order. |
714 | // Check if client wanted to allow reentrancy. |
715 | if ((m_dwFlags & CRST_REENTRANCY) == 0) |
716 | { |
717 | LOG((LF_SYNC, INFO3, "Crst Reentrancy violation on %s\n" , m_tag)); |
718 | // So that we can debug here. |
719 | _ASSERTE (g_fEEShutDown || !"Crst Reentrancy violation" ); |
720 | } |
721 | return ((m_dwFlags & CRST_REENTRANCY) != 0); |
722 | } |
723 | |
724 | // Is the current Crst exempt from the Crst ranking enforcement? |
725 | if (m_crstlevel == CRSTUNORDERED |
726 | // when the thread is doing a stressing GC, some Crst violations could be ignored |
727 | // also, we want to keep an explicit list of Crst's that we may take during GC stress |
728 | || (pThread && pThread->GetGCStressing () |
729 | && (m_crstType == CrstThreadStore || m_crstType == CrstHandleTable |
730 | || m_crstType == CrstSyncBlockCache || m_crstType == CrstIbcProfile |
731 | || m_crstType == CrstAvailableParamTypes || m_crstType == CrstSystemDomainDelayedUnloadList |
732 | || m_crstType == CrstAssemblyList || m_crstType == CrstJumpStubCache |
733 | || m_crstType == CrstSingleUseLock) |
734 | ) |
735 | || (pThread && pThread->GetUniqueStacking ()) |
736 | ) |
737 | { |
738 | return TRUE; |
739 | } |
740 | |
741 | // See if the current thread already owns a lower or sibling lock. |
742 | BOOL fSafe = TRUE; |
743 | for (CrstBase *pcrst = GetThreadsOwnedCrsts(); pcrst != NULL; pcrst = pcrst->m_next) |
744 | { |
745 | fSafe = |
746 | !pcrst->m_holderthreadid.IsCurrentThread() |
747 | || (pcrst->m_crstlevel == CRSTUNORDERED) |
748 | || (pcrst->m_crstlevel > m_crstlevel) |
749 | || (pcrst->m_crstlevel == m_crstlevel && (m_dwFlags & CRST_UNSAFE_SAMELEVEL) != 0); |
750 | if (!fSafe) |
751 | { |
752 | LOG((LF_SYNC, INFO3, "Crst Level violation: Can't take level %lu lock %s because you already holding level %lu lock %s\n" , |
753 | (ULONG)m_crstlevel, m_tag, (ULONG)(pcrst->m_crstlevel), pcrst->m_tag)); |
754 | // So that we can debug here. |
755 | if (!g_fEEShutDown) |
756 | { |
757 | CONSISTENCY_CHECK_MSGF(false, ("Crst Level violation: Can't take level %lu lock %s because you already holding level %lu lock %s\n" , |
758 | (ULONG)m_crstlevel, |
759 | m_tag, |
760 | (ULONG)(pcrst->m_crstlevel), |
761 | pcrst->m_tag)); |
762 | } |
763 | break; |
764 | } |
765 | } |
766 | return fSafe; |
767 | } |
768 | |
769 | #endif // _DEBUG |
770 | |
771 | #endif // !DACCESS_COMPILE |
772 | |
773 | #ifdef TEST_DATA_CONSISTENCY |
774 | // used for test purposes. Determines if a crst is held. |
775 | // Arguments: |
776 | // input: pLock - the lock to test |
777 | // Note: Throws if the lock is held |
778 | |
779 | void DebugTryCrst(CrstBase * pLock) |
780 | { |
781 | SUPPORTS_DAC; |
782 | |
783 | if (g_pConfig && g_pConfig->TestDataConsistency()) |
784 | { |
785 | CrstHolder crstHolder (pLock); |
786 | } |
787 | } |
788 | #endif |
789 | |
790 | |