Делаем переменные thread_local полностью изменчивыми

Я работаю над библиотекой времени выполнения, которая использует переключение контекста на уровне пользователя (используя Boost :: Context), и у меня возникают проблемы с использованием thread_level переменные. Рассмотрим следующий (сокращенный) код:

thread_local int* volatile tli;

int main()
{
tli = new int(1);   // part 1, done by thread 1
UserLevelContextSwitch();
int li = *tli;      // part 2, done by thread 2
cout << li;
}

Так как есть два доступа к thread_local переменная, основная функция преобразуется компилятором во что-то вроде этого (обратная от сборки):

register int** ptli = &tli; // cache address of thread_local variable
*ptli = new int(1);
UserLevelContextSwitch();
int li = **ptli;
cout << li;

Это похоже на правовую оптимизацию, так как Значение летучих tli не кэшируется в реестре. Но адрес летучих tli фактически кэшируется, а не читается из памяти в части 2.

И это проблема: после переключения контекста на уровне пользователя поток, который выполнил часть 1, уходит куда-то еще. Часть 2 затем берется другим потоком, который получает предыдущий стек и регистрирует состояние. Но теперь поток, который выполняет часть 2, читает значение tli который принадлежит потоку 1.

Я пытаюсь найти способ предотвратить кеширование компилятором локальной переменной потока адрес, а также volatile недостаточно глубоко Есть ли хитрость (желательно стандартная, возможно, специфичная для GCC) для предотвращения кеширования адресов локальных переменных потока?

11

Решение

Нет никакого способа соединить переключатели контекста уровня пользователя с TLS. Даже с атомарностью и полным ограничением памяти, адрес кэширования кажется законной оптимизацией, так как переменная thread_local является file-scope, статической переменной, которая не может быть перемещена, как предполагает компилятор. (хотя, возможно, некоторые компиляторы все еще могут быть чувствительными к таким барьерам памяти компилятора, как std::atomic_thread_fence а также asm volatile ("" : : : "memory");)

использования та же техника как вы описали, чтобы реализовать «кражу продолжения», когда другой поток может продолжить выполнение после точки синхронизации. И они явно препятствовать использование TLS в программе Cilk. Вместо этого они рекомендуют использовать «гиперобъекты» — особую функцию Cilk, которая заменяет TLS (а также обеспечивает семантику последовательного / детерминированного соединения). Смотрите также Cilk developer презентация около thread_local и параллелизм.

Кроме того, Windows предоставляет FLS (Fiber Local Storage) в качестве замены TLS, когда Волокна (те же легкие переключатели контекста) используются.

7

Другие решения


По вопросам рекламы [email protected]