Использование shared_ptr для реализации RCU (чтение-копирование-обновление)? - PullRequest
4 голосов
/ 13 мая 2010

Я очень заинтересован в пользовательском пространстве RCU (чтение-копирование-обновление), и, пытаясь смоделировать его с помощью tr1 :: shared_ptr, вот код, хотя я действительно новичок в параллельном программировании. некоторые эксперты помогают мне пересмотреть?

Основная идея заключается в том, что читатель вызывает get_reading_copy (), чтобы получить указатель текущих защищенных данных (скажем, это поколение 1, или G1). Writer вызывает get_updating_copy (), чтобы получить копию G1 (скажем, G2), и только одному писателю разрешено войти в критическую секцию. После того, как обновление выполнено, писатель вызывает update (), чтобы выполнить обмен, и делает m_data_ptr, указывающую на данные G2. Постоянные читатели и писатель теперь владеют shared_ptr (s) G1, и либо читатель, либо писатель в конечном итоге освободят данные G1.

Любой новый читатель получит указатель на G2, а новый писатель получит копию G2 (скажем, это G3). Возможно, G1 еще не выпущен, поэтому могут сосуществовать несколько поколений данных.

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;
};

1 Ответ

1 голос
/ 17 ноября 2010

На первый взгляд, я бы обменял вызовы spin_until_eq и связанные спин-блокировки на мьютекс. Если бы в одном критическом разделе было разрешено более одного писателя, я бы использовал семафор. Эти реализации механизма параллелизма могут зависеть от ОС, поэтому следует учитывать и соображения производительности; обычно они лучше, чем занятые ожидания.

...