Destructor semantics

D

Dilip

Is my understanding right here?

An object becomes totally inaccessible only after its dtor runs through
completely, right?

I have a situation where I need to hang around in the dtor of an
automatic object for a while -- this is to let various threads that
have access to my automatic object shutdown gracefully.

I do not want main() to return when myclass::runInfinitely() happens to
throw an exception without giving a chance for worker threads to die.

Currently I do it like this:

int main()
{
try
{
myclass myclassObj;
spawnthreads(&myclassObj);
myclass.runInfinitely();
}
catch(...)
{
}
}

~myclass()
{
while (threadsarestillrunning)
{
sleepsome();
}
}

while the above loop is executing, other threads that still hold a
pointer to myclass can safely access member variables and call methods,
right?
 
P

Phlip

Dilip said:
I do not want main() to return when myclass::runInfinitely() happens to
throw an exception without giving a chance for worker threads to die.

Currently I do it like this:

int main()
{
try
{
myclass myclassObj;
spawnthreads(&myclassObj);
myclass.runInfinitely();
}
catch(...)
{
}
}

~myclass()
{
while (threadsarestillrunning)
{
sleepsome();
}
}

while the above loop is executing, other threads that still hold a
pointer to myclass can safely access member variables and call methods,
right?

Syntactically, the code will work.

Logically, your threads own your object, not main(). Your threads should use
a shared_ptr (boost or tr1?), as each thread ends it releases a reference
count in this pointer. The last thread to end will trigger the pointer to
delete its pointee.

If you must go with the current design, use a reference counting semaphore
of some type, not a lazy loop. (Advice I frequently give myself!!) And your
threads should also guard access to this object based on a mutex semaphore.
So mutex + shared_ptr == reference counting semaphore...

Next, exceptions add a new screwball to the situation. Your code currently
defends stack unwinding, so your object lives long enough for other threads
to die, as the exception propagates in the main() thread. If those threads
don't know about the exception, they won't turn themselves off, so you may
have a race condition.

Throw and catch the exception, then trigger those threads, then let them
destroy the shared object.
 
D

Dilip

Phlip said:
Next, exceptions add a new screwball to the situation. Your code currently
defends stack unwinding, so your object lives long enough for other threads
to die, as the exception propagates in the main() thread. If those threads
don't know about the exception, they won't turn themselves off, so you may
have a race condition.

I can't use Boost (corporate policy and all). So I have a question
about what you said above. Why would the threads need to know whether
or not 'runInfinitely' threw an exception? They are just thread pool
threads -- they register themselves on creation with 'myclass' and
deregister themselves on finishing their work (these are all just
quickie jobs -- process some record and return). So when all threads
die, the deregistration mechanism effectively cleans out my
datastructure that I use to maintain a record of those threads.

So, in myclass's dtor, I wait around to see if the datastructure
becomes empty.. (for a reasonable amont of time).

I am not seeing the race condition you pointed out.
 
P

Pete Becker

Dilip said:
I can't use Boost (corporate policy and all).

Can you use std::tr1::shared_ptr? It's specified by an official ISO
technical report.

--

-- Pete

Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." For more information about this book, see
www.petebecker.com/tr1book.
 
D

Dilip

Pete said:
Can you use std::tr1::shared_ptr? It's specified by an official ISO
technical report.

Left to me I am ready to devour Boost. The problem is unless tr1
supplied by Dinkumware is bundled along with a Visual Studio release, I
stand no chance of using all those cool facilities. I just saw a few
mins ago that Visual Studio 2005 has released Service Pack 1. If I
don't find it there I have to wait for the next release of their
compiler to access tr1.
 
P

Pete Becker

Dilip said:
I just saw a few
mins ago that Visual Studio 2005 has released Service Pack 1. If I
don't find it there I have to wait for the next release of their
compiler to access tr1.

It's not there.

--

-- Pete

Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." For more information about this book, see
www.petebecker.com/tr1book.
 
P

Phlip

Dilip said:
int main()
{
try
{
myclass myclassObj;
spawnthreads(&myclassObj);
myclass.runInfinitely();
}
catch(...)
{
}
}

~myclass()
{
while (threadsarestillrunning)
{
sleepsome();
}
}

A note about the syntactic reason this is well-defined. If another thread
accesses myclassObj (and if a semaphore defends the object from concurrent
writes), then those threads access the object after control flow in this
thread has entered the destructor, but before encountering the closing
brace.

Only after this destructor starts calling its sub-destructors - after the
destructor body - do elements of the object start becoming unavailable.
Touching them will cause undefined behavior. So the threads can still freely
call methods on myclassObj, while its destructor is stuck in this holding
pattern.

However, if you inherit another object, and expect the loop in the parent
object's destructor to defend the child object's members, you are screwed!
 
P

Phlip

Dilip said:
Left to me I am ready to devour Boost. The problem is unless tr1
supplied by Dinkumware is bundled along with a Visual Studio release, I
stand no chance of using all those cool facilities. I just saw a few
mins ago that Visual Studio 2005 has released Service Pack 1. If I
don't find it there I have to wait for the next release of their
compiler to access tr1.

Google the source of shared_ptr, and just borrow it. If its contents depend
on other boosty things, whack them.

Writing your own shared pointer from scratch should be a nice coding
exercise, too!
 
D

Dilip

Phlip said:
Only after this destructor starts calling its sub-destructors - after the
destructor body - do elements of the object start becoming unavailable.
Touching them will cause undefined behavior. So the threads can still freely
call methods on myclassObj, while its destructor is stuck in this holding
pattern.

The above confirms what I suspected. Thanks Phlip for the vote of
confidence!
 
P

Phlip

Dilip said:
The above confirms what I suspected. Thanks Phlip for the vote of
confidence!

Look up 'delete this'. That's a FAQ ("can I call 'delete this' safely?"),
and the answer is related. C++ defines the act of returning from a method
after its object goes bye-bye.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top