|
22 | 22 | // in the GC heap, causing data corruption. This is a 'GC Hole', and is very bad. We have special modes (see
|
23 | 23 | // code:EEConfig.GetGCStressLevel) called GCStress to help find such issues.
|
24 | 24 | //
|
25 |
| -// In order to find all GC references on the stacks we need insure that no thread is manipulating a GC |
26 |
| -// reference at the time of the scan. This is the job of code:Thread.SuspendRuntime. Logically it suspends |
27 |
| -// every thread in the process. Unfortunately it can not literally simply call the OS SuspendThread API on |
28 |
| -// all threads. The reason is that the other threads MIGHT hold important locks (for example there is a lock |
29 |
| -// that is taken when unmanaged heap memory is requested, or when a DLL is loaded). In general process |
| 25 | +// In order to find all GC references on the stacks, we need to ensure that no thread is manipulating a GC |
| 26 | +// reference at the time of the scan. This is the job of code:Thread.SuspendRuntime. Logically, it suspends |
| 27 | +// every thread in the process. Unfortunately, it can not literally simply call the OS SuspendThread API on |
| 28 | +// all threads. The reason is that the other threads MIGHT hold important locks (for example, there is a lock |
| 29 | +// that is taken when unmanaged heap memory is requested, or when a DLL is loaded). In general, process |
30 | 30 | // global structures in the OS will be protected by locks, and if you suspend a thread it might hold that
|
31 |
| -// lock. If you happen to need that OS service (eg you might need to allocated unmanaged memory), then |
| 31 | +// lock. If you happen to need that OS service (eg you might need to allocate unmanaged memory), then |
32 | 32 | // deadlock will occur (as you wait on the suspended thread, that never wakes up).
|
33 | 33 | //
|
34 |
| -// Luckily, we don't need to actually suspend the threads, we just need to insure that all GC references on |
| 34 | +// Luckily, we don't need to actually suspend the threads, we just need to ensure that all GC references on |
35 | 35 | // the stack are stable. This is where the concept of cooperative mode and preemptive mode (a bad name) come
|
36 | 36 | // from.
|
37 | 37 | //
|
|
40 | 40 | // The runtime keeps a table of all threads that have ever run managed code in the code:ThreadStore table.
|
41 | 41 | // The ThreadStore table holds a list of Thread objects (see code:#ThreadClass). This object holds all
|
42 | 42 | // information about managed threads. Cooperative mode is defined as the mode the thread is in when the field
|
43 |
| -// code:Thread.m_fPreemptiveGCDisabled is non-zero. When this field is zero the thread is said to be in |
| 43 | +// code:Thread.m_fPreemptiveGCDisabled is non-zero. When this field is zero, the thread is said to be in |
44 | 44 | // Preemptive mode (named because if you preempt the thread in this mode, it is guaranteed to be in a place
|
45 | 45 | // where a GC can occur).
|
46 | 46 | //
|
47 | 47 | // When a thread is in cooperative mode, it is basically saying that it is potentially modifying GC
|
48 | 48 | // references, and so the runtime must Cooperate with it to get to a 'GC Safe' location where the GC
|
49 | 49 | // references can be enumerated. This is the mode that a thread is in MOST times when it is running managed
|
50 |
| -// code (in fact if the EIP is in JIT compiled code, there is only one place where you are NOT in cooperative |
| 50 | +// code (in fact, if the EIP is in JIT compiled code, there is only one place where you are NOT in cooperative |
51 | 51 | // mode (Inlined PINVOKE transition code)). Conversely, any time non-runtime unmanaged code is running, the
|
52 | 52 | // thread MUST NOT be in cooperative mode (you risk deadlock otherwise). Only code in mscorwks.dll might be
|
53 | 53 | // running in either cooperative or preemptive mode.
|
54 | 54 | //
|
55 | 55 | // It is easier to describe the invariant associated with being in Preemptive mode. When the thread is in
|
56 | 56 | // preemptive mode (when code:Thread.m_fPreemptiveGCDisabled is zero), the thread guarantees two things
|
57 | 57 | //
|
58 |
| -// * That it not currently running code that manipulates GC references. |
| 58 | +// * That it is not currently running code that manipulates GC references. |
59 | 59 | // * That it has set the code:Thread.m_pFrame pointer in the code:Thread to be a subclass of the class
|
60 | 60 | // code:Frame which marks the location on the stack where the last managed method frame is. This
|
61 | 61 | // allows the GC to start crawling the stack from there (essentially skip over the unmanaged frames).
|
|
70 | 70 | // the deadlock problem mentioned earlier, because threads that are running unmanaged code are allowed to
|
71 | 71 | // run. Enumeration of GC references starts at the first managed frame (pointed at by code:Thread.m_pFrame).
|
72 | 72 | //
|
73 |
| -// When a thread is in cooperative mode, it means that GC references might be being manipulated. There are |
74 |
| -// two important possibilities |
| 73 | +// When a thread is in cooperative mode, it means that GC references might be in the process of being |
| 74 | +// manipulated. There are two important possibilities |
75 | 75 | //
|
76 | 76 | // * The CPU is running JIT compiled code
|
77 | 77 | // * The CPU is running code elsewhere (which should only be in mscorwks.dll, because everywhere else a
|
|
87 | 87 | // what is called FullyInterruptible, then we have information for any possible instruction pointer in the
|
88 | 88 | // method and we can simply stop the thread (however we have to do this carefully TODO explain).
|
89 | 89 | //
|
90 |
| -// However for most methods, we only keep GC information for paticular EIP's, in particular we keep track of |
91 |
| -// GC reference liveness only at call sites. Thus not every location is 'GC Safe' (that is we can enumerate |
| 90 | +// However for most methods, we only keep GC information for particular EIPs, in particular we keep track of |
| 91 | +// GC reference liveness only at call sites. Thus, not every location is 'GC Safe' (that is, we can enumerate |
92 | 92 | // all references, but must be 'driven' to a GC safe location).
|
93 | 93 | //
|
94 | 94 | // We drive threads to GC safe locations by hijacking. This is a term for updating the return address on the
|
95 | 95 | // stack so that we gain control when a method returns. If we find that we are in JITTed code but NOT at a GC
|
96 |
| -// safe location, then we find the return address for the method and modfiy it to cause the runtime to stop. |
| 96 | +// safe location, then we find the return address for the method and modify it to cause the runtime to stop. |
97 | 97 | // We then let the method run. Hopefully the method quickly returns, and hits our hijack, and we are now at a
|
98 |
| -// GC-safe location (all call sites are GC-safe). If not we repeat the procedure (possibly moving the |
99 |
| -// hijack). At some point a method returns, and we get control. For methods that have loops that don't make |
100 |
| -// calls, we are forced to make the method FullyInterruptible, so we can be sure to stop the mehod. |
| 98 | +// GC-safe location (all call sites are GC-safe). If not, we repeat the procedure (possibly moving the |
| 99 | +// hijack). At some point, a method returns, and we get control. For methods that have loops that don't make |
| 100 | +// calls, we are forced to make the method FullyInterruptible, so we can be sure to stop the method. |
101 | 101 | //
|
102 | 102 | // This leaves only the case where we are in cooperative modes, but not in JIT compiled code (we should be in
|
103 |
| -// clr.dll). In this case we simply let the thread run. The idea is that code in clr.dll makes the |
| 103 | +// clr.dll). In this case, we simply let the thread run. The idea is that code in clr.dll makes the |
104 | 104 | // promise that it will not do ANYTHING that will block (which includes taking a lock), while in cooperative
|
105 |
| -// mode, or do anything that might take a long time without polling to see if a GC is needed. Thus this code |
106 |
| -// 'cooperates' to insure that GCs can happen in a timely fashion. |
| 105 | +// mode, or do anything that might take a long time without polling to see if a GC is needed. Thus, this code |
| 106 | +// 'cooperates' to ensure that GCs can happen in a timely fashion. |
107 | 107 | //
|
108 | 108 | // If you need to switch the GC mode of the current thread, look for the GCX_COOP() and GCX_PREEMP() macros.
|
109 | 109 | //
|
|
0 commit comments