Код: Выделить всё
struct stack
{
struct node { node* next; };
std::atomic head;
void push(node* new_node)
{
new_node->next = head.load(std::memory_order_relaxed);
// simplified: real code has an ABA counter in the low 12 bits of head
while (!head.compare_exchange_weak(new_node->next, new_node,
std::memory_order_release,
std::memory_order_relaxed)) {}
}
};
Код: Выделить всё
node* pop()
{
node* p = head_.load(std::memory_order_**acquire**);
while (p && !head_.compare_exchange_weak(p, p->next,
std::memory_order_**acq_rel**,
std::memory_order_**relaxed**));
return p;
}
Каковы правильные значения трех выделенных параметров?
Примечание: я знаю о проблеме ABA и в своем коде решаю(*) ее с помощью помеченных указателей, при этом младшие 12 бит заголовка являются счетчиком. Это распределитель страниц.
*solve = значительно уменьшает вероятность возникновения событий
После мысли (подробности см. в комментариях к ответу Питера):
- Похоже, что единственный способ иметь формально правильный список в C++26 — это:
(предотвратить данные Race UB) сделать node::next атомарным - (предотвратить строгое псевдонимирование UB) не выполнять неатомарный доступ к узлу; т. е. если вы используете список для пула страниц - часть страницы, содержащая узел, не может использоваться для других целей
- используйте версию pop() Питера; то есть приобрести+
Код: Выделить всё
relaxed+acquire
Подробнее здесь: https://stackoverflow.com/questions/797 ... ementation
Мобильная версия