Re: How do I make a thread-safe copy-constructor?
On Thu, 26 Apr 2007 23:51:53 CST, "h.yuzhao@gmail.com"
<h.yuzhao@gmail.com> wrote:
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;
}
}
I think the only thing you need to do is on the copy ctor: don't
initialize members in parameters list, there is no need to do like so.
You can't get any efficiency benifit from it. So, do it as follow:
Object::Object( const Object & that )
{
Lock lock; // Lock prevents other threads from running until
lock
// goes out of scope.
m_count = that.m_count;
m_resource = that.m_resource;
++( *m_count );
}
Is not enough?
Not quite. If you allow a reference count of zero, you should at
least check for it being zero before you copy, and secure the count
before you get the resource. If it is zero, you should ensure that
you're in a well-defined state, so you should initialize your members
to an "empty" state:
Object::Object( const Object & that )
: m_count(0), m_resource(0)
{
Lock lock(that.m_mutex); // Lock the other guy's mutex
if (that.m_count && *that.m_count > 0) {
++( *that.m_count );
m_count = that.m_count;
m_resource = that.m_resource;
}
}
Then there's the problem of what you're actually locking. Presumably,
it's a mutex, but who does it belong to? Is it an object level mutex?
Probably so, so you'd have to at least lock the other guy's mutex. The
same kind of thing has to be considered when you use atomic counts.
I think that copying an object in the midst of destruction is just a
bad idea. Like Hyman Rosen said, you just don't know what's going to
happen to the object under these conditions. If you take the extra
step of ensuring that you have an object to copy before you copy it,
these problems largely go away and you can just substitute shared_ptr
for the resource container.
That, or detach/attach as was mentioned in Brian McKeever's post. I
remember a post that James Kanze wrote a while back about using this
method to put pointers in a producer/consumer queue. You use
auto_ptrs, and release them into the queue, and rewrap them when they
come out, all as part of an encapsulated system. He had stated that a
bald pointer is the better element for the queue than a shared_ptr (I
think because the reference count semantics were too heavy for the
purpose).
Maybe this is what the OP needs? A producer-consumer model?
HTH,
John Moeller
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]