Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

C++ object locks like Java?

5 views
Skip to first unread message

Alexis

unread,
Mar 12, 2003, 7:47:15 AM3/12/03
to
Hi,

I know that in Java you can synchronise the use of an object to
protect it from multi-threaded access.

Is there such mechanism in C++ or do I have to implement my own (using
a CriticalSection as a member the object and EnterCriticalSection
every time a public function is called)?

Thanks in advance,
Alexis

Ivan Vecerina

unread,
Mar 12, 2003, 9:29:04 AM3/12/03
to
"Alexis" <iced...@hotmail.com> wrote in message
news:686d322e.03031...@posting.google.com...

You have to implement it on your own, but you can encapsulate it
well within C++ classes.

By using RAII (a stack-based object whose constructor acquires the
lock and whose destructor releases it automatically), all it takes
is to add a single statement at the beginning of every function
to be synchronized:
Locker lock( this );


Good luck,
--
Ivan Vecerina, Dr. med. <> http://www.post1.com/~ivec
Soft Dev Manger, xitact <> http://www.xitact.com
Brainbench MVP for C++ <> http://www.brainbench.com

Alexander Terekhov

unread,
Mar 12, 2003, 10:58:29 AM3/12/03
to

Ivan Vecerina wrote:
>
> "Alexis" <iced...@hotmail.com> wrote in message
> news:686d322e.03031...@posting.google.com...
> | I know that in Java you can synchronise the use of an object to
> | protect it from multi-threaded access.

http://groups.google.com/groups?selm=3D53F021.A80B1D9B%40web.de
(Subject: Re: Thread-Safe Classes)

> |
> | Is there such mechanism in C++ or do I have to implement my own (using
> | a CriticalSection as a member the object and EnterCriticalSection
> | every time a public function is called)?
>
> You have to implement it on your own, but you can encapsulate it
> well within C++ classes.
>
> By using RAII (a stack-based object whose constructor acquires the
> lock and whose destructor releases it automatically), all it takes

^^^^^^^^^^^^

> is to add a single statement at the beginning of every function
> to be synchronized:
> Locker lock( this );

Yeah. But...

http://groups.google.com/groups?selm=3C3ACEB1.94085050%40web.de
(...Also, for locking, you might want to use some RAII...)

http://groups.google.com/groups?selm=3D08A049.6850244E%40web.de
(...Silly Java VM is going to unwind the whole mess... )

regards,
alexander.

Alexis

unread,
Mar 13, 2003, 4:30:58 AM3/13/03
to
Thanks both, I used RAII and it works fine...

But I still have some questions.

If an exception occurs within the "locked" function, does the RAII
object's destructor get called or not?

And finally, is it possible for a destructor of a dynamic object to be
called twice from different threads? So basically do I need to lock
the destructor, and if yes how? since it is there that I delete the
critical section of the object?

Regards,
Alexis

Alexander Terekhov <tere...@web.de> wrote in message news:<3E6F5925...@web.de>...

Alexander Terekhov

unread,
Mar 13, 2003, 7:06:51 AM3/13/03
to

Alexis wrote:
>
> Thanks both, I used RAII and it works fine...
>
> But I still have some questions.
>
> If an exception occurs within the "locked" function, does the RAII
> object's destructor get called or not?

That depends. Read the C++ standard... and, perhaps, the recent
"exceptions" thread here (and you might want to follow the links
that I've posted to that thread).

>
> And finally, is it possible for a destructor of a dynamic object to be
> called twice from different threads?

The destructors of dynamic objects are called when someone says
"delete" or invoices destructor(s) explicitly/separately; not
triggering the deallocation.

Once the non-trivial-destructor-call starts, it's not an object
anymore. It's a "half-dead" container of whatever base-classes-
sub-objects and/or member objects you happen to have in it. You
simply can't destroy this thing "one more time"... without {re-}
creating a "full" object via the placement-new at the same place
after the destruction completes. Well, you certainly "can" do
whatever you want but once you trigger the "undefined behavior"
the result might be not what you want, so to speak.

> So basically do I need to lock the destructor, and if yes how?

You don't.

> since it is there that I delete the critical section of the object?

Yup, if your object really needs it -- things like monitors,
for example.

Well, you might want to follow the link below; among various
things, that code-snipped was supposed to "illustrate" the use
of RAII related to threading:

http://www.terekhov.de/DESIGN-futex-CV.cpp

regards,
alexander.

--
http://www.terekhov.de/DESIGN-futex-CV.txt

Christian Parpart

unread,
Mar 14, 2003, 6:40:56 AM3/14/03
to
Alexis inspired the electrons to say:

Did you try mutexes for that?
Well, C++ is not responsible for API's like an MT API, but you may implement
one, or have a look on google for one, I know, there're several out there
;)

At least Qt's QMutex class for example ;)

Greets,
Christian Parpart.

Ivan Vecerina

unread,
Mar 14, 2003, 9:14:48 AM3/14/03
to
"Alexis" <iced...@hotmail.com> wrote in message
news:686d322e.03031...@posting.google.com...
| If an exception occurs within the "locked" function, does the RAII
| object's destructor get called or not?

Yes.
AFAICT, the only counter-examples involve abrupt thread/process
termination -- but I admit I haven't read the whole thread that
Alexander refered to.

| And finally, is it possible for a destructor of a dynamic object to be
| called twice from different threads? So basically do I need to lock
| the destructor, and if yes how? since it is there that I delete the
| critical section of the object?

Once 'delete' is called, it is too late to perform synchronization.
To safely share heap-allocated objects across threads, you probably
need to use some reference-counting mechanism anyway.
A simple and effective approach is to use boost::shared_ptr
( http://www.boost.org/libs/smart_ptr/shared_ptr.htm ).

If you want a thread to be able to own and delete objects
regardless of what others are doing, it can still be nicely
encapsulated, but it gets more complicated...

Regards,

Alexander Terekhov

unread,
Mar 14, 2003, 11:50:09 AM3/14/03
to

Ivan Vecerina wrote:
>
> "Alexis" <iced...@hotmail.com> wrote in message
> news:686d322e.03031...@posting.google.com...
> | If an exception occurs within the "locked" function, does the RAII
> | object's destructor get called or not?
>
> Yes.

Again: that depends.

> AFAICT, the only counter-examples involve abrupt thread/process
> termination -- but I admit I haven't read the whole thread that
> Alexander refered to.

Well, for STARTERS, the C++ standard says:

http://groups.google.com/groups?selm=3C91397A.9FC5F5A4%40web.de
(Subject: Re: C++ and threads)

"The process of calling destructors for automatic objects constructed
on the path from a try block to a throw-expression is called 'stack
unwinding.'"

-and-

"If no matching handler is found in a program, the function terminate()
is called; whether or not the stack is unwound before this call to
terminate() is implementation-defined (15.5.1). "

Well, more info on that subject ("_UA_FORCE_UNWIND" including):

http://groups.google.com/groups?selm=3D205A8A.DEFC922A%40web.de
(Subject: Re: Some issues with Technical Report on C++ Performance)

Uhmm, and for BOOSTERS, I said: < you might want to read this entire
thread >

http://lists.boost.org/MailArchives/boost/msg36232.php
([boost] Re: Basic guarantee and synchronized multiple updates.)

regards,
alexander.

--
http://groups.google.com/groups?selm=3CE664A6.A5D4B620%40web.de

Ivan Vecerina

unread,
Mar 14, 2003, 12:43:33 PM3/14/03
to
"Alexander Terekhov" <tere...@web.de> wrote in message
news:3E720841...@web.de...

| > | If an exception occurs within the "locked" function, does the RAII
| > | object's destructor get called or not?
| >
| > Yes.
|
| Again: that depends.
...

So, in order to avoid spreading FUD, can we agree that:

The destructor of local objects will always be called,
as long as:
- The program or thread is not abruptly terminated by
calling exit(), terminate(), or another OS-specific
thread/process termination routine or fatal error.
- There is always an enclosing try-catch block to
catch any exceptions that are thrown
( a try--catch(...) block enclosing the function
at the root of the thread will always do).

(and of course, if no 'undefined behavior is triggered,
the user doesn't press the 'reset' button of his PC, etc ).

Please let me know if there are additional caveats.

Cheers,
Ivan

Alexander Terekhov

unread,
Mar 14, 2003, 1:51:21 PM3/14/03
to

Ivan Vecerina wrote:
>
> "Alexander Terekhov" <tere...@web.de> wrote in message
> news:3E720841...@web.de...
> | > | If an exception occurs within the "locked" function, does the RAII
> | > | object's destructor get called or not?
> | >
> | > Yes.
> |
> | Again: that depends.
> ...
>
> So, in order to avoid spreading FUD, can we agree that:
^^^^^^^^^^^^^

http://groups.google.com/groups?selm=3E648538.7E5D59D8%40web.de
(Subject: Re: Deriving from STL container)

>
> The destructor of local objects will always be called,
> as long as:
> - The program or thread is not abruptly terminated by
> calling exit(), terminate(), or another OS-specific
> thread/process termination routine or fatal error.
> - There is always an enclosing try-catch block to
> catch any exceptions that are thrown
> ( a try--catch(...) block enclosing the function
> at the root of the thread will always do).
>
> (and of course, if no 'undefined behavior is triggered,
> the user doesn't press the 'reset' button of his PC, etc ).
>
> Please let me know if there are additional caveats.

Sure. "caveat": never, ever, use anything that looks like a rather
popular "try/catch(...)"-idiom [I mean things like try/catch(const
std::exception&) as well] in the places where you just have no idea
what exactly you might catch --- "at the root of the thread", for
example.

http://groups.google.com/groups?threadm=3D8EEE36.C164536D%40web.de
(Subject: "PJP doesn't seem to care" ;-))

regards,
alexander.

Maciej Sobczak

unread,
Mar 14, 2003, 1:52:58 PM3/14/03
to
Hi,

"Ivan Vecerina" <iv...@myrealbox.com> wrote in message
news:3e7214c6$1...@news.swissonline.ch...

> So, in order to avoid spreading FUD, can we agree that:
>
> The destructor of local objects will always be called,
> as long as:
> - The program or thread is not abruptly terminated by
> calling exit(), terminate(), or another OS-specific
> thread/process termination routine or fatal error.
> - There is always an enclosing try-catch block to
> catch any exceptions that are thrown
> ( a try--catch(...) block enclosing the function
> at the root of the thread will always do).

This is straying off-the-group, but there is one more point: if we have many
local objects on the stack and if a destructor of one local object that gets
destroyed in the process of stack unwinding throws some exception, then I
think there is no guarantee that other local objects get properly destroyed,
even if your two points are met.
So let's add:

- The destructors (of automatic objects) do not throw (which is generally a
good idea).

> (and of course, if no 'undefined behavior is triggered,
> the user doesn't press the 'reset' button of his PC, etc ).

--
Maciej Sobczak
http://www.maciejsobczak.com/

Distributed programming lib for C, C++, Python & Tcl:
http://www.maciejsobczak.com/prog/yami/

Alexander Terekhov

unread,
Mar 14, 2003, 2:21:50 PM3/14/03
to

Maciej Sobczak wrote:
[...]

> This is straying off-the-group, but there is one more point: if we have many
> local objects on the stack and if a destructor of one local object that gets
> destroyed in the process of stack unwinding throws some exception, then I
> think there is no guarantee that other local objects get properly destroyed,

There is a guarantee that terminate() will be called in such situation.
Unfortunately, there is no guarantee that it will be done at throw point.

regards,
alexander.

Ivan Vecerina

unread,
Mar 14, 2003, 3:46:10 PM3/14/03
to
"Alexander Terekhov" <tere...@web.de> wrote in message
news:3E7224A9...@web.de...
: > So, in order to avoid spreading FUD, can we agree that:

: ^^^^^^^^^^^^^
: http://groups.google.com/groups?selm=3E648538.7E5D59D8%40web.de
: (Subject: Re: Deriving from STL container)

Hm. A link to a post with 4 additional links.
Please forgive me, I tend to lose focus too easily... ;)

: > The destructor of local objects will always be called,


: > as long as:
: > - The program or thread is not abruptly terminated by
: > calling exit(), terminate(), or another OS-specific
: > thread/process termination routine or fatal error.
: > - There is always an enclosing try-catch block to
: > catch any exceptions that are thrown
: > ( a try--catch(...) block enclosing the function
: > at the root of the thread will always do).
: >
: > (and of course, if no 'undefined behavior is triggered,
: > the user doesn't press the 'reset' button of his PC, etc ).
: >
: > Please let me know if there are additional caveats.
:
: Sure. "caveat": never, ever, use anything that looks like a rather
: popular "try/catch(...)"-idiom [I mean things like try/catch(const
: std::exception&) as well] in the places where you just have no idea
: what exactly you might catch --- "at the root of the thread", for
: example.

If I understand correctly, I think we agree.
Of course there could be many more side-notes, such as
knowing that throwing from a destructor (NEVER EVER)
may trigger a call to terminate().

I think I also get your point that, in several ways,
the behavior of exception handling in C++ is defined
too loosely. (but Java isn't any better, right?).


Kind regards,
Ivan


--
Ivan Vecerina, Dr. med. <> http://www.post1.com/~ivec

Soft Dev Manger, XiTact <> http://www.xitact.com

Alexander Terekhov

unread,
Mar 14, 2003, 4:24:22 PM3/14/03
to

Ivan Vecerina wrote:
>
> "Alexander Terekhov" <tere...@web.de> wrote in message
> news:3E7224A9...@web.de...
> : > So, in order to avoid spreading FUD, can we agree that:
> : ^^^^^^^^^^^^^
> : http://groups.google.com/groups?selm=3E648538.7E5D59D8%40web.de
> : (Subject: Re: Deriving from STL container)
>
> Hm. A link to a post with 4 additional links.

Don't miss the link that prompted my reply (with 4 additional links).

<quote source=http://wombat.doc.ic.ac.uk/foldoc/foldoc.cgi?query=FUD>

FUD

<jargon> /fuhd/ An acronym invented by Gene Amdahl after he left IBM
to found his own company: "FUD is the fear, uncertainty, and doubt that
IBM sales people instill in the minds of potential customers who might
be considering [Amdahl] products." The idea, of course, was to persuade
them to go with safe IBM gear rather than with competitors' equipment.
This implicit coercion was traditionally accomplished by promising that
Good Things would happen to people who stuck with IBM, but Dark
Shadows loomed over the future of competitors' equipment or software.

</quote>

I work for IBM (started in the "mainframes division"), you know.

> Please forgive me, I tend to lose focus too easily... ;)

Okay.

>
> : > The destructor of local objects will always be called,
> : > as long as:
> : > - The program or thread is not abruptly terminated by
> : > calling exit(), terminate(), or another OS-specific
> : > thread/process termination routine or fatal error.
> : > - There is always an enclosing try-catch block to
> : > catch any exceptions that are thrown
> : > ( a try--catch(...) block enclosing the function
> : > at the root of the thread will always do).
> : >
> : > (and of course, if no 'undefined behavior is triggered,
> : > the user doesn't press the 'reset' button of his PC, etc ).
> : >
> : > Please let me know if there are additional caveats.
> :
> : Sure. "caveat": never, ever, use anything that looks like a rather
> : popular "try/catch(...)"-idiom [I mean things like try/catch(const
> : std::exception&) as well] in the places where you just have no idea
> : what exactly you might catch --- "at the root of the thread", for
> : example.
>
> If I understand correctly, I think we agree.

Good.

> Of course there could be many more side-notes, such as
> knowing that throwing from a destructor (NEVER EVER)
> may trigger a call to terminate().
>
> I think I also get your point that, in several ways,
> the behavior of exception handling in C++ is defined
> too loosely. (but Java isn't any better, right?).

Java is much worse.

regards,
alexander.

Hillel Y. Sims

unread,
Mar 15, 2003, 12:38:35 AM3/15/03
to
"Alexander Terekhov" <tere...@web.de> wrote in message
news:3E7224A9...@web.de...

>
> Sure. "caveat": never, ever, use anything that looks like a
rather
> popular "try/catch(...)"-idiom [I mean things like
try/catch(const
> std::exception&) as well] in the places where you just have no
idea
> what exactly you might catch --- "at the root of the thread",
for
> example.
>

Agreed.

How do you feel about this:
------
dtor::~dtor()
{
try {
// cleanup
}
catch (runtime_error&) { } // swallow
}
------
?

hys
--
(c) 2003 Hillel Y. Sims
hsims AT factset.com


Alexander Terekhov

unread,
Mar 15, 2003, 10:44:31 AM3/15/03
to

"Hillel Y. Sims" wrote:
[...]

> How do you feel about this:
> ------
> dtor::~dtor()
> {
> try {
> // cleanup
> }
> catch (runtime_error&) { } // swallow
> }
> ------
> ?

I feel "not good" about it. I'd rather use something along the
lines of: < just an illustration, not real code >

template< typename error, typename handler, typename operation >
bool invoke_and_handle_failure(operation op, handler h) {
try { op(); } catch(const error& e) { return h(e, op); }
return true;
}

temp_dataset::~temp_dataset() {
invoke_and_handle_failure< output_error >(
/* "close-it" */,
/* "null-handler... do-nothing" */);
invoke_and_handle_failure< remove_error >(
/* "remove-it" */,
/* "logger" */);
}

regards,
alexander.

Hillel Y. Sims

unread,
Mar 17, 2003, 11:36:51 PM3/17/03
to
Not really about threads, so I'll OT myself..

"Alexander Terekhov" <tere...@web.de> wrote in message

news:3E734A5F...@web.de...

That is a pretty interesting technique. I think I like it. I was
kind of thinking in the context of something "generic" like
ScopeGuard's on-block-exit functionality
(http://www.cuj.com/experts/1812/alexandr.htm?topic=experts),
which was implemented thus (at least in the original source
accompanying the article):

template <typename J>
static void SafeExecute(J& j) throw()
{
if (!j.dismissed_)
try
{
j.Execute();
}
catch(...)
{
}
}

Well, I'm kind of not too crazy about the catch(...) there...
The idea is obviously that arbitrary cleanup functions may be
called that legitimately emit some exceptions, which should not
affect the integrity of the program. Of course,
catch(...)-swallow totally violates that assumption. It seems
not too horrible to me to catch runtime_error though (and I'm
even kind of on the fence about std::exception, except that I
guess logic_errors should probably terminate); that should
represent the class of exceptions that can be cleaned up and
continued successfully after (even if there's no logging). Maybe
it would actually be better implemented though in terms of
something like your invoke_and_handle_failure where the client
actually specifies exactly what kind of errors may be thrown by
the cleanup func and how/if to handle.. very interesting idea,
thanks.

Alexander Terekhov

unread,
Mar 18, 2003, 5:57:14 AM3/18/03
to

Exceptions have "context". A cleanup-type operation that is
invoked by the destructor and that throws "std::bad_alloc"...
most likely [there are exceptions, of course ;-) ] deserves
only one thing -- dumping the whole mess to some "coredump"-
or-whatever-you-call-it (and that shall be done at the throw
point -- to prevent things getting worse). If you expect some
thing to happen and it's OK to swallow it... just do it, but
be specific; to me, neither "..." nor std::runtime_error is
"specific-enough", so to speak. Well,

http://google.com/groups?selm=3E0C1BE5.7081282C%40web.de
(Subject: Re: Exceptions Rant)

regards,
alexander.

0 new messages