Navigate selection history via log jump up/down button
[TortoiseGit.git] / src / Utils / ReaderWriterLock.cpp
blob140a4287d8439fef075286a8f8ff1800a7a87833
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 #include "stdafx.h"
20 #include <crtdbg.h>
21 #include "ReaderWriterLock.h"
23 /////////////////////////////////////////////////////////////////
24 // Following macros make this file can be used in non-MFC project
25 #ifndef ASSERT
26 // Define ASSERT macro
27 # define ASSERT _ASSERT
29 // Define VERIFY macro
30 # ifdef _DEBUG
31 # define VERIFY ASSERT
32 # define DEBUG_ONLY(f) ((void)(f))
33 # else
34 # define VERIFY(f) ((void)(f))
35 # define DEBUG_ONLY(f)
36 # endif
37 #endif
39 ///////////////////////////////////////////////////////
40 // CReaderWriterLockNonReentrance implementation
42 CReaderWriterLockNonReentrance::CReaderWriterLockNonReentrance()
43 : m_hSafeToReadEvent(NULL)
44 , m_hSafeToWriteEvent(NULL)
45 , m_iNumOfWriter(0)
46 , m_iNumOfReaderWaiting(0)
47 , m_iNumOfReaderEntered(0)
49 SecureZeroMemory(this, sizeof(*this));
50 #if (_WIN32_WINNT >= 0x0403)
51 InitializeCriticalSectionAndSpinCount(&m_cs, READER_WRITER_SPIN_COUNT);
52 #else
53 InitializeCriticalSection(&m_cs);
54 #endif
57 CReaderWriterLockNonReentrance::~CReaderWriterLockNonReentrance()
59 _ASSERT( (NULL == m_hSafeToReadEvent) &&
60 (NULL == m_hSafeToWriteEvent) );
61 DeleteCriticalSection(&m_cs);
64 bool CReaderWriterLockNonReentrance::_ReaderWait(DWORD dwTimeout) throw()
66 bool blCanRead;
68 ++m_iNumOfReaderWaiting;
69 if(NULL == m_hSafeToReadEvent)
71 m_hSafeToReadEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
74 if(INFINITE == dwTimeout) // INFINITE is a special value
78 LeaveCS();
79 WaitForSingleObject(m_hSafeToReadEvent, INFINITE);
80 // There might be one or more Writers entered, that's
81 // why we need DO-WHILE loop here
82 EnterCS();
84 while(0 != m_iNumOfWriter);
86 ++m_iNumOfReaderEntered;
87 blCanRead = TRUE;
89 else
91 LeaveCS();
93 DWORD const dwBeginTime = GetTickCount();
94 DWORD dwConsumedTime = 0;
96 while(TRUE)
98 blCanRead = (WAIT_OBJECT_0 == WaitForSingleObject(m_hSafeToReadEvent,
99 dwTimeout - dwConsumedTime));
101 EnterCS();
103 if(0 == m_iNumOfWriter)
105 // Regardless timeout or not, there is no Writer
106 // So it's safe to be Reader right now
107 ++m_iNumOfReaderEntered;
108 blCanRead = TRUE;
109 break;
112 if(FALSE == blCanRead)
114 // Timeout after waiting
115 break;
118 // There are some Writers have just entered
119 // So leave CS and prepare to try again
120 LeaveCS();
122 dwConsumedTime = GetTickCount() - dwBeginTime;
123 if(dwConsumedTime > dwTimeout)
125 // Don't worry why the code here looks stupid
126 // Because this case rarely happens, it's better
127 // to optimize code for the usual case
128 blCanRead = FALSE;
129 EnterCS();
130 break;
135 if(0==--m_iNumOfReaderWaiting)
137 CloseHandle(m_hSafeToReadEvent);
138 m_hSafeToReadEvent = NULL;
141 return blCanRead;
144 void CReaderWriterLockNonReentrance::_ReaderRelease()
146 INT _iNumOfReaderEntered = --m_iNumOfReaderEntered;
147 _ASSERT(0 <= _iNumOfReaderEntered);
149 if( (0 == _iNumOfReaderEntered) &&
150 (NULL != m_hSafeToWriteEvent) )
152 SetEvent(m_hSafeToWriteEvent);
156 bool CReaderWriterLockNonReentrance::_WriterWaitAndLeaveCSIfSuccess(DWORD dwTimeout)
158 //EnterCS();
159 _ASSERT(0 != dwTimeout);
161 // Increase Writer-counter & reset Reader-event if necessary
162 INT _iNumOfWriter = ++m_iNumOfWriter;
163 if( (1 == _iNumOfWriter) && (NULL != m_hSafeToReadEvent) )
165 ResetEvent(m_hSafeToReadEvent);
168 if(NULL == m_hSafeToWriteEvent)
170 m_hSafeToWriteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
172 LeaveCS();
174 bool blCanWrite = (WAIT_OBJECT_0 == WaitForSingleObject(m_hSafeToWriteEvent, dwTimeout));
175 if(FALSE == blCanWrite)
177 // Undo what we changed after timeout
178 EnterCS();
179 if(0 == --m_iNumOfWriter)
181 CloseHandle(m_hSafeToWriteEvent);
182 m_hSafeToWriteEvent = NULL;
184 if(0 == m_iNumOfReaderEntered)
186 // Although it was timeout, it's still safe to be writer now
187 ++m_iNumOfWriter;
188 LeaveCS();
189 blCanWrite = TRUE;
191 else if(m_hSafeToReadEvent)
193 SetEvent(m_hSafeToReadEvent);
197 return blCanWrite;
200 bool CReaderWriterLockNonReentrance::_UpgradeToWriterLockAndLeaveCS(DWORD dwTimeout) throw()
202 _ASSERT(m_iNumOfReaderEntered > 0);
204 if(0 == dwTimeout)
206 LeaveCS();
207 return FALSE;
210 --m_iNumOfReaderEntered;
211 bool blCanWrite = _WriterWaitAndLeaveCSIfSuccess(dwTimeout);
212 if(FALSE == blCanWrite)
214 // Now analyze why it was failed to have suitable action
215 if(0 == m_iNumOfWriter)
217 _ASSERT(0 < m_iNumOfReaderEntered);
218 // There are some readers still owning the lock
219 // It's safe to be a reader again after failure
220 ++m_iNumOfReaderEntered;
222 else
224 // Reach to here, it's NOT safe to be a reader immediately
225 _ReaderWait(INFINITE);
226 if(1 == m_iNumOfReaderEntered)
228 // After wait, now it's safe to be writer
229 _ASSERT(0 == m_iNumOfWriter);
230 m_iNumOfReaderEntered = 0;
231 m_iNumOfWriter = 1;
232 blCanWrite = TRUE;
235 LeaveCS();
238 return blCanWrite;
241 void CReaderWriterLockNonReentrance::_WriterRelease(bool blDowngrade)
243 _ASSERT(0 == m_iNumOfReaderEntered);
245 if(blDowngrade)
247 ++m_iNumOfReaderEntered;
250 if(0 == --m_iNumOfWriter)
252 if(NULL != m_hSafeToWriteEvent)
254 CloseHandle(m_hSafeToWriteEvent);
255 m_hSafeToWriteEvent = NULL;
258 if(m_hSafeToReadEvent)
260 SetEvent(m_hSafeToReadEvent);
263 else
265 //////////////////////////////////////////////////////////////////////////
266 // Some WRITERs are queued
267 _ASSERT( (0 < m_iNumOfWriter) && (NULL != m_hSafeToWriteEvent));
269 if(FALSE == blDowngrade)
271 SetEvent(m_hSafeToWriteEvent);
276 bool CReaderWriterLockNonReentrance::AcquireReaderLock(DWORD dwTimeout)
278 bool blCanRead;
280 EnterCS();
281 if(0 == m_iNumOfWriter)
283 // Enter successful without wait
284 ++m_iNumOfReaderEntered;
285 blCanRead = TRUE;
287 else
289 blCanRead = (dwTimeout)? _ReaderWait(dwTimeout) : FALSE;
291 LeaveCS();
293 return blCanRead;
296 void CReaderWriterLockNonReentrance::ReleaseReaderLock()
298 EnterCS();
299 _ReaderRelease();
300 LeaveCS();
303 bool CReaderWriterLockNonReentrance::AcquireWriterLock(DWORD dwTimeout)
305 bool blCanWrite ;
307 EnterCS();
308 if(0 == (m_iNumOfWriter | m_iNumOfReaderEntered))
310 ++m_iNumOfWriter;
311 blCanWrite = TRUE;
313 else if(0 == dwTimeout)
315 blCanWrite = FALSE;
317 else
319 blCanWrite = _WriterWaitAndLeaveCSIfSuccess(dwTimeout);
320 if(blCanWrite)
322 return TRUE;
326 LeaveCS();
327 return blCanWrite;
330 void CReaderWriterLockNonReentrance::ReleaseWriterLock()
332 EnterCS();
333 _WriterRelease(FALSE);
334 LeaveCS();
337 void CReaderWriterLockNonReentrance::DowngradeFromWriterLock()
339 EnterCS();
340 _WriterRelease(TRUE);
341 LeaveCS();
344 bool CReaderWriterLockNonReentrance::UpgradeToWriterLock(DWORD dwTimeout) throw()
346 EnterCS();
347 return _UpgradeToWriterLockAndLeaveCS(dwTimeout);
350 // END CReaderWriterLockNonReentrance implementation
351 ///////////////////////////////////////////////////////
353 ///////////////////////////////////////////////////////
354 // CReaderWriterLock implementation
356 #define READER_RECURRENCE_UNIT 0x00000001
357 #define READER_RECURRENCE_MASK 0x0000FFFF
358 #define WRITER_RECURRENCE_UNIT 0x00010000
360 CReaderWriterLock::CReaderWriterLock()
364 CReaderWriterLock::~CReaderWriterLock()
368 bool CReaderWriterLock::AcquireReaderLock(DWORD dwTimeout)
370 const DWORD dwCurrentThreadId = GetCurrentThreadId();
372 m_impl.EnterCS();
373 CMapThreadToState::iterator ite = m_map.find(dwCurrentThreadId);
375 if(ite != m_map.end())
377 //////////////////////////////////////////////////////////////////////////
378 // Current thread was already a WRITER or READER
379 _ASSERT(0 < ite->second);
380 ite->second += READER_RECURRENCE_UNIT;
381 m_impl.LeaveCS();
383 return TRUE;
386 if(0 == m_impl.m_iNumOfWriter)
388 // There is NO WRITER on this RW object
389 // Current thread is going to be a READER
390 ++m_impl.m_iNumOfReaderEntered;
391 m_map.insert(std::make_pair(dwCurrentThreadId, READER_RECURRENCE_UNIT));
393 m_impl.LeaveCS();
394 return TRUE;
397 if(0 == dwTimeout)
399 m_impl.LeaveCS();
400 return FALSE;
403 bool blCanRead = m_impl._ReaderWait(dwTimeout);
404 if(blCanRead)
406 m_map.insert(std::make_pair(dwCurrentThreadId, READER_RECURRENCE_UNIT));
408 m_impl.LeaveCS();
410 return blCanRead;
413 void CReaderWriterLock::ReleaseReaderLock()
415 const DWORD dwCurrentThreadId = GetCurrentThreadId();
416 m_impl.EnterCS();
418 CMapThreadToState::iterator ite = m_map.find(dwCurrentThreadId);
419 _ASSERT( (ite != m_map.end()) && (READER_RECURRENCE_MASK & ite->second));
421 const DWORD dwThreadState = (ite->second -= READER_RECURRENCE_UNIT);
422 if(0 == dwThreadState)
424 m_map.erase(ite);
425 m_impl._ReaderRelease();
427 m_impl.LeaveCS();
430 bool CReaderWriterLock::AcquireWriterLock(DWORD dwTimeout)
432 const DWORD dwCurrentThreadId = GetCurrentThreadId();
433 bool blCanWrite;
435 m_impl.EnterCS();
436 CMapThreadToState::iterator ite = m_map.find(dwCurrentThreadId);
438 if(ite != m_map.end())
440 _ASSERT(0 < ite->second);
442 if(ite->second >= WRITER_RECURRENCE_UNIT)
444 // Current thread was already a WRITER
445 ite->second += WRITER_RECURRENCE_UNIT;
446 m_impl.LeaveCS();
447 return TRUE;
450 // Current thread was already a READER
451 _ASSERT(1 <= m_impl.m_iNumOfReaderEntered);
452 if(1 == m_impl.m_iNumOfReaderEntered)
454 // This object is owned by ONLY current thread for READ
455 // There might be some threads queued to be WRITERs
456 // but for effectiveness (higher throughput), we allow current
457 // thread upgrading to be WRITER right now
458 m_impl.m_iNumOfReaderEntered = 0;
459 ++m_impl.m_iNumOfWriter;
460 ite->second += WRITER_RECURRENCE_UNIT;
461 m_impl.LeaveCS();
462 return TRUE;
465 // Try upgrading from reader to writer
466 blCanWrite = m_impl._UpgradeToWriterLockAndLeaveCS(dwTimeout);
467 if(blCanWrite)
469 m_impl.EnterCS();
470 ite = m_map.find(dwCurrentThreadId);
471 ite->second += WRITER_RECURRENCE_UNIT;
472 m_impl.LeaveCS();
475 else
477 if(0 == (m_impl.m_iNumOfWriter | m_impl.m_iNumOfReaderEntered))
479 // This RW object is not owned by any thread
480 // --> it's safe to make this thread to be WRITER
481 ++m_impl.m_iNumOfWriter;
482 m_map.insert(std::make_pair(dwCurrentThreadId, WRITER_RECURRENCE_UNIT));
483 m_impl.LeaveCS();
484 return TRUE;
487 if(0 == dwTimeout)
489 m_impl.LeaveCS();
490 return FALSE;
493 blCanWrite = m_impl._WriterWaitAndLeaveCSIfSuccess(dwTimeout);
494 if(blCanWrite)
496 m_impl.EnterCS();
497 m_map.insert(std::make_pair(dwCurrentThreadId, WRITER_RECURRENCE_UNIT));
499 m_impl.LeaveCS();
502 return blCanWrite;
505 void CReaderWriterLock::ReleaseWriterLock()
507 const DWORD dwCurrentThreadId = GetCurrentThreadId();
508 m_impl.EnterCS();
510 CMapThreadToState::iterator ite = m_map.find(dwCurrentThreadId);
511 _ASSERT( (ite != m_map.end()) && (WRITER_RECURRENCE_UNIT <= ite->second));
513 const DWORD dwThreadState = (ite->second -= WRITER_RECURRENCE_UNIT);
514 if(0 == dwThreadState)
516 m_map.erase(ite);
517 m_impl._WriterRelease(FALSE);
519 else if (WRITER_RECURRENCE_UNIT > dwThreadState)
521 // Down-grading from writer to reader
522 m_impl._WriterRelease(TRUE);
524 m_impl.LeaveCS();
527 void CReaderWriterLock::ReleaseAllLocks()
529 const DWORD dwCurrentThreadId = GetCurrentThreadId();
531 m_impl.EnterCS();
532 CMapThreadToState::iterator ite = m_map.find(dwCurrentThreadId);
533 if(ite != m_map.end())
535 const DWORD dwThreadState = ite->second;
536 m_map.erase(ite);
537 if(WRITER_RECURRENCE_UNIT <= dwThreadState)
539 m_impl._WriterRelease(FALSE);
541 else
543 _ASSERT(0 < dwThreadState);
544 m_impl._ReaderRelease();
547 m_impl.LeaveCS();
550 DWORD CReaderWriterLock::GetCurrentThreadStatus() const throw()
552 DWORD dwThreadState;
553 const DWORD dwCurrentThreadId = GetCurrentThreadId();
555 m_impl.EnterCS();
556 CMapThreadToState::const_iterator ite = m_map.find(dwCurrentThreadId);
557 if(ite != m_map.end())
559 dwThreadState = ite->second;
560 m_impl.LeaveCS();
561 _ASSERT(dwThreadState > 0);
563 else
565 dwThreadState = 0;
566 m_impl.LeaveCS();
569 return dwThreadState;
572 void CReaderWriterLock::GetCurrentThreadStatus(DWORD* lpdwReaderLockCounter,
573 DWORD* lpdwWriterLockCounter) const
575 const DWORD dwThreadState = GetCurrentThreadStatus();
577 if(NULL != lpdwReaderLockCounter)
579 *lpdwReaderLockCounter = (dwThreadState & READER_RECURRENCE_MASK);
582 if(NULL != lpdwWriterLockCounter)
584 *lpdwWriterLockCounter = (dwThreadState / WRITER_RECURRENCE_UNIT);