Глупые поточные ошибки
Про глупую ошибку в собственном коде писать несколько стыдно, но я попробую. Вдруг кому-нибудь поможет.
Всем известно, что в некоторые функции libc, как в реку, дважды зайти нельзя. Против этого придуманы reeentrant версии.
Однако в reentrant версию тоже не всегда можно зайти дважды.
Конкретная функция называлась ctime_r, и вызывалась из процедуры записи строчки в лог. Изредка программа зависала, причем в стеке фигурировал именно ctime_r. Она же reentrant, WTF?!
Ключевая ошибка заключалась в том, что reentrant – не значит, что без side-effect-ов, и уж тем более не значит – что код можно прервать на любом месте.
Причем в случае с asctime_r, например, как раз можно прервать на любом месте: там действительно нет side-effects. Но не ctime_r (и не localtime_r)!
Это потому, что reenterability конкретно в случае ctime_r в одной из внутренних функций (__tz_convert) делается при помощи mutex-а. (Дело было на Linux, несложно посмотреть исходники.)
И вот когда случается вызов того ctime_r из того же потока, но прерванного сигналом, попытка заблокировать mutex виснет в syscall навсегда.
Век живи, век учись, бдительность ни на секунду не теряй: реэнтерабельность бывает разная.