Барьер volatile и компилятора с встроенной сборкой gcc

В нашем продукте у нас есть встроенная реализация мьютекса, использующая множество специфичных для платформы и компилятора методов для конкретных аппаратных частей. Одно из наших «правил» для чрезмерно оптимизированного кода, пытающегося «обмануть», заключается в том, что если доступ к переменной осуществляется снаружи и внутри мьютекса, то эта переменная должна быть объявлена ​​изменчивой. Я полагал, что это относится и к реализации непрозрачных мьютексов (например, pthread_mutex_lock/unlock), и это привело к интересным дебатам.

Один человек утверждал, что это указывает на ошибку компилятора (особенно когда реализация мьютекса встроена и «не непрозрачна» для компилятора). Я привел следующий пример, чтобы оспорить это

int v = pSharedMem->myVariable ;

__asm__ __volatile__(( "isync" : : :"memory" ))

v = pSharedMem->myVariable ;

В этом фрагменте gcc-кода LinuxPPC компилятор ничего не знает об эффектах isync во время выполнения, кроме того, что мы можем сказать ему через ограничение памяти. Вы найдете такую ​​инструкцию isync в хвостовой части захвата мьютекса, чтобы предотвратить любое выполнение инструкций, следующих за успешным захватом мьютекса до фактического удержания мьютекса (поэтому, если загрузка была выполнена до isync, она бы придется отбросить).

В этом фрагменте кода у нас есть барьер компилятора, который предотвращает переписывание кода, как если бы это было следующее

int v = pSharedMem->myVariable ;
v = pSharedMem->myVariable ;

__asm__ __volatile__(( "isync" : : :"memory" ))

or

__asm__ __volatile__(( "isync" : : :"memory" ))

int v = pSharedMem->myVariable ;
v = pSharedMem->myVariable ;

(т.е. оба этих переупорядочения компилятора должны быть запрещены атрибутом volatile)

У нас также есть сам isync, который предотвращает первое переупорядочение во время выполнения (но я не думаю, что предотвращает второе, что не так интересно).

Однако мой вопрос заключается в том, что если myVariable не объявлена ​​volatile, достаточно ли ограничения «памяти», чтобы gcc обязательно перезагрузил «v» после isync? Я бы по-прежнему был склонен использовать volatile для такого шаблона, поскольку такой код слишком чувствителен ко всем встроенным компиляторам для конкретных платформ. Тем не менее, если мы сведем обсуждение только к GCC и этому фрагменту кода, достаточно ли этого ограничения памяти asm, чтобы иметь код, который генерируется с парой загрузок вместо одной?


person Peeter Joot    schedule 24.06.2011    source источник


Ответы (1)


__asm__ __volatile__ с "memory" требуется и будет действовать как полный барьер переупорядочивания. volatile в переменной не требуется. Фактически, если вы посмотрите на определение ядра Linux atomic_t, он не использует никаких модификаторов volatile и полностью полагается на операторы __asm__ __volatile__ с соответствующими ограничениями.

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

person Jan Hudec    schedule 24.06.2011
comment
Да, но я говорю не о volatile на атомарном или блокировочном слове, а на данных, защищенных мьютексом. Я также не подразумеваю, что volatile имеет какое-либо значение синхронизации (т.е. синхронизация обеспечивается мьютексом, и меня интересуют данные, защищенные мьютексом). Если доступ к данным также осуществляется за пределами мьютекса, требуется volatile для принудительной перезагрузки значения после захвата мьютекса. - person Peeter Joot; 24.06.2011
comment
Вот некоторый дополнительный контекст (исходное обсуждение, породившее этот вопрос) peeterjoot.wordpress.com/2011/06/21/ - person Peeter Joot; 24.06.2011
comment
@Peeter: я почти уверен, что образец неправильный. Любой невстроенный вызов функции всегда вызывает повторную выборку нелокальных данных из памяти, потому что компилятор не может быть уверен, что функция не имеет простого указателя на эти данные и просто записывает их, что может произойти в однопоточном случае. и там семантика определена. С другой стороны, этот код неверен с volatile или без него, потому что на некоторых платформах чтение не является атомарным. Вам необходимо использовать специальные синхронизированные атомарные операции при доступе к общим переменным без блокировки. - person Jan Hudec; 24.06.2011
comment
@ Ян, ты прав насчет необходимости повторной выборки после вызова не встроенного кода, но в этом случае есть ссылка только на локальную переменную без псевдонима v, и я думаю, что ее значение всегда обновляется , Питер прав, говоря, что барьера компилятора недостаточно. Я тоже думаю, что вам нужно изменчиво квалифицировать источник данных, которые перехватываются в v local - person abigagli; 01.01.2012