Question

I 'm a novice in multithreading programing and i have still confusion with that.

Below is my reference counted class:

class Rbuffer
{
  private:
    char *m_pnData;
    volatile unsigned int mRefCount;

  public:
     Rbuffer(int nLength) : mRefCount(0)
    {
    m_pnData = new char[nLength]; 
    }
   ~Rbuffer(){
    delete[] m_pnData;
    }

   void decRef() {
     if(InterlockedDecrement(&mRefCount)==0){
               delete (Rbuffer *)this;
           }
    }

  void incRef() {
        InterlockedIncrement(&mRefCount);
    } 

}; 

is it fully thread safe? Can you exclude this situation:

ThreadA                                 ThreadB
PointerToRBuffer->incRef();//mRefCount 1
switch->  
                                        PointerToRBuffer->incRef();//mRefCount 2
                                          <-switch
PointerToRBuffer->decRef();           
InterlockedDecrement(&mRefCount)//mRefCount 1 
switch->                                
                                        PointerToRBuffer->decRef();//mRefCount 0!
                                        InterlockedDecrement(&mRefCount);
                                        if (0==0)
                                        delete (Rbuffer *)this; 
                                            <-switch
if (0==0) 
//deleting object, that doesn't exist 
delete (Rbuffer *)this;
//CRASH                               

The reasons of crashing could be that only (InterlockedDecrement(&mRefCount)) part is atomic, but if (InterlockedDecrement(&mRefCount)==0) not? Am i wrong with example above?

Thanks in advance for your opinions and advice to make my class fully thread safe.

Was it helpful?

Solution

Your analysis isn't right; The code you posted is using interlockedDecrement correctly.

This is a safe use

 if(InterlockedDecrement(&mRefCount)==0)
           cleanup();

.. but this would indeed have the problem you described

 InterlockedDecrement(&mRefCount);

 if (mRefCount==0)
           cleanup();

However, the use of delete this is much more likely to be the cause of the problem. It's very unlikely you're passing the 'absolutely positively 100% sure' tests described here: http://www.parashift.com/c++-faq-lite/delete-this.html

In particular, the following simple code would cause chaos.

{
  RBuffer x;  // count is what... ? zero
  x.incRef(); // make count one
  x.decRef(); // make count zero, and deletes itself 
}  // now x goes out of scope, so destructor is called a second time = chaos! 

A normal "reference counting" idiom involves a 'shared object' (with the count), and simple 'reference objects' (not C++ references, although the semantics would be similar) which refer to the shared object. The constructors and destructors of the 'reference objects' are responsible for calling the incref/decref methods on the shared object. So the shared object is automatically counting the number of active 'reference objects'.

OTHER TIPS

It is not 100% clear what is going on, but it looks like ThreadA deletes the RBuffer object, and then subsequently ThreadB dereferences it.

What you really need is mutual exclusion around decrement and the delete operation, furthermore you need to set some kind of flag to prevent dereference after delete. Usually setting pointers to NULL works and then checking for NULL before any dereference.

So your decRef might look something like this:

 void decRef() {
     lock(_mutex);
     if(InterlockedDecrement(&mRefCount)==0) {
               PointerToRBuffer = NULL;
               delete (Rbuffer *)this;
           }
    }

You might be better off using a shared_ptr.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top