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// File: ListLock.h
6//
7
8//
9// ===========================================================================
10// This file decribes the list lock and deadlock aware list lock.
11// ===========================================================================
12
13#ifndef LISTLOCK_H
14#define LISTLOCK_H
15
16#include "vars.hpp"
17#include "threads.h"
18#include "crst.h"
19
20template < typename ELEMENT >
21class ListLockBase;
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.
29template < typename ELEMENT >
30class ListLockEntryBase
31{
32 friend class ListLockBase<ELEMENT>;
33 typedef ListLockEntryBase<ELEMENT> Entry_t;
34 typedef ListLockBase<ELEMENT> List_t;
35 typedef typename List_t::LockHolder ListLockHolder;
36
37
38public:
39#ifdef _DEBUG
40 bool Check()
41 {
42 WRAPPER_NO_CONTRACT;
43
44 return m_dwRefCount != (DWORD)-1;
45 }
46#endif // DEBUG
47
48 DeadlockAwareLock m_deadlock;
49 List_t * m_pList;
50 ELEMENT m_data;
51 Crst m_Crst;
52 const char * m_pszDescription;
53 Entry_t * m_pNext;
54 DWORD m_dwRefCount;
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
62
63 ListLockEntryBase(List_t *pList, ELEMENT data, const char *description = NULL)
64 : m_deadlock(description),
65 m_pList(pList),
66 m_data(data),
67 m_Crst(CrstListLock,
68 (CrstFlags)(CRST_REENTRANCY | (pList->IsHostBreakable() ? CRST_HOST_BREAKABLE : 0))),
69 m_pszDescription(description),
70 m_pNext(NULL),
71 m_dwRefCount(1),
72 m_hrResultCode(S_FALSE),
73 m_hInitException(NULL),
74 m_pLoaderAllocator(dac_cast<PTR_LoaderAllocator>(nullptr))
75#ifdef FEATURE_CORRUPTING_EXCEPTIONS
76 ,
77 m_CorruptionSeverity(NotCorrupting)
78#endif // FEATURE_CORRUPTING_EXCEPTIONS
79 {
80 WRAPPER_NO_CONTRACT;
81 }
82
83 virtual ~ListLockEntryBase()
84 {
85 }
86
87 DEBUG_NOINLINE void Enter()
88 {
89 WRAPPER_NO_CONTRACT;
90 ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
91
92 m_deadlock.BeginEnterLock();
93 DeadlockAwareLock::BlockingLockHolder dlLock;
94 m_Crst.Enter();
95 m_deadlock.EndEnterLock();
96 }
97
98 BOOL CanDeadlockAwareEnter()
99 {
100 WRAPPER_NO_CONTRACT;
101
102 return m_deadlock.CanEnterLock();
103 }
104
105 DEBUG_NOINLINE BOOL DeadlockAwareEnter()
106 {
107 WRAPPER_NO_CONTRACT;
108 ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
109
110 if (!m_deadlock.TryBeginEnterLock())
111 return FALSE;
112
113 DeadlockAwareLock::BlockingLockHolder dlLock;
114 m_Crst.Enter();
115 m_deadlock.EndEnterLock();
116
117 return TRUE;
118 }
119
120 DEBUG_NOINLINE void Leave()
121 {
122 WRAPPER_NO_CONTRACT;
123 ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
124
125 m_deadlock.LeaveLock();
126 m_Crst.Leave();
127 }
128
129 static Entry_t *Find(List_t* pLock, ELEMENT data, const char *description = NULL)
130 {
131 CONTRACTL
132 {
133 THROWS;
134 GC_NOTRIGGER;
135 MODE_ANY;
136 }
137 CONTRACTL_END;
138
139 _ASSERTE(pLock->HasLock());
140
141 Entry_t *pEntry = pLock->Find(data);
142 if (pEntry == NULL)
143 {
144 pEntry = new Entry_t(pLock, data, description);
145 pLock->AddElement(pEntry);
146 }
147 else
148 pEntry->AddRef();
149
150 return pEntry;
151 };
152
153
154 void AddRef()
155 {
156 CONTRACTL
157 {
158 NOTHROW;
159 GC_NOTRIGGER;
160 MODE_ANY;
161 PRECONDITION(CheckPointer(this));
162 }
163 CONTRACTL_END;
164
165 FastInterlockIncrement((LONG*)&m_dwRefCount);
166 }
167
168 void Release()
169 {
170 CONTRACTL
171 {
172 NOTHROW;
173 GC_TRIGGERS;
174 MODE_ANY;
175 PRECONDITION(CheckPointer(this));
176 }
177 CONTRACTL_END;
178
179 ListLockHolder lock(m_pList);
180
181 if (FastInterlockDecrement((LONG*)&m_dwRefCount) == 0)
182 {
183 // Remove from list
184 m_pList->Unlink(this);
185 delete this;
186 }
187 };
188
189#ifdef _DEBUG
190 BOOL HasLock()
191 {
192 WRAPPER_NO_CONTRACT;
193 return(m_Crst.OwnedByCurrentThread());
194 }
195#endif
196
197 // LockHolder holds the lock of the element, not the element itself
198
199 DEBUG_NOINLINE static void LockHolderEnter(Entry_t *pThis) PUB
200 {
201 WRAPPER_NO_CONTRACT;
202 ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
203 pThis->Enter();
204 }
205
206 DEBUG_NOINLINE static void LockHolderLeave(Entry_t *pThis) PUB
207 {
208 WRAPPER_NO_CONTRACT;
209 ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
210 pThis->Leave();
211 }
212
213 DEBUG_NOINLINE void FinishDeadlockAwareEnter()
214 {
215 ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
216 DeadlockAwareLock::BlockingLockHolder dlLock;
217 m_Crst.Enter();
218 m_deadlock.EndEnterLock();
219 }
220
221 typedef Wrapper<Entry_t *, LockHolderEnter, LockHolderLeave> LockHolderBase;
222
223 class LockHolder : public LockHolderBase
224 {
225 public:
226
227 LockHolder()
228 : LockHolderBase(NULL, FALSE)
229 {
230 }
231
232 LockHolder(Entry_t *value, BOOL take = TRUE)
233 : LockHolderBase(value, take)
234 {
235 }
236
237 BOOL DeadlockAwareAcquire()
238 {
239 if (!this->m_acquired && this->m_value != NULL)
240 {
241 if (!this->m_value->m_deadlock.TryBeginEnterLock())
242 return FALSE;
243 this->m_value->FinishDeadlockAwareEnter();
244 this->m_acquired = TRUE;
245 }
246 return TRUE;
247 }
248 };
249};
250
251template < typename ELEMENT >
252class ListLockBase
253{
254 typedef ListLockBase<ELEMENT> List_t;
255 typedef ListLockEntryBase<ELEMENT> Entry_t;
256
257 protected:
258 CrstStatic m_Crst;
259 BOOL m_fInited;
260 BOOL m_fHostBreakable; // Lock can be broken by a host for deadlock detection
261 Entry_t * m_pHead;
262
263 public:
264
265 BOOL IsInitialized()
266 {
267 LIMITED_METHOD_CONTRACT;
268 return m_fInited;
269 }
270 inline void PreInit()
271 {
272 LIMITED_METHOD_CONTRACT;
273 memset(this, 0, sizeof(*this));
274 }
275
276 // DO NOT MAKE A CONSTRUCTOR FOR THIS CLASS - There are global instances
277 void Init(CrstType crstType, CrstFlags flags, BOOL fHostBreakable = FALSE)
278 {
279 WRAPPER_NO_CONTRACT;
280 PreInit();
281 m_Crst.Init(crstType, flags);
282 m_fInited = TRUE;
283 m_fHostBreakable = fHostBreakable;
284 }
285
286 void Destroy()
287 {
288 WRAPPER_NO_CONTRACT;
289 // There should not be any of these around
290 _ASSERTE(m_pHead == NULL || dbg_fDrasticShutdown || g_fInControlC);
291
292 if (m_fInited)
293 {
294 m_fInited = FALSE;
295 m_Crst.Destroy();
296 }
297 }
298
299 BOOL IsHostBreakable () const
300 {
301 LIMITED_METHOD_CONTRACT;
302 return m_fHostBreakable;
303 }
304
305 void AddElement(Entry_t* pElement)
306 {
307 WRAPPER_NO_CONTRACT;
308 pElement->m_pNext = m_pHead;
309 m_pHead = pElement;
310 }
311
312
313 DEBUG_NOINLINE void Enter()
314 {
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.
321 CONTRACTL
322 {
323 NOTHROW;
324 UNCHECKED(GC_TRIGGERS); // May trigger or not based on Crst's type
325 MODE_ANY;
326 PRECONDITION(CheckPointer(this));
327 }
328 CONTRACTL_END;
329#endif
330
331 m_Crst.Enter();
332 }
333
334 DEBUG_NOINLINE void Leave()
335 {
336 WRAPPER_NO_CONTRACT;
337 ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
338 m_Crst.Leave();
339 }
340
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)
344 {
345 CONTRACTL
346 {
347 NOTHROW;
348 GC_NOTRIGGER;
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() */);
356#else
357 PRECONDITION(m_Crst.OwnedByCurrentThread());
358#endif // DEBUGGING_SUPPORTED
359
360 }
361 CONTRACTL_END;
362
363 Entry_t *pSearch;
364
365 for (pSearch = m_pHead; pSearch != NULL; pSearch = pSearch->m_pNext)
366 {
367 if (pSearch->m_data == data)
368 return pSearch;
369 }
370
371 return NULL;
372 }
373
374 // Must own the lock before calling this!
375 Entry_t* Pop(BOOL unloading = FALSE)
376 {
377 LIMITED_METHOD_CONTRACT;
378#ifdef _DEBUG
379 if(unloading == FALSE)
380 _ASSERTE(m_Crst.OwnedByCurrentThread());
381#endif
382
383 if(m_pHead == NULL) return NULL;
384 Entry_t* pEntry = m_pHead;
385 m_pHead = m_pHead->m_pNext;
386 return pEntry;
387 }
388
389 // Must own the lock before calling this!
390 Entry_t* Peek()
391 {
392 LIMITED_METHOD_CONTRACT;
393 _ASSERTE(m_Crst.OwnedByCurrentThread());
394 return m_pHead;
395 }
396
397 // Must own the lock before calling this!
398 BOOL Unlink(Entry_t *pItem)
399 {
400 LIMITED_METHOD_CONTRACT;
401 _ASSERTE(m_Crst.OwnedByCurrentThread());
402 Entry_t *pSearch;
403 Entry_t *pPrev;
404
405 pPrev = NULL;
406
407 for (pSearch = m_pHead; pSearch != NULL; pSearch = pSearch->m_pNext)
408 {
409 if (pSearch == pItem)
410 {
411 if (pPrev == NULL)
412 m_pHead = pSearch->m_pNext;
413 else
414 pPrev->m_pNext = pSearch->m_pNext;
415
416 return TRUE;
417 }
418
419 pPrev = pSearch;
420 }
421
422 // Not found
423 return FALSE;
424 }
425
426#ifdef _DEBUG
427 BOOL HasLock()
428 {
429 WRAPPER_NO_CONTRACT;
430 STATIC_CONTRACT_SO_TOLERANT;
431 return(m_Crst.OwnedByCurrentThread());
432 }
433#endif
434
435 DEBUG_NOINLINE static void HolderEnter(List_t *pThis)
436 {
437 WRAPPER_NO_CONTRACT;
438 ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
439 pThis->Enter();
440 }
441
442 DEBUG_NOINLINE static void HolderLeave(List_t *pThis)
443 {
444 WRAPPER_NO_CONTRACT;
445 ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
446 pThis->Leave();
447 }
448
449 typedef Wrapper<List_t*, List_t::HolderEnter, List_t::HolderLeave> LockHolder;
450};
451
452class WaitingThreadListElement
453{
454public:
455 Thread * m_pThread;
456 WaitingThreadListElement * m_pNext;
457};
458
459typedef class ListLockBase<void*> ListLock;
460typedef class ListLockEntryBase<void*> ListLockEntry;
461
462// Holds the lock of the ListLock
463typedef ListLock::LockHolder ListLockHolder;
464
465// Holds the ownership of the lock element
466typedef ReleaseHolder<ListLockEntry> ListLockEntryHolder;
467
468// Holds the lock of the lock element
469typedef ListLockEntry::LockHolder ListLockEntryLockHolder;
470
471
472#endif // LISTLOCK_H
473