summaryrefslogtreecommitdiff
path: root/src/vm/crst.h
blob: fa8c307f3f579b8222738ba88b8e6e1584ffff86 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// 
// CRST.H
//

// 
// Debug-instrumented hierarchical critical sections.
//
//
// The hierarchy:
// --------------
//    The EE divides critical sections into numbered groups or "levels."
//    Crsts that guard the lowest level data structures that don't
//    use other services are grouped into the lowest-numbered levels.
//    The higher-numbered levels are reserved for high-level crsts
//    that guard broad swatches of code. Multiple groups can share the
//    same number to indicate that they're disjoint (their locks will never
//    nest.)
//
//    The fundamental rule of the hierarchy that threads can only request
//    a crst whose level is lower than any crst currently held by the thread.
//    E.g. if a thread current holds a level-3 crst, he can try to enter
//    a level-2 crst, but not a level-4 crst, nor a different level-3
//    crst. This prevents the cyclic dependencies that lead to deadlock.
//
//    For debugging purposes Crsts are all also grouped by a type (e.g.
//    CrstRemoting, the type of Crst used to synchronize certain remoting
//    operations). Each type maps to one level (though a level may map to
//    multiple types). The idea here is for the programmer to express Crst types
//    and their dependencies (e.g. a CrstClassInit instance may be acquired
//    while a CrstRemoting instance is already held) in a high level manner
//    while an external script handles the mechanical process of assigning
//    numerical levels to each type. See file:..\inc\CrstTypes.def for these high level
//    type definitions.
//
//
// To create a crst:
//
//    Crst *pcrst = new Crst(type);
//
//      where "type" is one of the enums created in the auto-generated
//      file:..\inc\CrstTypes.h header file (matching the definition in
//      file:..\inc\CrstTypes.def).
//
//      By default, crsts don't support nested enters by the same thread. If
//      you need reentrancy, use the alternate form:
//
//    Crst *pcrst = new Crst(type, TRUE);
//
//      Since reentrancies never block the caller, they're allowed to
//     "violate" the level ordering rule.
//
//
// To enter/leave a crst:
// ----------------------
//
//
//    pcrst->Enter();
//    pcrst->Leave();
//
// An assertion will fire on Enter() if a thread attempts to take locks
// in the wrong order.
//
// Finally, a few DEBUG-only methods:
//
// To assert taking a crst won't violate level order:
// --------------------------------------------------
//
//    _ASSERTE(pcrst->IsSafeToTake());
//
//    This is a good line to put at the start of any function that
//    enters a crst in some circumstances but not others. If it
//    always enters the crst, it's not necessary to call IsSafeToTake()
//    since Enter() does this for you.
//
// To assert that the current thread owns a crst:
// --------------------------------------------------
//
//   _ASSERTE(pcrst->OwnedByCurrentThread());



#ifndef __crst_h__
#define __crst_h__

#include "util.hpp"
#include "debugmacros.h"
#include "log.h"

#define ShutDown_Start                          0x00000001
#define ShutDown_Finalize1                      0x00000002
#define ShutDown_Finalize2                      0x00000004
#define ShutDown_Profiler                       0x00000008
#define ShutDown_COM                            0x00000010
#define ShutDown_SyncBlock                      0x00000020
#define ShutDown_IUnknown                       0x00000040
#define ShutDown_Phase2                         0x00000080

#ifndef DACCESS_COMPILE
extern bool g_fProcessDetach;
extern DWORD g_fEEShutDown;
#endif
// Total count of Crst lock  of the type (Shutdown) that are currently in use
extern Volatile<LONG> g_ShutdownCrstUsageCount;
extern Volatile<LONG> g_fForbidEnterEE;

// The CRST.
class CrstBase
{
// The following classes and methods violate the requirement that Crst usage be
// exception-safe, or they satisfy that requirement using techniques other than
// Holder objects:
friend class Thread;
friend class ThreadStore;
friend class ThreadSuspend;
template <typename ELEMENT>
friend class ListLockBase;
template <typename ELEMENT>
friend class ListLockEntryBase;
//friend class CExecutionEngine;
friend struct SavedExceptionInfo;
friend void EEEnterCriticalSection(CRITSEC_COOKIE cookie);
friend void EELeaveCriticalSection(CRITSEC_COOKIE cookie);
friend class CodeVersionManager;

friend class Debugger;
friend class Crst;

#ifdef FEATURE_DBGIPC_TRANSPORT_VM
    // The debugger transport code uses a holder for its Crst, but it needs to share the holder implementation
    // with its right side code as well (which can't see the Crst implementation and actually uses a
    // CRITICAL_SECTION as the base lock). So make DbgTransportSession a friend here so we can use Enter() and
    // Leave() in order to build a shared holder class.
    friend class DbgTransportLock;
#endif // FEATURE_DBGIPC_TRANSPORT_VM

    // PendingTypeLoadEntry acquires the lock during construction before anybody has a chance to see it to avoid 
    // level violations.
    friend class PendingTypeLoadEntry;

public:
#ifdef _DEBUG
    enum NoLevelCheckFlag
    {
        CRST_NO_LEVEL_CHECK = 1,
        CRST_LEVEL_CHECK = 0,
    };
#endif

private:
    // Some Crsts have a "shutdown" mode. 
    // A Crst in shutdown mode can only be taken / released by special 
    // (the helper / finalizer / shutdown) threads. Any other thread that tries to take
    // the a "shutdown" crst will immediately release the Crst and instead just block forever.
    //
    // This prevents random threads from blocking the special threads from doing finalization on shutdown. 
    // 
    // Unfortunately, each Crst needs its own "shutdown" flag because we can't convert all the locks
    // into shutdown locks at once. For eg, the TSL needs to suspend the runtime before
    // converting to a shutdown lock. But it can't suspend the runtime while holding 
    // a UNSAFE_ANYMODE lock (such as the debugger-lock). So at least the debugger-lock
    // and TSL need to be set separately.
    // 
    // So for such Crsts, it's the caller's responsibility to detect if the crst is in
    // shutdown mode, and if so, call this function after enter.
    void ReleaseAndBlockForShutdownIfNotSpecialThread();

    // Enter & Leave are deliberately private to force callers to use the
    // Holder class.  If you bypass the Holder class and access these members
    // directly, your lock is not exception-safe.
    //
    // noLevelCheckFlag parameter lets you disable the crst level checking. This is
    // very dangerous so it is only used when the constructor is the one performing
    // the Enter (that attempt cannot possibly block since the current thread is
    // the only one with a pointer to the crst.)
    //
    // For obvious reasons, this parameter must never be made public.
    void Enter(INDEBUG(NoLevelCheckFlag noLevelCheckFlag = CRST_LEVEL_CHECK));
    void Leave();

    void SpinEnter();

#ifndef DACCESS_COMPILE
    DEBUG_NOINLINE static void AcquireLock(CrstBase *c) PUB {
        WRAPPER_NO_CONTRACT;
        ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
        c->Enter(); 
    }

    DEBUG_NOINLINE static void ReleaseLock(CrstBase *c) PUB { 
        WRAPPER_NO_CONTRACT;
        ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
        c->Leave(); 
    }

#else // DACCESS_COMPILE

    // in DAC builds, we don't actually acquire the lock, we just determine whether the LS
    // already holds it. If so, we assume the data is inconsistent and throw an exception. 
    // Argument: 
    //     input: c - the lock to be checked. 
    // Note: Throws
    static void AcquireLock(CrstBase * c) PUB
    { 
        SUPPORTS_DAC;
        if (c->GetEnterCount() != 0) 
        {
            ThrowHR(CORDBG_E_PROCESS_NOT_SYNCHRONIZED); 
        }
    };

    static void ReleaseLock(CrstBase *c) PUB
    { 
        SUPPORTS_DAC;
    };
#endif // DACCESS_COMPILE

public:
    //-----------------------------------------------------------------
    // Clean up critical section
    // Safe to call multiple times or on non-initialized critical section
    //-----------------------------------------------------------------
    void Destroy();

#ifdef _DEBUG
    //-----------------------------------------------------------------
    // Check if attempting to take the lock would violate level order.
    //-----------------------------------------------------------------
    BOOL IsSafeToTake();
    // Checks that the lock can be taken
    BOOL Debug_CanTake()
    {
        WRAPPER_NO_CONTRACT;
        // Actually take the lock and release it immediatelly, that will do all the necessary checks
        Enter();
        Leave();
        return TRUE;
    }
    void SetCantLeave(BOOL bSet)
    {
        LIMITED_METHOD_CONTRACT;
        if (bSet)
            FastInterlockIncrement(&m_cannotLeave);
        else
        {
            _ASSERTE(m_cannotLeave);
            FastInterlockDecrement(&m_cannotLeave);
        }
    };    
    //-----------------------------------------------------------------
    // Is the current thread the owner?
    //-----------------------------------------------------------------
    BOOL OwnedByCurrentThread()
    {
        WRAPPER_NO_CONTRACT;
#ifdef CROSSGEN_COMPILE
        return TRUE;
#else
        return m_holderthreadid.IsCurrentThread();
#endif
    }
    
    CrstBase *GetThreadsOwnedCrsts();
    void SetThreadsOwnedCrsts(CrstBase *pCrst);

    __declspec(noinline) EEThreadId GetHolderThreadId()
    {
        LIMITED_METHOD_CONTRACT;
        return m_holderthreadid;
    }

#endif //_DEBUG
    
    //-----------------------------------------------------------------
    // For clients who want to assert whether they are in or out of the
    // region.
    //-----------------------------------------------------------------
    UINT GetEnterCount()
    {
        LIMITED_METHOD_DAC_CONTRACT;
#ifdef _DEBUG
        return m_entercount;
#else
        return 0;
#endif //_DEBUG
    }

protected:    

    VOID InitWorker(INDEBUG_COMMA(CrstType crstType) CrstFlags flags);

#ifdef _DEBUG
    void DebugInit(CrstType crstType, CrstFlags flags);
    void DebugDestroy();
#endif

    union {
        CRITICAL_SECTION    m_criticalsection;
    };

    typedef enum
    {
        // Mask to indicate reserved flags
        CRST_RESERVED_FLAGS_MASK = 0xC0000000,
        // private flag to indicate initialized Crsts
        CRST_INITIALIZED = 0x80000000,
        // private flag to indicate Crst is OS Critical Section
        CRST_OS_CRIT_SEC = 0x40000000,
        // rest of the flags are CrstFlags
    } CrstReservedFlags;
    DWORD               m_dwFlags;            // Re-entrancy and same level
#ifdef _DEBUG
    UINT                m_entercount;       // # of unmatched Enters.
    CrstType            m_crstType;         // Type enum (should have a descriptive name for debugging)
    const char         *m_tag;              // Stringized form of the tag for easy debugging
    int                 m_crstlevel;        // what level is the crst in?
    EEThreadId          m_holderthreadid;   // current holder (or NULL)
    CrstBase           *m_next;             // link for global linked list
    CrstBase           *m_prev;             // link for global linked list
    Volatile<LONG>      m_cannotLeave; 

    // Check for dead lock situation.
    ULONG               m_countNoTriggerGC;
    
    void                PostEnter ();
    void                PreEnter ();
    void                PreLeave  ();
#endif //_DEBUG
    
private:

    void SetOSCritSec ()
    {
        m_dwFlags |= CRST_OS_CRIT_SEC;
    }
    void ResetOSCritSec ()
    {
        m_dwFlags &= ~CRST_OS_CRIT_SEC;
    }
    BOOL IsOSCritSec ()
    {
        return m_dwFlags & CRST_OS_CRIT_SEC;
    }
    void SetCrstInitialized()
    {
        m_dwFlags |= CRST_INITIALIZED;
    }

    BOOL IsCrstInitialized()
    {
        return m_dwFlags & CRST_INITIALIZED;
    }

    BOOL CanBeTakenDuringShutdown()
    {
        return m_dwFlags & CRST_TAKEN_DURING_SHUTDOWN;
    }

    void SetFlags(CrstFlags f)
    {
        WRAPPER_NO_CONTRACT;
        _ASSERTE(((CrstFlags)(f & ~CRST_RESERVED_FLAGS_MASK)) == f);
        m_dwFlags = (f & ~CRST_RESERVED_FLAGS_MASK) | (m_dwFlags & CRST_RESERVED_FLAGS_MASK);
    }

    void ResetFlags() // resets the reserved and the CrstFlags
    {
        m_dwFlags = 0;
    }
    // ------------------------------- Holders ------------------------------
 public:
     //
     // CrstHolder is optimized for the common use that takes the lock in constructor 
     // and releases it in destructor. Users that require all Holder features
     // can use CrstHolderWithState.
     //
    class CrstHolder
    {
        CrstBase * m_pCrst;

    public:
        inline CrstHolder(CrstBase * pCrst)
            : m_pCrst(pCrst)
        {
            WRAPPER_NO_CONTRACT;
            AcquireLock(pCrst);
        }

        inline ~CrstHolder()
        {
            WRAPPER_NO_CONTRACT;

            VALIDATE_HOLDER_STACK_CONSUMPTION_FOR_TYPE(HSV_ValidateMinimumStackReq);
            ReleaseLock(m_pCrst);
        }
    };

    // Note that the holders for CRSTs are used in extremely low stack conditions. Because of this, they 
    // aren't allowed to use more than HOLDER_CODE_MINIMUM_STACK_LIMIT pages of stack.
    typedef DacHolder<CrstBase *, CrstBase::AcquireLock, CrstBase::ReleaseLock, 0, CompareDefault, HSV_ValidateMinimumStackReq> CrstHolderWithState;

    // We have some situations where we're already holding a lock, and we need to release and reacquire the lock across a window.
    // This is a dangerous construct because the backout code can block.
    // Generally, it's better to use a regular CrstHolder, and then use the Release() / Acquire() methods on it.
    // This just exists to convert legacy OS Critical Section patterns over to holders.
    typedef DacHolder<CrstBase *, CrstBase::ReleaseLock, CrstBase::AcquireLock, 0, CompareDefault, HSV_ValidateMinimumStackReq> UnsafeCrstInverseHolder;
};

typedef CrstBase::CrstHolder CrstHolder;
typedef CrstBase::CrstHolderWithState CrstHolderWithState;


// The CRST.
class Crst : public CrstBase
{
public:
    void *operator new(size_t size)
    {
        WRAPPER_NO_CONTRACT;
        return new BYTE[size];
    }

private:
    // Do not use inplace operator new on Crst. A wrong destructor would be called if the constructor fails.
    // Use CrstStatic or CrstExplicitInit instead of the inplace operator new.
    void *operator new(size_t size, void *pInPlace);

public:

#ifndef DACCESS_COMPILE

    //-----------------------------------------------------------------
    // Constructor.
    //-----------------------------------------------------------------
    Crst(CrstType crstType, CrstFlags flags = CRST_DEFAULT)
    {
        WRAPPER_NO_CONTRACT;

        // throw away the debug-only parameter in retail
        InitWorker(INDEBUG_COMMA(crstType) flags);
    }

    //-----------------------------------------------------------------
    // Destructor.
    //-----------------------------------------------------------------
    ~Crst()
    {
        WRAPPER_NO_CONTRACT;

        Destroy();
    };

#else

    Crst(CrstType crstType, CrstFlags flags = CRST_DEFAULT) {
        LIMITED_METHOD_CONTRACT;
    };

#endif

    Crst() {
        LIMITED_METHOD_CONTRACT;
    }
};

typedef DPTR(Crst) PTR_Crst;

/* to be used as static variable - no constructor/destructor, assumes zero 
   initialized memory */
class CrstStatic : public CrstBase
{
public:
    VOID Init(CrstType crstType, CrstFlags flags = CRST_DEFAULT)
    {
        WRAPPER_NO_CONTRACT;

        _ASSERTE((flags & CRST_INITIALIZED) == 0);

        // throw away the debug-only parameter in retail
        InitWorker(INDEBUG_COMMA(crstType) flags);
    }

    bool InitNoThrow(CrstType crstType, CrstFlags flags = CRST_DEFAULT)
    {
        CONTRACTL {
            NOTHROW;
        } CONTRACTL_END;    

        _ASSERTE((flags & CRST_INITIALIZED) == 0);

        bool fSuccess = false;

        EX_TRY
        {
            // throw away the debug-only parameter in retail
            InitWorker(INDEBUG_COMMA(crstType) flags);
            fSuccess = true;
        }
        EX_CATCH
        {
        }
        EX_END_CATCH(SwallowAllExceptions)

        return fSuccess;
    }
};

/* to be used as regular variable when a explicit call to Init method is needed */
class CrstExplicitInit : public CrstStatic
{
public:
    CrstExplicitInit() {
        m_dwFlags = 0;
    }
     ~CrstExplicitInit() {
#ifndef DACCESS_COMPILE
        Destroy();
#endif
    }   
};

__inline BOOL IsOwnerOfCrst(LPVOID lock)
{
    WRAPPER_NO_CONTRACT;

#ifdef _DEBUG
    return ((Crst*)lock)->OwnedByCurrentThread();
#else
    // This function should not be called on free build.
    DebugBreak();
    return TRUE;
#endif
}

#ifdef TEST_DATA_CONSISTENCY
// used for test purposes. Determines if a crst is held. 
void DebugTryCrst(CrstBase * pLock);
#endif
#endif // __crst_h__