Fix typos
[TortoiseGit.git] / src / Utils / ReaderWriterLock.cpp
blob2d964f68841c3f83e7b13b690e465c533c15ba22
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() noexcept
44 SecureZeroMemory(this, sizeof(*this));
45 #if (_WIN32_WINNT >= 0x0403)
46 InitializeCriticalSectionAndSpinCount(&m_cs, READER_WRITER_SPIN_COUNT);
47 #else
48 InitializeCriticalSection(&m_cs);
49 #endif
52 CReaderWriterLockNonReentrance::~CReaderWriterLockNonReentrance()
54 _ASSERT(!m_hSafeToReadEvent && !m_hSafeToWriteEvent);
55 DeleteCriticalSection(&m_cs);
58 bool CReaderWriterLockNonReentrance::_ReaderWait(ULONGLONG dwTimeout) noexcept
60 bool blCanRead;
62 ++m_iNumOfReaderWaiting;
63 if (!m_hSafeToReadEvent)
64 m_hSafeToReadEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
66 if(INFINITE == dwTimeout) // INFINITE is a special value
68 while (0 != m_iNumOfWriter)
70 LeaveCS();
71 WaitForSingleObject(m_hSafeToReadEvent, INFINITE);
72 // There might be one or more Writers entered, that's
73 // why we need loop here
74 EnterCS();
77 ++m_iNumOfReaderEntered;
78 blCanRead = TRUE;
80 else
82 LeaveCS();
84 ULONGLONG const dwBeginTime = GetTickCount64();
85 ULONGLONG dwConsumedTime = 0;
87 while(TRUE)
89 blCanRead = (WAIT_OBJECT_0 == WaitForSingleObject(m_hSafeToReadEvent, static_cast<DWORD>(dwTimeout - dwConsumedTime)));
91 EnterCS();
93 if(0 == m_iNumOfWriter)
95 // Regardless timeout or not, there is no Writer
96 // So it's safe to be Reader right now
97 ++m_iNumOfReaderEntered;
98 blCanRead = TRUE;
99 break;
102 if (!blCanRead)
104 // Timeout after waiting
105 break;
108 // There are some Writers have just entered
109 // So leave CS and prepare to try again
110 LeaveCS();
112 dwConsumedTime = GetTickCount64() - dwBeginTime;
113 if(dwConsumedTime > dwTimeout)
115 // Don't worry why the code here looks stupid
116 // Because this case rarely happens, it's better
117 // to optimize code for the usual case
118 blCanRead = FALSE;
119 EnterCS();
120 break;
125 if(0==--m_iNumOfReaderWaiting)
127 CloseHandle(m_hSafeToReadEvent);
128 m_hSafeToReadEvent = nullptr;
131 return blCanRead;
134 void CReaderWriterLockNonReentrance::_ReaderRelease() noexcept
136 INT _iNumOfReaderEntered = --m_iNumOfReaderEntered;
137 _ASSERT(0 <= _iNumOfReaderEntered);
139 if (0 == _iNumOfReaderEntered && m_hSafeToWriteEvent)
141 SetEvent(m_hSafeToWriteEvent);
145 bool CReaderWriterLockNonReentrance::_WriterWaitAndLeaveCSIfSuccess(ULONGLONG dwTimeout) noexcept
147 //EnterCS();
148 _ASSERT(0 != dwTimeout);
150 // Increase Writer-counter & reset Reader-event if necessary
151 INT _iNumOfWriter = ++m_iNumOfWriter;
152 if (_iNumOfWriter == 1 && m_hSafeToReadEvent)
154 ResetEvent(m_hSafeToReadEvent);
157 if (!m_hSafeToWriteEvent)
159 m_hSafeToWriteEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
161 LeaveCS();
163 bool blCanWrite = (WAIT_OBJECT_0 == WaitForSingleObject(m_hSafeToWriteEvent, static_cast<DWORD>(dwTimeout)));
164 if (!blCanWrite)
166 // Undo what we changed after timeout
167 EnterCS();
168 if(0 == --m_iNumOfWriter)
170 CloseHandle(m_hSafeToWriteEvent);
171 m_hSafeToWriteEvent = nullptr;
173 if(0 == m_iNumOfReaderEntered)
175 // Although it was timeout, it's still safe to be writer now
176 ++m_iNumOfWriter;
177 LeaveCS();
178 blCanWrite = TRUE;
180 else if(m_hSafeToReadEvent)
182 SetEvent(m_hSafeToReadEvent);
186 return blCanWrite;
189 bool CReaderWriterLockNonReentrance::_UpgradeToWriterLockAndLeaveCS(ULONGLONG dwTimeout) noexcept
191 _ASSERT(m_iNumOfReaderEntered > 0);
193 if(0 == dwTimeout)
195 LeaveCS();
196 return FALSE;
199 --m_iNumOfReaderEntered;
200 bool blCanWrite = _WriterWaitAndLeaveCSIfSuccess(dwTimeout);
201 if (!blCanWrite)
203 // Now analyze why it was failed to have suitable action
204 if(0 == m_iNumOfWriter)
206 _ASSERT(0 < m_iNumOfReaderEntered);
207 // There are some readers still owning the lock
208 // It's safe to be a reader again after failure
209 ++m_iNumOfReaderEntered;
211 else
213 // Reach to here, it's NOT safe to be a reader immediately
214 _ReaderWait(INFINITE);
215 if(1 == m_iNumOfReaderEntered)
217 // After wait, now it's safe to be writer
218 _ASSERT(0 == m_iNumOfWriter);
219 m_iNumOfReaderEntered = 0;
220 m_iNumOfWriter = 1;
221 blCanWrite = TRUE;
224 LeaveCS();
227 return blCanWrite;
230 void CReaderWriterLockNonReentrance::_WriterRelease(bool blDowngrade) noexcept
232 _ASSERT(0 == m_iNumOfReaderEntered);
234 if(blDowngrade)
236 ++m_iNumOfReaderEntered;
239 if(0 == --m_iNumOfWriter)
241 if (m_hSafeToWriteEvent)
243 CloseHandle(m_hSafeToWriteEvent);
244 m_hSafeToWriteEvent = nullptr;
247 if(m_hSafeToReadEvent)
249 SetEvent(m_hSafeToReadEvent);
252 else
254 //////////////////////////////////////////////////////////////////////////
255 // Some WRITERs are queued
256 _ASSERT(m_iNumOfWriter > 0 && m_hSafeToWriteEvent);
258 if (!blDowngrade)
260 SetEvent(m_hSafeToWriteEvent);
265 bool CReaderWriterLockNonReentrance::AcquireReaderLock(DWORD dwTimeout) noexcept
267 bool blCanRead;
269 EnterCS();
270 if(0 == m_iNumOfWriter)
272 // Enter successful without wait
273 ++m_iNumOfReaderEntered;
274 blCanRead = TRUE;
276 else
278 blCanRead = (dwTimeout)? _ReaderWait(dwTimeout) : FALSE;
280 LeaveCS();
282 return blCanRead;
285 void CReaderWriterLockNonReentrance::ReleaseReaderLock() noexcept
287 EnterCS();
288 _ReaderRelease();
289 LeaveCS();
292 bool CReaderWriterLockNonReentrance::AcquireWriterLock(DWORD dwTimeout) noexcept
294 bool blCanWrite ;
296 EnterCS();
297 if(0 == (m_iNumOfWriter | m_iNumOfReaderEntered))
299 ++m_iNumOfWriter;
300 blCanWrite = TRUE;
302 else if(0 == dwTimeout)
304 blCanWrite = FALSE;
306 else
308 blCanWrite = _WriterWaitAndLeaveCSIfSuccess(dwTimeout);
309 if(blCanWrite)
311 return TRUE;
315 LeaveCS();
316 return blCanWrite;
319 void CReaderWriterLockNonReentrance::ReleaseWriterLock() noexcept
321 EnterCS();
322 _WriterRelease(FALSE);
323 LeaveCS();
326 void CReaderWriterLockNonReentrance::DowngradeFromWriterLock() noexcept
328 EnterCS();
329 _WriterRelease(TRUE);
330 LeaveCS();
333 bool CReaderWriterLockNonReentrance::UpgradeToWriterLock(DWORD dwTimeout) noexcept
335 EnterCS();
336 return _UpgradeToWriterLockAndLeaveCS(dwTimeout);
339 // END CReaderWriterLockNonReentrance implementation
340 ///////////////////////////////////////////////////////
342 ///////////////////////////////////////////////////////
343 // CReaderWriterLock implementation
345 #define READER_RECURRENCE_UNIT 0x00000001
346 #define READER_RECURRENCE_MASK 0x0000FFFF
347 #define WRITER_RECURRENCE_UNIT 0x00010000
349 CReaderWriterLock::CReaderWriterLock()
353 CReaderWriterLock::~CReaderWriterLock()
357 bool CReaderWriterLock::AcquireReaderLock(DWORD dwTimeout) noexcept
359 const DWORD dwCurrentThreadId = GetCurrentThreadId();
361 m_impl.EnterCS();
362 CMapThreadToState::iterator ite = m_map.find(dwCurrentThreadId);
364 if(ite != m_map.end())
366 //////////////////////////////////////////////////////////////////////////
367 // Current thread was already a WRITER or READER
368 _ASSERT(0 < ite->second);
369 ite->second += READER_RECURRENCE_UNIT;
370 m_impl.LeaveCS();
372 return TRUE;
375 if(0 == m_impl.m_iNumOfWriter)
377 // There is NO WRITER on this RW object
378 // Current thread is going to be a READER
379 ++m_impl.m_iNumOfReaderEntered;
380 m_map.emplace(dwCurrentThreadId, READER_RECURRENCE_UNIT);
382 m_impl.LeaveCS();
383 return TRUE;
386 if(0 == dwTimeout)
388 m_impl.LeaveCS();
389 return FALSE;
392 bool blCanRead = m_impl._ReaderWait(dwTimeout);
393 if(blCanRead)
395 m_map.emplace(dwCurrentThreadId, READER_RECURRENCE_UNIT);
397 m_impl.LeaveCS();
399 return blCanRead;
402 void CReaderWriterLock::ReleaseReaderLock() noexcept
404 const DWORD dwCurrentThreadId = GetCurrentThreadId();
405 m_impl.EnterCS();
407 CMapThreadToState::iterator ite = m_map.find(dwCurrentThreadId);
408 _ASSERT( (ite != m_map.end()) && (READER_RECURRENCE_MASK & ite->second));
410 const DWORD dwThreadState = (ite->second -= READER_RECURRENCE_UNIT);
411 if(0 == dwThreadState)
413 m_map.erase(ite);
414 m_impl._ReaderRelease();
416 m_impl.LeaveCS();
419 bool CReaderWriterLock::AcquireWriterLock(DWORD dwTimeout) noexcept
421 const DWORD dwCurrentThreadId = GetCurrentThreadId();
422 bool blCanWrite;
424 m_impl.EnterCS();
425 CMapThreadToState::iterator ite = m_map.find(dwCurrentThreadId);
427 if(ite != m_map.end())
429 _ASSERT(0 < ite->second);
431 if(ite->second >= WRITER_RECURRENCE_UNIT)
433 // Current thread was already a WRITER
434 ite->second += WRITER_RECURRENCE_UNIT;
435 m_impl.LeaveCS();
436 return TRUE;
439 // Current thread was already a READER
440 _ASSERT(1 <= m_impl.m_iNumOfReaderEntered);
441 if(1 == m_impl.m_iNumOfReaderEntered)
443 // This object is owned by ONLY current thread for READ
444 // There might be some threads queued to be WRITERs
445 // but for effectiveness (higher throughput), we allow current
446 // thread upgrading to be WRITER right now
447 m_impl.m_iNumOfReaderEntered = 0;
448 ++m_impl.m_iNumOfWriter;
449 ite->second += WRITER_RECURRENCE_UNIT;
450 m_impl.LeaveCS();
451 return TRUE;
454 // Try upgrading from reader to writer
455 blCanWrite = m_impl._UpgradeToWriterLockAndLeaveCS(dwTimeout);
456 if(blCanWrite)
458 m_impl.EnterCS();
459 ite = m_map.find(dwCurrentThreadId);
460 ite->second += WRITER_RECURRENCE_UNIT;
461 m_impl.LeaveCS();
464 else
466 if(0 == (m_impl.m_iNumOfWriter | m_impl.m_iNumOfReaderEntered))
468 // This RW object is not owned by any thread
469 // --> it's safe to make this thread to be WRITER
470 ++m_impl.m_iNumOfWriter;
471 m_map.emplace(dwCurrentThreadId, WRITER_RECURRENCE_UNIT);
472 m_impl.LeaveCS();
473 return TRUE;
476 if(0 == dwTimeout)
478 m_impl.LeaveCS();
479 return FALSE;
482 blCanWrite = m_impl._WriterWaitAndLeaveCSIfSuccess(dwTimeout);
483 if(blCanWrite)
485 m_impl.EnterCS();
486 m_map.emplace(dwCurrentThreadId, WRITER_RECURRENCE_UNIT);
488 m_impl.LeaveCS();
491 return blCanWrite;
494 void CReaderWriterLock::ReleaseWriterLock() noexcept
496 const DWORD dwCurrentThreadId = GetCurrentThreadId();
497 m_impl.EnterCS();
499 CMapThreadToState::iterator ite = m_map.find(dwCurrentThreadId);
500 _ASSERT( (ite != m_map.end()) && (WRITER_RECURRENCE_UNIT <= ite->second));
502 const DWORD dwThreadState = (ite->second -= WRITER_RECURRENCE_UNIT);
503 if(0 == dwThreadState)
505 m_map.erase(ite);
506 m_impl._WriterRelease(FALSE);
508 else if (WRITER_RECURRENCE_UNIT > dwThreadState)
510 // Down-grading from writer to reader
511 m_impl._WriterRelease(TRUE);
513 m_impl.LeaveCS();
516 void CReaderWriterLock::ReleaseAllLocks() noexcept
518 const DWORD dwCurrentThreadId = GetCurrentThreadId();
520 m_impl.EnterCS();
521 CMapThreadToState::iterator ite = m_map.find(dwCurrentThreadId);
522 if(ite != m_map.end())
524 const DWORD dwThreadState = ite->second;
525 m_map.erase(ite);
526 if(WRITER_RECURRENCE_UNIT <= dwThreadState)
528 m_impl._WriterRelease(FALSE);
530 else
532 _ASSERT(0 < dwThreadState);
533 m_impl._ReaderRelease();
536 m_impl.LeaveCS();
539 DWORD CReaderWriterLock::GetCurrentThreadStatus() const noexcept
541 DWORD dwThreadState;
542 const DWORD dwCurrentThreadId = GetCurrentThreadId();
544 m_impl.EnterCS();
545 CMapThreadToState::const_iterator ite = m_map.find(dwCurrentThreadId);
546 if(ite != m_map.end())
548 dwThreadState = ite->second;
549 m_impl.LeaveCS();
550 _ASSERT(dwThreadState > 0);
552 else
554 dwThreadState = 0;
555 m_impl.LeaveCS();
558 return dwThreadState;
561 void CReaderWriterLock::GetCurrentThreadStatus(DWORD* lpdwReaderLockCounter,
562 DWORD* lpdwWriterLockCounter) const noexcept
564 const DWORD dwThreadState = GetCurrentThreadStatus();
566 if (lpdwReaderLockCounter)
567 *lpdwReaderLockCounter = (dwThreadState & READER_RECURRENCE_MASK);
569 if (lpdwWriterLockCounter)
570 *lpdwWriterLockCounter = (dwThreadState / WRITER_RECURRENCE_UNIT);