В нашем продукте у нас есть встроенная реализация мьютекса, использующая множество специфичных для платформы и компилятора методов для конкретных аппаратных частей. Одно из наших «правил» для чрезмерно оптимизированного кода, пытающегося «обмануть», заключается в том, что если доступ к переменной осуществляется снаружи и внутри мьютекса, то эта переменная должна быть объявлена изменчивой. Я полагал, что это относится и к реализации непрозрачных мьютексов (например, 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, чтобы иметь код, который генерируется с парой загрузок вместо одной?