Thread: Interlocked refcounting and possible crash

Threaded View

Previous Post Previous Post   Next Post Next Post
  1. #1
    Registered User
    Join Date
    Aug 2010
    Location
    Poland
    Posts
    733

    Question Interlocked refcounting and possible crash

    Hi there,

    my problem is related to thread-safe reference counting. I know this has been asked thousands of times as I did some google search. But there is a problem which I couldn't find on the internet and which I can't solve without critical sections (?). I noticed this might happen not only in my code as I looked at others' implementations.

    I have my own library with smart pointers (I know about boost, but I want to use my own), shared_ptr and instrusive_ptr which work pretty well.
    Here is the important part of my shared_ptr:


    Code:
    template <class base>
    class shared_ptr {
    private:
        typedef base* P_BASE;
        typedef base& R_BASE;
        /*
            Block with ref count and a pointer, self-explanatory
        */
        class block {
        private:
            P_BASE m_ptr;
            REF_COUNT m_refcount;
        // constructor & destructor
        public:
            block(P_BASE ptr)
                : m_ptr(ptr), m_refcount(1)
            {
            }
            ~block()
            {
                if (m_ptr != NULL)
                {
                    delete m_ptr;
                }
            }
        // inline methods
        public:
            inline P_BASE get_ptr() const
            {
                return m_ptr;
            }
            inline bool add_ref()
            {
                return InterlockedIncrement(&m_refcount) > 1; //return win32::interlocked_add_ref(m_refcount);
            }
            inline bool release()
            {
                return InterlockedDecrement(&m_refcount) == 0; //return win32::interlocked_release(m_refcount);
            }
        };
        typedef block* P_BLOCK;
    // fields
    private:
        P_BLOCK m_block;
    // methods
    public:
        void reset(P_BASE ptr)
        {
            if (m_block != NULL)            // if block doesnt exists
            {
                if (m_block->release())     // decrement ref count, returns true if reached 0
                {
                    delete m_block;         // deletes block
                }
                m_block = NULL;
            }
            if (ptr != NULL)                // creates new block, not important
            {
                m_block = new block(ptr);
            }
        }
    // operators
    public:
        shared_ptr& operator = (const shared_ptr& a)
        {
            reset(NULL);
            if (a.m_block != NULL)          // (IMPORTANT #1)
            {                               // ... HERE ANOTHER THREAD MIGHT HAVE CALLED delete m_block
                                            // (for example, by calling: reset(NULL) on 'a' object
                if (a.m_block->add_ref())   // (IMPORTANT #2) CRASH ??
                {
                    // ... if successfully added reference (adding 1 to 0 means it was being destructed)
                    m_block = a.m_block;
                }
            }
            return *this;
        }
    };


    It works in similar way to boost::shared_ptr, except it has a pointer to PTR - REFCOUNT block instread of two pointers (because each shared_ptr instance occupies less memory, it's important to me).

    And now:

    STARTUP:

    shared_ptr<int> a;
    a = new int(100); // ref count is 1 now (a.m_block->m_refcount == 1)

    THREAD 1:

    shared_ptr<int> b = a; // attemping to increment ref count first

    THREAD 2 (executing at the same time)

    a = NULL;


    and now:
    * THREAD 1 has been suspended after executing line (IMPORTANT #1)
    * THREAD 2 has successfully called release() and deleted the block
    * THREAD 1 is trying to execute line (IMPORTANT #2) but a.m_block points to an already deleted block

    I've been considering the following:

    * make delete only block->ptr instead of whole block, but it requires additional ref count (problem wouldn't disappear?)

    * make static critical section (in the template, so for each type it will be a seperate one), problem appears when there is a lot of instances or when one shared_ptr destructor calls other (for example, in deleting trees)

    * EDIT: yet another idea to make threaded_ptr with critical section and use it as a main node, I think it's the best solution


    Note:

    There is a win32 wrapper class (win32:, because this code is going to be cross-platform and used across various compilers, so I can't use inline assembly and I would really like to make it LOCK-FREE.

    Any ideas?

    Thanks in advance
    Last edited by kmdv; 08-01-2010 at 05:21 AM.

Popular pages Recent additions subscribe to a feed

Tags for this Thread