Обнаружение рекурсии устойчиво даже при наличии нелокальных прыжков
У меня есть определенная функция (обработчик сигнала), для которой я хотел бы обнаружить рекурсию, т.е. выяснить, вызвана ли функция прямо или косвенно называемой самой. Сложный бит в том, что функция вызывает некоторый код, который не находится под его контролем в какой-то момент, и этот код мог что-то сделать.
Обычно я просто пишу что-то вроде
void foo() {
static int recursed = 0;
if(recursed) {
...
}
recursed = 1;
othercode();
recursed = 0;
}
но в этом случае я обеспокоен тем, что othercode
может использовать longjmp
или аналогичный для разрыва, в результате чего recursed
остается в 1. В случае, если моя функция выскочила из этого, Я хочу убедиться, что он не считает себя рекурсивным, если он вызван позже (тот факт, что это longjmp
'd out, не является проблемой в противном случае).
Примечание. Я считаю longjmp
вероятным. othercode
является цепным сигнальным обработчиком из какого-либо другого кода в-диком, и существуют, например, обработчики, например. SIGSEGV
, которые используют longjmp
для восстановления контекста (например, как обработчики исключений "защита от ошибок" ). Обратите внимание, что использование longjmp
в синхронном обработчике сигналов, как правило, безопасно. В любом случае, я не особо забочусь о том, безопасен ли другой код вообще, потому что это не то, что под моим контролем.
Ответы
Ответ 1
Не уверен, какой именно код будет выглядеть для этого, но вместо статического int вы можете иметь статический void *. Вместо того, чтобы устанавливать его в 1, установите его для указания на текущий стек стека. В дополнение к проверке, если это отличное от нуля, вы проверяете, чтобы обратный адрес из следующего стека стека после recursed
фактически указывал на местоположение в коде foo, а также что recursed
находится выше текущего указателя стека, т.е. не выскочил.
Звучит очень хрупко и зависит от архитектуры.
Ответ 2
В соответствии со стандартом POSIX для signal (7) longjmp() не является одним из вызовов, безопасных для вызова изнутри обработчик сигнала. Прежде чем даже подумать об этом, в документации longjmp (3) вам нужно убедиться, что код, который вы вызываете, использует sigsetjmp() и siglongjmp ( )
Если код, вызываемый вами, выпрыгивает из обработчика сигнала, то я не вижу, как вы можете узнать, когда обновлять переменную recursed
, если вы также не контролируете функцию обратного вызова, которую этот неизвестный код вызывает в ваше приложение.