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 // ===========================================================================
9 // ===========================================================================
10 // This file decribes the list lock and deadlock aware list lock.
11 // ===========================================================================
20 template < typename ELEMENT >
22 // This structure is used for running class init methods or JITing methods
23 // (m_pData points to a FunctionDesc). This class cannot have a destructor since it is used
24 // in function that also have EX_TRY's and the VC compiler doesn't allow classes with destructors
25 // to be allocated in a function that used SEH.
26 // <TODO>@FUTURE Keep a pool of these (e.g. an array), so we don't have to allocate on the fly</TODO>
27 // m_hInitException contains a handle to the exception thrown by the class init. This
28 // allows us to throw this information to the caller on subsequent class init attempts.
29 template < typename ELEMENT >
30 class ListLockEntryBase
32 friend class ListLockBase<ELEMENT>;
33 typedef ListLockEntryBase<ELEMENT> Entry_t;
34 typedef ListLockBase<ELEMENT> List_t;
35 typedef typename List_t::LockHolder ListLockHolder;
44 return m_dwRefCount != (DWORD)-1;
48 DeadlockAwareLock m_deadlock;
52 const char * m_pszDescription;
55 HRESULT m_hrResultCode;
56 LOADERHANDLE m_hInitException;
57 PTR_LoaderAllocator m_pLoaderAllocator;
58 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
59 // Field to maintain the corruption severity of the exception
60 CorruptionSeverity m_CorruptionSeverity;
61 #endif // FEATURE_CORRUPTING_EXCEPTIONS
63 ListLockEntryBase(List_t *pList, ELEMENT data, const char *description = NULL)
64 : m_deadlock(description),
68 (CrstFlags)(CRST_REENTRANCY | (pList->IsHostBreakable() ? CRST_HOST_BREAKABLE : 0))),
69 m_pszDescription(description),
72 m_hrResultCode(S_FALSE),
73 m_hInitException(NULL),
74 m_pLoaderAllocator(dac_cast<PTR_LoaderAllocator>(nullptr))
75 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
77 m_CorruptionSeverity(NotCorrupting)
78 #endif // FEATURE_CORRUPTING_EXCEPTIONS
83 virtual ~ListLockEntryBase()
87 DEBUG_NOINLINE void Enter()
90 ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
92 m_deadlock.BeginEnterLock();
93 DeadlockAwareLock::BlockingLockHolder dlLock;
95 m_deadlock.EndEnterLock();
98 BOOL CanDeadlockAwareEnter()
102 return m_deadlock.CanEnterLock();
105 DEBUG_NOINLINE BOOL DeadlockAwareEnter()
108 ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
110 if (!m_deadlock.TryBeginEnterLock())
113 DeadlockAwareLock::BlockingLockHolder dlLock;
115 m_deadlock.EndEnterLock();
120 DEBUG_NOINLINE void Leave()
123 ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
125 m_deadlock.LeaveLock();
129 static Entry_t *Find(List_t* pLock, ELEMENT data, const char *description = NULL)
139 _ASSERTE(pLock->HasLock());
141 Entry_t *pEntry = pLock->Find(data);
144 pEntry = new Entry_t(pLock, data, description);
145 pLock->AddElement(pEntry);
161 PRECONDITION(CheckPointer(this));
165 FastInterlockIncrement((LONG*)&m_dwRefCount);
175 PRECONDITION(CheckPointer(this));
179 ListLockHolder lock(m_pList);
181 if (FastInterlockDecrement((LONG*)&m_dwRefCount) == 0)
184 m_pList->Unlink(this);
193 return(m_Crst.OwnedByCurrentThread());
197 // LockHolder holds the lock of the element, not the element itself
199 DEBUG_NOINLINE static void LockHolderEnter(Entry_t *pThis)
202 ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
206 DEBUG_NOINLINE static void LockHolderLeave(Entry_t *pThis)
209 ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
213 DEBUG_NOINLINE void FinishDeadlockAwareEnter()
215 ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
216 DeadlockAwareLock::BlockingLockHolder dlLock;
218 m_deadlock.EndEnterLock();
221 typedef Wrapper<Entry_t *, LockHolderEnter, LockHolderLeave> LockHolderBase;
223 class LockHolder : public LockHolderBase
228 : LockHolderBase(NULL, FALSE)
232 LockHolder(Entry_t *value, BOOL take = TRUE)
233 : LockHolderBase(value, take)
237 BOOL DeadlockAwareAcquire()
239 if (!this->m_acquired && this->m_value != NULL)
241 if (!this->m_value->m_deadlock.TryBeginEnterLock())
243 this->m_value->FinishDeadlockAwareEnter();
244 this->m_acquired = TRUE;
251 template < typename ELEMENT >
254 typedef ListLockBase<ELEMENT> List_t;
255 typedef ListLockEntryBase<ELEMENT> Entry_t;
260 BOOL m_fHostBreakable; // Lock can be broken by a host for deadlock detection
267 LIMITED_METHOD_CONTRACT;
270 inline void PreInit()
272 LIMITED_METHOD_CONTRACT;
273 memset(this, 0, sizeof(*this));
276 // DO NOT MAKE A CONSTRUCTOR FOR THIS CLASS - There are global instances
277 void Init(CrstType crstType, CrstFlags flags, BOOL fHostBreakable = FALSE)
281 m_Crst.Init(crstType, flags);
283 m_fHostBreakable = fHostBreakable;
289 // There should not be any of these around
290 _ASSERTE(m_pHead == NULL || dbg_fDrasticShutdown || g_fInControlC);
299 BOOL IsHostBreakable () const
301 LIMITED_METHOD_CONTRACT;
302 return m_fHostBreakable;
305 void AddElement(Entry_t* pElement)
308 pElement->m_pNext = m_pHead;
313 DEBUG_NOINLINE void Enter()
315 CANNOT_HAVE_CONTRACT; // See below
316 ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
317 #if 0 // The cleanup logic contract will cause any forbid GC state from the Crst to
318 // get deleted. This causes asserts from Leave. We probably should make the contract
319 // implementation tolerant of this pattern, or else ensure that the state the contract
320 // modifies is not used by any other code.
324 UNCHECKED(GC_TRIGGERS); // May trigger or not based on Crst's type
326 PRECONDITION(CheckPointer(this));
334 DEBUG_NOINLINE void Leave()
337 ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
341 // Must own the lock before calling this or is ok if the debugger has
342 // all threads stopped
343 inline Entry_t *Find(ELEMENT data)
349 PRECONDITION(CheckPointer(this));
350 #ifdef DEBUGGING_SUPPORTED
351 PRECONDITION(m_Crst.OwnedByCurrentThread() ||
352 CORDebuggerAttached()
353 // This condition should be true, but it is awkward to assert it because adding dbginterface.h creates lots of cycles in the includes
354 // It didn't seem valuable enough to refactor out a wrapper just to preserve it
355 /* && g_pDebugInterface->IsStopped() */);
357 PRECONDITION(m_Crst.OwnedByCurrentThread());
358 #endif // DEBUGGING_SUPPORTED
365 for (pSearch = m_pHead; pSearch != NULL; pSearch = pSearch->m_pNext)
367 if (pSearch->m_data == data)
374 // Must own the lock before calling this!
375 Entry_t* Pop(BOOL unloading = FALSE)
377 LIMITED_METHOD_CONTRACT;
379 if(unloading == FALSE)
380 _ASSERTE(m_Crst.OwnedByCurrentThread());
383 if(m_pHead == NULL) return NULL;
384 Entry_t* pEntry = m_pHead;
385 m_pHead = m_pHead->m_pNext;
389 // Must own the lock before calling this!
392 LIMITED_METHOD_CONTRACT;
393 _ASSERTE(m_Crst.OwnedByCurrentThread());
397 // Must own the lock before calling this!
398 BOOL Unlink(Entry_t *pItem)
400 LIMITED_METHOD_CONTRACT;
401 _ASSERTE(m_Crst.OwnedByCurrentThread());
407 for (pSearch = m_pHead; pSearch != NULL; pSearch = pSearch->m_pNext)
409 if (pSearch == pItem)
412 m_pHead = pSearch->m_pNext;
414 pPrev->m_pNext = pSearch->m_pNext;
430 return(m_Crst.OwnedByCurrentThread());
434 DEBUG_NOINLINE static void HolderEnter(List_t *pThis)
437 ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
441 DEBUG_NOINLINE static void HolderLeave(List_t *pThis)
444 ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
448 typedef Wrapper<List_t*, List_t::HolderEnter, List_t::HolderLeave> LockHolder;
451 class WaitingThreadListElement
455 WaitingThreadListElement * m_pNext;
458 typedef class ListLockBase<void*> ListLock;
459 typedef class ListLockEntryBase<void*> ListLockEntry;
461 // Holds the lock of the ListLock
462 typedef ListLock::LockHolder ListLockHolder;
464 // Holds the ownership of the lock element
465 typedef ReleaseHolder<ListLockEntry> ListLockEntryHolder;
467 // Holds the lock of the lock element
468 typedef ListLockEntry::LockHolder ListLockEntryLockHolder;