C - изменчивый квалификатор при удержании блокировки

Нужен ли квалификатор volatile для переменных, доступ к которым возможен только при удержании блокировки? В этом коде можно удалить квалификатор volatile из n возможно изменить поведение, когда concurrent_foo выполняется одновременно.

#ifndef __GNUC__
#error __sync_lock builtins are only available with GCC
#endif

volatile int n = 0;
static volatile int lock = 0;

void concurrent_foo () {
    while (__sync_lock_test_and_set (&lock, 1));
    // Non-atomic operation, protected by spinlock above.
    int x = n % 2 + 1;
    n = n + x;
    __sync_lock_release (&lock);
}

Я понимаю, что определитель volatile указывает компилятору не оптимизировать доступ к памяти для переменной. Я также понимаю, что встроенные функции __sync_lock создают (полный?) Барьер памяти, доступ к которому не должен пересекаться. Тем не менее, в этом примере было бы безопасно получить код nкэшируйте его в регистре, вычисляйте новое значение и затем записывайте обратно n,

Компиляция с GCC для источника i686 с использованием -O3 показывает, что две выборки памяти сделаны, необязательно:

concurrent_foo:
        movl        $1, %edx
.L2:
        movl        %edx, %eax
        xchgl        lock, %eax
        testl        %eax, %eax
        jne        .L2
        movl        n, %eax
        movl        n, %edx
        movl        %eax, %ecx
        shrl        $31, %ecx
        addl        %ecx, %eax
        andl        $1, %eax
        subl        %ecx, %eax
        leal        1(%edx,%eax), %eax
        movl        %eax, n
        movl        $0, lock
        ret

Без квалификатора volatile я получаю немного другой код, где n выбирается только один раз:

concurrent_foo:
        movl        $1, %edx
.L2:
        movl        %edx, %eax
        xchgl        lock, %eax
        testl        %eax, %eax
        jne        .L2
        movl        n, %edx
        movl        %edx, %ecx
        shrl        $31, %ecx
        leal        (%edx,%ecx), %eax
        andl        $1, %eax
        subl        %ecx, %eax
        leal        1(%edx,%eax), %eax
        movl        %eax, n
        movl        $0, lock
        ret

В обоих случаях доступ к памяти n происходят, пока блокировка удерживается, и поэтому должны быть "правильными". Однако я не уверен, действительно ли мне это гарантировано. Изменчивый квалификатор предотвращает оптимизацию производительности, которую я хотел бы, и не будет влиять на результат операции (ни в коем случае не будет n быть четным).

0 ответов

Другие вопросы по тегам