Deadlock for free или почему нельзя делать suspend потокам
Сегодня коллега напоролся на дамп программы, которая зависла. Такое бывает, но когда затык происходит на int* pInt = new int, Штирлицу положено насторожиться.
Налицо deadlock, но прикол в том, что в коде никакой синхронизации в принципе нет вообще. Зато была приаттачена куча всяких bounds-checker’ов вкупе с отладчиками, что заставило меня вспомнить, что я когда-то где-то читал о том, что неосторожное обращение с менеджером памяти может привести к дедлоку.
На самом деле, неосторожно обратиться с менеджером памяти достаточно сложно, но, как это обычно бывает, компьютер сделает все, что Вы ему скажете, но никто не обещает, что это будет то, что имелось в виду.
Так вот, интернет уже неоднократно объяснил, что делать suspend потоку извне – очень, очень плохая идея. Есть миллиард объяснений, почему, но все они сводятся к общему знаменателю – поток сам распоряжается своими ресурсами и поэтому должен сам контроллировать свое поведение. Даже если на первый взгляд поток не использует никаких разделяемых ресурсов, при более пристальном рассмотрении это может оказаться совсем не так.
Любой поток делит как минимум один ресурс с остальными потоками. Оперативную память. Распределение памяти контроллирует менеджер памяти, который, как правило, общий для всех потоков, в определенных рамках, конечно. Соответственно, есть очень неплохие шансы, что при процедура выделения памяти синхронизирована внутри менеджера памяти, грубо говоря, менеджер имеет mutex внутри. А дальше все просто – Вася запустил поток, поток радостно поскакал по инструкциями и весело побежал за оперативкой. Менеджер памяти залочил было мютекс, а тут Вася бац! Suspend потоку! И тут же в другом потоке начинает ломиться к тому же самому менеджеру памяти, которому пофиг, ибо мютекс зажат другим потоком. И, заметьте, совершенно бесплатно приходит им всем, включая Васю, первый, второй поток и менеджер памяти злой дед лок.
Конечно, это довольно грубая картина и в реальности все может быть несколько иначе, но суть от этого не меняется – приостановка потока извне – верный способ рано или поздно нарваться на deadlock.