由于异常而在展开时处理C ++析构函数中的pthread取消点

时间:2013-12-18 00:19:36

标签: c++ linux multithreading exception

我正在尝试实现一个范围保护类,并且遇到Linux上的取消点问题。根据我的发现,Linux在异常方面实现了pthread消除,其中异常无法在用户的catch块中处理(即必须重新抛出)。这会导致以下范围保护实现出现问题,该实现尝试忽略其关联函数可能抛出的任何异常:

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>

#include <functional>

struct scope_guard {
    template <typename T>
    scope_guard(T func) : function_(func) {}

    ~scope_guard() {
        try {
            function_();
        }
        catch (...) {}
    }

    std::function<void()> function_;
};

extern "C" {
    void* thread_func(void*) {
        try {
            scope_guard guard( [](void) {
                // wait for a cancellation event
                while (true) {
                    pthread_testcancel();
                }
            });
            throw int();
        }
        catch (int&) {}

        return 0;
    }
}

int main(void) {

    pthread_t thread;
    int ret;
    if (0 != (ret = pthread_create(&thread, NULL, &thread_func, NULL))) {
        fprintf(stderr, "pthread_create failed: %d\n", ret);
        return 1;
    }

    sleep(1);

    if (0 != (ret = pthread_cancel(thread))) {
        fprintf(stderr, "pthread_cancel failed: %d\n", ret);
        return 1;
    }

    void* thread_ret;
    if (0 != (ret = pthread_join(thread, &thread_ret))) {
        fprintf(stderr, "pthread_join failed: %d\n", ret);
        return 1;
    }

    return 0;
}

~scope_guard() {
    try {
        function_();
    }
    catch (abi::__forced_unwind&) {
        throw;
    }
    catch (...) {}
}

这在Solaris,HPUX和AIX上运行良好,但在Linux上它会产生以下错误并中止:

FATAL: exception not rethrown

这是由catch (...)中的~scope_guard阻止导致但不会重新抛出pthread取消异常。

根据this文章,处理这些取消异常的推荐方法似乎是从catch (...)块重新抛出异常,或者显式捕获并重新抛出取消异常,如下所示:

    ~scope_guard() {
        try {
            function_();
        }
        catch (abi::__forced_unwind&) {
            throw;
        }
        catch (...) {}
    }

然而,这两种方法都会导致从该析构函数抛出一个新的异常,这会导致问题终止,因为现有的异常已经在展开堆栈的过程中。在我的测试中报告了以下内容:

terminate called without an active exception

有没有办法在Linux上处理这种情况?

1 个答案:

答案 0 :(得分:0)

~scope_guard()的第二个版本是正确的。

问题在于,throw int()为什么要thread_func()? 删除它,你的代码将正常工作。

如果你真的想抛出int,你应该写如下:

void* thread_func(void*) {
  scope_guard guard( [](void) {
    // wait for a cancellation event
    while (true) {
      pthread_testcancel();
      throw int(); // Throw either here
    }
    throw int(); // Or here
  });
  return 0;
}

请注意,您应该将所有“可能的抛出代码”放在scope_guard的函数中。