2 // Copyright (c) Microsoft. All rights reserved.
3 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
9 #ifndef _SimpleRWLock_hpp_
10 #define _SimpleRWLock_hpp_
18 //-------------------------------------------------------------------------------------------
19 // GC_MODE defines custom CONTRACTs for TryEnterRead and TryEnterWrite.
21 // Contract differs when acquiring the lock depending on its lock mode.
24 // A SimpleRWLock can be one of the following modes. We only want to see the "PREEMPTIVE"
25 // type used in new code. Other types, kept for legacy reasons, are listed in
26 // order from least objectionable to most objectionable.
28 // PREEMPTIVE (equivalent to CRST's "normal")
29 // This is the preferred type of crst. Enter() will force-switch your thread
30 // into preemptive mode if it isn't already. Thus, the effective contract is:
37 // COOPERATIVE (equivalent to CRST_UNSAFE_COOPGC)
38 // You can only attempt to acquire this crst if you're already in coop mode. It is
39 // guaranteed no GC will occur while waiting to acquire the lock. While you hold
40 // the lock, your thread is in a GCFORBID state.
47 // COOPERATIVE_OR_PREEMPTIVE (equivalent to CRST_UNSAFE_ANYMODE)
48 // You can attempt to acquire this in either mode. Entering the crst will not change
49 // your thread mode but it will increment the GCNoTrigger count.
53 //------------------------------------------------------------------------------------------------
57 COOPERATIVE_OR_PREEMPTIVE} ;
61 // Allow Module access so we can use Offsetof on this class's private members during native image creation (determinism)
64 BOOL IsWriterWaiting()
66 LIMITED_METHOD_CONTRACT;
67 return m_WriterWaiting != 0;
70 void SetWriterWaiting()
72 LIMITED_METHOD_CONTRACT;
76 void ResetWriterWaiting()
78 LIMITED_METHOD_CONTRACT;
86 #ifdef ENABLE_CONTRACTS_IMPL
87 void CheckGCNoTrigger();
88 #endif //ENABLE_CONTRACTS_IMPL
90 // lock used for R/W synchronization
91 Volatile<LONG> m_RWLock;
93 // Does this lock require to be taken in PreemptiveGC mode?
94 const GC_MODE m_gcMode;
96 // spin count for a reader waiting for a writer to release the lock
99 // used to prevent writers from being starved by readers
100 // we currently do not prevent writers from starving readers since writers
101 // are supposed to be rare.
102 BOOL m_WriterWaiting;
105 // Check for dead lock situation.
106 Volatile<LONG> m_countNoTriggerGC;
109 // ensures that we are a multiple of 8-bytes
118 #ifndef DACCESS_COMPILE
119 static void AcquireReadLock(SimpleRWLock *s) { LIMITED_METHOD_CONTRACT; s->EnterRead(); }
120 static void ReleaseReadLock(SimpleRWLock *s) { LIMITED_METHOD_CONTRACT; s->LeaveRead(); }
122 static void AcquireWriteLock(SimpleRWLock *s) { LIMITED_METHOD_CONTRACT; s->EnterWrite(); }
123 static void ReleaseWriteLock(SimpleRWLock *s) { LIMITED_METHOD_CONTRACT; s->LeaveWrite(); }
124 #else // DACCESS_COMPILE
125 // in DAC builds, we don't actually acquire the lock, we just determine whether the LS
126 // already holds it. If so, we assume the data is inconsistent and throw an exception.
128 // input: s - the lock to be checked.
130 static void AcquireReadLock(SimpleRWLock *s)
133 if (s->IsWriterLock())
135 ThrowHR(CORDBG_E_PROCESS_NOT_SYNCHRONIZED);
138 static void ReleaseReadLock(SimpleRWLock *s) { };
140 static void AcquireWriteLock(SimpleRWLock *s) { SUPPORTS_DAC; ThrowHR(CORDBG_E_TARGET_READONLY); };
141 static void ReleaseWriteLock(SimpleRWLock *s) { };
142 #endif // DACCESS_COMPILE
145 SimpleRWLock (GC_MODE gcMode, LOCK_TYPE locktype)
154 #ifdef CLR_STANDALONE_BINDER
157 m_spinCount = (GetCurrentProcessCpuCount() == 1) ? 0 : 4000;
159 m_WriterWaiting = FALSE;
162 m_countNoTriggerGC = 0;
166 // Special empty CTOR for DAC. We still need to assign to const fields, but they won't actually be used.
168 : m_gcMode(COOPERATIVE_OR_PREEMPTIVE)
170 LIMITED_METHOD_CONTRACT;
173 m_countNoTriggerGC = 0;
177 #ifndef DACCESS_COMPILE
178 // Acquire the reader lock.
181 // Acquire the writer lock.
185 // Leave the reader lock.
187 // Leave the writer lock.
190 // Leave the reader lock.
193 LIMITED_METHOD_CONTRACT;
198 RWLock = InterlockedDecrement(&m_RWLock);
199 _ASSERTE (RWLock >= 0);
200 DECTHREADLOCKCOUNT();
201 EE_LOCK_RELEASED(this);
204 // Leave the writer lock.
207 LIMITED_METHOD_CONTRACT;
212 RWLock = InterlockedExchange (&m_RWLock, 0);
213 _ASSERTE(RWLock == -1);
214 DECTHREADLOCKCOUNT();
215 EE_LOCK_RELEASED(this);
219 #endif // DACCESS_COMPILE
221 typedef DacHolder<SimpleRWLock *, SimpleRWLock::AcquireReadLock, SimpleRWLock::ReleaseReadLock> SimpleReadLockHolder;
222 typedef DacHolder<SimpleRWLock *, SimpleRWLock::AcquireWriteLock, SimpleRWLock::ReleaseWriteLock> SimpleWriteLockHolder;
227 LIMITED_METHOD_CONTRACT;
228 return m_RWLock != 0;
233 LIMITED_METHOD_CONTRACT;
241 LIMITED_METHOD_DAC_CONTRACT;
247 typedef SimpleRWLock::SimpleReadLockHolder SimpleReadLockHolder;
248 typedef SimpleRWLock::SimpleWriteLockHolder SimpleWriteLockHolder;
249 typedef DPTR(SimpleRWLock) PTR_SimpleRWLock;
251 #ifdef TEST_DATA_CONSISTENCY
252 // used for test purposes. Determines if a crst is held.
254 // input: pLock - the lock to test
255 // Note: Throws if the lock is held
257 FORCEINLINE void DebugTryRWLock(SimpleRWLock * pLock)
261 SimpleReadLockHolder rwLock(pLock);
263 #endif // TEST_DATA_CONSISTENCY