Как заставить g++ в Linux обновлять указатель потока (для TLS), когда сопрограмма переключает потоки?

Я использую собственную реализацию сопрограмм на C++ (компилятор g++, на ARM). Сопрограммы могут мигрировать из одного потока в другой, вызывая функцию move_to_thread (или другими способами, но это позволит мне высказать свою точку зрения). Я упрощаю, но это примерно так:

__thread int x = 0;

void f() {
    x = 5;
    // do some more work on current thread (thread 1, say)
    move_to_thread(2);
    // do more work, now on thread 2
    int y = x; // with optimization, I'm getting the wrong x
}

Проблема, с которой я сталкиваюсь, заключается в том, что работа, выполненная до и после вызова move_to_thread, использует локальные переменные потока (с использованием __thread). При компиляции с оптимизацией код, работающий в потоке 2, по-прежнему обращается к локальным переменным потока 1 вместо своих собственных. Это потому, что доступ к локальной переменной потока делает следующее:

  1. Найдите указатель потока TLS для текущего потока.
  2. Добавьте смещение TLS x к указателю потока
  3. Используйте память по этому адресу как x

Однако при включенной оптимизации (1) и, возможно, (2) оптимизируются для второго доступа, поскольку компилятор предполагает, что функция, которая начинает выполняться в определенном потоке, останется в этом потоке. Это предположение неверно для моего кода.

Как я могу заставить компилятор смотреть на правильное локальное хранилище потока как до, так и после вызова move_to_thread, не отказываясь полностью от оптимизации?


person davidthomas    schedule 06.11.2013    source источник
comment
Компилятор уже ищет правильный tls, так как он ничего не знает о ваших пользовательских потоках. Таким образом, ваши пользовательские потоки должны поставляться с локальным хранилищем пользовательских потоков.   -  person PlasmaHH    schedule 06.11.2013
comment
Мои пользовательские вещи не являются потоками. Локальное хранилище потока должно быть локальным для потока. Итак, когда сопрограмма работает в потоке 1 и обращается к локальному хранилищу потока, она должна получить хранилище потока 1. Когда он работает в потоке 2 и обращается к TLS, он должен получить хранилище потока 2. Мне не нужно локальное хранилище сопрограмм.   -  person davidthomas    schedule 06.11.2013
comment
Зачем вам сопрограммы и? Сопрограмме все равно, в каком потоке она находится, поэтому вы вообще не должны использовать для нее TLS.   -  person mah    schedule 06.11.2013
comment
Это хороший момент. Однако вопрос остается в силе. Есть ли способ надежно получить доступ к локальному хранилищу потока в этой ситуации?   -  person davidthomas    schedule 06.11.2013


Ответы (1)


Что произойдет, если вы попытаетесь объявить свою переменную следующим образом:

__thread int volatile x = 0;

Это должно помешать компилятору кэшировать значение (хотя я не уверен, как volatile взаимодействует с __thread).

person coffeemug    schedule 06.11.2013
comment
Кроме того, больше предложений. Вы можете расширить -O2 до конкретных флагов оптимизации и бинарного поиска, пока не найдете тот, который вызывает ошибку. Затем вы можете отключить флаг нарушения, не отключая остальные оптимизации. Другой вариант — выбрать другой компилятор. Выдают ли gcc и clang одну и ту же ошибку на ARM с включенной оптимизацией? - person coffeemug; 07.11.2013