Topic: Lockiing/threading proposal for discussion
Author: austern@well.com (Matt Austern)
Date: Fri, 7 May 2004 21:10:15 +0000 (UTC) Raw View
petebecker@acm.org (Pete Becker) writes:
> Ian McCulloch wrote:
> >
> > To me, this suggests library solutions (locking smart pointers etc), backed
> > up by the necessary language guarantees, rather than new language features.
> >
>
> To me it suggests just the opposite. Writing fast, robust multi-threaded
> code requires an application-level design for locking and unlocking.
> Library solutions will rarely have the right level of granularity. For
> example:
>
> void f()
> {
> std::cout << "Hello, ";
> std::cout << "world";
> }
An even simpler example:
v[2] = v[1];
Relying on vector<>::operator[] to do locking is inefficient and
incorrect. You need a lock around the whole assignment statement.
That's the example that convinced me that automatic locking was
a bad idea.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: nagle@animats.com (John Nagle)
Date: Mon, 3 May 2004 16:35:47 +0000 (UTC) Raw View
Ian McCulloch wrote:
> John Nagle wrote:
> There are many ideas floating around the net; An example from Andrei
> Alexandrescu's book floated near the top of google (from http:/
> www.awprofessional.com/articles/article.asp?p=31529&seqNum=13):
That's a good article. It illustrates some of the limitations
of library-based solutions. He points out the problem with
object copying that someone else pointed out earlier in this
thread. To copy an object, you need to lock both source and
destination. The same problem comes up for any function
that takes an argument which is a pointer or object of a
type to which it needs internal access. At that point,
you need to lock multiple objects.
Alexandrescu says that at that point, you must lock
all objects of the type, with a class-wide lock. But
that's rather restrictive. All you really need is to
lock all the objects involved in the operation. I
outlined how to do this in a previous posting. When
you pass a reference or pointer of a synchronized
object into a function which has access to the
private members of the object, the object must
be locked.
It's not dereferencing that's the trigger event.
It is passage into a context where the object's internals
are visible.
Of course, locking multiple objects simultaneously
creates a potential deadlock. The nicest solution
would be to have locking primitives that can lock
multiple locks as an an atomic operation. This is
efficiently implementable, although a bit tricky.
But it's worth implementing right in the language,
because then application programmers can't get it wrong.
John Nagle
Animats
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: nobody@example.com (Ian McCulloch)
Date: Fri, 30 Apr 2004 05:47:45 +0000 (UTC) Raw View
Pete Becker wrote:
> John Nagle wrote:
>>
>>
>> 1. Upon external entry to a public function member of any class with the
>> function member
>>
>> void synchronized_lock();
>>
>> the function "synchronized_lock()" shall be implicitly called by the
>> implementation.
>>
>> 2. Upon exit from a function where "synchronized_lock" was called,
>> the function
>>
>> void synchronized_unlock();
>>
>> shall be implicitly called by the implementation.
>
> Java's experience is relevant here. Java has synchronized classes, which
> lock an unlock on entry to and exit from every member function. Trouble
> is, if you want fast code, those are often the wrong places to do the
> locking. You get major speedups by postponing the locking, and doing any
> computations that don't involve the object's data before you lock. This
> shortens the time that the function holds the lock, which improves
> throughput; it also sometimes avoids having to lock at all, if you can
> determine that the arguments mean that there's nothing that has to be
> done to the object. So if you look at the Java library specifications,
> you see that synchronized classes and synchronized methods were used
> often in the early library versions, and far less often (even for
> similar classes) in later versions.
Ok. This is consistent with what I think is the general wisdom nowdays,
namely that it is better to think of locking associated with a specific
piece of data, rather than some code region. Monitors and so on confuse
the picture by associating locking with calls to methods, which may or may
not actually need to access shared data. A locking mechanism should be
data-driven not code-driven.
To me, this suggests library solutions (locking smart pointers etc), backed
up by the necessary language guarantees, rather than new language features.
Cheers,
Ian McCulloch
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: nagle@animats.com (John Nagle)
Date: Fri, 30 Apr 2004 16:39:08 +0000 (UTC) Raw View
Ian McCulloch wrote:
> Pete Becker wrote:
>>John Nagle wrote:
>>
>>>
>>>1. Upon external entry to a public function member of any class with the
>>>function member
...
> Ok. This is consistent with what I think is the general wisdom nowdays,
> namely that it is better to think of locking associated with a specific
> piece of data, rather than some code region. Monitors and so on confuse
> the picture by associating locking with calls to methods, which may or may
> not actually need to access shared data. A locking mechanism should be
> data-driven not code-driven.
The main C++ mechanism for collecting data items together
is "objects". It's clear how to use that to manage locking.
>
> To me, this suggests library solutions (locking smart pointers etc), backed
> up by the necessary language guarantees, rather than new language features.
This proposes to create another mechanism, orthogonal to objects, to
describe collections of data and impose access controls on them.
I'd like to see an example.
John Nagle
Animats
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: petebecker@acm.org (Pete Becker)
Date: Sat, 1 May 2004 00:31:05 +0000 (UTC) Raw View
Ian McCulloch wrote:
>
> To me, this suggests library solutions (locking smart pointers etc), backed
> up by the necessary language guarantees, rather than new language features.
>
To me it suggests just the opposite. Writing fast, robust multi-threaded
code requires an application-level design for locking and unlocking.
Library solutions will rarely have the right level of granularity. For
example:
void f()
{
std::cout << "Hello, ";
std::cout << "world";
}
If the stream inserter that's used here locks and unlocks a mutex to
protect the stream's state the stream won't get corrupted, but the
output won't necessarily be coherent. The function needs to lock for the
duration of both insertions, and if that's done, the locks in the
library are redundant.
--
Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: nobody@example.com (Ian McCulloch)
Date: Sat, 1 May 2004 16:50:32 +0000 (UTC) Raw View
John Nagle wrote:
> Ian McCulloch wrote:
>> Pete Becker wrote:
>>>John Nagle wrote:
>>>
>>>>
>>>>1. Upon external entry to a public function member of any class with
>>>>the function member
> ...
>> Ok. This is consistent with what I think is the general wisdom nowdays,
>> namely that it is better to think of locking associated with a specific
>> piece of data, rather than some code region. Monitors and so on confuse
>> the picture by associating locking with calls to methods, which may or
>> may
>> not actually need to access shared data. A locking mechanism should be
>> data-driven not code-driven.
>
> The main C++ mechanism for collecting data items together
> is "objects". It's clear how to use that to manage locking.
Is it?
Your proposal is to add monitors as a synchronization primitive to C++. It
is not clear (at least to me) why that is the one true way of writing
multithread programs.
>>
>> To me, this suggests library solutions (locking smart pointers etc),
>> backed up by the necessary language guarantees, rather than new language
>> features.
>
> This proposes to create another mechanism, orthogonal to objects, to
> describe collections of data and impose access controls on them.
> I'd like to see an example.
I don't know what you mean by 'orthogonal to objects', especially in the
context of a library solution.
There are many ideas floating around the net; An example from Andrei
Alexandrescu's book floated near the top of google (from http:/
www.awprofessional.com/articles/article.asp?p=31529&seqNum=13):
template <class T>
class LockingProxy
{
public:
LockingProxy(T* pObj) : pointee_ (pObj)
{ pointee_->Lock(); }
~LockingProxy()
{ pointee_->Unlock(); }
T* operator->() const
{ return pointee_; }
private:
LockingProxy& operator=(const LockingProxy&);
T* pointee_;
};
template <class T>
class SmartPtr
{
...
LockingProxy<T> operator->() const
{ return LockingProxy<T>(pointee_); }
private:
T* pointee_;
};
In practice, this has roughly the same affect as a true monitor class, with
some advantages (no problems with member functions calling other member
functions, easier to control the granularity of the locking, no change to
the language other than what is required to support some threading model)
and some disadvantages (uses pointer indirection, user of class is
responsible for locking, not owner [but that is a non-issue with, eg.
pImpl]).
Cheers,
Ian McCulloch
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: nobody@example.com (Ian McCulloch)
Date: Sat, 1 May 2004 22:28:16 +0000 (UTC) Raw View
Pete Becker wrote:
> Ian McCulloch wrote:
>>
>> To me, this suggests library solutions (locking smart pointers etc),
>> backed up by the necessary language guarantees, rather than new language
>> features.
>>
>
> To me it suggests just the opposite. Writing fast, robust multi-threaded
> code requires an application-level design for locking and unlocking.
> Library solutions will rarely have the right level of granularity. For
> example:
>
> void f()
> {
> std::cout << "Hello, ";
> std::cout << "world";
> }
>
> If the stream inserter that's used here locks and unlocks a mutex to
> protect the stream's state the stream won't get corrupted, but the
> output won't necessarily be coherent. The function needs to lock for the
> duration of both insertions, and if that's done, the locks in the
> library are redundant.
>
Sorry, I wasn't clear enough. This was my point too: if ostream was a
"synchronized class" as in (what I understand to be) the original proposal,
then it has exactly this problem.
What is needed is a mutex (or a readers-writers lock, or ...) that can be
applied to the object from the outside. What I meant by 'library solution'
was some kind of higher level object that can be used to apply
synchronization externally, such as Andrei
Alexandrescu's locking smart pointer. This can be done already, so I don't
see why we need synchronization primitives built into the langauge.
Cheers,
Ian McCulloch
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: petebecker@acm.org (Pete Becker)
Date: Sat, 1 May 2004 22:38:16 +0000 (UTC) Raw View
Ian McCulloch wrote:
>
> What is needed is a mutex (or a readers-writers lock, or ...) that can be
> applied to the object from the outside. What I meant by 'library solution'
> was some kind of higher level object that can be used to apply
> synchronization externally, such as Andrei
> Alexandrescu's locking smart pointer. This can be done already, so I don't
> see why we need synchronization primitives built into the langauge.
>
Yes, what you need is tools, not "solutions".
--
Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: nagle@animats.com (John Nagle)
Date: Tue, 27 Apr 2004 22:41:39 +0000 (UTC) Raw View
Daniel Pfeffer wrote:
> 1. What happens when you are accessing two synchronised objects in the same
> statement? Are they both locked until the next sequence point (requires
> compiler modification)? Are they each locked for the duration of the call to
> the method (produced implementation-defined behaviour)? Is only the target
> object locked?
>
> For example:
>
> SyncObject a,b;
>
> // details omitted
>
> a = b; // translates to a.operator =(b);
>
> Are both a and b locked until access is complete? Is only a locked?
>
> It appears to me that proper synchronisation requires much more work.
Good point. The problem here is that when an class member
function references a object of the same type (or a friend type),
it gets internal access to the object.
The general ability to access two locked objects simultaneously
is very much worth having. But when do you "enter" the object?
Clearly, you enter the object at the point its private members
become accesssable. So when you pass a reference or a pointer to
an object into a function which can access its private members,
from a function which cannot, locking must occur.
This is actually rather nice. Suppose you have two data
structures like lists or trees, and you want to move some item
from one to the other. So you have a function like
void Tree::moveItem(Tree& source, int itemkey);
Entry to this function locks both the instant object
("this"), and the argument object "source"). Which is
what you want.
Note the key concept here. Locking must occur at the
point private member access becomes allowed.
That was a good point, and a good justification for
dealing with the problem in the language.
>
>
> 2. If we limit ourselves to the list of requirements in the proposal, it
> appears to me that instead of requiring more complexity on the part of the
> compiler, these features could be implemented much more simply using classes
> such as the following:
See above.
John Nagle
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: nagle@animats.com (John Nagle)
Date: Wed, 28 Apr 2004 03:27:09 +0000 (UTC) Raw View
===================================== MODERATOR'S COMMENT:
Please don't top-post.
===================================== END OF MODERATOR'S COMMENT
C and C++ are too loose for that level of formalism to
ever be sound. That's why I got out of proof of correctness work.
You can put "design by contract" features in, but they're decorative.
You can't trust them.
Every time I propose something that involves tightening up
the language, people scream. Look back at my notes on
"strict C++", and what people griped about then.
John Nagle
Animats
Francis Glassborow wrote:
> In article <408d9767$0$436$afc38c87@news.optusnet.com.au>, Thorsten
> Ottosen <nesotto@cs.auc.dk> writes
>
>> "John Nagle" <nagle@animats.com> wrote in message
>> news:7_cjc.41335$rN4.20692@newssvr29.news.prodigy.com...
>>
>>> Garth A. Dickie wrote:
>>
>>
>>> I'd like to see design by contract for C++, but realistically,
>>> it's not going to happen.
>>
>>
>> well, at least something similar to it might happen:
>>
>> http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/2004/n1613.pdf
>
>
> But IIRC the evolution work group were not exactly enthusiastic and
> suggested that the paper's author needed to do a great deal more work
> both technical and in gaining support. If you are keen on something like
> the proposal I suggest you contact the author and offer him assistance.
>
>
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: nagle@animats.com (John Nagle)
Date: Wed, 28 Apr 2004 03:27:21 +0000 (UTC) Raw View
Note that I discussed lock optimization.
Locking should not normally require significant
overhead. If you're making a system call for every mutex, your
implementation is broken. QNX gets this right, using
a compare-and-swap instruction on x86 and similar code on
RISC processors when a lock is to be set. Only if the
thread blocks is a system call made.
This is another reason to handle locking at the compiler
level. Locking may require generation of special
instructions. That's a compiler job.
John Nagle
Pete Becker wrote:
> Java's experience is relevant here. Java has synchronized classes, which
> lock an unlock on entry to and exit from every member function. Trouble
> is, if you want fast code, those are often the wrong places to do the
> locking. You get major speedups by postponing the locking, and doing any
> computations that don't involve the object's data before you lock. This
> shortens the time that the function holds the lock, which improves
> throughput; it also sometimes avoids having to lock at all, if you can
> determine that the arguments mean that there's nothing that has to be
> done to the object. So if you look at the Java library specifications,
> you see that synchronized classes and synchronized methods were used
> often in the early library versions, and far less often (even for
> similar classes) in later versions.
>
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: nesotto@cs.auc.dk ("Thorsten Ottosen")
Date: Wed, 28 Apr 2004 03:45:35 +0000 (UTC) Raw View
"John Nagle" <nagle@animats.com> wrote in message
news:ggyjc.41828$aV5.3794@newssvr29.news.prodigy.com...
| C and C++ are too loose for that level of formalism to
| ever be sound. That's why I got out of proof of correctness work.
IMO , it it is far more than a tool for static analysis.
| You can put "design by contract" features in, but they're decorative.
| You can't trust them.
Can you give an example?
| Every time I propose something that involves tightening up
| the language, people scream. Look back at my notes on
| "strict C++", and what people griped about then.
Can you give a link to these notes?
Thanks
Thorsten
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: nagle@animats.com (John Nagle)
Date: Wed, 28 Apr 2004 05:18:25 +0000 (UTC) Raw View
One could make synchronized objects airtight, but I don't
think the C++ community would stand for it. It would
require disallowing public data members, for example.
We probably have to allow loopholes. But loopholes
should have the following properties:
1. They must be sins of commission, not sins of
omission. You shouldn't be able to create a
race condition by failing to write something.
That's the main argument for automatic locking.
2. Loopholes should be visible on simple static
examination. It's obvious if a class has public
data members. It's obvious if the address of a
data member is taken. Style-checking tools
or compilers can report both as warnings.
Those design criteria for loopholes are worth keeping
in mind when analyzing some other proposals, like
enumeration type safety.
John Nagle
Thorsten Ottosen wrote:
> "John Nagle" <nagle@animats.com> wrote in message
> news:408AB3C9.7090209@animats.com...
>
> | Calls to private methods do not invoke locking, because they do not
> | involve "entering" an object.
>
> what happens if a pointer to a private member is "stolen" and called from
> somewhere else?
>
> br
>
> Thorsten
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: nesotto@cs.auc.dk ("Thorsten Ottosen")
Date: Wed, 28 Apr 2004 06:19:27 +0000 (UTC) Raw View
"Francis Glassborow" <francis@robinton.demon.co.uk> wrote in message
news:tVxELvCnKjjAFwXu@robinton.demon.co.uk...
| In article <408d9767$0$436$afc38c87@news.optusnet.com.au>, Thorsten
| Ottosen <nesotto@cs.auc.dk> writes
| >"John Nagle" <nagle@animats.com> wrote in message
| >news:7_cjc.41335$rN4.20692@newssvr29.news.prodigy.com...
| >> Garth A. Dickie wrote:
| >
| >> I'd like to see design by contract for C++, but realistically,
| >> it's not going to happen.
| >
| >well, at least something similar to it might happen:
| >
| >http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/2004/n1613.pdf
|
| But IIRC the evolution work group were not exactly enthusiastic
not exactly my impression.
| and
| suggested that the paper's author needed to do a great deal more work
| both technical
| and in gaining support.
Sure, more work needs to be done.
| If you are keen on something like
| the proposal I suggest you contact the author and offer him assistance.
Since I am the author, I will stay in contact with the author and offer him
my help :-)
Anyway, I *will* appreciate comments/help from anyone interested.
br
Thorsten
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: petebecker@acm.org (Pete Becker)
Date: Thu, 29 Apr 2004 00:45:39 +0000 (UTC) Raw View
John Nagle wrote:
>
> Note that I discussed lock optimization.
>
Note that I said that in a language that has this feature, as people get
more experience with it they stop using it, hypothetical optimizations
notwithstanding.
--
Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: nagle@animats.com (John Nagle)
Date: Thu, 29 Apr 2004 06:02:22 +0000 (UTC) Raw View
===================================== MODERATOR'S COMMENT:
Please don't top-post.
===================================== END OF MODERATOR'S COMMENT
In Java, though, you can't have an object which contains
other objects as data members. You can only have a reference
to one. In C++, you can have objects as data members, which
allows finer-grained locking.
Also, Java doesn't have the "public" mechanism I
described, which creates an inner unlocked region inside
the locked region. That's essential if you want to
unlock and block inside an object.
John Nagle
Animats
Pete Becker wrote:
> John Nagle wrote:
>
>> Note that I discussed lock optimization.
>>
>
>
> Note that I said that in a language that has this feature, as people get
> more experience with it they stop using it, hypothetical optimizations
> notwithstanding.
>
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: nagle@animats.com (John Nagle)
Date: Mon, 26 Apr 2004 01:40:49 +0000 (UTC) Raw View
Synchronized objects for C++
Informal proposal for discussion
John Nagle
Animats
April, 2004
Intent
The intent here is to define a usable mechanism for automatic locking
of objects against concurrent access.
Basics
1. Upon external entry to a public function member of any class with the function member
void synchronized_lock();
the function "synchronized_lock()" shall be implicitly called by the
implementation.
2. Upon exit from a function where "synchronized_lock" was called,
the function
void synchronized_unlock();
shall be implicitly called by the implementation. This call shall
take place AFTER any destructors invoked at block exit are called,
and shall take place on exit via "throw" or "return". (For
historical reasons, exit via "longjmp" is not required to
invoke "synchronized_unlock".)
Discussion of basics.
The intent here is to create "synchronized" objects generally along Java lines,
but without tying the language too tightly to the operating system in this
area. Locking would generally be implemented by implementing a base
class to handle synchronization, and all classes derived from that class
would be synchronized.
Calls to private methods do not invoke locking, because they do not
involve "entering" an object.
Calls to a public method from within the object itself do not
invoke locking. This adds some compile-time complexity, but
the idea is to invoke locking only when crossing the boundary
from outside an object to inside it. Alternatively, calls to
public function members from private and protected function
members could be prohibited, but it's probably better to make
the compiler resolve this issue automatically than to dump it
on the programmer.
Calls to protected functions generally do not invoke locking, but
there are some exceptions. If a synchronized_lock function is
callable in a derived class but not the parent class, entry to
a protected function of the derived class from a parent class
via a virtual function call must invoke locking. Note that
multiple inheritance can create this situation.
Temporary exit from synchronization
1. Within a "synchronized" class, the keyword "public" can be
used to indicate a section of code for which the class is to be unlocked.
public { /* code */ }
At entry to a block marked as "public", the function
void synchronized_public_begin()
shall be implicitly called by the implementation.
2. At exit from a block marked as "public", the function
void synchronized_public_end()
shall be implicitly called by the implementation. This call shall
take place AFTER any destructors invoked at block exit are called,
and shall take place on exit via "throw" or "return". (For
historical reasons, exit via "longjmp" is not required to
invoke "synchronized_public_end".)
3. Within a "public" section, calls to a "public" function member
require locking.
Discussion of temporary exit
The justification for "temporary exit" is to allow objects to
allow controlled reentrancy. For example, an thread which needs
to block for an event may need to temporarily unlock the object
while waiting so that others can use it.
Temporary exit could be implemented via ordinary function calls,
but if built in as shown here, it is exception-safe and immune to
off-by-one errors.
"synchronized_public_begin" essentially does a
"synchronized_unlock", and "synchronized_public_end" essentially
does a "synchronized_lock". But there are some situations involving
exceptions that need to be considered.
If "synchronized_public_end"
throws an exception without locking (perhaps because of a timeout
in the underlying locking primitive), we have a lock/unlock
synchronization problem. Control is now inside the object,
in its exception handlers, but the thread processing the
exception does not have the object locked. This is
a synchronization violation.
So an exception in "synchronized_public_end"
must result in invoking the dreaded "std::terminate()",
as with nested exceptions. Unless someone can figure out
a better way out of this.
Optimizations
Some locking optimizations are possible. For example, if
the compiler is compiling a public function which makes
no accesses to data or function members, it need not lock.
If the compiler is compiling a public function which
makes exactly one read of a data member, it need not lock
if the following is true:
- the data member access is performed with an atomic machine
instruction
- no function of the class can change that data member
more than once.
- all changes to that data member are performed with
atomic machine operations
This takes some compile-time analysis, but it makes access functions
much cheaper.
Comments?
John Nagle
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: nesotto@cs.auc.dk ("Thorsten Ottosen")
Date: Mon, 26 Apr 2004 18:29:39 +0000 (UTC) Raw View
"John Nagle" <nagle@animats.com> wrote in message
news:408AB3C9.7090209@animats.com...
| Calls to private methods do not invoke locking, because they do not
| involve "entering" an object.
what happens if a pointer to a private member is "stolen" and called from
somewhere else?
br
Thorsten
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: pfefferd@hotmail.co.il.nospam ("Daniel Pfeffer")
Date: Mon, 26 Apr 2004 18:30:08 +0000 (UTC) Raw View
"John Nagle" <nagle@animats.com> wrote in message
news:408AB3C9.7090209@animats.com...
> Synchronized objects for C++
> Informal proposal for discussion
> John Nagle
> Animats
> April, 2004
>
> Intent
>
> The intent here is to define a usable mechanism for automatic locking
> of objects against concurrent access.
>
> Basics
>
> 1. Upon external entry to a public function member of any class with the
function member
>
> void synchronized_lock();
>
> the function "synchronized_lock()" shall be implicitly called by the
> implementation.
>
> 2. Upon exit from a function where "synchronized_lock" was called,
> the function
>
> void synchronized_unlock();
>
> shall be implicitly called by the implementation. This call shall
> take place AFTER any destructors invoked at block exit are called,
> and shall take place on exit via "throw" or "return". (For
> historical reasons, exit via "longjmp" is not required to
> invoke "synchronized_unlock".)
>
[snipped discussion]
> Temporary exit from synchronization
>
> 1. Within a "synchronized" class, the keyword "public" can be
> used to indicate a section of code for which the class is to be unlocked.
>
> public { /* code */ }
>
> At entry to a block marked as "public", the function
>
> void synchronized_public_begin()
>
> shall be implicitly called by the implementation.
>
> 2. At exit from a block marked as "public", the function
>
> void synchronized_public_end()
>
> shall be implicitly called by the implementation. This call shall
> take place AFTER any destructors invoked at block exit are called,
> and shall take place on exit via "throw" or "return". (For
> historical reasons, exit via "longjmp" is not required to
> invoke "synchronized_public_end".)
>
> 3. Within a "public" section, calls to a "public" function member
> require locking.
[snipped discussion]
[snipped optimisations]
>
> Comments?
1. What happens when you are accessing two synchronised objects in the same
statement? Are they both locked until the next sequence point (requires
compiler modification)? Are they each locked for the duration of the call to
the method (produced implementation-defined behaviour)? Is only the target
object locked?
For example:
SyncObject a,b;
// details omitted
a = b; // translates to a.operator =(b);
Are both a and b locked until access is complete? Is only a locked?
It appears to me that proper synchronisation requires much more work.
2. If we limit ourselves to the list of requirements in the proposal, it
appears to me that instead of requiring more complexity on the part of the
compiler, these features could be implemented much more simply using classes
such as the following:
class CCriticalSection : private uncopyable
{
public:
CCriticalSection() { /* initialises the critical section */ }
~CCriticalSection() { /* terminates the critical section */ }
void lock() { /* enters critical section */ }
void unlock { /* leaves critical section */ }
private:
// implementation details omitted
};
Class CCriticalSectionLock : private uncopyable
{
public:
CCriticalSectionLock(CCriticalSection &cs) : cs_(cs) { cs_.lock(); }
~CCriticalSectionLock() {cs_.unlock(); }
private:
CCriticalSection &cs_;
};
Class CCriticalSectionUnlock : private uncopyable
{
public:
CCriticalSectionUnlock(CCriticalSection &cs) : cs_(cs) { cs_.unlock(); }
~CCriticalSectionUnlock() {cs_.lock(); }
private:
CCriticalSection &cs_;
};
I have made all three classes uncopyable because it obviously makes little
sense to copy such objects. The CCriticalSection class is obviously
implementation dependent. The only requirements on the underlying system
object are:
1. Multiple lock calls from the same thread should not block.
2. The object is only unlocked when the number of unlock calls equals the
number of lock calls.
The object would be used as follows:
class SychronisedObject
{
public:
// constructors omitted
int foo()
{
CCriticalSectionLock lock(cs_);
// details omitted
{
CCriticalSectionUnlock(cs_);
// details omitted
}
}
private:
CCriticalSection cs_;
// other details omitted
};
I think that this provides all of the necessary features without using any
special keywords.
The major disadvantage to my approach is that synchronised objects may not
be copied using a compiler-generated copy constructor or assignment
operator. This should not be a problem in "real" systems.
Daniel Pfeffer
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: dickie@acm.org (Garth A. Dickie)
Date: Mon, 26 Apr 2004 18:30:46 +0000 (UTC) Raw View
I like your proposal. The separation between what the compiler needs
to do for us and the actual threads implementation is a good one.
The proposal seems related to class invariants, function pre- and
post- conditions, and aspect-oriented programming.
For the sake of argument, replace synchronized_lock() with
pre_invariant() and synchronized_unlock() with post_invariant(). Then
in addition to the rules for public function entry and exit,
post_invariant() should be called on constructor exit, and
pre_invariant() should be called on destructor entry. (An
implementation of post_invariant() would suffice for checking class
invariants; pre-invariant isn't needed.)
AOP might make use of pre and post functions to enforce additional
invariants by modifying the object, rather than just checking an
invariant. Synchronization is just one example of this.
Finally, pre- and post- conditions are awkward today. It might be
helpful to have supporting syntax, such as:
class A {
public:
void set_sqrt(int arg)
pre { assert(arg >= 0); }
post { assert(m_foo * m_foo <= arg); assert((m_foo+1) * (m_foo+1)
> arg); }
{
m_foo = int(sqrt(arg));
}
private:
int m_foo;
};
Here the post condition code has access to the function's arguments
and to the class's private members. Trying to accomplish this with a
helper object constructor/destructor is painful.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: nagle@animats.com (John Nagle)
Date: Mon, 26 Apr 2004 21:37:22 +0000 (UTC) Raw View
Garth A. Dickie wrote:
> I like your proposal. The separation between what the compiler needs
> to do for us and the actual threads implementation is a good one.
>
> The proposal seems related to class invariants, function pre- and
> post- conditions, and aspect-oriented programming.
>
> For the sake of argument, replace synchronized_lock() with
> pre_invariant() and synchronized_unlock() with post_invariant().
I'd like to see design by contract for C++, but realistically,
it's not going to happen.
I've done considerable work in that area.
See my paper "Practical Program Verification" in POPL '83.
For more recent thinking on the subject, see the Extended
Static Checking for Java work at the now-defunct DEC WRL:
http://www.hpl.hp.com/techreports/Compaq-DEC/SRC-TN-1999-002.pdf
That system is freely downloadable, if HP hasn't deleted it yet.
John Nagle
Animats
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: nesotto@cs.auc.dk ("Thorsten Ottosen")
Date: Tue, 27 Apr 2004 00:10:31 +0000 (UTC) Raw View
"John Nagle" <nagle@animats.com> wrote in message
news:7_cjc.41335$rN4.20692@newssvr29.news.prodigy.com...
| Garth A. Dickie wrote:
| I'd like to see design by contract for C++, but realistically,
| it's not going to happen.
well, at least something similar to it might happen:
http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/2004/n1613.pdf
br
Thorsten
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: petebecker@acm.org (Pete Becker)
Date: Tue, 27 Apr 2004 01:42:29 +0000 (UTC) Raw View
John Nagle wrote:
>
>
> 1. Upon external entry to a public function member of any class with the function member
>
> void synchronized_lock();
>
> the function "synchronized_lock()" shall be implicitly called by the
> implementation.
>
> 2. Upon exit from a function where "synchronized_lock" was called,
> the function
>
> void synchronized_unlock();
>
> shall be implicitly called by the implementation.
Java's experience is relevant here. Java has synchronized classes, which
lock an unlock on entry to and exit from every member function. Trouble
is, if you want fast code, those are often the wrong places to do the
locking. You get major speedups by postponing the locking, and doing any
computations that don't involve the object's data before you lock. This
shortens the time that the function holds the lock, which improves
throughput; it also sometimes avoids having to lock at all, if you can
determine that the arguments mean that there's nothing that has to be
done to the object. So if you look at the Java library specifications,
you see that synchronized classes and synchronized methods were used
often in the early library versions, and far less often (even for
similar classes) in later versions.
--
Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: francis@robinton.demon.co.uk (Francis Glassborow)
Date: Tue, 27 Apr 2004 18:20:55 +0000 (UTC) Raw View
In article <408d9767$0$436$afc38c87@news.optusnet.com.au>, Thorsten
Ottosen <nesotto@cs.auc.dk> writes
>"John Nagle" <nagle@animats.com> wrote in message
>news:7_cjc.41335$rN4.20692@newssvr29.news.prodigy.com...
>> Garth A. Dickie wrote:
>
>> I'd like to see design by contract for C++, but realistically,
>> it's not going to happen.
>
>well, at least something similar to it might happen:
>
>http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/2004/n1613.pdf
But IIRC the evolution work group were not exactly enthusiastic and
suggested that the paper's author needed to do a great deal more work
both technical and in gaining support. If you are keen on something like
the proposal I suggest you contact the author and offer him assistance.
--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]