Возможно, объявление функции смутило вас. Эта функция не возвращает указатель на старое значение (чего?), Но старое значение из памяти указывает target
(который действительно должен быть указателем на void *, т.е. void* volatile * target
).
Обычно, если примитив CAS возвращает старое значение, а не bool, вы проверяете успех CAS следующим образом:
void* atomic_ptr; // global atomically modified pointer
void* oldval, newval, comparand; // local variables
/* ... */
oldval = atomic_cas_ptr( (void*)&atomic_ptr, /* note that address is taken */
comparand, newval );
if( oldval == comparand ) {
// success
} else {
// failure
}
Таким образом, когда вы сравниваете old_val и сравнение, вы работаете с локальными переменными, которые не изменяются одновременно (хотя глобальный atomic_ptr может быть изменен снова), и вы сравниваете значения указателя без разыменования.
Функция, которую вы хотите, должна быть такой:
bool my_atomic_cas_ptr(volatile void* target, void* comparand, void* newval)
{
return (comparand == atomic_cas_ptr(target, comparand, newval));
}
Обратите внимание, что поскольку в некоторых алгоритмах должно быть известно старое значение (которое до CAS), лучше иметь примитив CAS, возвращающий старое значение, а не bool, поскольку вы можете легко построить последнее из первого, пока противоположность более сложна и неэффективна (см. следующий код, который пытается получить правильное старое значение из примитива MacOS CAS , который возвращает логическое значение).
void* CAS(void* volatile* target, void* comparand, void* newval)
{
while( !OSAtomicCompareAndSwapPtr(comparand, newval, target) ) {
void* snapshot = *target;
if( snapshot!=comparand ) return snapshot;
}
return comparand;
}
Что касается блокировки памяти CAS, она зависит от аппаратного обеспечения. Это было верно для старых процессоров x86, но в современных системах x86 все по-другому. Во-первых, нет центрального автобуса; его заменили AMD HyperTransport и Intel QuickPath Interconnect. Во-вторых, в последних поколениях ЦП заблокированные инструкции не все сериализуются (см. некоторые данные , показывающие, что заблокированные инструкции на разных адресах памяти не мешают). И наконец, в общепринятом определении свобода блокировки является гарантией прогресса всей системы, а не отсутствия сериализационной синхронизации .