Monday, December 29, 2008

comp.programming.threads - 25 new messages in 3 topics - digest

comp.programming.threads
http://groups.google.com/group/comp.programming.threads?hl=en

comp.programming.threads@googlegroups.com

Today's topics:

* Why are Boost thread mutexes so slow compared to Pthreads? - 21 messages, 6
authors
http://groups.google.com/group/comp.programming.threads/t/9c9fd9b9ccafc16a?hl=en
* Core i7 CMPXCHG-performance - 3 messages, 2 authors
http://groups.google.com/group/comp.programming.threads/t/14d357b5d0c9bfb9?hl=en
* problem with pthread_cancel - 1 messages, 1 author
http://groups.google.com/group/comp.programming.threads/t/e3ae6dbb7de05a21?hl=en

==============================================================================
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 21 ==
Date: Mon, Dec 29 2008 12:46 pm
From: Daniel James


In article news:<Uw36l.28093$J84.21749@tornado.fastwebnet.it>, Giancarlo
Niccolai wrote:
> Please, reconsider the statement that "automatic" means "easier".
> Automatic just looks easier, and can spare you some code line, but it
> can't just be used mindlessly as it's "automaticity" would suggest.

"Automatic" *is* easier ... but easier isn't necessarily better. I agree
that one needs to think about what one is doing -- you do have to get
the scope right when using a scoped lock.

The important thing is that -- assuming you /have/ got the scope right
-- you don't have to write boilerplate code to free the lock before
every return nor do you have to handle any thrown exception explicitly
to release the lock. There's a lot of code that you don't have to write
because the lock is handled automatically, and (because you haven't
written it) that is code that can't contain any error and that doesn't
add complexity to make the code confusing to read.

Looking again at Juha's example, without using a scoped lock you'd have
to do something like:

int foo()
{
aMutex.lock();

try
{
if(error1)
{
aMutex.unlock();
return 1;
}

do_something();
if(error2)
{
aMutex.unlock();
return 2;
}

do_something_else();
if(error3)
{
aMutex.unlock();
return 3;
}
}
catch( const exception_from_do_something & e1 )
{
aMutex.unlock();
throw;
}
catch( const exception_from_do_something_else & e2 )
{
aMutex.unlock();
throw;
}

aMutex.unlock();
do_some_unlocked_stuff();
return 0;
}

Which is an awful lot less clear to read than the scoped example, and is
an awful lot less likely to be written correctly by the programmer (and
any error he does make won't exactly be obvious).

> Locking is a complex matter. Automatisms in it solve problems (but
> very trivial ones) and brings in others (potentially more dangerous).
> You must know what you're doing whether you lock manually or scoped,
> and in the second case you must know it even better.

Yeah, using locks correctly is hard. Scoped locks are one of the tools
that we can use to simplify that task -- a good tool in some cases, but
less so in others. As with any powerful tool it's possible to damage by
using them badly or in inappropriate situations -- but that doesn't make
them a bad tool.

> We just got shivers down our spines when we read them being "easier",
> "safer" and "better" (always). They're not. They're just another tool.

I don't quite agree. Scoped locks *are* easier, safer, and better than
alternative programming techniques in those situations in which scoped
locks are an appropriate tool. Those situations are quite common, so
scoped locks are usually a good tool -- not always, but usually.

Cheers,
Daniel.

== 2 of 21 ==
Date: Mon, Dec 29 2008 12:46 pm
From: Daniel James


In article news:<9k46l.28129$J84.195@tornado.fastwebnet.it>, Giancarlo
Niccolai wrote:
> ... ultimately, a C++ programmer must consider an exception as an
> ERROR that MUST NOT HAPPEN, unless something is wrong with the
> program ...

Not at all.

An exception is something that you would prefer did not happen, but
correct programs can and do throw exceptions (out of memory, file
doesn't exist, network failed ... even if your own code doesn't use
exceptions to report errors (and IMHO it should) there are lots of
conditions that are reported by the standard library that indicate data
errors rather than a bad program) and your program *MUST* be able to
respond to these in a threadsafe manner.

> Exactly. And in fact, you totally missed the point. The point is that
> THIS CODE IS BROKEN, and unlocking automatically WON'T SAVE IT.

The code is broken quite regardless of any locking. It would be broken
in a single-threaded environment.

> The point I sustain is that, OTOH, unlocking automatically can have
> the side effect of having this broken data to be visible and
> propagated to other threads.

.. and NOT unlocking (whether automatically or otherwise) WILL lead to
deadlocks. Fix the broken code and automatic unlocking will work
perfectly. The error is not in the locking code.

> In the first case, your medical data processing program hangs, and
> you are called to fix it. In the second case, the error is the same,
> but you kill a patient, and you are called in a trial. Now, which is
> the "safest" way to do (human programming) error-resistant programs?

It's just as likely that you will kill a patient because the program
deadlocked. Whatever techniques you use, you have to use them
correctly.

> > Actually, to me, automatic _is_ simpler,
>
> No. It just saves some coding line, but doesn't make your programming
> experience simpler, nor your code simpler to write, nor safer. It's
> just another whole set of responsibility for the programmer to work
> on; it's a choice, a possible one but not a win-win one. It must be
> chosen knowingly, and not just on the base of "hey, look, i can't
> forget locks open, so it's SIMPLER!" appraisal.

Using scoped locks is a design choice, and it has to be an informed
choice. It *does*, though, lead to code that is simpler and easier to
maintain, and less prone to programmer error. Yes, of course,
programmers can make errors in other places but they are much less
likely to make errors in the locking/unlocking code if they don't
actually have to write any. They must still understand the way that the
unlocking works and the responsibilities of the code that executes
within the locked section -- but they have to understand that anyway if
they are not to introduce bugs. The scoped lock doesn't obviate the
need for careful design or for understanding how the code works, but it
does remove a lot of the potential for simple coding bugs.

> [BUT, it can give you the false feeling that it's simpler, safer
> and so on. That's why we insist that it is not.]

I would say: It is both simpler and safer -- though it is not
absolutely simple nor absolutely safe.

Cheers,
Daniel.


== 3 of 21 ==
Date: Mon, Dec 29 2008 12:46 pm
From: Daniel James


In article
news:<db9b1dcf-0924-4988-a837-1231db85d827@e1g2000pra.googlegroups.com>,
David Schwartz wrote:
> It depends on the architecture. But it's certainly better to have a
> broken invariant protected by a held lock than not.

I don't consider it acceptable to have a broken invariant ... and I
don't think it's productive to argue about the relative merits of
different kinds of broken behaviour.

> > 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.
>
> Exactly. The point is that scoped locks make it easier to break
> code because it makes the code more fragile.

I don't know why you say "exactly". Scoped locks make the behaviour of
the locks more robust ... if the code is fragile elsewhere that isn't
the fault of the locking.

> > 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 could also have code that catches the exception in a higher scope
> and discards the possibly-contaminated objects. However, the lock must
> be held until the invariants can be restored.

It is, in general, too late by then ...

Even if you have an application in which contaiminated objects can be
reliably detected and discarded at a higher level doing so implies
holding the lock on the object for a much longer time than would be
needed if you just wrote the code so that it didn't crap all over your
data in the first place.

Prevention is better than cure!

> > 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.
>
> Right, but this is another example of how scoped locks make code
> fragile.

Read that again. It has nothing to do with locking, scoped ot otherwise.
Even in a single-threaded application with no locking at all you *MUST*
ensure that your code is exception-safe or you will have all kinds of
problems. Locking doesn't make the code any more or less fragile, nor
does scoped locking -- if your C++ code is not exception safe it is
broken and there is no point at all in discussing locking at any level.
End of story.

I'm talking about "normal" C++ here: code that uses the standard library
and so must allow for exceptions from that, at least. If you don't use
any exceptions and don't call any libraries that use them either you can
ignore exception safety -- but you'll be ignoring a good part of the
power of the language and you will end up with some very idiomatic and
non-portable C++.

Cheers,
Daniel.


== 4 of 21 ==
Date: Mon, Dec 29 2008 12:46 pm
From: Daniel James


In article
news:<5577f07e-f4cf-4058-842a-29fbb7aa9773@w39g2000prb.googlegroups.com>
, David Barrett-Lennard wrote:
> > > 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.

I didn't think it needed qualifying ... having explicit keywords for
different block stert/end tokens gives a syntactic clue -- more
information is generally better than less. You don't always /need/ more
information, if the code is well laid-out, so maybe I should make that
"more information is not-worse than less".

Maybe it's just that I cut my teeth on Algol68 so I'm used to IF ... FI
and CASE ... ESAC (but not BEGIN ... NIGEB)?

> 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?

That's unspecified <smile>

No, seriously, my hypothetical example was only intended to show the
sort of syntax that I think might add clarity -- don't go looking for
tricky semantics because I didn't make any up! The hypothetical language
may not even support exceptions.

However, what I might have had in mind -- if I'd been thinking that
deeply -- is something that had semantics similar to those of a C++
scoped lock, so the mutex would be unlocked as it was destroyed as the
stack unwound (i.e. the LOCK and UNLOCK statements would define a scope,
and the mutex would be unlocked as that scope was torn down).

> > 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.

I imagine so ... of course, in C++ -- with no direct language support
for locks (or scoped locks) -- you have the problem that any explicit
locking mechanism requires a lot of code that isn't necessary if you use
a scoped lock. That's especially true if you effectively use a scoped
locking object anyway, even if it only asserts in debug builds.

What DS proposes has, AIUI, another problem, which is that the code
paths in the debug and release versions are sufficiently different that
the release-mode code is never tested in debug builds, which sounds like
another great opportunity for bugs to creep in.

> 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.

Agreed.

Cheers,
Daniel.

== 5 of 21 ==
Date: Mon, Dec 29 2008 12:46 pm
From: Daniel James


In article
news:<ebaf40a6-24d9-4aa5-a8e5-99b5f82aeed4@u18g2000pro.googlegroups.com>
, David Schwartz wrote:
> I don't think a compile time check is imaginable. How can the compiler
> tell whether or not a function releases a lock? Consider:

In the little hypothetical example I gave the lock can ONLY be released
by the UNLOCK statement matching the LOCK statement, so the situation
cannot arise. That is: my hypothetical MUTEX type is a language
primitive for use in LOCK and UNLOCK blocks, and doesn't provide any
methods by which it can be manipulated directly.

That's a limitation, of course, a MUTEX can only be locked and unlocked
within a lexical scope -- but it was just an example.

There are other ways to do it, of course ...

> But that's exactly the wrong approach. It is absolutely vital that you
> know precisely where locks are acquired and where they are released.
> Every time you add or remove code from a function, it is vital that
> you know what the lock context in which that code is (or was going to)
> run in is.

Using scoped locks doesn't introduce any uncertainty ... it just
provides a syntactically simple and convenient mechanism for managing
the scope of a lock by tying it to a lexical scope. That's not always
what you want to do, but it very often is so the technique is
convenient.

Cheers,
Daniel.

== 6 of 21 ==
Date: Mon, Dec 29 2008 12:46 pm
From: Daniel James


In article
news:<3c59e674-6bea-423f-8fe6-7302a0403051@d36g2000prf.googlegroups.com>
, David Schwartz wrote:
> What is the "safety of block-scoping the lock"? That you cannot forget
> to release the lock on any code path?

Let's not insult programmers by suggesting that they might forget ...
let's say that there is no possibility of the lock not being released on
exit from the scope to which the lock is tied, regardless of the means
by which that scope is exited, and that no code needs to be written by
the programmer to ensure this.

That's quite a powerful guarantee -- and if it's what you want to happen
then it's very useful.

> First, I've already explained that you can get the same safety with
> explicit unlock functions.

Of course you can ... but you have to write them every time, which is a
chore and leads to untidy unreadable code even when done by experienced
programmers who don't forget.

> Second, blocked-scoped locks add a variety of new possible errors ...

No they don't. Not to any errors you wouldn't get by writing equivalent
code with explicit unlocks.

Of course, if you actually want NOT to unlock something when the scope
ends than the scoped lock is the wrong tool for the job ... but that's
not usually something that one wants to do.

Cheers,
Daniel.

== 7 of 21 ==
Date: Mon, Dec 29 2008 2:59 pm
From: Ulrich Eckhardt


Giancarlo Niccolai wrote:
> Ulrich Eckhardt ha scritto:
>> Giancarlo Niccolai wrote:
>>> Ulrich Eckhardt ha scritto:
>>>> I don't see how use of exceptions for reporting errors is limiting me,
>>>> rather the opposite.
>>> Do you copy?
>>
>> I don't understand what you mean here...
>
> It's radio-amateur talk. It means "are you receiving me"?

Hmmm. It sounds a bit like "Do you understand what I say?" with an
unspoken "..or are you too stupid?".


>>> Exception are to report errors, I said, not to do
>>> exception-based-control-flow.
>>>
>>> Errors are ERRORS. That is, rare and NOT happening when EVERYTHING GOES
>>> FINE.
>>
>> Sorry, but there are basically two kinds of errors. Firstly, there are
>> errors made by the programmer, which you can detect to some extent by
>> defensive programming, unit testing and assertions.
>>
>> Then, there are errors caused by external influences, e.g. by the user
>> misspelling a path. And, of course, a misspelled path will change the
>> flow of control in a program. Why shouldn't you use exceptions to signal
>> that user error, which isn't rare at all?
>
> This is exactly what C++ exceptions were not designed to handle.

Why? There is a function that is supposed to do something and return a
result. However, errors can happen, so what will your function do when it
can't return the result? There are several approaches and throwing an
exception is often the best of them. For the sake of completeness, there
are
- ignore the error
- return a default value
- return an error value, like NULL, which might require adding this value to
the possible set of returned values
- always return an error code and pass in a pointer/reference to the result
- replace the return type with a variant that is either info about the error
or the real result (this is basically equivalent to the former version)
- write error to stderr and abort() instead of returning
- throw an exception

Which alternative do you pick? Seriously, provide an example, examples often
make this so much clearer.

> For the simple case of an user input error, which happens, but at
> sparse times, (unless your user can type faster than Superman),
> breaking the conceptual model of exceptions-as-errors doesn't cause
> any problem at all, and we can even see that as a matter of taste.

Why does a user input error merit any different handling than any other
exceptional event? Only because the user is too slow to make it happen more
than once a second? Note that I'm not talking about the validation that
immediately follows input. The errors I mean might only be detected several
levels deep in the call stack.

> But you may then start to put in the same category errors as misspelled
> paths in web server GET requests, which may happen several thousands
> times per seconds, and handling that as exceptions instead of performing
> proper preliminary checks and correct defensive programming WILL do a
> difference.

Preliminary checks on HTTP GET requests? How? You just received one on a
connection that was just opened on a port. All you do now is handle the
request, and if on the way you find that it is ill-formed, you throw an
exception which will cause an error to be sent to the client and the
connection closed. Do you suggest that the request should be parsed, then
validated, then executed? Three steps over the same data? I'm asking
because you seem to imply that using an exception while handling the
request would perform or scale badly. In any case, applying special error
handling as optimisation to performance-critical parts is a completely
different thing, the general case much more benefits from correctness and
clarity.

> Said this, enough with C++ here, really.
>
> We're having an enough hard time dealing with MT in the first place here.

Sorry, but you are making up claims here that are in my eyes unfounded.
That, plus an unhealthy dose of ignorance you already demonstrated about
C++.

Uli

== 8 of 21 ==
Date: Mon, Dec 29 2008 3:04 pm
From: Ulrich Eckhardt


Daniel James wrote:
> try
> {
[...]
> }
> catch( const exception_from_do_something & e1 )
> {
> aMutex.unlock();
> throw;
> }
> catch( const exception_from_do_something_else & e2 )
> {
> aMutex.unlock();
> throw;
> }

You don't care what exception is thrown here, so just use

catch(...)
{
aMutex.unlock();
throw;
}

Uli

== 9 of 21 ==
Date: Mon, Dec 29 2008 3:44 pm
From: "Chris M. Thomasson"


"David Schwartz" <davids@webmaster.com> wrote in message
news:3d7403b4-4b25-4850-97c3-b572b19d1873@d42g2000prb.googlegroups.com...
On Dec 29, 5:00 am, David Barrett-Lennard <davi...@iinet.net.au>
wrote:

> > It strikes me as messy and confusing that a lock object can continue
> > to exist without actually holding a lock.

> Then what do you do when some code uses a scoped lock holder and you
> need to run a small piece of code inside that scope without holding
> the lock? Do you re-architect the code completely?

You can use a scoped unlock RAII helper object:


static mutex g_mutex;


void foo() {
{
scoped_lock lock(g_mutex);

do_locked_stuff();

{
scoped_unlock unlock(g_mutex);

do_unlocked_stuff();

} // mutex.lock();

do_more_locked_stuff();

} // mutex.unlock();
}


[...]

== 10 of 21 ==
Date: Mon, Dec 29 2008 5:31 pm
From: David Schwartz


On Dec 29, 3:44 pm, "Chris M. Thomasson" <n...@spam.invalid> wrote:

> > > It strikes me as messy and confusing that a lock object can continue
> > > to exist without actually holding a lock.

> > Then what do you do when some code uses a scoped lock holder and you
> > need to run a small piece of code inside that scope without holding
> > the lock? Do you re-architect the code completely?

> You can use a scoped unlock RAII helper object:
>
> static mutex g_mutex;
>
> void foo() {
>   {
>     scoped_lock lock(g_mutex);
>
>     do_locked_stuff();
>
>     {
>       scoped_unlock unlock(g_mutex);
>
>       do_unlocked_stuff();
>
>     } // mutex.lock();
>
>     do_more_locked_stuff();
>
>   } // mutex.unlock();
>
> }

I actually like that. I'm not sure whether that violates the rule that
the scoped lock holder should hold the lock during its existence. But
honestly, I don't see why that's any better that calling an 'unlock'
and a 'relock' method on the scoped lock holder. I guess the symmetry
is nice.

DS


== 11 of 21 ==
Date: Mon, Dec 29 2008 5:33 pm
From: David Schwartz


On Dec 29, 12:46 pm, Daniel James <wastebas...@nospam.aaisp.org>
wrote:

> In the little hypothetical example I gave the lock can ONLY be released
> by the UNLOCK statement matching the LOCK statement, so the situation
> cannot arise. That is: my hypothetical MUTEX type is a language
> primitive for use in LOCK and UNLOCK blocks, and doesn't provide any
> methods by which it can be manipulated directly.

Okay, but that's makes it a fairly useless toy.

> That's a limitation, of course, a MUTEX can only be locked and unlocked
> within a lexical scope -- but it was just an example.

Right, and then if something changes where the toy is no longer
adequate, you have to rearchitect everything. Yuck. Why would anyone
even consider such an abomination?

> > But that's exactly the wrong approach. It is absolutely vital that you
> > know precisely where locks are acquired and where they are released.
> > Every time you add or remove code from a function, it is vital that
> > you know what the lock context in which that code is (or was going to)
> > run in is.

> Using scoped locks doesn't introduce any uncertainty ... it just
> provides a syntactically simple and convenient mechanism for managing
> the scope of a lock by tying it to a lexical scope. That's not always
> what you want to do, but it very often is so the technique is
> convenient.

But it does introduce uncertainty. 50 lines later, where you see '}',
you may be uncertain that it unlocks a mutex. Or, worse, if you see a
'return' and decide to add some code before it, you may well be
uncertain what locks you hold at that point in the code.

The point is, because code must know what mutexes it holds, having '}'
possibly unlock mutexes is unacceptable except in the case where the
'}' is very close to the '{'. But in that case, the benefits of the
scoped lock holder don't emerge anyway. (Because there can't be many
exit points in a small block of code.)

DS


== 12 of 21 ==
Date: Mon, Dec 29 2008 5:38 pm
From: David Schwartz


On Dec 29, 12:46 pm, Daniel James <wastebas...@nospam.aaisp.org>
wrote:

> Let's not insult programmers by suggesting that they might forget ...
> let's say that there is no possibility of the lock not being released on
> exit from the scope to which the lock is tied, regardless of the means
> by which that scope is exited, and that no code needs to be written by
> the programmer to ensure this.

That's a load of bull. Any time the lock is going to be released, the
programmer must make sure all invariants are restored right at that
point. Hiding where this happens just increases the chance that the
lock will be released without the invariants being restored, which is
fatal.

The programmer must know every single point that does or might release
a lock, because it is precisely at that point that he must make sure
the invariants are restored. If they were already restored, the lock
would already be released. So it's always right where the unlock
occurs.

The only time this is an advantage is when you write your code
inefficiently such that the invariants are restored but you continue
to hold the lock. This is precisely the type of bad code that is the
reason I strongly urge beginners *not* to use scoped locks.

They must make a conscious effort at every possible mutex unlock point
to put the unlock in the right place and ensure the invariants are
restored right at that point.

> That's quite a powerful guarantee -- and if it's what you want to happen
> then it's very useful.

There is no such guarantee. Where is the guarantee that the invariants
will be restored and hence it is safe to release the mutex. A
guarantee that mutex will be unlocked even though the invariants have
not been restored is not useful.

> > First, I've already explained that you can get the same safety with
> > explicit unlock functions.

> Of course you can ... but you have to write them every time, which is a
> chore and leads to untidy unreadable code even when done by experienced
> programmers who don't forget.

No, it doesn't.

> > Second, blocked-scoped locks add a variety of new possible errors ...

> No they don't. Not to any errors you wouldn't get by writing equivalent
> code with explicit unlocks.

Yes, they do. And no, you don't get those errors by writing explicit
unlocks because you *know* when you call 'unlock' that you must
restore the invariants. And when someone else inspects the code, they
know all the points where they must verify that the invariants are
restored. And when someone else modifies the code, they must make a
conscious choice to put new code before or after the unlock.

DS


== 13 of 21 ==
Date: Mon, Dec 29 2008 5:39 pm
From: David Schwartz


On Dec 29, 12:46 pm, Daniel James <wastebas...@nospam.aaisp.org>
wrote:

> What DS proposes has, AIUI, another problem, which is that the code
> paths in the debug and release versions are sufficiently different that
> the release-mode code is never tested in debug builds, which sounds like
> another great opportunity for bugs to creep in.

The only difference is that there is an 'assert' in the debug path.

DS


== 14 of 21 ==
Date: Mon, Dec 29 2008 6:17 pm
From: David Barrett-Lennard


On Dec 30, 2:18 am, David Schwartz <dav...@webmaster.com> wrote:
> On Dec 29, 5:00 am, David Barrett-Lennard <davi...@iinet.net.au>
> wrote:
>
> > It strikes me as messy and confusing that a lock object can continue
> > to exist without actually holding a lock.
>
> Then what do you do when some code uses a scoped lock holder and you
> need to run a small piece of code inside that scope without holding
> the lock? Do you re-architect the code completely?

In my experience that has never been a problem. Usually the code
protected by the lock is performing a well defined "atomic" task which
cannot sensibly be divided into smaller pieces with separate locks.
This typically happens in the original design because I try to hold
locks for as short a time as possible in the first place.

If you're changing your mind about the scope of the lock all the time
then I would suggest you *do* need to re-architect the code
completely :)


> > The following removes a line of code and makes the scope of the lock
> > visually obvious:
>
> > int foo()
> > {
> > {
> > ScopedLock lock;
> > if(error1) return 1;
> > do_something();
> > if(error2) return 2;
> > do_something_else();
> > if(error3) return 3;
> > }
> > do_some_unlocked_stuff();
> > return 0;
>
> > }
>
> Sure, in this case that's easy to do. But what if after the
> 'do_some_unlocked_stuff' you need to re-acquire the lock and
> manipulate some variables that only exist inside the scope of the
> scoped lock?

Easy. Move those variable declarations so they are visible.


> Consider:
>
> ScopedLock lock;
> if(x)
> {
> if(y)
> {
> foo();
> if(!CanDoItNow())
> {
> // here we need to release the lock and wait until we can do it
> }
> bar();
> }
>
> }
>
> Now what?

This probably wouldn't happen in my design in the first place.

In the original design I tend to focus attention on the "atomic"
functions that are required. Perhaps one of them is this...

bool tryfoo()
{
ScopedLock lock;
if(x && y)
{
foo();
return true;
}
return false;
}


> > Isn't it better for it to always be the case that the lock protects
> > all code that falls within the scope of the ScopedLock?
>
> Sure, if you luck out and that design model works for you. But what if
> it seemed like that would be fine at first, but later evolution of the
> code creates a situation where deep inside the scoped lock, the lock
> must be dropped? Then what? Do you re-architect the whole thing?

I occasionally re-architect parts of my code if I realise there is a
better, simpler alternative.


> > Why make
> > something declarative into something procedural? Why make a construct
> > with a trivial mathematical definition into something more complex,
> > invites inconsistent usage and hurts readability? Why defeat the
> > whole purpose which was to create a trivial association between a
> > lexical scope and a lock?
>
> Because that trivial association is rarely right.

Perhaps in your designs :)


> It's only used
> because it's so trivial. It's a very bad habit to get into, because
> you wind up changing everything else to keep it fitting.


You don't change things, instead you design things in a good way. In
a decent design it should *always* be true that the work done while
holding a lock is semantically meaningful, and making it explicit in
your code (perhaps as a separate function or an independent indented
block) is an excellent approach.

You almost sound like you perform decomposition without regard for
threading and locking. i.e. there is a subsequent phase where you
insert the required lock and unlock statements! Otherwise, I just
can't understand why you think the scope of locks is liable to change
so dramatically, or to be at odds with lexical scopes.

== 15 of 21 ==
Date: Mon, Dec 29 2008 7:01 pm
From: David Barrett-Lennard


On Dec 30, 8:44 am, "Chris M. Thomasson" <n...@spam.invalid> wrote:
> "David Schwartz" <dav...@webmaster.com> wrote in message
>
> news:3d7403b4-4b25-4850-97c3-b572b19d1873@d42g2000prb.googlegroups.com...
> On Dec 29, 5:00 am, David Barrett-Lennard <davi...@iinet.net.au>
> wrote:
>
> > > It strikes me as messy and confusing that a lock object can continue
> > > to exist without actually holding a lock.
> > Then what do you do when some code uses a scoped lock holder and you
> > need to run a small piece of code inside that scope without holding
> > the lock? Do you re-architect the code completely?
>
> You can use a scoped unlock RAII helper object:
>
> static mutex g_mutex;
>
> void foo() {
> {
> scoped_lock lock(g_mutex);
>
> do_locked_stuff();
>
> {
> scoped_unlock unlock(g_mutex);
>
> do_unlocked_stuff();
>
> } // mutex.lock();
>
> do_more_locked_stuff();
>
> } // mutex.unlock();
>
> }
>
> [...]


This doesn't stop the fact that a lock object can exist yet a lock is
not actually held. Why are (procedural) programmers so quick to
defeat simple declarative truths?

One example of the pursuant damage: a scoped_lock no longer identifies
an atomic unit of work on the protected state.

I would go as far as saying that a scoped_unlock class only exists to
create a lie in its now ugly brother. It is a hack! It is an example
of what you get when you only think procedurally.

It has added complexity and inconsistency and forces one to more
carefully read the code to understand where locks are actually in
place.

Why not instead do this?

void foo()
{
{
scoped_lock lock(g_mutex);
do_locked_stuff();
}

do_unlocked_stuff();

{
scoped_lock lock(g_mutex);
do_more_locked_stuff();
}
}


This code is now logically coherent in its decomposition. The parts
are semantically meaningful. The parts are better decoupled. It is
far more likely that you parts are reusable in a different context.


== 16 of 21 ==
Date: Mon, Dec 29 2008 7:52 pm
From: JC


On Dec 29, 10:01 pm, David Barrett-Lennard <davi...@iinet.net.au>
wrote:
> On Dec 30, 8:44 am, "Chris M. Thomasson" <n...@spam.invalid> wrote:
>
>
>
> > "David Schwartz" <dav...@webmaster.com> wrote in message
>
> >news:3d7403b4-4b25-4850-97c3-b572b19d1873@d42g2000prb.googlegroups.com...
> > On Dec 29, 5:00 am, David Barrett-Lennard <davi...@iinet.net.au>
> > wrote:
>
> > > > It strikes me as messy and confusing that a lock object can continue
> > > > to exist without actually holding a lock.
> > > Then what do you do when some code uses a scoped lock holder and you
> > > need to run a small piece of code inside that scope without holding
> > > the lock? Do you re-architect the code completely?
>
> > You can use a scoped unlock RAII helper object:
>
> > static mutex g_mutex;
>
> > void foo() {
> >   {
> >     scoped_lock lock(g_mutex);
>
> >     do_locked_stuff();
>
> >     {
> >       scoped_unlock unlock(g_mutex);
>
> >       do_unlocked_stuff();
>
> >     } // mutex.lock();
>
> >     do_more_locked_stuff();
>
> >   } // mutex.unlock();
>
> > }
>
> > [...]
>
> This doesn't stop the fact that a lock object can exist yet a lock is
> not actually held.  Why are (procedural) programmers so quick to
> defeat simple declarative truths?
>
> One example of the pursuant damage: a scoped_lock no longer identifies
> an atomic unit of work on the protected state.


If you start off with a rule that "locks are held when a scoped_lock
is in scope" then your rule may not be useful and you should change
it. Note that the name "scoped_lock" is not a great name, perhaps
renaming the object to something like "auto_lock" will make it more
palatable. In general, I've noticed that when programming, if one
finds that something is philosophically incorrect, it is frequently
one's philosophy that is incorrect.


> I would go as far as saying that a scoped_unlock class only exists to
> create a lie in its now ugly brother.  It is a hack!  It is an example
> of what you get when you only think procedurally.


The code is semantically equivalent to your approach, which is also
not a hack. Both are still exception safe, still 100% effective, clear
to read, and present the same solution to the same problem (locking
portions of code with automatic unlocking).


> It has added complexity and inconsistency and forces one to more
> carefully read the code to understand where locks are actually in
> place.


It's not complex, and it is well-defined, consistent, and convenient.
I personally do not have to scrutinize that code (or your code)
carefully to know exactly where locks are and are not held, and would
argue that it's more your unfamiliarity with the technique that causes
the confusion rather than a flaw in the technique itself. In that
case, becoming familiar with the technique is the easiest solution (it
is a simple technique to learn), the second easiest solution is a
simple one-word comment in the code. Neither solution is a major
setback.


> Why not instead do this?
>
> void foo()
> {
>   {
>     scoped_lock lock(g_mutex);
>     do_locked_stuff();
>   }
>
>   do_unlocked_stuff();
>
>   {
>     scoped_lock lock(g_mutex);
>     do_more_locked_stuff();
>   }
>
> }
>
> This code is now logically coherent in its decomposition. The parts
> are semantically meaningful.  The parts are better decoupled.  It is
> far more likely that you parts are reusable in a different context.


You are both wrong and sometimes right. You are right in that it is
sometimes logically coherent and that the parts are sometimes
semantically meaningful -- however, this all depends on the nature of
the code. With Chris Thomasson's approach, deleting the unlocked
portion of the code also removes the unlock itself, with your approach
it does not. Using a scoped_unlock gives a completely isolated bit of
code that can be removed without affecting the code around it. Of
course, that also may not always be appropriate, but his approach is
*also* sometimes logically coherent and sometimes semantically
meaningful.


However, you are wrong in that it is not necessarily the most
convenient answer to your specific question, which was:


> Then what do you do when some code uses a scoped lock holder and you
> need to run a small piece of code inside that scope without holding
> the lock? Do you re-architect the code completely?


Your approach reorganized the existing code. Chris Thomasson's
approach did not. Both your code and Chris's code are clear,
meaningful, and effective, but in addition, Chris's solution is a more
convenient solution.

While both forms are just as effective, and equivalent, *if* the
problem is to conveniently add an unlocked portion to existing code,
*then* Chris's solution is better.

All that said, personally I don't think I would use scoped_unlock
either; I would either stick to something closer to your form, or I
may just add explicit lock / unlock to the scoped_lock instead (again
renaming it to something more appropriate):


void function () {

lock_holder holder(g_mutex);
do_locked_stuff();

holder.release_lock();
do_unlocked_stuff();

holder.acquire_lock();
do_more_locked_stuff();

}


Or auto_lock, or scoped_lock, or lock/unlock or acquire/release, the
choice is irrelevant. Defining a rule that says "acquire_lock when
already acquired is a no-op, and the same for release_lock" could
allow you to further group your code into logical, isolated parts,
while still giving you the features of a scope-based automatic
release. Whatever.

That approach, your approach, and Chris Thomasson's approach are all
semantically equivalent, all are easy to read, all are convenient to
deal with, none deserve to be called a "hack".

Also note that the topic is now turning to how exactly to express an
idea in C++. A somewhat more language-neutral description of the above
would be:


scope {
lock g_mutex
do locked stuff
unlock g_mutex
do unlocked stuff
lock g_mutex
do locked stuff
(note: if we leave scope for *any* reason while g_mutex is locked,
it is automatically unlocked).
}


In that case, anything that expresses that in any programming language
is equally valid, and it does not matter which of a dozen synonymous
ways in a given language that it is expressed, and it is certainly not
meaningful to discuss what one's favorite way of expressing it with C+
+ is.


Jason


== 17 of 21 ==
Date: Mon, Dec 29 2008 8:00 pm
From: JC


On Dec 29, 10:52 pm, JC <jason.cipri...@gmail.com> wrote:
> However, you are wrong in that it is not necessarily the most
> convenient answer to your specific question, which was:

I'm sorry, that should actually read "... to David Schwartz's specific
question ...".

Jason


== 18 of 21 ==
Date: Mon, Dec 29 2008 8:02 pm
From: David Barrett-Lennard


On Dec 30, 12:31 am, David Schwartz <dav...@webmaster.com> wrote:

> Sometimes there can be, but the problem is that if you design with the
> assumption that there can be, and later it turns out there can't be,
> you may have to redesign large chunks of code.

I don't buy that.


> This is best shown with
> recursive locks.

The example you gave doesn't show anything because the code snippet
only has the ultimate purpose of acquiring a lock, not of acquiring a
lock doing something specific then releasing it, so it is obvious from
the outset that a scoped lock is inapplicable.


> Consider:
>
> Object.Lock();
> while(Object.IsNotReady())
> {
> waiter=Object.GetWaiter();
> Object.Unlock();
> waiter->WaitFor();
> delete waiter;}
>
> // ...
>
> Now, if "Object.Unlock()' doesn't actually release the lock, you are
> screwed here.

Is this code missing a call to Object.Lock() at the end of the body of
the while statement?

I think your code can be put into a function as follows:

void AcquireLock(X& Object)
{
Object.Lock();
while(Object.IsNotReady())
{
waiter = Object.GetWaiter();
Object.Unlock();
waiter->WaitFor();
delete waiter;
Object.Lock();
}
}

It is now clear how this could be used to acquire the initial lock for
an instance of a lexically scoped lock.


== 19 of 21 ==
Date: Mon, Dec 29 2008 8:18 pm
From: JC


On Dec 29, 8:00 am, David Barrett-Lennard <davi...@iinet.net.au>
wrote:
> On Dec 29, 8:05 pm, Juha Nieminen <nos...@thanks.invalid> wrote:
>
>
>
> > Giancarlo Niccolai wrote:
> > > 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.
>
> >   You seem to assume that scoped locks cannot be explicitly released
> > (iow. they can only be released by them going out of scope).
>
> >   If you want to be able to release a lock early, then just add a member
> > function for that purpose. I don't see the problem.
>
> >   What the automatic release does is that it makes your life easier. For
> > example:
>
> > int foo()
> > {
> >     ScopedLock lock;
> >     if(error1) return 1;
> >     do_something();
> >     if(error2) return 2;
> >     do_something_else();
> >     if(error3) return 3;
> >     lock.release();
> >     do_some_unlocked_stuff();
> >     return 0;
> > }
>
> It strikes me as messy and confusing that a lock object can continue
> to exist without actually holding a lock.


In many situations, the rule that "if a lock object exists, it holds a
lock" is unnecessary and overly limiting. The existence of that rule
causes you to feel uncomfortable when you have to work around the rule
after finding that it is not appropriate. It is better to not define
that rule in the first place.


> <generic-rant>
>
> I think many programmers tend to jump in and add flexibility to their
> classes at a moments notice without realising the damage to their
> design.


While you have a point in general, that does not apply in the context
of automatic lock holders. There is a difference between "adding
flexibility at a moments notice" and "designing your class to be
flexible to begin with".

If you designed an automatic lock holder using your rule that "if the
object exists, it holds a lock", then your design is actually only
useful for the most trivial situations. Because of that rule and the
design based around it, you are *forced* to "jump in and add
flexibility" when you realize the mistake.

On the other hand, if you design an automatic lock holder correctly in
the first place, it is neither messy nor confusing. For example, in C+
+:


// this object provides a means to acquire and release a
// lock, automatically releasing an acquired lock when
// this goes out of scope.
class lock_holder {

public:

// constructor will acquire the mutex lock
lock_holder (mutex &m);

// destructor will release the mutex lock if acquired
~lock_holder ();

// release the lock, no-op if not acquired
void release_lock ();

// acquire the lock, no-op if already acquired
void acquire_lock ();

};


There, it was done correctly from the start, and is useful in a far
wider range of scenarios without being messy or confusing.


> If one is after clarity then for a start one should strive for the
> principle that one can think of a relatively simple name of a class
> that accurately tells exactly how it behaves.  I don't get that
> feeling at all with the above ScopedLock class.


ScopedLock is a poor name for it.


> I don't like the release method. I don't believe it should exist. If
> anything in this thread could be called "evil" then I think the
> release method is a good candidate.


This is because it violates the broken rule that you defined earlier.
The reason you defined said broken rule may very well be because
either A) "ScopedLock" was a poor choice for the name, or B) a pure
"scope-based" lock was the wrong tool for the job. A and B are mostly
equivalent, the difference is inconsequential and is only how you
prefer to look at it.


> </generic-rant>
>
> The following removes a line of code and makes the scope of the lock
> visually obvious:
>
> int foo()
> {
>     {
>         ScopedLock lock;
>         if(error1) return 1;
>         do_something();
>         if(error2) return 2;
>         do_something_else();
>         if(error3) return 3;
>     }
>     do_some_unlocked_stuff();
>     return 0;
>
> }
>
> Isn't it better for it to always be the case that the lock protects
> all code that falls within the scope of the ScopedLock?   Why make
> something declarative into something procedural?  Why make a construct
> with a trivial mathematical definition into something more complex,
> invites inconsistent usage and hurts readability?  Why defeat the
> whole purpose which was to create a trivial association between a
> lexical scope and a lock?


It does not matter; see my response to you else-thread. In your case,
a "scoped" lock is not what you want. In any case, this:


int function () {
{
SomeAutomaticLockHolder lock;
if(error1) return 1;
do_something();
if(error2) return 2;
do_something_else();
if(error3) return 3;
}
do_some_unlocked_stuff();
return 0;
}


And this:


int function () {
SomeAutomaticLockHolder lock;
if(error1) return 1;
do_something();
if(error2) return 2;
do_something_else();
if(error3) return 3;
lock.release();
do_some_unlocked_stuff();
return 0;
}


Are semantically identical. Which one you use is irrelevant and is
only a matter of whether or not you've provided an explicit release
with your lock holder implementation -- and whether or not you've done
that has no bearing on the usefulness of automatic lock holders in
general, in any similar language.


Jason


== 20 of 21 ==
Date: Mon, Dec 29 2008 9:17 pm
From: David Schwartz


On Dec 29, 8:02 pm, David Barrett-Lennard <davi...@iinet.net.au>
wrote:

> I think your code can be put into a function as follows:
>
> void AcquireLock(X& Object)
> {
>     Object.Lock();
>     while(Object.IsNotReady())
>     {
>         waiter = Object.GetWaiter();
>         Object.Unlock();
>         waiter->WaitFor();
>         delete waiter;
>         Object.Lock();
>     }
>
> }
>
> It is now clear how this could be used to acquire the initial lock for
> an instance of a lexically scoped lock.

Except this will fail if the lock is already held. To call this
function, you must 100% for certain know whether or not you hold the
lock. Now, a very common situation is that you are in a function that
you know can only be called with the lock not held and only returns
with the lock not held. If there are destructors that release the
lock, how do you reliably figure out whether or not you held the lock
when you contemplate calling this code?

DS


== 21 of 21 ==
Date: Mon, Dec 29 2008 10:03 pm
From: David Barrett-Lennard


On Dec 30, 12:52 pm, JC <jason.cipri...@gmail.com> wrote:
> On Dec 29, 10:01 pm, David Barrett-Lennard <davi...@iinet.net.au>
> wrote:
>
>
>
>
>
> > On Dec 30, 8:44 am, "Chris M. Thomasson" <n...@spam.invalid> wrote:
>
> > > "David Schwartz" <dav...@webmaster.com> wrote in message
>
> > >news:3d7403b4-4b25-4850-97c3-b572b19d1873@d42g2000prb.googlegroups.com...
> > > On Dec 29, 5:00 am, David Barrett-Lennard <davi...@iinet.net.au>
> > > wrote:
>
> > > > > It strikes me as messy and confusing that a lock object can continue
> > > > > to exist without actually holding a lock.
> > > > Then what do you do when some code uses a scoped lock holder and you
> > > > need to run a small piece of code inside that scope without holding
> > > > the lock? Do you re-architect the code completely?
>
> > > You can use a scoped unlock RAII helper object:
>
> > > static mutex g_mutex;
>
> > > void foo() {
> > > {
> > > scoped_lock lock(g_mutex);
>
> > > do_locked_stuff();
>
> > > {
> > > scoped_unlock unlock(g_mutex);
>
> > > do_unlocked_stuff();
>
> > > } // mutex.lock();
>
> > > do_more_locked_stuff();
>
> > > } // mutex.unlock();
>
> > > }
>
> > > [...]
>
> > This doesn't stop the fact that a lock object can exist yet a lock is
> > not actually held. Why are (procedural) programmers so quick to
> > defeat simple declarative truths?
>
> > One example of the pursuant damage: a scoped_lock no longer identifies
> > an atomic unit of work on the protected state.
>
> If you start off with a rule that "locks are held when a scoped_lock
> is in scope" then your rule may not be useful and you should change
> it.

But the rule *is* useful. It leads to the trivial correspondence
between lexical scopes and locks. If you mess with the rule then you
mess with the trivial correspondence.


> Note that the name "scoped_lock" is not a great name, perhaps
> renaming the object to something like "auto_lock" will make it more
> palatable.

Perhaps you would find auto_unlock even more palatable.

It appears you don't appreciate the declarative nature of a
scoped_lock. I like to read it as a declarative statement of
incontrovertible fact that the entire block of code shall always be
protected by a lock, and no code anywhere can do anything to change
that.


> In general, I've noticed that when programming, if one
> finds that something is philosophically incorrect, it is frequently
> one's philosophy that is incorrect.

So your philosophy must be to avoid having a philosophy :)


> > I would go as far as saying that a scoped_unlock class only exists to
> > create a lie in its now ugly brother. It is a hack! It is an example
> > of what you get when you only think procedurally.
>
> The code is semantically equivalent to your approach, which is also
> not a hack. Both are still exception safe, still 100% effective, clear
> to read, and present the same solution to the same problem (locking
> portions of code with automatic unlocking).

Oh yes at some level they are equivalent. However one maps much more
directly to simple logic. If you were to formalise the meaning of the
code in logic you would find my version to be much simpler.


> > It has added complexity and inconsistency and forces one to more
> > carefully read the code to understand where locks are actually in
> > place.
>
> It's not complex, and it is well-defined, consistent, and convenient.


Ok, have you considered arbitrary nestings of try-catch blocks,
scoped_locks and unscoped_locks. What are the rules? Eg is it
permissible to use an unscoped_lock at the outermost level? If not
does it cause an assertion in debug builds? Is it permissible for a
scoped_lock to be nested within an unscoped_lock? Are there any
surprises when an exception is thrown and the stack unwinds? What are
the efficiency repercussions? Are counters or bools required to keep
track of what's happening? If so where is this state stored? In
thread local storage?

What's an appropriate methodology to achieve code reuse when using
these scoped_locks and unscoped_locks? Is it based on somewhat
blindly factoring otherwise repeated code into functions, or are there
some rules of thumb that help? If one is following the principle of
design by contract, how does one carefully state contracts associated
with making used of these scoped_locks and unscoped_locks?

> I personally do not have to scrutinize that code (or your code)
> carefully to know exactly where locks are and are not held

I would hope so, the example was simple.


> , and would
> argue that it's more your unfamiliarity with the technique that causes
> the confusion rather than a flaw in the technique itself.

I had no trouble understanding the technique. My problem is with the
damage it would do if I used it in my own production code.


> In that
> case, becoming familiar with the technique is the easiest solution (it
> is a simple technique to learn), the second easiest solution is a
> simple one-word comment in the code. Neither solution is a major
> setback.

Part of the damage is collateral. It badly affects the way you think
and decompose problems.


> > Why not instead do this?
>
> > void foo()
> > {
> > {
> > scoped_lock lock(g_mutex);
> > do_locked_stuff();
> > }
>
> > do_unlocked_stuff();
>
> > {
> > scoped_lock lock(g_mutex);
> > do_more_locked_stuff();
> > }
>
> > }
>
> > This code is now logically coherent in its decomposition. The parts
> > are semantically meaningful. The parts are better decoupled. It is
> > far more likely that you parts are reusable in a different context.
>
> You are both wrong and sometimes right. You are right in that it is
> sometimes logically coherent and that the parts are sometimes
> semantically meaningful -- however, this all depends on the nature of
> the code.

I disagree. Doing stuff in a separate lock generally implies it is a
semantically meaningful chunk of work.

However my point was really that Chris's approach is incoherent in the
sense that on its face a scoped_lock appears to state that a lock is
held for the scope of the instance, and yet that is not actually the
case. As you said yourself, it needs a "better" (verbose) name,
associated with its much more complex semantic - including its curious
interaction with an unscoped_lock.

Let C = SEQ(C1,C2,C3) represent the result of executing C1 then C2
then C3. Chris's approach doesn't actually achieve that very powerful
and useful form of decomposition. There is increased coupling, and
less opportunity for code reuse.


> With Chris Thomasson's approach, deleting the unlocked
> portion of the code also removes the unlock itself, with your approach
> it does not. Using a scoped_unlock gives a completely isolated bit of
> code that can be removed without affecting the code around it. Of
> course, that also may not always be appropriate, but his approach is
> *also* sometimes logically coherent and sometimes semantically
> meaningful.
>
> However, you are wrong in that it is not necessarily the most
> convenient answer to your specific question, which was:
>
> > Then what do you do when some code uses a scoped lock holder and you
> > need to run a small piece of code inside that scope without holding
> > the lock? Do you re-architect the code completely?
>
> Your approach reorganized the existing code. Chris Thomasson's
> approach did not. Both your code and Chris's code are clear,
> meaningful, and effective, but in addition, Chris's solution is a more
> convenient solution.

Are you certain that Chris's solution involves less typing? In my
favourite editor adding braces and changing indentation is very fast.
Duplicating the line that gets the lock only requires copy and paste.
With Chris's solution I might want his comments to help the (poor)
reader understand what's happening.


> While both forms are just as effective, and equivalent, *if* the
> problem is to conveniently add an unlocked portion to existing code,
> *then* Chris's solution is better.

In the sense of a bandaid on a festering sore :)


> All that said, personally I don't think I would use scoped_unlock
> either; I would either stick to something closer to your form, or I
> may just add explicit lock / unlock to the scoped_lock instead (again
> renaming it to something more appropriate):
>
> void function () {
>
> lock_holder holder(g_mutex);
> do_locked_stuff();
>
> holder.release_lock();
> do_unlocked_stuff();
>
> holder.acquire_lock();
> do_more_locked_stuff();
>
> }


YUK!


> Or auto_lock, or scoped_lock, or lock/unlock or acquire/release, the
> choice is irrelevant. Defining a rule that says "acquire_lock when
> already acquired is a no-op, and the same for release_lock" could
> allow you to further group your code into logical, isolated parts,
> while still giving you the features of a scope-based automatic
> release. Whatever.

The best way to group your code into logical, isolated parts is to
design that way from the start and to recognise forms of decomposition
that have a simple basis in logic.

> That approach, your approach, and Chris Thomasson's approach are all
> semantically equivalent, all are easy to read, all are convenient to
> deal with, none deserve to be called a "hack".

I set high standards for myself and others :)


> Also note that the topic is now turning to how exactly to express an
> idea in C++. A somewhat more language-neutral description of the above
> would be:
>
> scope {
> lock g_mutex
> do locked stuff
> unlock g_mutex
> do unlocked stuff
> lock g_mutex
> do locked stuff
> (note: if we leave scope for *any* reason while g_mutex is locked,
> it is automatically unlocked).
>
> }

What syntactic device implies (other than a comment) that g_mutex is
automatically unlocked? You're not suggesting that 'lock g_mutex'
does that are you? Sometimes the purpose of a function is to acquire
a lock on behalf of its caller.


==============================================================================
TOPIC: Core i7 CMPXCHG-performance
http://groups.google.com/group/comp.programming.threads/t/14d357b5d0c9bfb9?hl=en
==============================================================================

== 1 of 3 ==
Date: Mon, Dec 29 2008 1:42 pm
From: Elcaro Nosille


David Schwartz schrieb:

> You won't get official throughput/latency numbers from Intel anymore
> because there are so many possibly cases. However, I have heard from
> Intel sources that LOCK CMPXCHG will be 40% less expensive (in terms
> of clock cycles) on Core i7 than it is on Core 2. The same source that
> LOCK CMPXCHG is 65% less expensive on Core 2 than it is on late-model
> P4s.

I've written a little program that repeatedly does LOCK CMPXCHG of
the same value (zero exchanged by zero). On a Core i7 of a friend,
each LOCK CMPXCHG takes about 22 cycles; on my QX6850 (3GHz, 65nm
Quadcore) it takes about 25 cycles. Not a big difference.


== 2 of 3 ==
Date: Mon, Dec 29 2008 2:32 pm
From: Elcaro Nosille


Elcaro Nosille schrieb:

> I've written a little program that repeatedly does LOCK CMPXCHG of
> the same value (zero exchanged by zero). On a Core i7 of a friend,
> each LOCK CMPXCHG takes about 22 cycles; on my QX6850 (3GHz, 65nm
> Quadcore) it takes about 25 cycles. Not a big difference.

I've changed the code to alternately exchange a (DWORD)1 and
(DWORD)0; same throughput.


== 3 of 3 ==
Date: Mon, Dec 29 2008 3:12 pm
From: David Schwartz


On Dec 29, 1:42 pm, Elcaro Nosille <ElcaroNosi...@akapost.com> wrote:

> David Schwartz schrieb:

> > You won't get official throughput/latency numbers from Intel anymore
> > because there are so many possibly cases. However, I have heard from
> > Intel sources that LOCK CMPXCHG will be 40% less expensive (in terms
> > of clock cycles) on Core i7 than it is on Core 2. The same source that
> > LOCK CMPXCHG is 65% less expensive on Core 2 than it is on late-model
> > P4s.

> I've written a little program that repeatedly does LOCK CMPXCHG of
> the same value (zero exchanged by zero). On a Core i7 of a friend,
> each LOCK CMPXCHG takes about 22 cycles; on my QX6850 (3GHz, 65nm
> Quadcore) it takes about 25 cycles. Not a big difference.

That's about 12%, which is a far cry from the claimed 40%.

However, testing in a tight loop is not always fair. For one thing,
you're probably only testing the case where the cache line is already
in the L1 cache. That may or may not be the case you care most about.
For another thing, you may not be testing the effect of the locked
instruction on the pipeline status.

It's also possible that your test is accurate.

DS

==============================================================================
TOPIC: problem with pthread_cancel
http://groups.google.com/group/comp.programming.threads/t/e3ae6dbb7de05a21?hl=en
==============================================================================

== 1 of 1 ==
Date: Mon, Dec 29 2008 9:28 pm
From: vamsi


Hi,
I have an application where it creates 32 threads. Later in the
logic, it has to kill some 10 threads. All the 10 threads does a
recvfrom on a unique socket in a while loop. To cancel the thread,
pthread_cancel API is used to send a cancel signal to the target
thread from the main thread, from which threads got created. All the
threads are initialized as canceltype - asynchronous. The
pthread_cancel API sends signal to 8 threads succesfully, however for
the 9th thread the pthread_cancel fails with an error number 11
(EAGAIN).

I tried to send pthread_cancel in a while loop, stil the API fails
with the same error number. Does anyone have any suggestion, how to
proceed on this ?

Unfortunately, the exact code cannot be shared.

Regards,
Vamsi


==============================================================================

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: