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
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
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.
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>...
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.
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.
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,
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
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
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.
"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/
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.
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
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.
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
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.
"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.
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.