Boost logo

Boost :

From: Yuval Ronen (ronen_yuval_at_[hidden])
Date: 2006-12-08 09:12:57


Roland Schwarz wrote:
> Yuval Ronen wrote:
>> Well, I wouldn't dare argue with Butenhof, but:
>>
>> A. Even he says that with morphing, there is no difference. You
>> mentioned that such morphing would require some OS support, and I agree.
>> No doubt that synchronization primitives require OS support, and such
>> support might not be available. Perhaps this point makes this discussion
>> more appropriate for a POSIX forum, for example. I apologize if it has
>> gone OT. Peter, in his post, claims that even with morphing there's a
>> difference, because there's an optimization to avoid kernel call that
>> can't be used when morphing is used in this case. Whether this
>> optimization is a good enough reason to influence the CV interface is
>> something that can be argued about.
>
> Sorry, but I do not understand your point.

I was referring to your claim that not having a requirement to hold the
lock while signaling was aimed to improve speed, and therefor it's
impossible that it will cause the opposite. Point A quoted above was to
show that it doesn't necessarily improves speed. Pointed B below was
specifically about Windows - where I believe that speed can be improved
by adding such a requirement.

>> B. Butenhof is talking about a theoretical, ideal OS, which Windows is
>> known to be not... But seriously, if notify() requires the mutex to be
>> locked - the same mutex locked during wait() - then the CV
>> implementation can use this fact to synchronize its own members. I've
>> been able to implements a CV on Windows with only one semaphore - rather
>> than the current impl which uses 2 semaphores and an additional mutex.
>> This means that there are much less sync objects to play with, and a
>> simpler, more efficient code. Surely, as I'm not an expert, I might have
>> some bugs in my impl, but I guess you can see the opportunities in
>> requiring the user some stricter locking, to make the impl simpler.
>
> There is a very long history of attempts to get condition variables
> correct on windows. Most of them (just the seemingly simple and logical
> ones) failing badly due to subtle bugs. Since there is no formal prove
> available I think the most reasonable thing is to use the best known
> algorithms which had undergone a extensive peer review. If you think you
> can come up with something better, please move ahead, but be prepared
> that it will be very hard to a) either give a formal prove of
> correctness for your algorithm or b) survive review by the more
> knowledgeable than I am.

I will gladly put my code here to be scrutinized by the experts on this
forum (after the weekend, as the code is at work, and I'm at home now
:-) ), but I fear that they will little incentive to do it. My code is
based on an interface they claim is invalid, and they might have no
motivation to do a thorough check of it. One point I raised was that in
the general case, requiring more from the user, will make the library
writer's life easier, which might explain why such a thing is even possible.

>> True, but requiring the mutex to be locked upon entering notify(), makes
>> it more difficult for the user to forget locking.
>
> This is what I don't quite understand:
> How does it help when the condition holds a reference to a mutex to make
> it more difficult for the user to forget locking?
> You need to have the mutex locked _before_ you call the signal!
>
>> Having
>> the CV contain a reference to the mutex makes it hard to accidentally
>> lock a different mutex for wait() and notify().
>
> Ok, lets be more explicit:
>
> mutex m;
> condition c(m);
>
> waiting:
>
> lock l(m);
> c.wait();
>
> Ok, no need for l& in call to wait. But is this really better to avoid
> mistakes? See:
>
> mutex m1;
> condition c1(m1);
> mutex m2;
>
> waiting:
>
> lock l(m2);
> c.wait(); // ouch! this will lock the wrong one, and there is no
> apparent glue in the source.
>
> Now to signaling:
>
> mutex m:
> condition c(m);
>
> lock l(m);
> c.notify_one(); // How is this different from current interface? How
> do you make use of the referenced mutex?

Ok, I'm afraid I didn't explain myself properly. Apologies for that.
What I mentioned only briefly, and had to emphasize more, is that the
call to notify() will check to see if the mutex is locked - the mutex
that was passed in the ctor, and throw an exception if not. The wait()
function does it already, except that it should be changed to check the
stored mutex&.

I hope this explanation helps answering your questions above.

>> And locking while
>> notifying has some objective advantages - it makes our threads respect
>> the priority policy better, as Butenhof explains.
>
> This again I cannot understand how it relates to your suggested change
> in interface. I suggest being more explicit.

If the mutex is always locked while notifying,

<copy from your Butenhof quote>
the high-priority waiter will be placed before the lower-priority waiter
onr the mutex and will be scheduled first.
</quote>

which is a good thing.

>> Of course I didn't mean that the signal() will lock, signal, and unlock.
>> I meant that the user will lock before calling notify().
>
> Again: How does the referenced mutex help to achieve this goal?

I hope this was answered.

> About initialisation
> ...
>> Easy:
>>
>> class foo {
>> foo() : m(), c(m) { }
>>
>> mutex m;
>> condition c;
>> };
>
> Agreed.
>
> But you are giving away the possibility of sharing a condition variable
> among several predicates. Altough this is not a recommended practice
> since you can easily get it wrong, it is nevertheless a valid usage
> scenario, which would be ruled out by your suggested change.
> I do not think that we should forbid the alert user to use the interface
> in uncommon (but not wrong) ways.

I agree that my proposed interface is more limiting, for example, as you
said, sharing a condition variable among several predicates (only if
they are protected by different mutexes). Whether or not it forbids
*valid* use cases is the real question. I never encountered such a
scenario, but hey, I'm just one simple programmer, and I can definitely
be wrong. If all you experts think these use cases are valid, then I
probably am.

Yuval


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk