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