The constraints are: 1. The code must run on Win98 too. 2. No lost wakeups are allowed. Spurios wakeups allowed, though. 3. Simple and clear implementation.
The hot answers: 1. Yes, I've successfully run several tests. 2. Yes, I've read http://www.cs.wustl.edu/~schmidt/win32-cv-1.html 3. Yes, I know about http://sourceware.org/pthreads-win32/ 4. Yes, I've read "Windows System Programming Third Edition" by Johnson M. Hart. 5. Yes, I've read "Programming with POSIX Threads" by David R. Butenhof.
So please be precise and specific. Thank you. -- With all respect, Sergey. http://ders.stml.net/ mailto : ders at skeptik.net
There's a race condition here for some threads waiting on a broadcast. The broadcast event could get signaled and some other thread waiting on that generation of the broadcast resets the event before all threads waiting on that generation gets a chance to wait.
Unsynchronized updating of gen. There seems to be an implicit assumption that the lock will be held.
> BOOL rc=SetEvent(evs[1]); > assert(rc!=0); > }
> Do you see any problems?
> The constraints are: > 1. The code must run on Win98 too. > 2. No lost wakeups are allowed. Spurios wakeups allowed, though. > 3. Simple and clear implementation.
-- Joe Seigh
When you get lemons, you make lemonade. When you get hardware, you make software.
Joe Seigh wrote: > > void win_cond_var::wait(long timeout) > > { > > int currGen=gen; > > m.unlock();
> There's a race condition here for some threads waiting on a broadcast. > The broadcast event could get signaled and some other thread waiting > on that generation of the broadcast resets the event before all threads > waiting on that generation gets a chance to wait.
> Unsynchronized updating of gen. There seems to be an implicit > assumption that the lock will be held.
Sure, notifyXXX calls must always be synchronized using the mutex. In particular, it prevents from the RC you're talking above. -- With all respect, Sergey. http://ders.stml.net/ mailto : ders at skeptik.net
>>>void win_cond_var::wait(long timeout) >>>{ >>> int currGen=gen; >>> m.unlock();
>>There's a race condition here for some threads waiting on a broadcast. >>The broadcast event could get signaled and some other thread waiting >>on that generation of the broadcast resets the event before all threads >>waiting on that generation gets a chance to wait.
>>Unsynchronized updating of gen. There seems to be an implicit >>assumption that the lock will be held.
> Sure, notifyXXX calls must always be synchronized using the mutex. > In particular, it prevents from the RC you're talking above.
It doesn't prevent the race condition. A thread can arbitrarily pause in execution at any time and you have to logic that prevents resetting an event before all waiters have had a chance to actually wait on the event. It's a race condition that SignalAndWait was seemingly designed to fix except you can only wait on a single event object. -- Joe Seigh
When you get lemons, you make lemonade. When you get hardware, you make software.
Joe Seigh wrote: > > void win_cond_var::wait(long timeout) > > { > > int currGen=gen; > > m.unlock();
> There's a race condition here for some threads waiting on a broadcast. > The broadcast event could get signaled and some other thread waiting > on that generation of the broadcast resets the event before all threads > waiting on that generation gets a chance to wait.
Yes, this is a RC. You're right. Sorry, I didn't get it at the first reading. -- With all respect, Sergey. http://ders.stml.net/ mailto : ders at skeptik.net
Did you look at any previous discussions about this programming topic to reuse any well-known experiences? How useful are the descriptions in your case?
Markus Elfring wrote: > Did you look at any previous discussions about this programming topic to reuse > any well-known experiences? How useful are the descriptions in your case?
I look for a _simple_ implementation. Unfortunately, my tests didn't check for lost wakeups correctly. -- With all respect, Sergey. http://ders.stml.net/ mailto : ders at skeptik.net
Sergey P. Derevyago wrote: > Markus Elfring wrote:
>>Did you look at any previous discussions about this programming topic to reuse >>any well-known experiences? How useful are the descriptions in your case?
> I look for a _simple_ implementation. > Unfortunately, my tests didn't check for lost wakeups correctly.
Well, we've all been there. How do you think we spotted the race condition so quickly? :)
-- Joe Seigh
When you get lemons, you make lemonade. When you get hardware, you make software.
Sergey P. Derevyago wrote: > Markus Elfring wrote:
>>Did you look at any previous discussions about this programming topic to reuse >>any well-known experiences? How useful are the descriptions in your case?
> I look for a _simple_ implementation. > Unfortunately, my tests didn't check for lost wakeups correctly.
Pthreads and Windows threads are too different to have a simple emulation either way. I used the pthread for WIN32 for a few years, and it works well at emulating pthreads on Windows, but it has a high overhead.
So, I choose to create my own API that is 'in between' pthreads and Windows threads for my own portable projects. The API is closer to Windows than pthreads, but it is easy to use with much less overhead than some other wrappers. More details at my site below if you are interested.
"Phil Frisbie, Jr." wrote: > Pthreads and Windows threads are too different to have a simple > emulation either way.
Yes, that's the problem.
> I used the pthread for WIN32 for a few years, and > it works well at emulating pthreads on Windows, but it has a high overhead.
Except for the overhead, I don't like the messages like "The Borland Builder 5.5 version of the library produces memory read exceptions in some tests." My goal is to build something simple and robust (in particular, I don't need the cancellation).
> So, I choose to create my own API that is 'in between' pthreads and > Windows threads for my own portable projects. The API is closer to > Windows than pthreads, but it is easy to use with much less overhead > than some other wrappers. More details at my site below if you are > interested.
I'll take a look, thank you. -- With all respect, Sergey. http://ders.stml.net/ mailto : ders at skeptik.net
Phil Frisbie, Jr. wrote: > Sergey P. Derevyago wrote: > So, I choose to create my own API that is 'in between' pthreads and > Windows threads for my own portable projects. The API is closer to > Windows than pthreads, but it is easy to use with much less overhead > than some other wrappers.
Quick question: did you make the pthread mutexes recursive? Or did you leave recursive behaviour undefined? I won't pretend to be impartial: I hate recursive mutexes, they feel "mushy". When a piece of code written by an author that seems to have heard of threads and not mutexes falls into my lap I like to slowly sure things up with some well placed mutexes, backing off when deadlocks occur (to later remove because the code is almost always gratuitously threaded). For me deadlock can be a handy debugging tool, so it drives me up the wall when clanlib, win32 and inhouse-thread-wrapper-N go to great lengths to not implement the simplest solution (non recursive). Occam would be turning in his grave.
Sergey P. Derevyago wrote: > "Phil Frisbie, Jr." wrote:
>>So, I choose to create my own API that is 'in between' pthreads and >>Windows threads for my own portable projects.
> Your implementation uses PulseEvent() so it's obviously buggy. > --
Depending on your event handling logic, SetEvent/ResetEvent has the same problem. It just has a much larger window so the race condition is rarely observed. I think we discussed this before on c.p.t.
-- Joe Seigh
When you get lemons, you make lemonade. When you get hardware, you make software.
Joe Seigh wrote: > Depending on your event handling logic, SetEvent/ResetEvent has > the same problem.
I've already agreed that my implementation has a RC. No point to discuss it farther.
BTW could you please suggest some good test cases for the lost wakeups? I'm currently testing the second version and I don't want to present yet another buggy attempt. -- With all respect, Sergey. http://ders.stml.net/ mailto : ders at skeptik.net
>>Depending on your event handling logic, SetEvent/ResetEvent has >>the same problem.
> I've already agreed that my implementation has a RC. No point to discuss it > farther.
This is a totally different issue that was discussed much further back on c.p.t. It has to do with a thread waiting on an event, being removed from the event waitset for a kernel APC, and a SetEvent/ResetEvent occurring before the thread returned from the kernel APC and was added back into the event waitset. The point is that it's not just PulseEvent that has this kind of problem. So if you did a broadcast only condvar with SignalAndWait to avoid the race condition in your original code, there would still be a kernel APC issue which wasn't discussed earlier w.r.t. your earlier code. This is a side discussion but probably one that's important in whatever condvar implementation you ultimately come up with. Most of the solutions that work use some form of explicit waitset management.
> BTW could you please suggest some good test cases for the lost wakeups? I'm > currently testing the second version and I don't want to present yet another > buggy attempt.
This would mostly be a code and logic review issue. You could put Sleeps in certain code sections to exaggerate timings but you'd have to recognize which sections were important.
-- Joe Seigh
When you get lemons, you make lemonade. When you get hardware, you make software.
Sergey P. Derevyago wrote: > Joe Seigh wrote: > > Depending on your event handling logic, SetEvent/ResetEvent has > > the same problem.
> I've already agreed that my implementation has a RC. No point to discuss it > farther.
I don't know... it was a pretty buggy, racey condition. I'm sure we could get a few more good yuks out of it.
> BTW could you please suggest some good test cases for the lost wakeups? I'm > currently testing the second version and I don't want to present yet another > buggy attempt.
Boh, I don't know, you could write some interesting condvar code and compare the results using yours and pthread-win32... Sorry for the content-free response.
Sergey P. Derevyago wrote: > Joe Seigh wrote: > > Depending on your event handling logic, SetEvent/ResetEvent has > > the same problem.
> I've already agreed that my implementation has a RC. No point to discuss it > farther.
I don't know... it was a pretty buggy, racey condition. I'm sure we could get a few more good yuks out of it.
> BTW could you please suggest some good test cases for the lost wakeups? I'm > currently testing the second version and I don't want to present yet another > buggy attempt.
Boh, I don't know, you could write some interesting condvar code and compare the results using yours and pthread-win32... Sorry for the content-free response.
> So, I choose to create my own API that is 'in between' pthreads and > Windows threads for my own portable projects.
Which are the relevant design decisions for your choice?
> The API is closer to Windows than pthreads, but it is easy to use with > much less overhead than some other wrappers.
Would you like to explain the specific differences?
I have got doubts about coding correctness if I see instructions like the following in your source file "htcondition.c": (void)pthread_mutex_lock((pthread_mutex_t *)&cv->mutex);
Why do you (intentionally) ignore the return value from this function call? Would you like to add any checking for error codes? How do you think that an implementation with correct error handling would look like in your approach?
Which of the solutions from the description "Strategies for Implementing POSIX Condition Variables on Win32" by Douglas C. Schmidt and Irfan Pyarali did you try to implement? http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
The waitset cannot be used as a condvar. Joe pointed out why. However, you can "wrap" the waitset with a heavily fast-pathed eventcount algorithm. I invented the algorithm presented above. It outperforms all of the synchronization mechanism on Vista!
I skimmed over this topic and it seems like there were some problems with your approach. Nevertheless, the synchronization primitives must not be the bottleneck of a good MT code (if you pay so much to transfer some work from one thread to another, why don't you do this work in the current thread?).
Finally, I've come to the following simple and straightforward implementation and it seems like it serves well to my needs. The essence is:
class win_cond_var { mutex& m; event_cache evc; vector<HANDLE> wts;