http://groups.google.com/group/comp.programming.threads?hl=en
comp.programming.threads@googlegroups.com
Today's topics:
* Do I need a memory barrier here? - 1 messages, 1 author
http://groups.google.com/group/comp.programming.threads/t/527cb992c30093ef?hl=en
* Why are Boost thread mutexes so slow compared to Pthreads? - 11 messages, 5
authors
http://groups.google.com/group/comp.programming.threads/t/9c9fd9b9ccafc16a?hl=en
==============================================================================
TOPIC: Do I need a memory barrier here?
http://groups.google.com/group/comp.programming.threads/t/527cb992c30093ef?hl=en
==============================================================================
== 1 of 1 ==
Date: Sat, Dec 27 2008 10:15 pm
From: JC
On Dec 26, 12:13 pm, Marcel Müller <news.5.ma...@spamgourmet.com>
wrote:
> Hi!
>
> JC wrote:
> > On Dec 24, 10:39 pm, "Chris M. Thomasson" <n...@spam.invalid> wrote:
> > What about the compiler; do I have to worry about it reordering the
> > assignment and function2() call? Or are there some rules in C++ that
> > state that global variable assignments and function calls can't be
> > reordered?
>
> Any function call or statemend end (at ';') is a sequence point. That
> does not replace the membar, but it ensures that the membar is doing its
> work.
I see, thanks!
Jason
==============================================================================
TOPIC: Why are Boost thread mutexes so slow compared to Pthreads?
http://groups.google.com/group/comp.programming.threads/t/9c9fd9b9ccafc16a?hl=en
==============================================================================
== 1 of 11 ==
Date: Sun, Dec 28 2008 8:52 am
From: Daniel James
In article
news:<e911a34d-b93e-4181-9726-f5d14689300e@q30g2000prq.googlegroups.com
>, David Barrett-Lennard wrote:
> I disagree. It increases the amount of code and therefore *reduces*
> clarity.
I agree with you there.
> As an analogy would you say a language that uses explicit 'endif',
> 'endfor', 'endwhile' statements increases clarity?
I think it does, in that it makes it easier to match up the various
start-of-block tokens with their corresponding end-of-block tokens
(even when the indentation has gone awry) and so makes the code more
readable.
That's not quite germane to this discussion, though, as locking is not
a part of the syntax of the language (at least while we're dicussing
C++). If one could write:
MUTEX m
...
LOCK m
DoStuff();
UNLOCK m;
where LOCK and UNLOCK were elemnts of the language syntax you would not
be able to forget the UNLOCK because the compiler would report an
error. You would then have the clarity ascribed to the explicit
lock/unlock idiom but also the safety achieved by using a ScopedLock
object.
What David Schwartz and others are suggesting is a style of programming
which gives the supposed clarity of explicit locking but cannot give
the safety of block-scoping the lock. ISTMT safety is far more valuable
than any slight increase in readability.
Cheers,
Daniel.
In a language that had syntactic primitives LOCK and UNLOCK
== 2 of 11 ==
Date: Sun, Dec 28 2008 8:52 am
From: Daniel James
In article
news:<209297bb-6614-43b2-892c-af8d2d89ced5@i18g2000prf.googlegroups.com>
, David Schwartz wrote:
> If you throw an exception in a case where you broke invariants and the
> lock is automatically release, you are screwed.
Yes ... but you are still screwed if the lock is not automatically
released. How you manage the lock isn't important here, if you break
invariants you are screwed.
> In other words, if an exception occurs while you are holding a lock,
> either your code was broken to be begin with or you are totally
> screwed.
No. You must ensure that your code does not leave any object in a
non-stable state in the event that an exception is thrown. Unless you do
that you will break invariants and you are screwed. Having ensured that
no invariants will be broken it does no harm to release the lock
(explicitly or using a scoped lock). To fail to release the lock in this
case would be an error (and might well cause a deadlock elsewhere).
It doesn't matter at all, for the purposes of this argument, whether the
lock is released explicitly or using a scoped lock object ... it's just
easier, simpler, more reliable, and clearer to use the scoped object.
> There are exceptions, this is not a hard-and-fast rule. But the use of
> lexically-scoped locks does encourage people to write code that is
> broken in this way.
No it doesn't. A failure to understand the exception mechanism and the
requirements of exception safety lead to people writing code that is
broken in this way. How locks are managed is orthogonal to the problem.
> Lexically-scoped locks let people forget that they
> have to choose the precise point where every lock is unlocked. By
> hiding the choice, you are less likely to get it right.
I tend to disagree. IME people are quite good at failing to understand
where they need to unlock however they're managing their locks ... if
anything I'd say that the notion that the lock is tied to the block
structure of the program helps them to get it right as block structure
is something that they seem to find easier to understand.
Either way, you need to understand what a lock does and when you need
one in order not to write broken code. That's much harder than learning
to use either the explicit locking idiom or scoped locks.
Cheers,
Daniel.
== 3 of 11 ==
Date: Sun, Dec 28 2008 8:52 am
From: Daniel James
In article
news:<f6b6c30c-7ec9-48e9-a6ba-63f6bf601784@w1g2000prm.googlegroups.com>,
David Schwartz wrote:
> Maybe your scoped lock has a 'do not unlock on destructor' function.
Don't de daft! It wouldn't be a scoped lock, then, would it?
There may be other kinds of lock-management objects that would allow you
to do that, but one shouldn't call them scoped locks if they don't
manage locks according to a lexical scope.
.. and to think that you were the one who was harping on about clarity
and self-documenting code!
> > > If you throw an exception in a case where you broke invariants
> > > and the lock is automatically release, you are screwed.
>
> > You are screwed because you broke invariants. Not because the lock
> > was automatically released.
>
> No, you're fine if the invariants are broken so long as the lock is
> still held. It is perfectly valid to break invariants so long as you
> do not release the lock. That's, in fact, what locks are for.
How is it going to help that you still hold a lock that you can now
never release? An error has occurred in code that broke some invariant
and that error wasn't handled locally -- and the invariant restored --
so the code is broken. End of story. This has nothing to do with locks.
You would have the code maintain the lock forever to prevent other
threads from using the data it protects -- which isn't the answer:
you'll prevent other code from breaking when it tries to use those data,
but in doing so you will cause deadlocks because the lock cannot be
released and the other code will still break.
If you throw an exception in a case where you broke invariants you
*MUST* restore those invariants before allowing the exception to
propagate. Yes, you need to keep the object locked while you do that,
but once you've done it and you allow the exception to unwind you no
longer need the lock (at least, you do not need it any more than you
would have if your code had completed without the error that caused the
exception) so it is perfectly all right for the scoped lock object to
release the lock at the normal time.
It *MUST* be OK for the scoped lock to do this, and your code *MUST* be
written in such a way that it is so. Otherwise your code is broken --
and it would be broken even in an unapologetically singly-threaded
scenario without a lock in sight.
Cheers,
Daniel.
== 4 of 11 ==
Date: Sun, Dec 28 2008 8:52 am
From: Daniel James
In article news:<6rdbb6Fo6jcU1@mid.individual.net>, Ian Collins wrote:
> It's too easy to use a scoped lock as a band-aid for a poor design.
I'd say that was actually very hard. If the design is bad then nothing
as simple as a scoped lock will save the whole program from being a
buggy mess.
OTOH scoped locks do simplify the task of managing locks correctly, and
that's an important part of writing correct code.
> They do have their place, but the aren't the silver bullet some people
> make them out to be.
Scoped locks are a silver bullet that kills the werewolf called "failing
to unlock a lock" -- they are, of course, no defence against the vampire
of non-exception-safety or the skeletons in the cupboard of bad design.
That is: They are a tool that achieves one thing well, but doing that
one thing correctly won't save you from other errors in your code.
Cheers,
Daniel.
== 5 of 11 ==
Date: Sun, Dec 28 2008 1:03 pm
From: "rajiv.k.shukla@gmail.com"
One problem which I have experienced with scoped locks is that
programmers tend to overestimate the scope by being just plain lazy. I
have seen scope extended to cover a deep call stack when there was no
need to and which in fact resulted in serilization that went
undetected for a while.
Cheers, -- Rajiv
== 6 of 11 ==
Date: Sun, Dec 28 2008 4:12 pm
From: Juha Nieminen
Ian Collins wrote:
> It's too easy to use a scoped lock as a band-aid for a poor design.
Manual locks won't make your design any better. In fact, they won't
have any effect whatsoever. The only thing they will do is force you to
write unnecessary (and error-prone) code.
Do you also oppose things like using std::string or std::vector in
C++? After all, they are scoped as well.
== 7 of 11 ==
Date: Sun, Dec 28 2008 6:21 pm
From: Giancarlo Niccolai
Ulrich Eckhardt ha scritto:
> David Schwartz wrote:
>> It's intrinsically evil to replace:
>>
>> l.Unlock();
>> }
>>
>> with:
>>
>> } // implicitly unlocks 'l'
>>
>> Self-commenting code is always better than code that requires
>> comments.
>
> If you knew C++ well enough, you would be familiar with resources that are
> bound to a scope.
Locking is not a resource. Is a thing you do to other surrounding
(unsuspecting) code.
Scoped locks are not evil by themselves. OTOH, scoped lock encourages
harmful practices in code that needs not to be subject to more harmful
practices than those it is usually already subject too.
I explain.
Suppose you have to log something before return, like in the following code:
bool haveIt()
{
if ( shared_resource == available )
{
Log( "Yay, we did it it!" );
return true;
}
Log( "Nay, we didn't make it" );
return false;
}
An unscoped locking version allows this:
bool haveIt()
{
l.lock();
if ( shared_resource == available )
{
l.unlock();
Log( "Yay, we did it it!" );
return true;
}
l.unlock();
Log( "Nay, we didn't make it" );
return false;
}
As you can see, the lock can be held for a minimal time, without the
need to extend it to the log operation, where the lock is useless and
actually harmful.
Compare that with the two possible solutions with scoped lock:
bool haveIt_full()
{
ScopeLock local_lock_for( l );
if ( shared_resource == available )
{
Log( "Yay, we did it it!" );
return true;
}
Log( "Nay, we didn't make it" );
return false;
}
In this first version, we're very readable, and very inefficient. A
second approach which respects correct lock usage semantics would be:
bool haveIt_short()
{
bool haveIt;
{
ScopeLock local_lock_for( l );
haveIt = shared_resource == available;
}
if ( haveIt )
{
Log( "Yay, we did it it!" );
return true;
}
Log( "Nay, we didn't make it" );
return false;
}
but then again, I can't see how this can be more readable and correct than
bool haveIt_short_once_again()
{
l.lock();
bool haveIt = shared_resource == available;
l.unlock();
if ( haveIt )
{
Log( "Yay, we did it it!" );
return true;
}
Log( "Nay, we didn't make it" );
return false;
}
;=======================
IMHO if you (you in general, as "one") hold a mutex long enough to be
possibly affected by an exception raising, you have a problem. If you
(again, general) think that scoped locks are good because they save an
unlock in catch() blocks, then you should first ask yourself why you
have mutexes around when try/catch firings are possible outcomes.
First, because you're very probably breaking the basic principle that
mutexes must be held for the shortest possible time, that is, the time
needed to coordinate shared states visibility.
Second, because you're saving a possible deadlock for sure, but you're
inviting a much more dreadful problem: the dead patient syndrome.
;=========================
Dead patient syndrome is what happens when you messup too much with
threads without control over what happens behind the scenes.
Consider the following code.
...
ScopeLock very_big_lock( l );
...
try {
shared1 = opThatCanRaise( a );
shared2 = opThatCanRaise( b );
NewDataAvailable();
}
catch( MyException &excp ) {
Log( "Sorry, we're out of luck" );
}
Now, suppose that shared1 is the name of the patient that needs to
undergo a surgery operation next, and shared2 is the surgery name.
If you raise after shared1 is set, you have a perfectly consistent,
fault-free, deadlock-free, ever running program, and perfectly dead patient.
Real cases are much more complex and subtle, and resetting the state of
the shared data you mangle with in the wide locks encouraged by scoped
locking on all those paths, some of which not even known in advance, as
exceptions and automatisms are usually thought for future extensions,
can be troublesome, error prone and generally a big headache.
;==================================
All this to say: the two methods aren't one superior to the other in any
aspect. Both carry risks and advantages. BUT, with scoped locking, risks
are often underestimated and hidden. While they require a better
understanding of the underlying situation, and a deeper mastering of the
multithreading mechanics, I often see them just fed to newbies as
"simpler", "easier" and "safer", and this is HARDLY the case.
Actually, if MT requires you to KNOW WHAT YOU'RE DOING much more than
any other development field in IT, automatisms in MT requires even more
expertize.
Zen, which is very similar to MT programming, has a kohan saying that
the newby don't see the road; the practicer follows the road; the master
don't see the road. If you're a master, any new technique is good, and
you can just follow your inspiration. But encouraging the use of "scoped
lock first" as "just better" to newcomers will earn you (once again, you
in a generic sense) programs that never deadlock, but do a lot of worse
things. After all, a deadlock is easily spotted. A dead patient too, but
it's usually too late.
Bottom line: NEVER confuse "automatic" with "simpler" or "safer" (some
confuses it even with "better"). They are non-overlapping concepts.
Bests,
GN.
== 8 of 11 ==
Date: Sun, Dec 28 2008 6:25 pm
From: Giancarlo Niccolai
Sorry for the double reply:
Ian Collins ha scritto:
> JC wrote:
>> Really, the *only* compelling reason to not use lock holders is if you
>> are using scopes that don't fit cleanly inside the locking scope, e.g.
>> a function like pthread_cond_wait would be hard to implement using a
>> scope-based lock holder for the mutex instead of just a "raw" mutex.
Not correct. It is wonderfully re-implemented posix-semanitc-like with
scoping in C#.
> The most compelling reason not to use lock holders as discussed
> up-thread is you can't (at least not without a great deal of effort -
> more than they save) guarantee the resource protected by the lock is in
> a sane state in the event of an exception.
I undersign this (cfr. the dead patient syndrome I talked about).
>
> By all means use lock holders, but use the destructor to assert the lock
> has been released not to release the lock.
>
I take that "by all means" is meant in the sense: "please, do so if you
prefer, there is no real reason not to do it, if you do it in the proper
way". Correct?
Bests,
GN.
== 9 of 11 ==
Date: Sun, Dec 28 2008 7:05 pm
From: Giancarlo Niccolai
Juha Nieminen ha scritto:
> Ian Collins wrote:
>> It's too easy to use a scoped lock as a band-aid for a poor design.
>
> Manual locks won't make your design any better. In fact, they won't
> have any effect whatsoever. The only thing they will do is force you to
> write unnecessary (and error-prone) code.
>
> Do you also oppose things like using std::string or std::vector in
> C++? After all, they are scoped as well.
This question has the same relevance and significance as asking if one
should oppose war on cancer because he thinks that war is bad.
Also and besides, please notice that those that are opposing your
position here are not opposing the usage of scoped locking at all; they
are opposing just your positions, which, to my best understanding, are
the followings:
1) scoped locking is always good or better than the other ways of doing
locking.
2) all you have to do about multithreading safety is that of releasing
mutexes before returning from functions.
I am sorry to say that both this points are incorrect. If those two
points doesn't match your position, please clarify it as this is what
emerges from this thread.
Bests,
GN.
== 10 of 11 ==
Date: Sun, Dec 28 2008 7:21 pm
From: Giancarlo Niccolai
JC ha scritto:
>
> More accurately, I should say, it ensured a resource was left in a
> well-defined state when a function exited for any reason -- and that's
> what you should do for *all* resources. If the postconditions of a
> function are:
>
> - Shared object A is in some well-defined state.
> - Mutex M is released.
>
> And the function throws an exception, leaving shared object A in an
> undefined state but with mutex M released, where is the problem? It's
> not in the lock guard, that's the part that *worked*.
See the dead patient syndrome. When you do a mistake about shared object
being left in a non-well defined state:
- if you don't unlock the mutex, the state of the object stays non-well
defined, but no other part of the program can use it. If it tries, it
will deadlock, and your program will eventually halt without any
possible error propagation.
- if you unlock the mutex, the rest of the program may think that the
object is in a well defined state and use it. What happens then depends
on many things, but it ranges from simple data corruption to disaster.
So, even if I agree that you have at least one part over two that
*worked* in the second case, I must dissent on the fact that having one
part working over two possible failing is *always* better than having
both failing.
In this particular case, having a double failure is usually better and
less risky.
Bests,
GN
== 11 of 11 ==
Date: Sun, Dec 28 2008 9:02 pm
From: David Barrett-Lennard
On Dec 29, 1:52 am, Daniel James <wastebas...@nospam.aaisp.org> wrote:
> In article
> <news:e911a34d-b93e-4181-9726-f5d14689300e@q30g2000prq.googlegroups.com
>
> >, David Barrett-Lennard wrote:
> > I disagree. It increases the amount of code and therefore *reduces*
> > clarity.
>
> I agree with you there.
>
> > As an analogy would you say a language that uses explicit 'endif',
> > 'endfor', 'endwhile' statements increases clarity?
>
> I think it does, in that it makes it easier to match up the various
> start-of-block tokens with their corresponding end-of-block tokens
> (even when the indentation has gone awry) and so makes the code more
> readable.
You didn't qualify that statement - are you saying it (always) makes
the code more readable? I disagree. I hardly ever feel like the
indentation is so complex I would be better off with verbose matching
start/end tokens.
> That's not quite germane to this discussion, though, as locking is not
> a part of the syntax of the language (at least while we're dicussing
> C++). If one could write:
>
> MUTEX m
>
> ...
> LOCK m
> DoStuff();
> UNLOCK m;
>
> where LOCK and UNLOCK were elemnts of the language syntax you would not
> be able to forget the UNLOCK because the compiler would report an
> error. You would then have the clarity ascribed to the explicit
> lock/unlock idiom but also the safety achieved by using a ScopedLock
> object.
I don't see the clarity in your example :) I'm left wondering what
will happen if DoStuff() can throw an exception. Does the
hypothetical compiler check that DoStuff() has a no-throw spec?
I don't place much weight on the question of whether declarative
lexical locks are more or less easy to understand than explicit locks.
That is subjective, and so too is the term 'clarity' given that to
some folks it can apparently require redundant and particularly
verbose changes to the code to avoid potential misunderstandings!
I place more weight on what I can quantify. ScopedLocks eliminate the
need to explicitly state when to unlock. That elimination of code can
help simplify the source code.
> What David Schwartz and others are suggesting is a style of programming
> which gives the supposed clarity of explicit locking but cannot give
> the safety of block-scoping the lock. ISTMT safety is far more valuable
> than any slight increase in readability.
David Schwartz will argue that his approach (of using an object
declared on the frame that asserts if the lock is held when it
destructs) is safe because it will pinpoint a failure to call unlock
at run time. He may claim he gets both clarity (with explicit unlock)
and safety (with the assertions) in a similar sense to what you
described above with the special language feature. Of course a
compile time check would be far more desirable.
IMO the more important distinction has to do with simplicity and
elegance. Source code that promotes short lived lexically scoped
locks will tend to be well structured. It avoids approaches which
make it necessary to analyse the run time behaviour (as a 'state
machine') to know when locks are taken and released.
A lexically scoped lock immediately tells the reader (without any
further analysis) that a lock will be held by any thread within the
given block of code. When reading complex code (perhaps with many
exit points), that's useful to know.
==============================================================================
You received this message because you are subscribed to the Google Groups "comp.programming.threads"
group.
To post to this group, visit http://groups.google.com/group/comp.programming.threads?hl=en
To unsubscribe from this group, send email to comp.programming.threads+unsubscribe@googlegroups.com
To change the way you get mail from this group, visit:
http://groups.google.com/group/comp.programming.threads/subscribe?hl=en
To report abuse, send email explaining the problem to abuse@googlegroups.com
==============================================================================
Google Groups: http://groups.google.com/?hl=en
No comments:
Post a Comment