Fixed issue #3307: Abort Merge on a single file always results in a parameter error...
[TortoiseGit.git] / src / Utils / ReaderWriterLock.h
blob9b6f0628356488f6ba4baf3832688addbb1b59a3
1 /*********************************************************************
2 CReaderWriterLock: A simple and fast reader-writer lock class in C++
3 has characters of .NET ReaderWriterLock class
4 Copyright (C) 2006 Quynh Nguyen Huu
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 Email questions, comments or suggestions to quynhnguyenhuu@gmail.com
17 *********************************************************************/
19 /*********************************************************************
20 Introduction:
21 This implementation is inspired by System.Threading.ReaderWriterLock in
22 .NET framework. Following are some important statements I excerpted
23 (with some words modified) from .NET document.
25 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemThreadingReaderWriterLockClassTopic.asp
27 "ReaderWriterLock is used to synchronize access to a resource.
28 At any given time, it allows either concurrent read access for
29 multiple threads, or write access for a single thread.
30 In a situation where a resource is changed infrequently, a
31 ReaderWriterLock provides better throughput than a simple
32 one-at-a-time lock, such as CriticalSection or Mutex.
34 This library works best where most accesses are reads, while
35 writes are infrequent and of short duration.
37 While a writer is waiting for active reader locks to be released,
38 threads requesting new reader locks will have to wait in the reader
39 queue. Their requests are not granted, even though they could share
40 concurrent access with existing reader-lock holders; this helps
41 protect writers against indefinite blockage by readers..."
42 *********************************************************************/
44 #pragma once
46 #include <windows.h>
47 #include <map>
49 #if (_WIN32_WINNT >= 0x0403)
50 //////////////////////////////////////////////////////////////////
51 // On multiprocessor systems, this value define number of times
52 // that a thread tries to spin before actually performing a wait
53 // operation (see InitializeCriticalSectionAndSpinCount API)
54 #ifndef READER_WRITER_SPIN_COUNT
55 #define READER_WRITER_SPIN_COUNT 400
56 #endif READER_WRITER_SPIN_COUNT
57 #endif _WIN32_WINNT
59 // Forward reference
60 class CReaderWriterLock;
62 //////////////////////////////////////////////////////////////////
63 // CReaderWriterLockNonReentrance class
64 // NOTE: This class doesn't support reentrance & lock escalation.
65 // May be deadlock in one of following situations:
66 // 1) Call AcquireReaderLock twice (reentrance)
67 // --> Revise execution flow.
68 // 2) Call AcquireWriterLock twice (reentrance)
69 // --> Revise execution flow.
70 // 3) Call AcquireReaderLock then AcquireWriterLock (lock escalation)
71 // --> Use ReleaseReaderAndAcquireWriterLock method
72 // 4) Call AcquireWriterLock then AcquireReaderLock (lock deescalation)
73 // --> Use DowngradeFromWriterLock method
74 class CReaderWriterLockNonReentrance
76 public:
77 CReaderWriterLockNonReentrance() throw();
78 ~CReaderWriterLockNonReentrance() throw();
79 bool AcquireReaderLock(DWORD dwTimeout = INFINITE) throw();
80 void ReleaseReaderLock() throw();
81 bool AcquireWriterLock(DWORD dwTimeout = INFINITE) throw();
82 void ReleaseWriterLock() throw();
83 void DowngradeFromWriterLock() throw();
85 // When a thread calls UpgradeToWriterLock, the reader lock is released,
86 // and the thread goes to the end of the writer queue. Thus, other threads
87 // might write to resources before this method returns
88 bool UpgradeToWriterLock(DWORD dwTimeout = INFINITE) throw();
89 protected:
90 // A critical section to guard all the other members
91 mutable CRITICAL_SECTION m_cs;
92 // Auto-reset event, will be dynamically created/destroyed on demand
93 volatile HANDLE m_hSafeToWriteEvent;
94 // Manual-reset event, will be dynamically created/destroyed on demand
95 volatile HANDLE m_hSafeToReadEvent;
96 // Total number of writers on this object
97 volatile INT m_iNumOfWriter;
98 // Total number of readers have already owned this object
99 volatile INT m_iNumOfReaderEntered;
100 // Total number of readers are waiting to be owners of this object
101 volatile INT m_iNumOfReaderWaiting;
102 // Internal/Real implementation
103 void EnterCS() const throw();
104 void LeaveCS() const throw();
105 bool _ReaderWait(DWORD dwTimeout) throw();
106 bool _WriterWaitAndLeaveCSIfSuccess(DWORD dwTimeout) throw();
107 bool _UpgradeToWriterLockAndLeaveCS(DWORD dwTimeout) throw();
108 void _ReaderRelease() throw();
109 void _WriterRelease(bool blDowngrade) throw();
111 friend CReaderWriterLock;
114 //////////////////////////////////////////////////////////////////
115 // CReaderWriterLock class
116 // This class supports reentrance & lock escalation
117 class CReaderWriterLock
119 public:
120 CReaderWriterLock();
121 ~CReaderWriterLock();
123 bool AcquireReaderLock(DWORD dwTimeout = INFINITE) throw();
124 void ReleaseReaderLock() throw();
126 // If current thread was already a reader
127 // it will be upgraded to be writer automatically.
128 // BE CAREFUL! Other threads might write to the resource
129 // before current thread is successfully upgraded.
130 bool AcquireWriterLock(DWORD dwTimeout = INFINITE) throw();
131 void ReleaseWriterLock() throw();
133 // Regardless of how many times current thread acquired reader
134 // or writer locks, a call to this method will release all locks.
135 // After that, any call to ReleaseWriterLock or ReleaseReaderLock
136 // will raise exception in DEBUG mode.
137 void ReleaseAllLocks() throw();
139 // Query thread's status
140 DWORD GetCurrentThreadStatus() const throw();
141 void GetCurrentThreadStatus(DWORD* lpdwReaderLockCounter,
142 DWORD* lpdwWriterLockCounter) const throw();
143 protected:
144 CReaderWriterLockNonReentrance m_impl;
146 typedef std::map<DWORD,DWORD> CMapThreadToState;
147 CMapThreadToState m_map;
150 //////////////////////////////////////////////////////////////////
151 // CAutoReadLockT & CAutoWriteLockT classes
152 // Couple of template helper classes which would let one acquire a lock
153 // in a body of code and not have to worry about explicitly releasing
154 // that lock if an exception is encountered in that piece of code or
155 // if there are multiple return points out of that piece.
157 template<typename T>
158 class CAutoReadLockT
160 public:
161 CAutoReadLockT(T& objLock) throw() : m_lock(objLock)
163 m_lock.AcquireReaderLock();
165 ~CAutoReadLockT() throw()
167 m_lock.ReleaseReaderLock();
169 CAutoReadLockT(CAutoReadLockT&) = delete;
170 CAutoReadLockT(CAutoReadLockT&& that) = default;
171 CAutoReadLockT& operator=(const CAutoReadLockT&) = delete;
172 protected:
173 T& m_lock;
176 template<typename T>
177 class CAutoWriteLockT
179 public :
180 CAutoWriteLockT(T& objLock) throw() : m_lock(objLock)
182 m_lock.AcquireWriterLock();
184 ~CAutoWriteLockT() throw()
186 m_lock.ReleaseWriterLock();
188 CAutoWriteLockT(CAutoWriteLockT&) = delete;
189 CAutoWriteLockT(CAutoWriteLockT&&) = default;
190 CAutoWriteLockT& operator=(const CAutoWriteLockT&) = delete;
191 protected:
192 T& m_lock;
195 template<typename T>
196 class CAutoReadWeakLockT
198 public:
199 CAutoReadWeakLockT(T& objLock, DWORD timeout = 1) throw() : m_lock(objLock)
201 isAcquired = m_lock.AcquireReaderLock(timeout);
203 ~CAutoReadWeakLockT() throw()
205 if (isAcquired)
206 m_lock.ReleaseReaderLock();
208 bool IsAcquired() const
210 return isAcquired;
212 CAutoReadWeakLockT(CAutoReadWeakLockT&) = delete;
213 CAutoReadWeakLockT(CAutoReadWeakLockT&&) = default;
214 CAutoReadWeakLockT& operator=(const CAutoReadWeakLockT&) = delete;
215 protected:
216 T& m_lock;
217 bool isAcquired;
220 template<typename T>
221 class CAutoWriteWeakLockT
223 public :
224 CAutoWriteWeakLockT(T& objLock, DWORD timeout = 1) throw() : m_lock(objLock)
226 isAcquired = m_lock.AcquireWriterLock(timeout);
228 ~CAutoWriteWeakLockT() throw()
230 release();
232 void Release()
234 release();
236 bool IsAcquired() const
238 return isAcquired;
240 CAutoWriteWeakLockT(CAutoWriteWeakLockT&) = delete;
241 CAutoWriteWeakLockT(CAutoWriteWeakLockT&&) = default;
242 CAutoWriteWeakLockT& operator=(const CAutoWriteWeakLockT&) = delete;
243 protected:
244 T& m_lock;
245 bool isAcquired;
247 void release()
249 if (isAcquired)
251 m_lock.ReleaseWriterLock();
252 isAcquired = false;
257 //////////////////////////////////////////////////////////////////
258 // Instances of above template helper classes
260 typedef CAutoReadLockT<CReaderWriterLock> CAutoReadLock;
261 typedef CAutoWriteLockT<CReaderWriterLock> CAutoWriteLock;
262 typedef CAutoReadWeakLockT<CReaderWriterLock> CAutoReadWeakLock;
263 typedef CAutoWriteWeakLockT<CReaderWriterLock> CAutoWriteWeakLock;
265 //////////////////////////////////////////////////////////////////
266 // Inline methods
268 __forceinline
269 void CReaderWriterLockNonReentrance::EnterCS() const throw() {
270 ::EnterCriticalSection(&m_cs);
273 __forceinline
274 void CReaderWriterLockNonReentrance::LeaveCS() const throw(){
275 ::LeaveCriticalSection(&m_cs);