views:

312

answers:

0

I'm very interested in the user-space RCU (read-copy-update), and trying to simulate one via tr1::shared_ptr, here is the code, while I'm really a newbie in concurrent programming, would some experts help me to review?

The basic idea is, reader calls get_reading_copy() to gain the pointer of current protected data (let's say it's generation one, or G1). writer calls get_updating_copy() to gain a copy of the G1 (let's say it's G2), and only one writer is allowed to enter the critical section. After the updating is done, writer calls update() to do a swap, and make the m_data_ptr pointing to the G2 data. The ongoing readers and the writer now hold the shared_ptr(s) of G1, and either a reader or a writer will eventually deallocate the G1 data.

Any new readers would get the pointer to G2, and a new writer would get the copy of G2 (let's say it's G3). It's possible the G1 is not released yet, so multiple generations of data may co-exist.

template <typename T>
class rcu_protected
{
public:
    typedef T                                   type;
    typedef const T                             const_type;
    typedef std::tr1::shared_ptr<type>          rcu_pointer;
    typedef std::tr1::shared_ptr<const_type>    rcu_const_pointer;

    rcu_protected() : m_is_writing(0),
                      m_is_swapping(0),
                      m_data_ptr (new type())
    {}

    rcu_const_pointer get_reading_copy ()
    {
        spin_until_eq (m_is_swapping, 0);

        return m_data_ptr;
    }

    rcu_pointer get_updating_copy ()
    {
        spin_until_eq (m_is_swapping, 0);

        while (!CAS (m_is_writing, 0, 1))
        {/* do sleep for back-off when exceeding maximum retry times */}

        rcu_pointer new_data_ptr(new type(*m_data_ptr));

        // as spin_until_eq does not have memory barrier protection,
        // we need to place a read barrier to protect the loading of
        // new_data_ptr not to be re-ordered before its construction
        _ReadBarrier();

        return new_data_ptr;
    }

    void update (rcu_pointer new_data_ptr)
    {
        while (!CAS (m_is_swapping, 0, 1))
        {}

        m_data_ptr.swap (new_data_ptr);

        // as spin_until_eq does not have memory barrier protection,
        // we need to place a write barrier to protect the assignments of
        // m_is_writing/m_is_swapping be re-ordered bofore the swapping
        _WriteBarrier();

        m_is_writing = 0;
        m_is_swapping = 0;
    }

private:
    volatile long m_is_writing;
    volatile long m_is_swapping;
    rcu_pointer m_data_ptr;
};