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
6//
7// #Overview
8//
9// This file provides convenient wrappers for the GC stress functionality.
10//
11// Exposed APIs:
12// GCStressPolicy::InhibitHolder
13// GCStressPolicy::GlobalEnable()
14// GCStressPolicy::GlobalDisable()
15// GCStressPolicy::IsEnabled()
16//
17// GCStress<> template classes with its IsEnabled() & MaybeTrigger members.
18//
19// Use GCStress<> to abstract away the GC stress related decissions. The
20// template definitions will resolve to nothing when STRESS_HEAP is not
21// defined, and will inline the function body at the call site otherwise.
22//
23// Examples:
24// GCStress<cfg_any>::IsEnabled()
25// GCStress<cfg_any, EeconfigFastGcSPolicy, CoopGcModePolicy>::MaybeTrigger()
26//
27
28#ifndef _GC_STRESS_
29#define _GC_STRESS_
30
31#include "mpl/type_list"
32
33
34struct alloc_context;
35
36
37enum gcs_trigger_points {
38 // generic handling based on EEConfig settings
39 cfg_any, // any bit set in EEConfig::iGCStress
40 cfg_alloc, // trigger on GC allocations
41 cfg_transition, // trigger on transitions
42 cfg_instr_jit, // trigger on JITted instructions
43 cfg_instr_ngen, // trigger on NGENed instructions
44 cfg_easy, // trigger on allocs or transitions
45 cfg_instr, // trigger on managed instructions (JITted or NGENed)
46 cfg_last, // boundary
47
48 // special handling at particular trigger points
49 jit_on_create_jump_stub,
50 jit_on_create_il_stub,
51 gc_on_alloc,
52 vsd_on_resolve
53};
54
55
56namespace GCStressPolicy
57{
58
59#ifdef STRESS_HEAP
60
61#ifdef __GNUC__
62#define UNUSED_ATTR __attribute__ ((unused))
63#else // __GNUC__
64#define UNUSED_ATTR
65#endif // __GNUC__
66
67#ifndef __UNUSED
68#define __UNUSED(x) ((void)(x))
69#endif // __UNUSED
70
71 class InhibitHolder
72 {
73 private:
74 // This static controls whether GC stress may induce GCs. EEConfig::GetGCStressLevel() still
75 // controls when GCs may occur.
76 static Volatile<DWORD> s_nGcStressDisabled;
77
78 bool m_bAquired;
79
80 public:
81 InhibitHolder()
82 { LIMITED_METHOD_CONTRACT; ++s_nGcStressDisabled; m_bAquired = true; }
83
84 ~InhibitHolder()
85 { LIMITED_METHOD_CONTRACT; Release(); }
86
87 void Release()
88 {
89 LIMITED_METHOD_CONTRACT;
90 if (m_bAquired)
91 {
92 --s_nGcStressDisabled;
93 m_bAquired = false;
94 }
95 }
96
97 friend bool IsEnabled();
98 friend void GlobalDisable();
99 friend void GlobalEnable();
100 } UNUSED_ATTR;
101
102 FORCEINLINE bool IsEnabled()
103 { return InhibitHolder::s_nGcStressDisabled == 0U; }
104
105 FORCEINLINE void GlobalDisable()
106 { ++InhibitHolder::s_nGcStressDisabled; }
107
108 FORCEINLINE void GlobalEnable()
109 { --InhibitHolder::s_nGcStressDisabled; }
110
111#else // STRESS_HEAP
112
113 class InhibitHolder
114 { void Release() {} };
115
116 FORCEINLINE bool IsEnabled()
117 { return false; }
118
119 FORCEINLINE void GlobalDisable()
120 {}
121
122 FORCEINLINE void GlobalEnable()
123 {}
124
125#endif // STRESS_HEAP
126}
127
128
129
130namespace _GCStress
131{
132
133#ifdef STRESS_HEAP
134
135 // Support classes to allow easy customization of GC Stress policies
136 namespace detail
137 {
138 using namespace mpl;
139
140 // Selecting a policy from a type list and a fallback/default policy
141 // GetPolicy<>:type will represent either a type in ListT with the same "tag" as DefPolicy
142 // or DefPolicy, based on the Traits passed in.
143 template <
144 typename ListT,
145 typename DefPolicy,
146 template <typename> class Traits
147 >
148 struct GetPolicy;
149
150 // Common case: recurse over the type list
151 template <
152 typename HeadT,
153 typename TailT,
154 typename DefPolicy,
155 template<typename> class Traits
156 >
157 struct GetPolicy<type_list<HeadT, TailT>, DefPolicy, Traits>
158 {
159 // is true if HeadT and DefPolicy evaluate to the same tag,
160 // through Traits<>
161 static const bool sameTag = std::is_same<
162 typename Traits<HeadT>::tag,
163 typename Traits<DefPolicy>::tag
164 >::value;
165
166 typedef typename std::conditional<
167 sameTag,
168 HeadT,
169 typename GetPolicy<TailT, DefPolicy, Traits>::type
170 >::type type;
171 };
172
173 // Termination case.
174 template <
175 typename DefPolicy,
176 template<typename> class Traits
177 >
178 struct GetPolicy <null_type, DefPolicy, Traits>
179 {
180 typedef DefPolicy type;
181 };
182 }
183
184
185 // GC stress specific EEConfig accessors
186 namespace detail
187 {
188 // no definition provided so that absence of concrete implementations cause compiler errors
189 template <enum gcs_trigger_points>
190 bool IsEnabled();
191
192 template<> FORCEINLINE
193 bool IsEnabled<cfg_any>()
194 {
195 // Most correct would be to test for each specific bits, but we've
196 // always only tested against 0...
197 return g_pConfig->GetGCStressLevel() != 0;
198 // return (g_pConfig->GetGCStressLevel() &
199 // (EEConfig::GCSTRESS_ALLOC|EEConfig::GCSTRESS_TRANSITION|
200 // EEConfig::GCSTRESS_INSTR_JIT|EEConfig::GCSTRESS_INSTR_NGEN) != 0);
201 }
202
203 #define DefineIsEnabled(cfg_enum, eeconfig_bits) \
204 template<> FORCEINLINE \
205 bool IsEnabled<cfg_enum>() \
206 { \
207 return (g_pConfig->GetGCStressLevel() & (eeconfig_bits)) != 0; \
208 }
209
210 DefineIsEnabled(cfg_alloc, EEConfig::GCSTRESS_ALLOC);
211 DefineIsEnabled(cfg_transition, EEConfig::GCSTRESS_TRANSITION);
212 DefineIsEnabled(cfg_instr_jit, EEConfig::GCSTRESS_INSTR_JIT);
213 DefineIsEnabled(cfg_instr_ngen, EEConfig::GCSTRESS_INSTR_NGEN);
214 DefineIsEnabled(cfg_easy, EEConfig::GCSTRESS_ALLOC|EEConfig::GCSTRESS_TRANSITION);
215 DefineIsEnabled(cfg_instr, EEConfig::GCSTRESS_INSTR_JIT|EEConfig::GCSTRESS_INSTR_NGEN);
216
217 #undef DefineIsEnabled
218
219 }
220
221
222 //
223 // GC stress policy classes used by GCSBase and GCStress template classes
224 //
225
226 // Fast GS stress policies that dictate whether GCStress<>::MaybeTrigger()
227 // will consider g_pConfig->FastGCStressLevel() when deciding whether
228 // to trigger a GC or not.
229
230 // This is the default Fast GC stress policy that ignores the EEConfig
231 // setting
232 class IgnoreFastGcSPolicy
233 {
234 public:
235 FORCEINLINE
236 static bool FastGcSEnabled(DWORD minValue = 0)
237 { return false; }
238 };
239
240 // This is the overriding Fast GC stress policy that considers the
241 // EEConfig setting on checked/debug builds
242 class EeconfigFastGcSPolicy
243 {
244 public:
245 FORCEINLINE
246 static bool FastGcSEnabled(DWORD minValue = 0)
247 {
248 #ifdef _DEBUG
249 return g_pConfig->FastGCStressLevel() > minValue;
250 #else // _DEBUG
251 return false;
252 #endif // _DEBUG
253 }
254 };
255
256 // GC Mode policies that determines whether to switch the GC mode before
257 // triggering the GC.
258
259 // This is the default GC Mode stress policy that does not switch GC modes
260 class AnyGcModePolicy
261 {
262 };
263
264 // This is the overriding GC Mode stress policy that forces a switch to
265 // cooperative mode before MaybeTrigger() will trigger a GC
266 class CoopGcModePolicy
267 {
268#ifndef DACCESS_COMPILE
269 // implicit constructor an destructor will do the right thing
270 GCCoop m_coop;
271#endif // DACCESS_COMPILE
272
273 public:
274 FORCEINLINE CoopGcModePolicy()
275 { WRAPPER_NO_CONTRACT; }
276 FORCEINLINE ~CoopGcModePolicy()
277 { WRAPPER_NO_CONTRACT; }
278 } UNUSED_ATTR;
279
280 // GC Trigger policy classes define how a garbage collection is triggered
281
282 // This is the default GC Trigger policy that simply calls
283 // IGCHeap::StressHeap
284 class StressGcTriggerPolicy
285 {
286 public:
287 FORCEINLINE
288 static void Trigger()
289 {
290 // BUG(github #10318) - when not using allocation contexts, the alloc lock
291 // must be acquired here. Until fixed, this assert prevents random heap corruption.
292 _ASSERTE(GCHeapUtilities::UseThreadAllocationContexts());
293 GCHeapUtilities::GetGCHeap()->StressHeap(GetThread()->GetAllocContext());
294 }
295
296 FORCEINLINE
297 static void Trigger(::gc_alloc_context* acontext)
298 { GCHeapUtilities::GetGCHeap()->StressHeap(acontext); }
299 };
300
301 // This is an overriding GC Trigger policy that triggers a GC by calling
302 // PulseGCMode
303 class PulseGcTriggerPolicy
304 {
305 public:
306 FORCEINLINE
307 static void Trigger()
308 {
309 DEBUG_ONLY_REGION();
310 GetThread()->PulseGCMode();
311 }
312 };
313
314
315 // GC stress policy tags
316 struct fast_gcs_policy_tag {};
317 struct gc_mode_policy_tag {};
318 struct gc_trigger_policy_tag {};
319
320
321 template <class GCSPolicy>
322 struct GcStressTraits
323 { typedef mpl::null_type tag; };
324
325 #define DefineGCStressTraits(Policy, policy_tag) \
326 template <> struct GcStressTraits<Policy> \
327 { typedef policy_tag tag; }
328
329 DefineGCStressTraits(IgnoreFastGcSPolicy, fast_gcs_policy_tag);
330 DefineGCStressTraits(EeconfigFastGcSPolicy, fast_gcs_policy_tag);
331
332 DefineGCStressTraits(AnyGcModePolicy, gc_mode_policy_tag);
333 DefineGCStressTraits(CoopGcModePolicy, gc_mode_policy_tag);
334
335 DefineGCStressTraits(StressGcTriggerPolicy, gc_trigger_policy_tag);
336 DefineGCStressTraits(PulseGcTriggerPolicy, gc_trigger_policy_tag);
337
338 #undef DefineGCStressTraits
339
340 // Special handling for GC stress policies
341 template <class GCPolicies, class DefPolicy>
342 struct GetPolicy:
343 public detail::GetPolicy<GCPolicies, DefPolicy, GcStressTraits>
344 {};
345
346
347 //
348 // The base for any customization GCStress class. It accepts an identifying
349 // GC stress trigger point and at most three overriding policies.
350 //
351 // It defines FastGcSPolicy, GcModePolicy, and GcTriggerPolicy as either
352 // the overriding policy or the default policy, if no corresponding
353 // overriding policy is specified in the list. These names can then be
354 // accessed from the derived GCStress class.
355 //
356 // Additionally it defines the static methods IsEnabled and MaybeTrigger and
357 // how the policy classes influence their behavior.
358 //
359 template <
360 enum gcs_trigger_points tp,
361 class Policy1 = mpl::null_type,
362 class Policy2 = mpl::null_type,
363 class Policy3 = mpl::null_type
364 >
365 class GCSBase
366 {
367 public:
368 typedef typename mpl::make_type_list<Policy1, Policy2, Policy3>::type Policies;
369
370 typedef typename GetPolicy<Policies, IgnoreFastGcSPolicy>::type FastGcSPolicy;
371 typedef typename GetPolicy<Policies, AnyGcModePolicy>::type GcModePolicy;
372 typedef typename GetPolicy<Policies, StressGcTriggerPolicy>::type GcTriggerPolicy;
373
374 typedef GCSBase<tp, FastGcSPolicy, GcModePolicy, GcTriggerPolicy> GcStressBase;
375
376 // Returns true iff:
377 // . the bitflag in EEConfig::GetStressLevel() corresponding to the
378 // gc stress trigger point is set AND
379 // . when a Fast GC Stress policy is specified, if the minFastGC argument
380 // is below the EEConfig::FastGCStressLevel
381 FORCEINLINE
382 static bool IsEnabled(DWORD minFastGc = 0)
383 {
384 static_assert(tp < cfg_last, "GCSBase only supports cfg_ trigger points.");
385 return detail::IsEnabled<tp>() && !FastGcSPolicy::FastGcSEnabled(minFastGc);
386 }
387
388 // Triggers a GC iff
389 // . the GC stress is not disabled globally (thru GCStressPolicy::GlobalDisable)
390 // AND
391 // . IsEnabled() returns true.
392 // Additionally it switches the GC mode as specified by GcModePolicy, and it
393 // uses GcTriggerPolicy::Trigger() to actually trigger the GC
394 FORCEINLINE
395 static void MaybeTrigger(DWORD minFastGc = 0)
396 {
397 if (IsEnabled(minFastGc) && GCStressPolicy::IsEnabled())
398 {
399 GcModePolicy gcModeObj; __UNUSED(gcModeObj);
400 GcTriggerPolicy::Trigger();
401 }
402 }
403
404 // Triggers a GC iff
405 // . the GC stress is not disabled globally (thru GCStressPolicy::GlobalDisable)
406 // AND
407 // . IsEnabled() returns true.
408 // Additionally it switches the GC mode as specified by GcModePolicy, and it
409 // uses GcTriggerPolicy::Trigger(alloc_context*) to actually trigger the GC
410 FORCEINLINE
411 static void MaybeTrigger(::gc_alloc_context* acontext, DWORD minFastGc = 0)
412 {
413 if (IsEnabled(minFastGc) && GCStressPolicy::IsEnabled())
414 {
415 GcModePolicy gcModeObj; __UNUSED(gcModeObj);
416 GcTriggerPolicy::Trigger(acontext);
417 }
418 }
419 };
420
421 template <
422 enum gcs_trigger_points tp,
423 class Policy1 = mpl::null_type,
424 class Policy2 = mpl::null_type,
425 class Policy3 = mpl::null_type
426 >
427 class GCStress
428 : public GCSBase<tp, Policy1, Policy2, Policy3>
429 {
430 };
431
432
433 //
434 // Partial specializations of GCStress for trigger points requiring non-default
435 // handling.
436 //
437
438 template <>
439 class GCStress<jit_on_create_jump_stub>
440 : public GCSBase<cfg_any>
441 {
442 public:
443
444 FORCEINLINE
445 static void MaybeTrigger(DWORD minFastGc = 0)
446 {
447 if ((GetThreadNULLOk() != NULL) && (GetThreadNULLOk()->PreemptiveGCDisabled()))
448 {
449 // Force a GC if the stress level is high enough
450 GcStressBase::MaybeTrigger(minFastGc);
451 }
452 }
453
454 };
455
456 template <>
457 class GCStress<gc_on_alloc>
458 : public GCSBase<cfg_alloc>
459 {
460 public:
461
462 FORCEINLINE
463 static void MaybeTrigger(::gc_alloc_context* acontext)
464 {
465 GcStressBase::MaybeTrigger(acontext);
466
467#ifdef _DEBUG
468 Thread *pThread = GetThread();
469 if (pThread)
470 {
471 pThread->EnableStressHeap();
472 }
473#endif //_DEBUG
474 }
475 };
476
477 template <>
478 class GCStress<vsd_on_resolve>
479 : public GCSBase<cfg_any>
480 {
481 public:
482
483 // Triggers a GC iff
484 // . the GC stress is not disabled globally (thru GCStressPolicy::GlobalDisable)
485 // AND
486 // . IsEnabled() returns true.
487 // Additionally it protects the passed in OBJECTREF&, and it uses
488 // GcTriggerPolicy::Trigger() to actually trigger the GC
489 //
490 // Note: the OBJECTREF must be passed by reference so MaybeTrigger can protect
491 // the calling function's stack slot.
492 FORCEINLINE
493 static void MaybeTriggerAndProtect(OBJECTREF& objref)
494 {
495 if (GcStressBase::IsEnabled() && GCStressPolicy::IsEnabled())
496 {
497 GCPROTECT_BEGIN(objref);
498 GcTriggerPolicy::Trigger();
499 GCPROTECT_END();
500 }
501 }
502 };
503
504#else // STRESS_HEAP
505
506 class IgnoreFastGcSPolicy {};
507 class EeconfigFastGcSPolicy {};
508 class AnyGcModePolicy {};
509 class CoopGcModePolicy {};
510 class StressGcTriggerPolicy {};
511 class PulseGcTriggerPolicy {};
512
513 // Everything here should resolve to inlined empty blocks or "false"
514 template <
515 enum gcs_trigger_points tp,
516 class Policy1 = mpl::null_type,
517 class Policy2 = mpl::null_type,
518 class Policy3 = mpl::null_type
519 >
520 class GCStress
521 {
522 public:
523 FORCEINLINE
524 static bool IsEnabled(DWORD minFastGc = 0)
525 {
526 static_assert(tp < cfg_last, "GCStress::IsEnabled only supports cfg_ trigger points.");
527 return false;
528 }
529
530 FORCEINLINE
531 static void MaybeTrigger(DWORD minFastGc = 0)
532 {}
533
534 FORCEINLINE
535 static void MaybeTrigger(::alloc_context* acontext, DWORD minFastGc = 0)
536 {}
537
538 template<typename T>
539 FORCEINLINE
540 static void MaybeTrigger(T arg)
541 {}
542 };
543
544#endif // STRESS_HEAP
545
546}
547
548using _GCStress::IgnoreFastGcSPolicy;
549using _GCStress::EeconfigFastGcSPolicy;
550using _GCStress::AnyGcModePolicy;
551using _GCStress::CoopGcModePolicy;
552using _GCStress::StressGcTriggerPolicy;
553using _GCStress::PulseGcTriggerPolicy;
554
555using _GCStress::GCStress;
556
557
558
559#endif
560