Eva*_*ran 10 c++ raii signal-handling longjmp
所以我有一个库(不是我写的),不幸的是它abort()
用来处理某些错误.在应用程序级别,这些错误是可恢复的,所以我想处理它们而不是用户看到崩溃.所以我最终编写这样的代码:
static jmp_buf abort_buffer;
static void abort_handler(int) {
longjmp(abort_buffer, 1); // perhaps siglongjmp if available..
}
int function(int x, int y) {
struct sigaction new_sa;
struct sigaction old_sa;
sigemptyset(&new_sa.sa_mask);
new_sa.sa_handler = abort_handler;
sigaction(SIGABRT, &new_sa, &old_sa);
if(setjmp(abort_buffer)) {
sigaction(SIGABRT, &old_sa, 0);
return -1
}
// attempt to do some work here
int result = f(x, y); // may call abort!
sigaction(SIGABRT, &old_sa, 0);
return result;
}
Run Code Online (Sandbox Code Playgroud)
代码不是很优雅.由于这种模式最终必须在代码的几个点重复,我想简化一点,并可能将它包装在一个可重用的对象中.我的第一次尝试涉及使用RAII来处理信号处理程序的设置/拆除(需要完成,因为每个函数需要不同的错误处理).所以我想出了这个:
template <int N>
struct signal_guard {
signal_guard(void (*f)(int)) {
sigemptyset(&new_sa.sa_mask);
new_sa.sa_handler = f;
sigaction(N, &new_sa, &old_sa);
}
~signal_guard() {
sigaction(N, &old_sa, 0);
}
private:
struct sigaction new_sa;
struct sigaction old_sa;
};
static jmp_buf abort_buffer;
static void abort_handler(int) {
longjmp(abort_buffer, 1);
}
int function(int x, int y) {
signal_guard<SIGABRT> sig_guard(abort_handler);
if(setjmp(abort_buffer)) {
return -1;
}
return f(x, y);
}
Run Code Online (Sandbox Code Playgroud)
当然,身体function
是很多更简单,更清晰的这种方式,但今天上午一个念头出现在我.这保证有效吗?这是我的想法:
setjmp
/ 之间没有变化longjmp
.longjmp
堆栈帧中的位置,所以我允许代码执行编译器在函数的出口点发出的清理代码.setjmp
return
但我仍然觉得这可能是未定义的行为.你们有什么感想?
我认为这f
是在第三方库/应用程序,因为否则你可以修复它不要调用中止.鉴于此,并且RAII可能会或可能不会在所有平台/编译器上可靠地产生正确的结果,您有几个选择.
abort
和LD_PRELOAD它.然后你控制在中止时发生的事情,而不是在信号处理程序中.f
在子进程中运行.然后,您只需检查返回代码,如果失败,请尝试使用更新的输入.function
从多个呼叫点呼叫您的原始设备,然后让它手动执行设置/拆卸.在这种情况下,它仍然会消除复制粘贴.