How do I make a thread-safe copy-constructor?
Can anybody suggest how to design a thread-safe copy-constructor?
The problem is how to make the copy-constructor work even as the
copied object gets deleted by another thread. The objects have a
shared resource and use a reference count to keep track of how many
objects use the resource. When the last object sharing the resource
dies, the reference count goes to zero, and releases the resource. I
discovered that locking the resource does not always work.
Here is a look at the first attempt for a copy-constructor and
destructor.
Object::Object( const Object & that ) :
m_count( that.m_count ), // Pointer to reference count.
m_resource( that.m_resource ) // pointer to shared resource.
{
AtomicIncrement( *m_count );
}
Object::~Object( void )
{
if ( !AtomicDecrement( *m_count ) )
{
delete m_count;
delete m_resource;
}
}
One possible order of events:
1: In thread-1, object a's destructor gets called, but after the call
to AtomicDecrement, thread-1 gets swapped out.
2: In thread-2, something calls the copy-constructor, which runs
through completion before thread-2 gets swapped out.
3: Thread-1 gets swapped back in, and deletes the reference count, and
the resource.
The object in thread 2 now has pointers to a dead resource and a bogus
reference count.
So, I tried a locking pattern:
Object::Object( const Object & that ) :
m_count( that.m_count ), // Pointer to reference count.
m_resource( that.m_resource ) // pointer to shared resource.
{
Lock lock; // Lock prevents other threads from running until lock
goes out of scope.
++( *m_count );
}
Object::~Object( void )
{
Lock lock;
--( *m_count );
if ( *m_count == 0 )
{
delete m_count;
delete m_resource;
}
}
Now I have to consider this possible order of events:
1: In thread-1, object a's destructor gets called, and runs through
completion before thread-1 gets swapped out.
2: In thread-2, something calls the copy-constructor, which runs
through completion before thread-2 gets swapped out.
The object in thread 2 now has pointers to a dead resource and a bogus
reference count.
Or this possible order of events:
1: In thread-2, something calls the copy-constructor, which runs until
just before the Lock statement and then thread-2 gets swapped out.
2: In thread-1, object a's destructor gets called, and runs through
completion before thread-1 gets swapped out.
3: Thread-2 gets swapped back in, and tries to lock a mutex and
increment a bogus reference count.
An ideal solution will:
* allow the object in thread-2 to take over control of the resource
from the object in thread-1 if the resource still exists after the
destructor ends.
* construct the object in thread-2 with both m_count and m_resource
pointers as NULL if the resource gets deleted.
* provide the no-throw exception safety level.
I would appreciate suggestions that compile on both GCC and MS Visual
Studio.
Thanks!
Rich
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]