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 | |
8 | #ifndef _SimpleRWLock_hpp_ |
9 | #define _SimpleRWLock_hpp_ |
10 | |
11 | #include "threads.h" |
12 | |
13 | class SimpleRWLock; |
14 | |
15 | //------------------------------------------------------------------------------------------- |
16 | // GC_MODE defines custom CONTRACTs for TryEnterRead and TryEnterWrite. |
17 | // |
18 | // Contract differs when acquiring the lock depending on its lock mode. |
19 | // |
20 | // GC/MODE |
21 | // A SimpleRWLock can be one of the following modes. We only want to see the "PREEMPTIVE" |
22 | // type used in new code. Other types, kept for legacy reasons, are listed in |
23 | // order from least objectionable to most objectionable. |
24 | // |
25 | // PREEMPTIVE (equivalent to CRST's "normal") |
26 | // This is the preferred type of crst. Enter() will force-switch your thread |
27 | // into preemptive mode if it isn't already. Thus, the effective contract is: |
28 | // |
29 | // MODE_ANY |
30 | // GC_TRIGGERS |
31 | // |
32 | // |
33 | // |
34 | // COOPERATIVE (equivalent to CRST_UNSAFE_COOPGC) |
35 | // You can only attempt to acquire this crst if you're already in coop mode. It is |
36 | // guaranteed no GC will occur while waiting to acquire the lock. While you hold |
37 | // the lock, your thread is in a GCFORBID state. |
38 | // |
39 | // MODE_COOP |
40 | // GC_NOTRIGGER |
41 | // |
42 | // |
43 | // |
44 | // COOPERATIVE_OR_PREEMPTIVE (equivalent to CRST_UNSAFE_ANYMODE) |
45 | // You can attempt to acquire this in either mode. Entering the crst will not change |
46 | // your thread mode but it will increment the GCNoTrigger count. |
47 | // |
48 | // MODE_ANY |
49 | // GC_NOTRIGGER |
50 | //------------------------------------------------------------------------------------------------ |
51 | enum GC_MODE { |
52 | COOPERATIVE, |
53 | PREEMPTIVE, |
54 | COOPERATIVE_OR_PREEMPTIVE} ; |
55 | |
56 | class SimpleRWLock |
57 | { |
58 | // Allow Module access so we can use Offsetof on this class's private members during native image creation (determinism) |
59 | friend class Module; |
60 | private: |
61 | BOOL IsWriterWaiting() |
62 | { |
63 | LIMITED_METHOD_CONTRACT; |
64 | return m_WriterWaiting != 0; |
65 | } |
66 | |
67 | void SetWriterWaiting() |
68 | { |
69 | LIMITED_METHOD_CONTRACT; |
70 | m_WriterWaiting = 1; |
71 | } |
72 | |
73 | void ResetWriterWaiting() |
74 | { |
75 | LIMITED_METHOD_CONTRACT; |
76 | m_WriterWaiting = 0; |
77 | } |
78 | |
79 | BOOL TryEnterRead(); |
80 | |
81 | BOOL TryEnterWrite(); |
82 | |
83 | #ifdef ENABLE_CONTRACTS_IMPL |
84 | void CheckGCNoTrigger(); |
85 | #endif //ENABLE_CONTRACTS_IMPL |
86 | |
87 | // lock used for R/W synchronization |
88 | Volatile<LONG> m_RWLock; |
89 | |
90 | // Does this lock require to be taken in PreemptiveGC mode? |
91 | const GC_MODE m_gcMode; |
92 | |
93 | // spin count for a reader waiting for a writer to release the lock |
94 | LONG m_spinCount; |
95 | |
96 | // used to prevent writers from being starved by readers |
97 | // we currently do not prevent writers from starving readers since writers |
98 | // are supposed to be rare. |
99 | BOOL m_WriterWaiting; |
100 | |
101 | #ifdef _DEBUG |
102 | // Check for dead lock situation. |
103 | Volatile<LONG> m_countNoTriggerGC; |
104 | |
105 | #ifdef _WIN64 |
106 | // ensures that we are a multiple of 8-bytes |
107 | UINT32 pad; |
108 | #endif |
109 | |
110 | void PostEnter (); |
111 | void PreEnter (); |
112 | void PreLeave (); |
113 | #endif //_DEBUG |
114 | |
115 | #ifndef DACCESS_COMPILE |
116 | static void AcquireReadLock(SimpleRWLock *s) { LIMITED_METHOD_CONTRACT; s->EnterRead(); } |
117 | static void ReleaseReadLock(SimpleRWLock *s) { LIMITED_METHOD_CONTRACT; s->LeaveRead(); } |
118 | |
119 | static void AcquireWriteLock(SimpleRWLock *s) { LIMITED_METHOD_CONTRACT; s->EnterWrite(); } |
120 | static void ReleaseWriteLock(SimpleRWLock *s) { LIMITED_METHOD_CONTRACT; s->LeaveWrite(); } |
121 | #else // DACCESS_COMPILE |
122 | // in DAC builds, we don't actually acquire the lock, we just determine whether the LS |
123 | // already holds it. If so, we assume the data is inconsistent and throw an exception. |
124 | // Argument: |
125 | // input: s - the lock to be checked. |
126 | // Note: Throws |
127 | static void AcquireReadLock(SimpleRWLock *s) |
128 | { |
129 | SUPPORTS_DAC; |
130 | if (s->IsWriterLock()) |
131 | { |
132 | ThrowHR(CORDBG_E_PROCESS_NOT_SYNCHRONIZED); |
133 | } |
134 | }; |
135 | static void ReleaseReadLock(SimpleRWLock *s) { }; |
136 | |
137 | static void AcquireWriteLock(SimpleRWLock *s) { SUPPORTS_DAC; ThrowHR(CORDBG_E_TARGET_READONLY); }; |
138 | static void ReleaseWriteLock(SimpleRWLock *s) { }; |
139 | #endif // DACCESS_COMPILE |
140 | |
141 | public: |
142 | SimpleRWLock (GC_MODE gcMode, LOCK_TYPE locktype) |
143 | : m_gcMode (gcMode) |
144 | { |
145 | CONTRACTL { |
146 | NOTHROW; |
147 | GC_NOTRIGGER; |
148 | } CONTRACTL_END; |
149 | |
150 | m_RWLock = 0; |
151 | m_spinCount = (GetCurrentProcessCpuCount() == 1) ? 0 : 4000; |
152 | m_WriterWaiting = FALSE; |
153 | |
154 | #ifdef _DEBUG |
155 | m_countNoTriggerGC = 0; |
156 | #endif |
157 | } |
158 | |
159 | // Special empty CTOR for DAC. We still need to assign to const fields, but they won't actually be used. |
160 | SimpleRWLock() |
161 | : m_gcMode(COOPERATIVE_OR_PREEMPTIVE) |
162 | { |
163 | LIMITED_METHOD_CONTRACT; |
164 | |
165 | #ifdef _DEBUG |
166 | m_countNoTriggerGC = 0; |
167 | #endif //_DEBUG |
168 | } |
169 | |
170 | #ifndef DACCESS_COMPILE |
171 | // Acquire the reader lock. |
172 | void EnterRead(); |
173 | |
174 | // Acquire the writer lock. |
175 | void EnterWrite(); |
176 | |
177 | // Leave the reader lock. |
178 | void LeaveRead() |
179 | { |
180 | LIMITED_METHOD_CONTRACT; |
181 | #ifdef _DEBUG |
182 | PreLeave (); |
183 | #endif //_DEBUG |
184 | LONG RWLock; |
185 | RWLock = InterlockedDecrement(&m_RWLock); |
186 | _ASSERTE (RWLock >= 0); |
187 | DECTHREADLOCKCOUNT(); |
188 | EE_LOCK_RELEASED(this); |
189 | } |
190 | |
191 | // Leave the writer lock. |
192 | void LeaveWrite() |
193 | { |
194 | LIMITED_METHOD_CONTRACT; |
195 | #ifdef _DEBUG |
196 | PreLeave (); |
197 | #endif //_DEBUG |
198 | LONG RWLock; |
199 | RWLock = InterlockedExchange (&m_RWLock, 0); |
200 | _ASSERTE(RWLock == -1); |
201 | DECTHREADLOCKCOUNT(); |
202 | EE_LOCK_RELEASED(this); |
203 | } |
204 | |
205 | #endif // DACCESS_COMPILE |
206 | |
207 | typedef DacHolder<SimpleRWLock *, SimpleRWLock::AcquireReadLock, SimpleRWLock::ReleaseReadLock> SimpleReadLockHolder; |
208 | typedef DacHolder<SimpleRWLock *, SimpleRWLock::AcquireWriteLock, SimpleRWLock::ReleaseWriteLock> SimpleWriteLockHolder; |
209 | |
210 | #ifdef _DEBUG |
211 | BOOL LockTaken () |
212 | { |
213 | LIMITED_METHOD_CONTRACT; |
214 | return m_RWLock != 0; |
215 | } |
216 | |
217 | BOOL IsReaderLock () |
218 | { |
219 | LIMITED_METHOD_CONTRACT; |
220 | return m_RWLock > 0; |
221 | } |
222 | |
223 | #endif |
224 | |
225 | BOOL IsWriterLock () |
226 | { |
227 | LIMITED_METHOD_DAC_CONTRACT; |
228 | return m_RWLock < 0; |
229 | } |
230 | |
231 | }; |
232 | |
233 | typedef SimpleRWLock::SimpleReadLockHolder SimpleReadLockHolder; |
234 | typedef SimpleRWLock::SimpleWriteLockHolder SimpleWriteLockHolder; |
235 | typedef DPTR(SimpleRWLock) PTR_SimpleRWLock; |
236 | |
237 | #ifdef TEST_DATA_CONSISTENCY |
238 | // used for test purposes. Determines if a crst is held. |
239 | // Arguments: |
240 | // input: pLock - the lock to test |
241 | // Note: Throws if the lock is held |
242 | |
243 | FORCEINLINE void DebugTryRWLock(SimpleRWLock * pLock) |
244 | { |
245 | SUPPORTS_DAC; |
246 | |
247 | SimpleReadLockHolder rwLock(pLock); |
248 | } |
249 | #endif // TEST_DATA_CONSISTENCY |
250 | #endif |
251 | |