Разобрался.
Есть 2 замечания:
1. Поближе посмотрел на сигнатуру
bool __atomic_compare_exchange (type *ptr, type *expected, type *desired, bool weak, int success_memorder, int failure_memorder)
Короче говоря, мне нужно было передавать не указатели, а указатели на указатели. Исправил:
if (__atomic_compare_exchange(&l.head->next, (struct entry**)NULL, &new_next, 1, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
Но потом начал ловить SEGFAULT. И тут пришел к 2 замечанию
2. Не заметил следующего в документации:
This built-in function implements an atomic compare and exchange operation. This compares the contents of *ptr with the contents of *expected. If equal, the operation is a read-modify-write operation that writes desired into *ptr. If they are not equal, the operation is a read and the current contents of *ptr are written into *expected. weak is true for weak compare_exchange, which may fail spuriously, and false for the strong variation, which never fails spuriously. Many targets only offer the strong variation and ignore the parameter. When in doubt, use the strong variation.
Т.е. если значение во 2 аргументе не равно значению из 1, то (!!!) по месту указателя 2 записывается полученное из указателя 1 значение. А у меня там был NULL (конец списка обозначается NULL). Заменил на корректно выделенный элемент списка и все заработало
int main(int argc, char const *argv[])
{
struct list l;
l.head = (struct entry *)malloc(sizeof(struct entry));
l.head->next = NULL;
l.head->value = 123;
struct entry *old_next = (struct entry *)malloc(sizeof(struct entry));
old_next->next = NULL;
old_next->value = 555;
l.head->next = old_next;
struct entry *new_next = (struct entry *)malloc(sizeof(struct entry));
new_next->value = 444;
new_next->next = NULL;
if (__atomic_compare_exchange(&l.head->next, &old_next, &new_next, 1, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
{
printf("ok\n");
printf("success: next = %d\n", l.head->next->value);
}
else
{
printf("failed\n");
}
return 0;
}
Не хочу возиться со всякими dummy узлами, поэтому буду использовать
__sync_bool_compare_and_swap. Вот такой код сработает:
int main(int argc, char const *argv[])
{
struct list l;
l.head = (struct entry *)malloc(sizeof(struct entry));
l.head->next = NULL;
l.head->value = 123;
struct entry *new_next = (struct entry *)malloc(sizeof(struct entry));
new_next->value = 444;
new_next->next = NULL;
if (__sync_bool_compare_and_swap(&l.head->next, (struct entry *)NULL, new_next))
{
printf("ok\n");
printf("success: next = %d\n", l.head->next->value);
}
else
{
printf("failed\n");
}
return 0;
}
UPD: спасибо
res2001, разобрался лучше. итоговый вариант
#include <stdatomic.h>
int main(int argc, char const *argv[])
{
struct list l;
l.head = (struct entry *)malloc(sizeof(struct entry));
l.head->next = NULL;
l.head->value = 123;
struct entry *new_next = (struct entry *)malloc(sizeof(struct entry));
new_next->value = 444;
new_next->next = NULL;
struct entry *tmp = NULL;
if (atomic_compare_exchange_strong(&l.head->next, &tmp, new_next))
{
printf("ok\n");
printf("success: next = %d\n", l.head->next->value);
}
else
{
printf("failed\n");
}
return 0;
}
Просто надо создать переменную указатель, которой присвоить NULL. Также использовал atomic_compare_exchange_strong из stdatomic вместо расширения GCC - он под капотом использует __atomic_* вместо устаревшего __sync_*