summaryrefslogtreecommitdiff
path: root/src/vm/gcstress.h
blob: edf92a947c16fa6ee833f4d6b8250dbffe094814 (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
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
// 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.


// 
// #Overview
// 
// This file provides convenient wrappers for the GC stress functionality.
// 
// Exposed APIs:
//  GCStressPolicy::InhibitHolder
//  GCStressPolicy::GlobalEnable()
//  GCStressPolicy::GlobalDisable()
//  GCStressPolicy::IsEnabled()
// 
//  GCStress<> template classes with its IsEnabled() & MaybeTrigger members.
// 
//  Use GCStress<> to abstract away the GC stress related decissions. The 
//  template definitions will resolve to nothing when STRESS_HEAP is not 
//  defined, and will inline the function body at the call site otherwise.
// 
// Examples:
//  GCStress<cfg_any>::IsEnabled()
//  GCStress<cfg_any, EeconfigFastGcSPolicy, CoopGcModePolicy>::MaybeTrigger()
// 

#ifndef _GC_STRESS_
#define _GC_STRESS_

#include "mpl/type_list"


struct alloc_context;


enum gcs_trigger_points {
    // generic handling based on EEConfig settings
    cfg_any,                        // any bit set in EEConfig::iGCStress
    cfg_alloc,                      // trigger on GC allocations
    cfg_transition,                 // trigger on transitions
    cfg_instr_jit,                  // trigger on JITted instructions
    cfg_instr_ngen,                 // trigger on NGENed instructions
    cfg_easy,                       // trigger on allocs or transitions
    cfg_instr,                      // trigger on managed instructions (JITted or NGENed)
    cfg_last,                       // boundary

    // special handling at particular trigger points
    jit_on_create_jump_stub,
    jit_on_create_il_stub,
    gc_on_alloc,
    vsd_on_resolve
};


namespace GCStressPolicy
{

#ifdef STRESS_HEAP

#ifdef __GNUC__
#define UNUSED_ATTR __attribute__ ((unused))
#else  // __GNUC__
#define UNUSED_ATTR
#endif // __GNUC__

#ifndef __UNUSED
#define __UNUSED(x) ((void)(x))
#endif // __UNUSED

    class InhibitHolder
    {
    private:
        // This static controls whether GC stress may induce GCs. EEConfig::GetGCStressLevel() still 
        // controls when GCs may occur.
        static Volatile<DWORD> s_nGcStressDisabled;

        bool m_bAquired;

    public:
        InhibitHolder()
        { LIMITED_METHOD_CONTRACT; ++s_nGcStressDisabled; m_bAquired = true; }

        ~InhibitHolder()
        { LIMITED_METHOD_CONTRACT; Release(); }

        void Release()
        { 
            LIMITED_METHOD_CONTRACT; 
            if (m_bAquired)
            {
                --s_nGcStressDisabled;
                m_bAquired = false;
            }
        }

        friend bool IsEnabled();
        friend void GlobalDisable();
        friend void GlobalEnable();
    } UNUSED_ATTR;

    FORCEINLINE bool IsEnabled()
    { return InhibitHolder::s_nGcStressDisabled == 0U; }

    FORCEINLINE void GlobalDisable()
    { ++InhibitHolder::s_nGcStressDisabled; }
    
    FORCEINLINE void GlobalEnable()
    { --InhibitHolder::s_nGcStressDisabled; }
    
#else // STRESS_HEAP

    class InhibitHolder
    { void Release() {} };

    FORCEINLINE bool IsEnabled()
    { return false; }

    FORCEINLINE void GlobalDisable()
    {}
    
    FORCEINLINE void GlobalEnable()
    {}

#endif // STRESS_HEAP
}



namespace _GCStress
{

#ifdef STRESS_HEAP

    // Support classes to allow easy customization of GC Stress policies
    namespace detail
    {
        using namespace mpl;

        // Selecting a policy from a type list and a fallback/default policy
        // GetPolicy<>:type will represent either a type in ListT with the same "tag" as DefPolicy
        // or DefPolicy, based on the Traits passed in.
        template <
            typename ListT, 
            typename DefPolicy,
            template <typename> class Traits
        >
        struct GetPolicy;

        // Common case: recurse over the type list
        template <
            typename HeadT, 
            typename TailT, 
            typename DefPolicy,
            template<typename> class Traits
        >
        struct GetPolicy<type_list<HeadT, TailT>, DefPolicy, Traits>
        {
            // is true if HeadT and DefPolicy evaluate to the same tag, 
            // through Traits<>
            static const bool sameTag = std::is_same<
                        typename Traits<HeadT>::tag, 
                        typename Traits<DefPolicy>::tag
                    >::value;

            typedef typename std::conditional<
                        sameTag, 
                        HeadT, 
                        typename GetPolicy<TailT, DefPolicy, Traits>::type 
                    >::type type;
        };

        // Termination case.
        template <
            typename DefPolicy,
            template<typename> class Traits
        >
        struct GetPolicy <null_type, DefPolicy, Traits>
        {
            typedef DefPolicy type;
        };
    }


    // GC stress specific EEConfig accessors
    namespace detail
    {
        // no definition provided so that absence of concrete implementations cause compiler errors
        template <enum gcs_trigger_points>
        bool IsEnabled();

        template<> FORCEINLINE
        bool IsEnabled<cfg_any>()
        {
            // Most correct would be to test for each specific bits, but we've
            // always only tested against 0...
            return g_pConfig->GetGCStressLevel() != 0;
            // return (g_pConfig->GetGCStressLevel() & 
            //       (EEConfig::GCSTRESS_ALLOC|EEConfig::GCSTRESS_TRANSITION|
            //        EEConfig::GCSTRESS_INSTR_JIT|EEConfig::GCSTRESS_INSTR_NGEN) != 0);
        }

        #define DefineIsEnabled(cfg_enum, eeconfig_bits)                    \
        template<> FORCEINLINE                                              \
        bool IsEnabled<cfg_enum>()                                   \
        {                                                                   \
            return (g_pConfig->GetGCStressLevel() & (eeconfig_bits)) != 0;  \
        }

        DefineIsEnabled(cfg_alloc,      EEConfig::GCSTRESS_ALLOC);
        DefineIsEnabled(cfg_transition, EEConfig::GCSTRESS_TRANSITION);
        DefineIsEnabled(cfg_instr_jit,  EEConfig::GCSTRESS_INSTR_JIT);
        DefineIsEnabled(cfg_instr_ngen, EEConfig::GCSTRESS_INSTR_NGEN);
        DefineIsEnabled(cfg_easy, EEConfig::GCSTRESS_ALLOC|EEConfig::GCSTRESS_TRANSITION);
        DefineIsEnabled(cfg_instr, EEConfig::GCSTRESS_INSTR_JIT|EEConfig::GCSTRESS_INSTR_NGEN);

        #undef DefineIsEnabled

    }


    //
    // GC stress policy classes used by GCSBase and GCStress template classes
    //

    // Fast GS stress policies that dictate whether GCStress<>::MaybeTrigger()
    // will consider g_pConfig->FastGCStressLevel() when deciding whether
    // to trigger a GC or not.

    // This is the default Fast GC stress policy that ignores the EEConfig
    // setting
    class IgnoreFastGcSPolicy
    {
    public:
        FORCEINLINE
        static bool FastGcSEnabled(DWORD minValue = 0)
        { return false; }
    };

    // This is the overriding Fast GC stress policy that considers the 
    // EEConfig setting on checked/debug builds
    class EeconfigFastGcSPolicy
    {
    public:
        FORCEINLINE
        static bool FastGcSEnabled(DWORD minValue = 0)
        {
        #ifdef _DEBUG
            return g_pConfig->FastGCStressLevel() > minValue;
        #else // _DEBUG
            return false;
        #endif // _DEBUG
        }
    };

    // GC Mode policies that determines whether to switch the GC mode before
    // triggering the GC.

    // This is the default GC Mode stress policy that does not switch GC modes
    class AnyGcModePolicy
    {
    };

    // This is the overriding GC Mode stress policy that forces a switch to
    // cooperative mode before MaybeTrigger() will trigger a GC
    class CoopGcModePolicy
    {
#ifndef DACCESS_COMPILE
        // implicit constructor an destructor will do the right thing
        GCCoop m_coop;
#endif // DACCESS_COMPILE

    public:
        FORCEINLINE CoopGcModePolicy()
        { WRAPPER_NO_CONTRACT; }
        FORCEINLINE ~CoopGcModePolicy()
        { WRAPPER_NO_CONTRACT; }
    } UNUSED_ATTR;

    // GC Trigger policy classes define how a garbage collection is triggered

    // This is the default GC Trigger policy that simply calls 
    // IGCHeap::StressHeap
    class StressGcTriggerPolicy
    {
    public:
        FORCEINLINE
        static void Trigger()
        {
            // BUG(github #10318) - when not using allocation contexts, the alloc lock
            // must be acquired here. Until fixed, this assert prevents random heap corruption.
            _ASSERTE(GCHeapUtilities::UseThreadAllocationContexts());
            GCHeapUtilities::GetGCHeap()->StressHeap(GetThread()->GetAllocContext());
        }

        FORCEINLINE
        static void Trigger(::gc_alloc_context* acontext)
        { GCHeapUtilities::GetGCHeap()->StressHeap(acontext); }
    };

    // This is an overriding GC Trigger policy that triggers a GC by calling
    // PulseGCMode
    class PulseGcTriggerPolicy
    {
    public:
        FORCEINLINE
        static void Trigger()
        { 
            DEBUG_ONLY_REGION();
            GetThread()->PulseGCMode();
        }
    };


    // GC stress policy tags
    struct fast_gcs_policy_tag {};
    struct gc_mode_policy_tag {};
    struct gc_trigger_policy_tag {};


    template <class GCSPolicy>
    struct GcStressTraits
    { typedef mpl::null_type tag; };

    #define DefineGCStressTraits(Policy, policy_tag)                        \
    template <> struct GcStressTraits<Policy>                               \
    { typedef policy_tag tag; }

    DefineGCStressTraits(IgnoreFastGcSPolicy, fast_gcs_policy_tag);
    DefineGCStressTraits(EeconfigFastGcSPolicy, fast_gcs_policy_tag);

    DefineGCStressTraits(AnyGcModePolicy, gc_mode_policy_tag);
    DefineGCStressTraits(CoopGcModePolicy, gc_mode_policy_tag);

    DefineGCStressTraits(StressGcTriggerPolicy, gc_trigger_policy_tag);
    DefineGCStressTraits(PulseGcTriggerPolicy, gc_trigger_policy_tag);

    #undef DefineGCStressTraits

    // Special handling for GC stress policies
    template <class GCPolicies, class DefPolicy>
    struct GetPolicy:
        public detail::GetPolicy<GCPolicies, DefPolicy, GcStressTraits>
    {};


    //
    // The base for any customization GCStress class. It accepts an identifying
    // GC stress trigger point and at most three overriding policies.
    //
    // It defines FastGcSPolicy, GcModePolicy, and GcTriggerPolicy as either
    // the overriding policy or the default policy, if no corresponding
    // overriding policy is specified in the list. These names can then be
    // accessed from the derived GCStress class.
    //
    // Additionally it defines the static methods IsEnabled and MaybeTrigger and
    // how the policy classes influence their behavior. 
    //
    template <
        enum gcs_trigger_points tp, 
        class Policy1 = mpl::null_type,
        class Policy2 = mpl::null_type,
        class Policy3 = mpl::null_type
    >
    class GCSBase
    {
    public:
        typedef typename mpl::make_type_list<Policy1, Policy2, Policy3>::type Policies;

        typedef typename GetPolicy<Policies, IgnoreFastGcSPolicy>::type FastGcSPolicy;
        typedef typename GetPolicy<Policies, AnyGcModePolicy>::type GcModePolicy;
        typedef typename GetPolicy<Policies, StressGcTriggerPolicy>::type GcTriggerPolicy;

        typedef GCSBase<tp, FastGcSPolicy, GcModePolicy, GcTriggerPolicy> GcStressBase;

        // Returns true iff:
        //   . the bitflag in EEConfig::GetStressLevel() corresponding to the
        //     gc stress trigger point is set AND
        //   . when a Fast GC Stress policy is specified, if the minFastGC argument
        //     is below the EEConfig::FastGCStressLevel
        FORCEINLINE
        static bool IsEnabled(DWORD minFastGc = 0)
        {
            static_assert(tp < cfg_last, "GCSBase only supports cfg_ trigger points.");
            return detail::IsEnabled<tp>() && !FastGcSPolicy::FastGcSEnabled(minFastGc);
        }

        // Triggers a GC iff 
        //   . the GC stress is not disabled globally (thru GCStressPolicy::GlobalDisable)
        //     AND
        //   . IsEnabled() returns true.
        // Additionally it switches the GC mode as specified by GcModePolicy, and it 
        // uses GcTriggerPolicy::Trigger() to actually trigger the GC
        FORCEINLINE
        static void MaybeTrigger(DWORD minFastGc = 0)
        {
            if (IsEnabled(minFastGc) && GCStressPolicy::IsEnabled())
            {
                GcModePolicy gcModeObj; __UNUSED(gcModeObj); 
                GcTriggerPolicy::Trigger();
            }
        }

        // Triggers a GC iff 
        //   . the GC stress is not disabled globally (thru GCStressPolicy::GlobalDisable)
        //     AND
        //   . IsEnabled() returns true.
        // Additionally it switches the GC mode as specified by GcModePolicy, and it 
        // uses GcTriggerPolicy::Trigger(alloc_context*) to actually trigger the GC
        FORCEINLINE
        static void MaybeTrigger(::gc_alloc_context* acontext, DWORD minFastGc = 0)
        {
            if (IsEnabled(minFastGc) && GCStressPolicy::IsEnabled())
            {
                GcModePolicy gcModeObj; __UNUSED(gcModeObj); 
                GcTriggerPolicy::Trigger(acontext);
            }
        }
    };

    template <
        enum gcs_trigger_points tp, 
        class Policy1 = mpl::null_type,
        class Policy2 = mpl::null_type,
        class Policy3 = mpl::null_type
    >
    class GCStress 
        : public GCSBase<tp, Policy1, Policy2, Policy3>
    {
    };


    //
    // Partial specializations of GCStress for trigger points requiring non-default
    // handling.
    //

    template <>
    class GCStress<jit_on_create_jump_stub>
        : public GCSBase<cfg_any>
    {
    public:

        FORCEINLINE
        static void MaybeTrigger(DWORD minFastGc = 0)
        {
            if ((GetThreadNULLOk() != NULL) && (GetThreadNULLOk()->PreemptiveGCDisabled()))
            {
                // Force a GC if the stress level is high enough
                GcStressBase::MaybeTrigger(minFastGc);
            }
        }

    };

    template <>
    class GCStress<gc_on_alloc>
        : public GCSBase<cfg_alloc>
    {
    public:

        FORCEINLINE
        static void MaybeTrigger(::gc_alloc_context* acontext)
        {
            GcStressBase::MaybeTrigger(acontext);

#ifdef _DEBUG
            Thread *pThread = GetThread();
            if (pThread) 
            {
                pThread->EnableStressHeap();
            }
#endif //_DEBUG
        }
    };

    template <>
    class GCStress<vsd_on_resolve>
        : public GCSBase<cfg_any>
    {
    public:

        // Triggers a GC iff 
        //   . the GC stress is not disabled globally (thru GCStressPolicy::GlobalDisable)
        //     AND
        //   . IsEnabled() returns true.
        // Additionally it protects the passed in OBJECTREF&, and it uses 
        // GcTriggerPolicy::Trigger() to actually trigger the GC
        //
        // Note: the OBJECTREF must be passed by reference so MaybeTrigger can protect 
        // the calling function's stack slot.
        FORCEINLINE
        static void MaybeTriggerAndProtect(OBJECTREF& objref)
        {
            if (GcStressBase::IsEnabled() && GCStressPolicy::IsEnabled())
            {
                GCPROTECT_BEGIN(objref);
                GcTriggerPolicy::Trigger();
                GCPROTECT_END();
            }
        }
    };

#else // STRESS_HEAP

    class IgnoreFastGcSPolicy {};
    class EeconfigFastGcSPolicy {};
    class AnyGcModePolicy {};
    class CoopGcModePolicy {};
    class StressGcTriggerPolicy {};
    class PulseGcTriggerPolicy {};

    // Everything here should resolve to inlined empty blocks or "false"
    template <
        enum gcs_trigger_points tp, 
        class Policy1 = mpl::null_type,
        class Policy2 = mpl::null_type,
        class Policy3 = mpl::null_type
    >
    class GCStress
    {
    public:
        FORCEINLINE
        static bool IsEnabled(DWORD minFastGc = 0)
        { 
            static_assert(tp < cfg_last, "GCStress::IsEnabled only supports cfg_ trigger points.");
            return false;
        }

        FORCEINLINE
        static void MaybeTrigger(DWORD minFastGc = 0)
        {}

        FORCEINLINE
        static void MaybeTrigger(::alloc_context* acontext, DWORD minFastGc = 0)
        {}

        template<typename T>
        FORCEINLINE
        static void MaybeTrigger(T arg)
        {}
    };

#endif // STRESS_HEAP

}

using _GCStress::IgnoreFastGcSPolicy;
using _GCStress::EeconfigFastGcSPolicy;
using _GCStress::AnyGcModePolicy;
using _GCStress::CoopGcModePolicy;
using _GCStress::StressGcTriggerPolicy;
using _GCStress::PulseGcTriggerPolicy;

using _GCStress::GCStress;



#endif