dangling reference
Освежающая легкость летнего утра давно сменилась вязкими предрассветными холодами. Потом, если едкая прохлада и отступала, то день все равно оставался придавлен низкими серыми облаками. Тяжесть эта отдавалась в теле, распадаясь внутри на тоску и туман. Такой же поднимался вечерами в поле от безымянной речки. Плотный и сырой, огромными ватными клочьями выползал на берег.
От этой картины сами собой закрывались глаза, голова чугунела и тянула вниз.
"Возраст наверное, - подумал я, и, лихо по-стариковски крякнув, оторвался от стула, - четверть века прожить - чай, не поле перейти..."
Тут же закружилась голова.
"И вот третьего дня в спину вступило, вчера в ухе стреляло, - мысленно стал перебирать неизлечимые болезни, зевнул, - э-эх, и указатели ломит, деструктор отваливается... а ссылки-то! Ссылки прям висят..."
Да, такое бывает. Многие мужчины старше сорока лет сталкиваются с такой проблемой. Не конец света.
По-научному она называется "проблема синхронизации времени жизни объектов". Чаще всего проявляется у тех, кто любит баловаться потоками.
Тут легче всего поймать рассинхрон, особенно когда есть какие-нибудь thread_local переменные.
Signal тут просто для иллюстрации
осмотрим, кто, где и когда:
о вполне себе ожидаемо, создание идет в порядке живой очереди.
А вот уничтожение немного удивляет.
есть, тут концепция RAII подводит нас под монастырь. Вы ожидаете... ну хорошо, я ожидаю, что деструктор s будет вызван после уничтожения tls, а выходит наоборот.
Казалось бы, ерунда. Однако, если постараться, то можно поймать серьезные проблемы.
Например, при использовании эксклюзивных ресурсов и отсоединенных потоков, вы же любите так делать?
оток. Допустим, нам очень интересно, когда он завершится, поэтому синхронизируемся глобальным мьютексом.
Еще деталь, - объект track при уничтожении вызывает метод gasoline.unload().
ок на свободу через detach и ждем его завершения. Выглядит глупо, конечно, но это для примера, я так не пишу на самом деле. Обычно не пишу. А если и пишу, то не я.
Так вот, lock в thread_worker разрушается ближе к концу, но поток, как и Ленин, все еще живой.
Зато в главном потоке мы выходим из цикла и портим из вредности наш эксклюзивный ресурс - gasoline.spoil().
Допустим, объект тотально разрушен, но в рабочем потоке наступает очередь деструктора track, который попытается выполнить gasoline.unload() на римских развалинах. Дальше, сами понимаете, случается катастрофа.
Освежающая легкость летнего утра давно сменилась вязкими предрассветными холодами. Потом, если едкая прохлада и отступала, то день все равно оставался придавлен низкими серыми облаками. Тяжесть эта отдавалась в теле, распадаясь внутри на тоску и туман. Такой же поднимался вечерами в поле от безымянной речки. Плотный и сырой, огромными ватными клочьями выползал на берег.
От этой картины сами собой закрывались глаза, голова чугунела и тянула вниз.
"Возраст наверное, - подумал я, и, лихо по-стариковски крякнув, оторвался от стула, - четверть века прожить - чай, не поле перейти..."
Тут же закружилась голова.
"И вот третьего дня в спину вступило, вчера в ухе стреляло, - мысленно стал перебирать неизлечимые болезни, зевнул, - э-эх, и указатели ломит, деструктор отваливается... а ссылки-то! Ссылки прям висят..."
Да, такое бывает. Многие мужчины старше сорока лет сталкиваются с такой проблемой. Не конец света.
По-научному она называется "проблема синхронизации времени жизни объектов". Чаще всего проявляется у тех, кто любит баловаться потоками.
Тут легче всего поймать рассинхрон, особенно когда есть какие-нибудь thread_local переменные.
void thread_worker(int id) { Signal s{1}; thread_local Signal tls{2};}Класс
Signal тут просто для иллюстрации
struct Signal { int x; Signal(int x) : x{x} { std::cout << "+sig" << x << "\r\n"; } ~Signal() { std::cout << "-sig" << x << "\r\n"; }};Просто п
осмотрим, кто, где и когда:
+sig1+sig2Эт
о вполне себе ожидаемо, создание идет в порядке живой очереди.
А вот уничтожение немного удивляет.
-sig1-sig2То
есть, тут концепция RAII подводит нас под монастырь. Вы ожидаете... ну хорошо, я ожидаю, что деструктор s будет вызван после уничтожения tls, а выходит наоборот.
Казалось бы, ерунда. Однако, если постараться, то можно поймать серьезные проблемы.
Например, при использовании эксклюзивных ресурсов и отсоединенных потоков, вы же любите так делать?
std::mutex mtx;bool is_done = false;struct ExclusiveResource {...} gasoline;}Обычный рабочий п
void thread_worker() { thread_local Vehicle track{gasoline}; ... std::lock_guard<std::mutex> lock{mtx}; is_done = true;
оток. Допустим, нам очень интересно, когда он завершится, поэтому синхронизируемся глобальным мьютексом.
Еще деталь, - объект track при уничтожении вызывает метод gasoline.unload().
int main() { std::thread t(thread_worker); t.detach();;}Теперь выпускаем пот
while(true) { std::lock_guard<std::mutex> lock{mtx}; if (is_done) break; } gasoline.spoil(); // Но поток еще работает! return 0
ок на свободу через detach и ждем его завершения. Выглядит глупо, конечно, но это для примера, я так не пишу на самом деле. Обычно не пишу. А если и пишу, то не я.
Так вот, lock в thread_worker разрушается ближе к концу, но поток, как и Ленин, все еще живой.
Зато в главном потоке мы выходим из цикла и портим из вредности наш эксклюзивный ресурс - gasoline.spoil().
Допустим, объект тотально разрушен, но в рабочем потоке наступает очередь деструктора track, который попытается выполнить gasoline.unload() на римских развалинах. Дальше, сами понимаете, случается катастрофа.