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
26Volatile<LONG> g_ShutdownCrstUsageCount = 0;
27
28//-----------------------------------------------------------------
29// Initialize critical section
30//-----------------------------------------------------------------
31VOID 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//-----------------------------------------------------------------
59void 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
91extern 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//-----------------------------------------------------------------
99void 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
142void 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
156void 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//-----------------------------------------------------------------
330void 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
379void 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
435void 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
504void 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.
568struct CrstDebugInfo
569{
570 CrstBase *pAddress;
571 CrstType crstType;
572};
573const int crstDebugInfoCount = 4000;
574CrstDebugInfo crstDebugInfo[crstDebugInfoCount];
575
576CrstBase *CrstBase::GetThreadsOwnedCrsts()
577{
578 return (CrstBase*)ClrFlsGetValue(TlsIdx_OwnedCrstsChain);
579}
580void CrstBase::SetThreadsOwnedCrsts(CrstBase *pCrst)
581{
582 WRAPPER_NO_CONTRACT;
583 ClrFlsSetValue(TlsIdx_OwnedCrstsChain, (LPVOID) (pCrst));
584}
585
586void 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
628void 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//-----------------------------------------------------------------
685BOOL 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
779void 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