C++: Multi threading and reference counting

Fire Lancer picture Fire Lancer · Jul 16, 2009 · Viewed 8.8k times · Source

Currently ive got some reference counted classes using the following:

class RefCounted
{
public:
    void IncRef()
    {
        ++refCnt;
    }
    void DecRef()
    {
        if(!--refCnt)delete this;
    }
protected:
    RefCounted():refCnt(0){}
private:
    unsigned refCnt;
    //not implemented
    RefCounted(RefCounted&);
    RefCounted& operator = (RefCounted&};
};

I also have a smart pointer class that handles reference counting , all though its not uniformly used (eg in one or two bits of performance critical code, where I minimised the number of IncRef and DecRef calls).

template<class T>class RefCountedPtr
{
public:
    RefCountedPtr(T *p)
    :p(p)
    {
        if(p)p->IncRef();
    }
    ~RefCountedPtr()
    {
        if(p)p->DecRef();
    }
    RefCountedPtr<T>& operator = (T *newP)
    {
        if(newP)newP->IncRef();
        if(p)   p   ->DecRef();
        p = newP;
        return *this;
    }
    RefCountedPtr<T>& operator = (RefCountedPtr<T> &newP)
    {
        if(newP.p)newP.p->IncRef();
        if(p)     p     ->DecRef();
        p = newP.p;
        return *this;
    }
    T& operator *()
    {
        return *p;
    }
    T* operator ->()
    {
        return p;
    }
    //comparison operators etc and some const versions of the above...
private:
    T *p;
};

For the general use of the classes themselves I plan to use a reader/writer locking system, however I dont really want to have to get a writer lock for every single IncRef and DecRef call.

I also just thought of a scenario where the pointer may be invalidated just before the IncRef call, consider:

class Texture : public RefCounted
{
public:
    //...various operations...
private:
    Texture(const std::string &file)
    {
        //...load texture from file...
        TexPool.insert(this);
    }
    virtual ~Texture()
    {
        TexPool.erase(this);
    }
    freind CreateTextureFromFile;
};
Texture *CreateTexture(const std::string &file)
{
    TexPoolIterator i = TexPool.find(file);
    if(i != TexPool.end())return *i;
    else return new Texture(file);
}
ThreadA                                ThreadB
t = CreateTexture("ball.png");
t->IncRef();
...use t...                            t2 = CreateTexture("ball.png");//returns *t
...                                    thread suspended...
t->DecRef();//deletes t                ...
...                                    t2->IncRef();//ERROR

So I guess I need to change the ref counting model entirely, the reason I added a ref after the return in the design was to support things like the following:

MyObj->GetSomething()->GetSomethingElse()->DoSomething();

rather than having to:

SomeObject a = MyObj->GetSomething();
AnotherObject *b = a->GetSomethingElse();
b->DoSomething();
b->DecRef();
a->DecRef();

Is there a clean way for fast reference counting in c++ in a multi threaded environment?

Answer

Edouard A. picture Edouard A. · Jul 16, 2009

Make the reference counting atomic and you won't need any lock. In Windows ::InterlockedIncrement and ::InterlockedDecrement can be used. In C++ 0x, you have atomic<>.