c++ - longjmp 和 RAII

标签 c++ raii signal-handling setjmp

所以我有一个库(不是我写的),不幸的是它使用 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;
}

不是很优雅的代码。由于这种模式最终不得不在代码的一些地方重复,我想稍微简化它并可能将它包装在一个可重用的对象中。我的第一次尝试涉及使用 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);
}

当然 function 的主体通过这种方式简单得多也更加清晰,但是今天早上我想到了一个想法。 这保证有效吗?这是我的想法:

  1. 在对 setjmp/longjmp 的调用之间,没有变量是易变的或变化的。
  2. 我正在 longjmping 到与 setjmp 相同的堆栈帧中的位置并正常返回 return,所以我允许代码执行编译器在函数导出点发出的清理代码。
  3. 它似乎按预期工作。

但我仍然觉得这可能是未定义的行为。大家怎么看?

最佳答案

我假设 f 在第三方库/应用程序中,否则你可以修复它不调用中止。鉴于此,并且 RAII 可能会或可能不会在所有平台/编译器上可靠地产生正确的结果,您有几个选择。

  • 创建一个定义abort 和LD_PRELOAD 的小型共享对象。然后您可以控制中止时发生的事情,而不是在信号处理程序中。
  • 在子进程中运行 f。然后您只需检查返回代码,如果失败,请使用更新的输入重试。
  • 不使用 RAII,只需从多个调用点调用您的原始 函数 并让它手动明确地进行设置/拆卸。在那种情况下,它仍然消除了复制粘贴。

关于c++ - longjmp 和 RAII,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5393859/

相关文章:

c++ - 有条件地使用 RAII 的最佳方法

c - 编写我自己的 shell - 无法正确处理 ctrl+c (SIGINT)

c++ - 创建全局已知并自动删除的临时目录(C++)?

C++初始化全局变量

c++ - FILE* 类的分配失败

c - 信号处理输出显示问题

C++ 运算符前置和后置递增

c++ - 什么时候返回递归函数?

c++ - 给定字符串中每个字符出现的次数

c++ - 你如何在QT中绘制点?