Давайте сделаем это шаг за шагом. Прежде всего, попробуем использовать __sync_val_compare_and_swap в простом случае:
#include <stdio.h>
typedef struct node
{
int data;
struct node * next;
struct node * backlink;
}node_lf;
int cs1(node_lf *address, int old_val, int new_val)
{
int *p2 = &address->data;
int p3 = __sync_val_compare_and_swap(p2, old_val, new_val);
return p3;
}
int main()
{
node_lf n;
n.data = 17;
int res = cs1(&n, 1, 2);
printf("Old Value: %d Cur Value: %d\n", res, n.data);
res = cs1(&n, 17, 2);
printf("Old Value: %d Cur Value: %d\n", res, n.data);
}
Итак, код здесь довольно прост. Мы манипулируем node.data. Мы начинаем с инициализации его значением 17. Затем мы пытаемся выполнить cas, чтобы изменить его на 2. Однако при первом вызове мы даем неверное значение для oldval (1 против 17). В результате __sync_val_compare_and_swap (правильно!) не выполняет своп, а возвращает текущее значение. Второй вызов, который дает правильное старое значение, выполняет замену и возвращает старое значение.
Итак, при запуске получаем:
Old Value: 17 Cur Value: 17 <- Didn't change the value
Old Value: 17 Cur Value: 2 <- Did change the value
Достаточно просто, но не совсем то, что вы ищете. Давайте немного приблизим его:
#include <stdio.h>
typedef struct node
{
int data;
struct node * next;
struct node * backlink;
}node_lf;
typedef struct csArg
{
node_lf * node;
}cs_arg;
node_lf *cs2(node_lf *address, const cs_arg *old_val, const cs_arg *new_val)
{
unsigned long long *p2 = (unsigned long long *)&address->next;
unsigned long long p3 = __sync_val_compare_and_swap (p2, (unsigned long long)old_val->node, (unsigned long long)new_val->node);
return (node *)p3;
}
int main()
{
node_lf n;
cs_arg oldval, newval;
n.next = (node *)18;
oldval.node = (node *)1;
newval.node = (node *)2;
node *res2 = cs2(&n, &oldval, &newval);
printf("Old Value: %p Cur Value: %p\n", res2, n.next);
oldval.node = (node *)18;
res2 = cs2(&n, &oldval, &newval);
printf("Old Value: %p Cur Value: %p\n", res2, n.next);
}
В этом случае мы пытаемся обновить node.next, используя узлы в 2 cs_args. Здесь важно отметить, что, поскольку __sync_val_compare_and_swap работает с целочисленными типами, нам нужно привести указатели к типу int соответствующего размера. Предполагается, что мы используем 64-битный компилятор, поэтому указатели имеют размер 8 байт (то же самое, что и unsigned long long).
Здесь нет никакой необходимости использовать структуру cs_arg. Использование узла * должно работать нормально. Но вы использовали его, так что, по-видимому, здесь есть какой-то долгосрочный план.
Запустив это, мы видим:
Old Value: 0000000000000012 Cur Value: 0000000000000012 <- Didn't change the value
Old Value: 0000000000000012 Cur Value: 0000000000000002 <- Did change the value
Обратите внимание, что можно увеличить это значение до 16 байт (используя __int128 и cmpxchg16b), если вы используете x64. Тем не менее, есть некоторые хитрости, о которых следует знать. Например, данные должны быть выровнены по 16 байтам (что НЕ является node.next).
Теперь, проделав все это, ваше требование о том, что вам «нужно старое значение address->node->next в качестве возвращаемого значения», кажется подозрительным. Глядя на приведенный выше код, как определить, что cas не сработал? Если он работает, он возвращает 18, а если нет, он возвращает 18. Поскольку это то, что вы сказали, что хотели, это то, что я пытался предоставить.
Но я предполагаю, что вы хотите знать, сработало ли это (возможно, чтобы попробовать еще раз). В таком случае, возможно, что-то более похожее на это:
#include <stdio.h>
typedef struct node
{
int data;
struct node * next;
struct node * backlink;
}node_lf;
bool cs2(node_lf *address, const node *old_val, const node *new_val)
{
unsigned long long *p2 = (unsigned long long *)&address->next;
return __sync_bool_compare_and_swap (p2, (unsigned long long)old_val, (unsigned long long)new_val);
}
int main()
{
node_lf n;
node *oldval, *newval;
n.next = (node *)18;
oldval = (node *)1;
newval = (node *)2;
bool res2;
do {
printf("Trying to change %p from %p to %p: ", n.next, oldval, newval);
res2 = cs2(&n, oldval, newval);
printf("Worked: %d Cur Value: %p\n", res2, n.next);
if (res2)
break;
oldval = n.next;
} while (1);
}
Когда вы выйдете из цикла, oldval будет тем, что было раньше (должно быть, иначе cas не сработает), newval будет тем, что на самом деле было записано. Обратите внимание, что если бы это действительно было многопоточным, нет никакой гарантии, что newval будет таким же, как n.next, поскольку другой поток уже мог появиться и изменить его снова.
Хотя использование ассемблера может сэкономить вам одну или две инструкции, выигрыш с точки зрения удобочитаемости, ремонтопригодности, переносимости и т. д. почти наверняка стоит затрат.
Если только это не классный проект, где учителю требуется встроенный ассемблер. В этом случае посмотрите https://stackoverflow.com/a/37825052/2189500.
person
David Wohlferd
schedule
16.06.2016
cmpxchg8b
. Есть ли справочное руководство, где я могу прочитать оcmpxchg8b
с использованием встроенного ассемблера? - person Sumant   schedule 14.06.2016%0
относится к 0-му параметру (также известному какvalue
). Эта (неинициализированная) переменная, вероятно, не та, с которой вы собирались сравнивать. Сообщение об ошибке связано с использованием q, который на i386 представляет собой 8-битное значение (прокрутите вниз до i386). - person David Wohlferd   schedule 15.06.2016__sync_bool_compare_and_swap
, так как он возвращает логическое значение. Мне нужно старое значениеaddress->node->next
в качестве возвращаемого значения. Будет ли приведенный выше код работать, если я изменю=q
на=d
и%0
на%1
, поскольку мне нужно сравнитьaddress->node->next
? - person Sumant   schedule 15.06.2016(int *)
в качестве адреса. Я пытался задать этот вопрос здесь link а> - person Sumant   schedule 15.06.2016