Как std::atomic_ref реализован для неатомарных типов?

Мне интересно, как можно 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 (если только это не большая главная блокировка, используемая всеми объектами одного типа).

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

2 ответа

Решение

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

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

Например, в 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-битных архитектурах.)

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

Другие вопросы по тегам