异常可以通过异常指针“复制”吗?

Posted

技术标签:

【中文标题】异常可以通过异常指针“复制”吗?【英文标题】:Can exceptions be "duplicated" via exception pointers? 【发布时间】:2012-10-02 12:56:25 【问题描述】:

对于一些多线程代码,我想捕获所有异常并将它们传递给单个异常处理线程。这是消息传递框架:

#include <exception>

struct message

    virtual ~message() = default;
    virtual void act() = 0;
;

struct exception_message : message

    std::exception_ptr ep;

    virtual void act()
    
        std::rethrow_exception(ep);
    

    // ...
;

这是用例:

try

    // ...

catch (...)

    exception_message em  std::current_exception(); 
    handler_thread.post_message(em);

处理程序线程遍历其所有消息并调用act(),它可以安装自己的try/catch 块来处理所有已发布的异常。

现在我想知道如果我将此消息的副本发送给多个接收者会发生什么。一般来说,消息可能有任意数量的收件人,因此我不想对异常传播消息进行任意限制。 exception_ptr 被记录为“共享所有权”智能指针,rethrow_exception“不引入数据竞争”。

所以我的问题是:通过将活动异常存储在exception_ptr 中、复制指针并多次调用rethrow_exception 来复制活动异常是否合法?

【问题讨论】:

随便google一下:msdn.microsoft.com/en-us/library/dd293602.aspx"Transporting Exceptions between Threads" 从未尝试过这样的事情。将异常复制/通信到另一个线程并在那里重新引发它听起来有点奇怪,因为一旦收到异常,它就失去了所有的上下文。原始线程,除了它包含的内容。在一个线程中检查信号异常,并可能记录它或其他什么,我可以理解。 @PiotrNycz:那个页面不是 C++。 C++ 中没有std::copy_exception @KerrekSB:实际上"std::"copy_exception 在标准C++ 中的意思是std::make_exception_ptr @KerrekSB - 诚然,该页面是 Windows 密集型的(有趣的是,微软声称实现了 C++ 标准所需的内容),但 copy_exception 是早期 C 草案中的名称++11 代表make_exception_ptr 【参考方案1】:

根据我对标准的理解,它是合法的。但是我会注意到,重新抛出不会复制异常,因此如果您修改它并同时从其他线程访问它,共享异常对象本身就会提交给数据竞争。如果异常是只读的(一旦抛出),那么您应该没有任何问题。

关于存储时间:

15.1 抛出异常 [except.throw]

4 异常对象的内存以未指定的方式分配,除非在 3.7.4.1 中注明。如果一个处理程序通过重新抛出而退出,则控制权将传递给另一个处理程序以处理相同的异常。在异常的最后一个剩余活动处理程序以除重新抛出之外的任何方式退出之后,异常对象被销毁,或者引用异常对象的std::exception_ptr(18.8.5)类型的最后一个对象是 销毁,以较晚者为准。在前一种情况下,销毁发生在处理程序退出时,在处理程序的异常声明中声明的对象(如果有)销毁之后立即发生。在后一种情况下,销毁发生在std::exception_ptr 的析构函数返回之前。

关于数据竞赛:

18.8.5 异常传播[propagation]

7 为了确定是否存在数据竞争,对exception_ptr 对象的操作应仅访问和修改exception_ptr 对象本身,而不是它们引用的异常。在引用相同异常对象的exception_ptr 对象上使用rethrow_exception 不应引入数据竞争。 [注意:如果rethrow_exception 重新抛出相同的异常对象(而不是副本),并发访问该重新抛出的异常对象可能会引入数据竞争。引用特定异常的 exception_ptr 对象数量的变化不会引入数据 种族。 ——尾注]

关于rethrow

[[noreturn]] void rethrow_exception(exception_ptr p);

9 要求p 不得为空指针。

10 次抛出:p 引用的异常对象。

【讨论】:

我也读过,但这是什么意思?实际的 same 异常对象是否被多次抛出?那样可以么?这是否意味着什么? @KerrekSB:thrown 是什么意思? throw 只是线程上下文中的 goto/jump,恰好尊重堆栈展开。因此多次rethrow同一个对象是没有问题的,对象本身可以看作是指导跳转(向右catch子句)。 @KerrekSB - 我刚刚完成了同样的练习,我认为没有任何东西可以说明多次重新抛出同一 exception_ptr 对象的副本是否会引发底层异常的不同副本。 “如果rethrow_exception 重新抛出相同的异常对象(而不是副本)......”的含义是允许任何一个。在实践中,我认为这并不重要。通过引用捕获异常并修改其内容是很奇怪的,这几乎是您遇到不同行为的唯一方法,具体取决于是否复制异常。 我同意,但这里有两个单独的问题:首先,对current_exception 的两次调用是否获得指向不同对象的指针;第二,rethrow_exception 是否复制异常对象。在这两种情况下,我认为答案是未指定。 皮特是正确的。 rethrow_exception 允许但不需要抛出异常的副本。 Itanium ABI 目前不提供制作异常副本的功能。多家供应商遵循此 ABI,包括 gcc/Linux 和 Apple。 IIRC,微软确实在rethrow_exception 复制了一份副本,但我不太确定后一种说法。无论如何,这两种方法都是一致的。

以上是关于异常可以通过异常指针“复制”吗?的主要内容,如果未能解决你的问题,请参考以下文章

在 C++ 中通过指针捕获异常

捕获空指针异常是代码异味吗?

访问片段的子视图时出现空指针异常

setText时的空指针异常 - TextView [复制]

尝试通过 websocket 转换和发送时出现空指针异常

java servlet中的空指针异常[关闭]