[Tizen] Unify dnetmemoryenumlib terms to match the codebase (#291)
[platform/upstream/coreclr.git] / src / vm / listlock.h
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
20 template < typename ELEMENT >
21 class 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.
29 template < typename ELEMENT >
30 class 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
38 public:
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)
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)
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
251 template < typename ELEMENT >
252 class 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         return(m_Crst.OwnedByCurrentThread());
431     }
432 #endif
433
434     DEBUG_NOINLINE static void HolderEnter(List_t *pThis)
435     {
436         WRAPPER_NO_CONTRACT;
437         ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
438         pThis->Enter();
439     }
440
441     DEBUG_NOINLINE static void HolderLeave(List_t *pThis)
442     {
443         WRAPPER_NO_CONTRACT;
444         ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
445         pThis->Leave();
446     }
447
448     typedef Wrapper<List_t*, List_t::HolderEnter, List_t::HolderLeave> LockHolder;
449 };
450
451 class WaitingThreadListElement
452 {
453 public:
454     Thread *                   m_pThread;
455     WaitingThreadListElement * m_pNext;
456 };
457
458 typedef class ListLockBase<void*> ListLock;
459 typedef class ListLockEntryBase<void*> ListLockEntry;
460
461 // Holds the lock of the ListLock
462 typedef ListLock::LockHolder ListLockHolder;
463
464 // Holds the ownership of the lock element
465 typedef ReleaseHolder<ListLockEntry> ListLockEntryHolder;
466
467 // Holds the lock of the lock element
468 typedef ListLockEntry::LockHolder ListLockEntryLockHolder;
469
470
471 #endif // LISTLOCK_H