简体   繁体   English

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

[英]Handling pthread cancellation points in C++ destructors when unwinding due to an exception

I'm attempting to implement a scope guard class and am running into problems with cancellation points on Linux. 我正在尝试实现一个范围保护类,并且遇到Linux上的取消点问题。 From what I've found, Linux implements pthread cancellation in terms of exceptions, where the exception cannot be handled in a user's catch block (ie it must be rethrown). 根据我的发现,Linux在异常方面实现了pthread取消,其中异常无法在用户的catch块中处理(即必须重新抛出)。 This causes problems for the following scope guard implementation that attempts to ignore any exceptions that may be thrown by its associated function: 这会导致以下范围保护实现出现问题,该实现尝试忽略其关联函数可能抛出的任何异常:

#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 (...) {}
}

This works fine on Solaris, HPUX and AIX, but on Linux it produces the following error and aborts: 这在Solaris,HPUX和AIX上运行良好,但在Linux上它会产生以下错误并中止:

FATAL: exception not rethrown

This is caused by the catch (...) block in ~scope_guard which catches but does not rethrow the pthread cancellation exception. 这是由~scope_guardcatch (...)块引起的,该块捕获但不重新抛出pthread取消异常。

Based on this article, it appears that the recommended way to handle these cancellation exceptions is to either rethrow the exception from a catch (...) block, or to explicitly catch and rethrow the cancellation exception like so: 根据这篇文章,似乎处理这些取消异常的推荐方法是从catch (...)块重新抛出异常,或者显式捕获并重新抛出取消异常,如下所示:

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

Both of these approaches however cause a new exception to be thrown from this descructor, which causes the problem to terminate as an existing exception is already in the process of unwinding the stack. 但是这两种方法都会导致从该析构函数抛出一个新的异常,这会导致问题终止,因为现有的异常已经在展开堆栈的过程中。 In my tests the following is reported: 在我的测试中报告了以下内容:

terminate called without an active exception

Is there a way to handle this scenario on Linux? 有没有办法在Linux上处理这种情况?

The second version of ~scope_guard() is correct. ~scope_guard()的第二个版本是正确的。

The problem here is, why do you throw int() in thread_func() ? 这里的问题是,为什么要在thread_func() throw int() thread_func() Delete it, your code will work fine. 删除它,你的代码将正常工作。

And if you really want to throw int, you should need to write like below: 如果你真的想抛出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;
}

Note you should put all the "possible throw code" in scope_guard's function. 请注意,您应该将所有“可能的抛出代码”放在scope_guard的函数中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM