 |
|
 |
boost already support all the suggested functionality:
- shared ownership as well as exclusive ownership
- timeouts
- upgradable/downgradable ownership
see http://www.boost.org/doc/libs/1_41_0/doc/html/thread/synchronization.html[^]
here is a simple example:
#include <boost/thread/locks.hpp>
typedef boost::unique_lock<boost::shared_mutex> WrtieLock;
typedef boost::shared_lock<boost::shared_mutex> ReadLock ;
typedef boost::shared_mutex myLock ;
void ReadFunction () { ReadLock r_lock(myLock); void WriteFunction() { WriteLock w_lock(myLock);
|
|
|
|
 |
|
 |
Look when the article was published. 4 years ago. And probably it was written even earlier. That time boost did not include synchronization.
|
|
|
|
 |
|
 |
Hi, I'd like your class and I want to use it but the LGPL license is very restricted. Do you allow me to include the code in a project and don't separate it as a different .lib or .dll as LGPL require? I think that LGPL doesn't work for this type of projects that you just want to include these 2 files in a project.
Thanks,
-PY
|
|
|
|
 |
|
 |
In the case defined like this (3 threads):
- Th1 takes the read lock
- Th2 want to take the write lock (infinite timeout, so it reset the read event)
- Th3 want to take the read lock, but it can't (it enters ReaderWait..., and wait on the read event)
Th1 call UpgradeToWriter, which enter the critical section, decrement the reader count (so it's now 0), and enter _WriterWaitAndLeaveCSIfSuccess
Th1 now wait on the writer event, which isn't set, so it deadlock.
Th2 also wait on the writer event
Th3 waits on the read event, but as no writer have set it, it won't be set either.
The solution is to set the write event while upgrading to the writer lock, or put simply release reader (correctly, with the ReaderRelease method) and acquire writer.
I know that the typical use of upgradeToWriter is to ensure that the reader that release its lock immediately take the write lock, but unless your add a method similar to WriterWaitAndLeaveCSIfSuccess that doesn't wait on the writer event while still preventing concurrent access, it's an unsafe method to use.
|
|
|
|
 |
|
 |
Hi,
This test code snippet gives the following output:
int value = 0;
volatile LONG test = 0;
int ReadThread(LPVOID param)
{
CReaderWriterLock * lock = (CReaderWriterLock *)param;
while (1)
{
if (!lock->AcquireReaderLock(INFINITE))
{
OutputDebugStringA("Error acquiring reader lock\n");
continue;
}
if (test == 1)
{
OutputDebugStringA("Error have reader lock while writer is taken\n");
}
char buffer[256];
sprintf(buffer, "R[%d] value : %d (%d)\n", GetCurrentThreadId(), value, test);
OutputDebugStringA(buffer);
lock->ReleaseReaderLock();
}
}
int WriteThread(LPVOID param)
{
CReaderWriterLock * lock = (CReaderWriterLock *)param;
while (1)
{
Sleep(1000);
if (!lock->AcquireWriterLock(Infinite))
{
OutputDebugStringA("Error acquiring writer lock\n");
continue;
}
InterlockedIncrement(&test);
char buffer[256];
value = rand();
sprintf(buffer, "W[%d] value : %d\n", GetCurrentThreadId(), value);
OutputDebugStringA(buffer);
InterlockedDecrement(&test);
lock->ReleaseWriterLock();
}
}
gives this output (with 6 read thread and 2 write thread):
R[4052] value : 32391 (0)
R[2884] value : 32391 (0)
R[5948] value : 32391 (0)
Error have reader lock while writer is taken
R[1740] value : 26500 (1)
Error have reader lock while writer is taken
R[5464] value : 26500 (1)
W[4252] value : 14604
W[4176] value : 26500
The test boolean is written atomically so the reader should NEVER see it at 1 (as it's set to 1 and then back to 0 while writer is locked).
|
|
|
|
 |
|
 |
Here are some necessary changes that fix this problem.
There are three places that need the scoping lock moved down. Search for this sequence:
m_impl.LeaveCS();
ite->second += ...;
those three places need the call to LeaveCS moved to below the addition:
ite->second += ...;
m_impl.LeaveCS();
Otherwise there is a risk that other threads have altered the m_map member and (due to reallocs or insertions above the current iterator) caused the iterator value (ite) to be referencing the wrong thread's data.
Similarly, there's a gap when upgrading from a reader to writer. Here's the problem code in CReaderWriterLock::AcquireWriterLock(...) :
blCanWrite = m_impl._UpgradeToWriterLockAndLeaveCS(dwTimeout);
if(blCanWrite)
{
ite->second += WRITER_RECURRENCE_UNIT;
}
This iterator can't be used here without first searching again (within a CS) as the m_map may have been altered again since leaving the CS. The corrected code should look like this:
blCanWrite = m_impl._UpgradeToWriterLockAndLeaveCS(dwTimeout);
if(blCanWrite)
{
m_impl.EnterCS();
_HASSERT(m_impl.m_iNumOfReaderEntered == 0);
_HASSERT(m_impl.m_iNumOfWriter >= 1);
CMapThreadToState::iterator ite = m_map.find(dwCurrentThreadId);
_HASSERT(ite != m_map.end() && ite->second > 0);
ite->second += WRITER_RECURRENCE_UNIT;
m_impl.LeaveCS();
}
I don't think this last problem is as likely to occur as the first three (above) but it is still possible.
modified on Friday, July 23, 2010 6:55 PM
|
|
|
|
 |
|
 |
in function
BOOL CReaderWriterLockNonReentrance::_UpgradeToWriterLockAndLeaveCS(DWORD dwTimeout) throw()
{
//question 1
if(0 == dwTimeout)
{
LeaveCS();
return FALSE;
}
......
//question 2
--m_iNumOfReaderEntered;
BOOL blCanWrite = _WriterWaitAndLeaveCSIfSuccess(dwTimeout);
......
}
1.why return false when dwTimeout equal 0?
if there is neither a thread hold write lock nor hold read lock,it could be upgrade directly?
2.if there is thread (thread A) hold read lock, and another thread (thread B) is waitting a write event. then thread A call _UpgradeToWriterLockAndLeaveCS function to get writting event,and now thread A also wait write event, and there is no any other thread to signal write event?
|
|
|
|
 |
|
 |
Hello,
your class is very nice and very helpful. I would like to introduce timeout support (when acquiring locks) but am not very successfull right now. Do you already have a version that supports this?
The main issue I have comes when a reader thread is refused the lock. In that case I must handle the event deletion if needed and must correctly handle the access to the critical section...
Regards
|
|
|
|
 |
|
 |
Nicolas Bonamy wrote: Do you already have a version that supports this?
No, I don't.
Since you have contributed some great articles to the CodeProject community, I will support you. Please wait.
Regards.
|
|
|
|
 |
|
 |
Thanks! Your remark just gave me some motivation to write another article!
Waiting for the CReaderWriterLock with timeout
|
|
|
|
 |
|
 |
Nicolas Bonamy wrote: Your remark just gave me some motivation to write another article!
Congratulation to your new great article!
Nicolas Bonamy wrote: Waiting for the CReaderWriterLock with timeout
Currently when trying to implement timeout, I'm facing a problem that should also occur with .NET ReaderWriterLock. That's about UpgradeToWriterLock in .NET or auto-upgrade in my implementation. Let see following situation:
Thread A hold reader-lock
Thread B hold reader-lock too
There is no any other thread in our application
Thread A wants upgrading with timeout 10 ms
- If successful, A will do a job that takes 10000 ms
Thread B also wants upgrading with timeout 10 ms right after that
- If successful, B will also do a job that takes 10000 ms
With .NET, A will be granted writer-lock first because it waited first. With mine, B will be granted immediately and let A is still waiting. Whichever A or B is granted first is not a problem. Assuming A is granted first as with .NET and starts doing some modifications in 10000 ms. Obviously, B's timeout is expired after 10 ms. If UpgradeToWriterLock just simply return FALSE indicates that timeout was expired, thread B believe that although it was failed when upgrading, it's still safe to read. That's WRONG totally. I don't know what should B do at that time?
Currently I'm searching on the Internet to see if there is any discussion and solution for this problem. In the meanwhile, please tell me what do you want to see in such situation then I will implement it for your own purpose as I promised.
|
|
|
|
 |
|
 |
Hello,
I do not use lock escalation in my context so whatever will do
Thanks,
Nicolas
|
|
|
|
 |
|
 |
Nicolas Bonamy wrote: I do not use lock escalation in my context
You meant you only use the CReaderWriterLockNonReentrance class, didn't you? If so please tell a way to send the modification of this class to you.
|
|
|
|
 |
|
|
 |
|
 |
Well I havnt gone thru your code but as I want to use a Reader Write Lock for my developement(in C#) i dont find .Net ReaderWriterLock Class appropriate for my use because
1. i have more of Writes than read
2. I want my reads to be faster than the Writes
So please guide me whether your code supports it or not ...
Because when I tried .Net Readerwriterlock it was a total failure for my kind of application
Please help me out
|
|
|
|
 |
|
 |
My code can't support too. It would be better to use Monitor or Mutex in your case.
|
|
|
|
 |
|
 |
1. In CReaderWriterLockNonReentrance::_AcquireReaderLockAndLeaveCS(), why "EnterCriticalSection" call was commented out?
2. In the same function above, it seems you only protect 'Write' with CS. I do believe you need to protect both 'Read' and 'Write' on the same variable.
3. In the same function above, why call "CreateEvent" everytime? Would one event is enough for each class instance? can you put it in your ctor? I guess it depends on how you want user to use it.
Thanks.
|
|
|
|
 |
|
 |
ncpga wrote: 1. In CReaderWriterLockNonReentrance::_AcquireReaderLockAndLeaveCS(), why "EnterCriticalSection" call was commented out?
2. In the same function above, it seems you only protect 'Write' with CS. I do believe you need to protect both 'Read' and 'Write' on the same variable.
CReaderWriterLockNonReentrance::_AcquireReaderLockAndLeaveCS() is a protected method. We can not use it directly but other public methods will call it. Please review source code again, you would see that "EnterCriticalSection" is always called before call to _AcquireReaderLockAndLeaveCS().
ncpga wrote: 3. In the same function above, why call "CreateEvent" everytime? Would one event is enough for each class instance? can you put it in your ctor? I guess it depends on how you want user to use it.
Event is a kernel object so it is kind of scarce resource. To save system resource, we just create it when actually needed. Put it in _ctor is OK but please be noted on following things.
1) If "Writers" & "Readers" have nearly the same rate, CriticalSection is beter than ReaderWriterLock. Actually, CriticalSection also creates kernel Event on demand but not do that when we call "InitializeCriticalSection".
2) When we just have "Readers" but no "Writers" at a point of time, "Readers" don't need to use the "Reader Event". So creating Event for "Readers" in _ctor but rarely use it would be considered as a waste.
3) The same thing for "Writers". If you use the classes in a right way, the rate of "Writers" is low, we don't need to create "Writer Event" in _ctor but on demand.
One more thing, there is some articles on CodeProject implement ReaderWriterLock by creating kernel objects (Semaphore, Event...) in _ctor and intensive use them. I personally don't satisfy with these implementations and that is why this article appears on CodeProject.
|
|
|
|
 |
|
 |
Thanks. I thoght event in user object. The reason I said one event object is because we only need one event for both reader & writer, you are already counting number of readers and writers, would that be sufficient?
|
|
|
|
 |
|
 |
"one event for both reader & writer" is not sufficient. Readers wait on manual-reset event. Writers wait on auto-reset event. When we call "SetEvent" on a manual-reset event, all readers would be waken up. But when we call "SetEvent" on an auto-reset event, only one writer (if any) is allowed to be waken up, other writers would still be in WAIT state.
|
|
|
|
 |
|
 |
Could you implement an inter-process version of the reader/writer lock? Or point me to some existing implementations?
Thanks,
|
|
|
|
 |
|
 |
zli98 wrote: Could you implement an inter-process version of the reader/writer lock?
I want to do that but quite busy at this time.
zli98 wrote: Or point me to some existing implementations?
Sorry, I couldn't find any inter-process implementation for RW lock.
|
|
|
|
 |
|
|
 |
|
 |
The article linked to, "Break Free of Code Deadlocks in Critical Sections under Windows" is also majorly misleading and in error. It confuses Windows critical sections (which are actually lightweight mutexes) with the conventional computer science definition of a critical section (which is a particular section of *code* that is critical in the sense that it must be accessed by only one thread at a time).
For example, it says:
"Critical sections, a mechanism that prohibits more than one thread at a time from executing a particular section of code, is a topic that has not received much attention and thus tends not to be well understood."
This is correct for the classic computer science definition of a critical section but not the Windows CRITICAL_SECTION structure. For example, if you look at the code for this article, and consider two instances of the ReaderWriterLock class. They both use the same code, but they each have their *own* CRITICAL_SECTION. Wait -- that means the same section of code can run in two threads at the same time. Isn't that what the article just said critical sections prohibited?
The article is majorly confused, someone needs to explain to the authors that Windows CRITICAL_SECTION objects are not critical sections, they are mutexes. They don't prevent conflicting access to the same code but to the same data.
(Microsoft consistently seems to get threading badly wrong in their developer documentation. Seriously, do not try to learn anything about threading for MS documentation. Sadly, a lot of developers who use Windows as their primary development platform make the same mistakes Microsoft tends to.)
|
|
|
|
 |
|
 |
JoelKatz wrote: The article is majorly confused, someone needs to explain to the authors that Windows CRITICAL_SECTION objects are not critical sections, they are mutexes. They don't prevent conflicting access to the same code but to the same data.
It look likes you are very good in multithreading theory. And I agree with you that:
- Mutex is to prevent conflicting access to the same DATA
- Critical Section is to prevent conflicting access to the same CODE
However, please notice to the following facts
1) In Windows, Mutex & CRITICAL_SECTION are almost the same. The only different thing is: Mutex can be used to synchronize for different threads belong to different processes. CRITICAL_SECTION is just for those threads of the same process --> Your understanding is correct.
2) Either Mutex or CRITICAL_SECTION can be used to prevent conflicting access to the same DATA or CODE. Windows provides mechanism (Mutex or CRITICAL_SECTION) and We use the mechanism to achieve our purpose. Usually, our purpose belongs to one of two following categories:
I) prevent conflicting access to the same DATA
II) guarantee that two or more different code blocks will be executed by CPUs sequentially (not overlap). "Same code" is just a special case in which all code blocks are identical.
3) The link I refer here is to explain why CRITICAL_SECTION is more efficient than Mutex in my implementation.
JoelKatz wrote: Sadly, a lot of developers who use Windows as their primary development platform make the same mistakes Microsoft tends to.
Hope you will get familiar with multithreading in Windows world soon.
|
|
|
|
 |