Read/write locks on Windows

Source: Internet
Author: User

Read/write locks on Windows
This section briefly introduces the read/write locks and their implementations on Windows.

Background
Windows does not provide the read/write lock API after Vista and Server2008, that is, the SRW series functions (InitializeSRWLock, AcquireSRWLockShared, AcquireSRWLockExclusive, etc ). considering the current installation volume of Windows XP, only one read/write lock can be implemented.

Objective and requirements of read/write locks
The most basic purpose of a read/write lock is that the read lock can be shared and the write lock must be exclusive. In addition, I think there are two additional considerations:
1. If a write lock request is waiting, the new read lock request should also wait. Otherwise, the program may never get the write lock.
2. Once the write lock is released, all requests for reading and writing locks in waiting should compete fairly. Regardless of the order of requests, as long as they have been waiting, there should be a chance to get the lock.
If the first point can be further considered, the second point should be guaranteed.

Implementation of read/write locks
The general idea is relatively simple: The read/write lock maintains some status values, and uses the critical CRITICAL_SECTION to protect the atomicity of access to these status values and event objects in combination for waiting.

Lock entry:
1. If the current lock status is idle, no matter whether the write lock or read lock request is allowed to enter, and set the corresponding status value.

2. If the current lock status is read, the new write lock request needs to wait for event notification and add one to the write lock wait count.
3. If the current lock status is read, the new read lock request:
3.1 If no write lock request is waiting, allow the read lock to enter and add one to the read lock count.
3.2 If a write lock request is waiting, wait for the event notification, and read the lock wait count plus one (the purpose of doing so is to give the write lock a chance to enter as described above ).

4. If the current lock status is write, no matter whether the read lock request or write lock request, wait for the event notification and add one to the read lock wait count or write lock wait count respectively.

Unlock:
If the read lock wait count or write lock wait count is not 0 when the lock is released, the event object is triggered.

I use a manual event object, so that once the lock is released, all waiting lock requests will be activated, and then compete for the lock to enter the right in the form of a competitive critical segment to ensure fairness. regardless of the order of waiting for a request, as long as the lock is released and enters the waiting state, the opportunity to access the lock once it is released is equal.

Postscript
Before implementing the read/write lock, I searched by Google and found that the implementation of several Windows read/write locks is not ideal. The main problems are as follows:
1. wait for the event object to occupy the mutex. This is a very bad design. In this way, other lock requests will be blocked outside the critical segment, and only one lock request will be blocked in the Wait function, it is unfair to other lock requests.
2. I did not consider the purpose 2 I started to raise, that is, the write lock may never have a chance to enter. I don't know if this is a disadvantage, but this is not allowed in my applications, so I re-designed a read/write lock.

One of the most difficult issues during implementation is whether to use automatic events or manual events. the advantage of using manual events is that once triggered, all requests in the waiting state are activated and then re-compete. The logic is simple and clear. the disadvantage is that there may be many redundant operations. For example, some write locks and several read lock requests are waiting for entry. Once the lock is released, all requests (threads) are activated, however, there must be only one request that can enter. A request that fails to compete tests the conditions and then suspends. if Automatic events are used, only one lock request thread will be awakened (the Wait function features that the Awakened thread is equivalent to a thread that has been successfully competed), which seems to be more efficient. the lock request that obtains the access permission then determines whether to trigger the event object based on the waiting conditions: if there are still read and write requests waiting, it is not triggered; if only the Read Request is waiting, then, it is triggered again so that other read requests can enter. over and over again, I decided to adopt manual events. After all, the number of read locks is much larger than the number of write locks (this is also a common scenario for read/write locks) faster (no need to wait for multiple events ).

Appendix 1-C ++ code

# Define RWLOCK_IDLE 0/* idle */# define RWLOCK_R 0x01/* read lock */# define RWLOCK_W 0x02/* write lock */class RWLock {private: int _ st; /* Lock status value */int _ rlockCount;/* read lock count */int _ rwaitingCount;/* read wait count */int _ wwaitingCount; /* write wait count */HANDLE _ ev;/* notification Event * // HANDLE _ stLock;/* mutually exclusive access status value * // * if you need to wait for timeout, use Mutex */CRITICAL_SECTION _ stLock; public: RWLock (void );~ RWLock (void); void rlock (); void wlock (); void unlock () ;}; RWLock: RWLock (void): _ rlockCount (0 ), _ st (RWLOCK_IDLE), _ rwaitingCount (0), _ wwaitingCount (0) {// _ stLock = CreateMutex (NULL, FALSE, NULL); // assert (_ stLock! = INVALID_HANDLE_VALUE); InitializeCriticalSection (& _ stLock);/** assume that multiple read lock requests are waiting for the write lock to be released. When the write lock is released, all these locks should have the opportunity to be executed. */_ ev = CreateEvent (NULL, TRUE, FALSE, NULL); assert (_ ev! = INVALID_HANDLE_VALUE);} RWLock ::~ RWLock (void) {// CloseHandle (_ stLock); DeleteCriticalSection (& _ stLock); CloseHandle (_ ev);} void RWLock: rlock () {bool isWaitReturn = false; while (1) {// WaitForSingleObject (_ stLock, INFINITE); EnterCriticalSection (& _ stLock); if (isWaitReturn) {/** wait for the event to return and compete for the lock again. */-- _ rwaitingCount;} if (_ st = RWLOCK_IDLE) {/** Idle State, directly obtain control */_ st = RWLOCK_R; _ rlockCount ++; // ReleaseMutex (_ stLock); LeaveCriticalSection (& _ stLock); break;} else if (_ st = RWLOCK_R) {if (_ wwaitingCount> 0) {/** if a write lock is waiting, wait together so that the write lock can gain a competitive opportunity. */++ _ rwaitingCount; ResetEvent (_ ev); // SignalObjectAndWait (_ stLock, _ ev, INFINITE, FALSE); LeaveCriticalSection (& _ stLock ); /** although there is a time window between LeaveCriticalSection () and WaitForSingleObject (), * the event signal on the windows platform will not be lost, so there is no problem. */WaitForSingleObject (_ ev, INFINITE);/** wait for the returned result and continue to apply the lock. */isWaitReturn = true;} else {/** get the read lock, Count + 1 */+ _ rlockCount; // ReleaseMutex (_ stLock); LeaveCriticalSection (& _ stLock ); break ;}} else if (_ st = RWLOCK_W) {/** wait for the write lock to be released */++ _ rwaitingCount; ResetEvent (_ ev); // SignalObjectAndWait (_ stLock, _ ev, INFINITE, FALSE); LeaveCriticalSection (& _ stLock); WaitForSingleObject (_ ev, INFINITE);/** wait for the returned result and continue to lock. */isWaitReturn = true;} else {assert (0); break ;}} void RWLock: wlock () {bool isWaitReturn = false; while (1) {// WaitForSingleObject (_ stLock, INFINITE); EnterCriticalSection (& _ stLock); if (isWaitReturn) -- _ wwaitingCount; if (_ st = RWLOCK_IDLE) {_ st = RWLOCK_W; // ReleaseMutex (_ stLock); LeaveCriticalSection (& _ stLock); break;} else {++ _ wwaitingCount; ResetEvent (_ ev); // SignalObjectAndWait (_ stLock, _ ev, INFINITE, FALSE); LeaveCriticalSection (& _ stLock); WaitForSingleObject (_ ev, INFINITE); isWaitReturn = true ;}} void RWLock: unlock () {// WaitForSingleObject (_ stLock, INFINITE); EnterCriticalSection (& _ stLock); if (_ rlockCount> 0) {/* unlock read locks */-- _ rlockCount; if (0 = _ rlockCount) {_ st = RWLOCK_IDLE;/* release */if (_ wwaitingCount> 0 | _ rwaitingCount> 0) {/** a lock request is waiting to activate all waiting threads. (manual event ). * Re-compete these requests for locks. */SetEvent (_ ev);} else {/* idle */} else {/* read lock */} else {_ st = RWLOCK_IDLE; /* unlock the write lock */if (_ wwaitingCount> 0 | _ rwaitingCount> 0) {/** if an event is triggered when the mutex _ stLock exists, some lock requests may be unable to compete. * assume that when unlock is called, another thread calls rlock or wlock. if no mutex is released, only the lock requests that have been waiting for have the opportunity to obtain lock control. */SetEvent (_ ev);} else {/* idle */} // ReleaseMutex (_ stLock); LeaveCriticalSection (& _ stLock );}
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.