Как std :: atomic_ref реализован для неатомарных типов? - PullRequest
8 голосов
/ 11 октября 2019

Мне интересно, как можно эффективно реализовать std::atomic_ref (по одному std::mutex на объект) для неатомарных объектов, так как следующее свойство кажется довольно сложным для применения:

Атомарные операции, применяемые к объекту через atomic_ref, являются атомарными по отношению к атомарным операциям, применяемым через любой другой atomic_ref, ссылающийся на тот же объект.

В частности, следующий код:

void set(std::vector<Big> &objs, size_t i, const Big &val) {
    std::atomic_ref RefI{objs[i]};
    RefI.store(val);
}

Кажется довольно сложным для реализации, поскольку std::atomic_ref нужно будет каким-то образом каждый раз выбирать один и тот же std::mutex (если только это не большая мастер-блокировка, общая для всех объектов одного типа).

AmЯ что-то пропустил? Или каждый объект отвечает за реализацию std::atomic_ref и, следовательно, либо должен быть атомарным, либо иметь std::mutex?

Ответы [ 2 ]

6 голосов
/ 11 октября 2019

Реализация может использовать хэш на основе адреса объекта, чтобы определить, какой из набора блокировок получить при выполнении операции.

5 голосов
/ 12 октября 2019

Реализация в значительной степени точно такая же, как std::atomic<T> сама. Это не новая проблема.

См. Где находится блокировка для std :: atomic? Типичная реализация std::atomic / std::atomic_ref статическая хеш-таблица блокировок, проиндексированнаяпо адресу, для объектов без блокировки. Хеш-коллизии приводят только к дополнительному конфликту, а не к проблеме корректности. (Взаимные блокировки все еще невозможны; блокировки используются только атомарными функциями, которые никогда не пытаются брать 2 одновременно.)

Например, в GCC std::atomic_ref - это просто еще один способ вызвать __atomic_store вобъект. (См. Руководство GCC: атомарные встроенные функции )

Компилятор знает, достаточно ли T достаточно мал, чтобы не было блокировки или нет. Если нет, то она вызывает функцию библиотеки libatomic, которая будет использовать блокировку.

(забавный факт: это означает, что это работает, только если объект имеет достаточное выравнивание для atomic<T>. Но на многих 32-битных платформах, включая x86, uint64_t может иметь только 4-байтовое выравнивание. atomic_ref на таком объекте будет компилироваться и запускаться, но на самом деле не будет атомарным, если компилятор использует 8-байтовую загрузку / сохранение SSE в 32-битном режиме для его реализации. К счастью, нет опасности для объектов с alignof(T) == sizeof(T), как у большинства примитивных типов в 64-битных архитектурах.)

...