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
13class 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//------------------------------------------------------------------------------------------------
51enum GC_MODE {
52 COOPERATIVE,
53 PREEMPTIVE,
54 COOPERATIVE_OR_PREEMPTIVE} ;
55
56class 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;
60private:
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
141public:
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
233typedef SimpleRWLock::SimpleReadLockHolder SimpleReadLockHolder;
234typedef SimpleRWLock::SimpleWriteLockHolder SimpleWriteLockHolder;
235typedef 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
243FORCEINLINE void DebugTryRWLock(SimpleRWLock * pLock)
244{
245 SUPPORTS_DAC;
246
247 SimpleReadLockHolder rwLock(pLock);
248}
249#endif // TEST_DATA_CONSISTENCY
250#endif
251