2015-04-28 4 views
3

Я думал о возвращаемых значениях этих двух функций. Возвращаемое значение функции __sync_bool_compare_and_swap, по-видимому, имеет очевидные преимущества, то есть я могу использовать его для определения того, имела ли место операция свопинга. Однако я не вижу хорошего использования возвращаемого значения __sync_val_compare_and_swap.__sync_val_compare_and_swap vs __sync_bool_compare_and_swap

Во-первых, позволяет иметь функцию подписи для справки (от GCC документы минус вар арг):

type __sync_val_compare_and_swap (type *ptr, type oldval type newval); 

Проблема, которую я вижу, что возвращаемое значение __sync_val_compare_and_swap является старое значение * PTR. Точнее, это значение, которое было замечено в реализации этой функции после того, как были установлены соответствующие барьеры памяти. Я прямо заявляю это, чтобы удовлетворить тот факт, что между вызовом __sync_val_compare_and_swap и выполнением инструкций для обеспечения соблюдения барьера памяти значение * ptr может легко измениться.

Теперь, когда функция возвращает то, что я могу сделать с этим возвращаемым значением? Нет смысла пытаться сравнить его с * ptr, потому что теперь можно изменить ptr на других потоках. Точно так же сравнение newval и * ptr действительно не помогает мне (если я не блокирую * ptr, что, вероятно, подрывает мое использование атоматики в первую очередь).

Так что все, что мне осталось сделать, это спросить, действительно ли возвращается значение == oldval, которое эффективно (см. Ниже для оговорки), спрашивая, имела место операция свопинга. Поэтому я мог бы просто использовать __sync_bool_compare_and_swap.

Предостережение, о котором я только что упомянул, состоит в том, что единственное, что я вижу здесь, это то, что это не говорит мне, произошел ли обмен или нет, он просто говорит мне, что в какой-то момент до того, ptr имеет то же значение, что и newval. Я рассматриваю возможность, что oldval == newval (хотя я бы изо всех сил пытался эффективно реализовать функцию, чтобы сначала проверить эти значения и не менять их, если они были одинаковыми, так что это, вероятно, спорный вопрос). Однако я не вижу ситуации, когда знание этой разницы повлияло бы на меня на сайте вызова. На самом деле, я не могу представить ситуацию, когда я ставил бы oldval и newval равными.

Мой вопрос таким образом:

Стоит ли случай, в котором с помощью __sync_val_compare_and_swap и __sync_bool_compare_and_swap не будут эквивалентны, т.е. существует ситуация, когда один дает больше информации, чем другой?

ASIDE

Причина, я думал об этом было то, что я нашел реализацию __sync_val_compare_and_swap с точки зрения sync_bool_compare_and_swap, который имеет гонку:

inline int32_t __sync_val_compare_and_swap(volatile int32_t* ptr, int32_t oldval, int32_t newval) 
{ 
    int32_t ret = *ptr; 
    (void)__sync_bool_compare_and_swap(ptr, oldval, newval); 
    return ret; 
} 

Гонка быть на хранение * ptr в ret, поскольку * ptr может измениться до вызова __sync_bool_compare_and_swap. Это заставило меня понять, что я, похоже, не безопасен (без дополнительных барьеров или блокировок) реализации __sync_val_compare_and_swap с точки зрения sync_bool_compare_and_swap. Это заставило меня думать, что первый должен предоставить больше «информации», чем последний, но по моему вопросу я не вижу, что это действительно так.

+0

Одно использование: возвращаемое текущее значение может использоваться для повтора. Я знаю, что есть атомарные приращения, но для иллюстрации предположим, что вы хотите реализовать это. Вы можете просто вызвать val_compare_and_swap со значением oldvalue как pre-increment и newvalue как pos-increment. Если вам это удастся, вы закончите. Если нет, повторите попытку, увеличив возвращаемое значение и снова вызовите val_compare_and_swap. С версией bool вам нужно будет сделать дополнительное чтение текущего значения для повтора. – kaylum

+0

Вы правы, что код в вашем «в сторону» является ошибкой/имеет серьезное состояние гонки. Для правильной работы требуется петля повтора. См. Мой ответ. –

ответ

3

Операция обеспечивается __sync_val_compare_and_swap всегда могут быть реализованы в терминах __sync_bool_compare_and_swap (и, конечно, в другом направлении, очевидно, возможно), поэтому с точки зрения мощности два эквивалентны.Однако внедрение __sync_val_compare_and_swap в терминах __sync_bool_compare_and_swap не очень эффективно. Это выглядит примерно так:

for (;;) { 
    bool success = __sync_bool_compare_and_swap(ptr, oldval, newval); 
    if (success) return oldval; 
    type tmp = *ptr; 
    __sync_synchronize(); 
    if (tmp != oldval) return tmp; 
} 

Дополнительная работа необходима, потому что вы могли наблюдать отказ от __sync_bool_compare_and_swap, но затем прочитать новое значение *ptr, что происходит, чтобы соответствовать oldval.

Что касается почему вы могли бы предпочесть __sync_val_compare_and_swap поведения, значение, которое вызвало сбой может дать вам отправную точку, чтобы более эффективно повторить операцию или могут указывать значимую причины сбоя для некоторой операции, которая выиграла» «повторить». В качестве примера смотрите код pthread_spin_trylock в MUSL LIBC (для которого я являюсь автором):

http://git.musl-libc.org/cgit/musl/tree/src/thread/pthread_spin_trylock.c?id=afbcac6826988d12d9a874359cab735049c17500

Там a_cas эквивалентно __sync_val_compare_and_swap. В некотором роде это глупый пример, так как он просто сохраняет ветвь или условное перемещение, используя старое значение, но есть и другие ситуации, когда возможны несколько старых значений и знание того, что вызвало операцию с ошибкой.

+0

Я не уверен, что я понимаю вашу реализацию __sync_val_compare_and_swap. Почему вы зацикливаетесь? Мое понимание __sync_bool_compare_and_swap заключается в том, что он попытается поменять местами только один раз, а «отказ» означает oldval! = * Ptr, а не то, что функция каким-то образом не сработала. Что касается линий от типа tmp = * ptr; вперед, мне кажется, что там есть гонка, но я буду ждать вашего ответа сначала, так как это может что-то прояснить. Спасибо, «почему» часть вашего ответа. –

+0

@AndrewParker: поскольку для обеспечения рабочей реализации '__sync_val_compare_and_swap', вы ** должны ** возвращать значение, которое заостренный объект имел в течение некоторого времени во время операции, которая не равна аргументу' oldval'. Найти такое значение невозможно без цикла, пока вы его не найдете. Представьте себе, что другой параллельный поток быстро переключает (посредством действительных атомных свопов) значение объекта между «oldval» и некоторым другим скрытым значением. –

+0

Я вижу. Одна последняя проблема беспокоит меня. * ptr может измениться, прежде чем вы сможете сравнить его с oldval, но он может измениться на другое значение, которое не равно oldval. Таким образом, эта реализация может вернуть значение, которое не имеет отношения к тому, почему своп был неудачным. Это педантичная точка, но мне интересно, противоречит ли она спецификации функции. Процитировать страницы GCC: «Версия« val »возвращает содержимое * ptr перед операцией». Эта реализация не будет возвращать содержимое * ptr ** до ** op. –