Требование atomic_thread_fence в блокировке последовательности - PullRequest
1 голос
/ 03 апреля 2019

Я пишу простую блокировку последовательности , и после прочтения документации по c ++ по атомарности и atomic_thread_fence кажется, что мне нужен забор. Тем не менее, я написал это без забора, и у меня есть довольно строгий тест, который, кажется, проходит без него.

Основная концепция блокировки последовательности выглядит следующим образом. У вас есть один писатель, который постоянно обновляет часть общей памяти на месте. Это связывает увеличивающийся порядковый номер с данными. Перед тем, как начать запись, он увеличивается один раз, а когда запись закончится, он снова увеличивается.

На стороне считывателя, прежде чем он начнет просматривать данные, он ждет, когда порядковый номер станет четным (если он нечетный, это означает, что мы находимся в середине записи). Затем он отслеживает порядковый номер, который он видит в начале, копирует данные локально, а затем снова проверяет порядковый номер, когда это будет сделано. Если они совпадают, то данные являются чистыми и пригодными для использования. Если они не совпадают, то мы подвержены частичному чтению и пытаемся снова.

Номер секвенсора хранится в std :: atomic. Автор обновляет его хранилищем (std :: memory_order_release) в начале и конце блока. Читатель читает порядковый номер с загрузкой (std :: memory_order_acquire) в начале и конце чтения.

Данные, о которых идет речь, должны быть скопированы с помощью функции writer -> reader полностью независимой от атомарной последовательности, поэтому у меня сложилось впечатление, что мне может понадобиться ограждение для синхронизации данных без зависимостей ... но, похоже, работать независимо от того, что я бросаю на это. Я работаю на многоядерной / многоядерной машине Intel Xeon, может, она работает именно на этом процессоре?

Фрагменты кода:

void SequenceLock::writeLock()
{
    m_seqno.store( m_seqno.load( std::memory_order_relaxed ) + 1, std::memory_order_release );
}

void SequenceLock::writeUnlock()
{
    m_seqno.store( m_seqno.load( std::memory_order_relaxed ) + 1, std::memory_order_release );
}

uint64_t SequenceLock::enterRead() const
{
    uint64_t seqno;
    while( ( seqno = m_seqno.load( std::memory_order_acquire ) ) & 1 ) {} //no yield, expect readers to be pinned
    return seqno;
}

bool SequenceLock::validateRead( uint64_t prevSeqno ) const
{
    return m_seqno.load( std::memory_order_acquire ) == prevSeqno;
}


//sample writer
lock.writeLock()
//update some shared data
lock.writeUnlock()

//sample reader
uint64_t seqno;
do {
    seqno = lock.enterRead()
    //copy shared data locally
} while( !lock.validateRead( seqno ) )

РЕДАКТИРОВАТЬ: все еще странно, я изменил всю память, доступную выше, на memory_order_relaxed, и он все еще , казалось, работал ... странно

...