跨线程复制 boost::exception 崩溃

Posted

技术标签:

【中文标题】跨线程复制 boost::exception 崩溃【英文标题】:copying boost::exception across threads crashing 【发布时间】:2012-05-25 12:33:48 【问题描述】:

下面的示例代码将 boost::exception 对象从 1 个线程复制/传输到另一个线程,由于在破坏 exception/exception_ptr 内部状态期间的竞争条件而崩溃。我不确定解决它的最佳方法是什么。

使用的 boost 版本是 1.42,平台是在双核 Intel m/c 上运行的 Ubuntu Lucid。编译器是 gcc 4.4.3。

#include <iostream>

#include <boost/exception/all.hpp>
#include <boost/thread.hpp>

struct Exception
    : public virtual std::exception
    , public virtual boost::exception

;

struct MyException : public virtual Exception ;

struct MyTag ;

typedef boost::error_info<MyTag, std::string> MyError;

struct Test

    Test()
    
        _t.reset(new boost::thread(boost::bind(&Test::executor, this)));
    

    ~Test()
    
        _t->join();
    

    void executor()
    
        std::cerr << "executor: starting ...\n";
        for (;;)
        
            boost::unique_lock<boost::mutex> lk(_mx);
            while(_q.empty())
            
                _cv.wait(lk);
            
            
                boost::shared_ptr<boost::promise<int> > pt = _q.front();
                _q.pop_front();
                lk.unlock();
                pt->set_exception(boost::copy_exception(MyException() << MyError("test")));
            
        
    

    void run_impl()
    
        try
        
            boost::shared_ptr< boost::promise<int> > pm(new boost::promise<int>());

            boost::unique_future<int> fu = pm->get_future();
            
                boost::unique_lock<boost::mutex> lk(_mx);
                _q.push_back(pm);
                pm.reset();
            
            _cv.notify_one();

            fu.get();
            assert(false);
        
        catch (const MyException& e)
        
            throw;
        
        catch (const boost::exception& )
        
            assert(false);
        
        catch (...)
        
            assert(false);
        
    

    void run()
    
        std::cerr << "run: starting ...\n";
        for (;;)
        
            try
            
                run_impl();
            
            catch (...)
            
            
        
    

private:

    boost::mutex _mx;
    std::list< boost::shared_ptr< boost::promise<int> > > _q;
    boost::shared_ptr<boost::thread> _t;
    boost::condition_variable_any _cv;
;

int main()

    Test test;
    test.run();


/*
#0  0x080526bd in boost::exception_detail::refcount_ptr<boost::exception_detail::error_info_container>::release (this=0x806e26c) at /boost_1_42_0/boost/exception/exception.hpp:79            
#1  0x0804f7c5 in ~refcount_ptr (this=0x806e26c, __in_chrg=<value optimized out>) at /boost_1_42_0/boost/exception/exception.hpp:34                                                           
#2  0x0804bb61 in ~exception (this=0x806e268, __in_chrg=<value optimized out>) at /boost_1_42_0/boost/exception/exception.hpp:254                                                             
#3  0x0805579a in ~clone_impl (this=0x806e260, __in_chrg=<value optimized out>, __vtt_parm=<value optimized out>) at /boost_1_42_0/boost/exception/exception.hpp:391                          
#4  0x001ff633 in ?? () from /usr/lib/libstdc++.so.6                                                                                                                                                           
#5  0x0027233d in _Unwind_DeleteException () from /lib/libgcc_s.so.1                                                                                                                                           
#6  0x001fe110 in __cxa_end_catch () from /usr/lib/libstdc++.so.6                                                                                                                                              
#7  0x0804f7a4 in Test::run (this=0xbffff74c) at ex_org.cpp:89                                                                                                                                                 
#8  0x0804b869 in main () at ex_org.cpp:106 
*/

【问题讨论】:

捕获非引用而不是引用呢?这样就创建了本地副本。 没有帮助,仍然以同样的崩溃告终。 尝试使用 g++ 4.7.0 和 boost 1.49 重现此问题,但您的示例在那里运行良好(在 Intel i5 上的 Arch Linux 3.4.0 下) 我不确定 boost 1.42 和 1.49 之间是否有任何改变可能已经解决了这个问题,但核心堆栈跟踪表明存在竞争条件(refcount_ptr 析构函数),因此它可能看起来运行良好一段时间。对我来说,它也可以正常运行大约 1-2 分钟(通常),然后最终出现段错误。 我已经运行了该程序的 5 个实例 40 分钟,但没有一个实例崩溃。要么我非常(不)幸运,要么它已经在通往新版本的路上被修复了 【参考方案1】:

我不是 boost 专家,但我注意到了一些比赛问题,它在调试模式下运行正常吗?

对于执行者我会这样写

void executor()

    std::cerr << "executor: starting ...\n";

    for (;;)
                
        _cv.wait(lk);

        boost::unique_lock<boost::mutex> lk(_mx);

        __sync_synchronize (); // Tells the compiler to prevent optimizations

        if ( !_q.empty() )                 
            boost::shared_ptr<boost::promise<int> > pt = _q.front();
            _q.pop_front();
            pt->set_exception(boost::copy_exception(MyException() << MyError("test")));
        
    

还有

void run_impl()

    try
    
        boost::shared_ptr< boost::promise<int> > pm(new boost::promise<int>());

        boost::unique_future<int> fu = pm->get_future();

        
            boost::unique_lock<boost::mutex> lk(_mx);

            // prevent the compiler from mixing above and below code
            __sync_synchronize ();

            _q.push_back(pm);
            pm.reset();
        

        _cv.notify_one();

        // This one is the paranoïd's one ;), one must check without ! 
        __sync_synchronize ();

        
            // since fu must holds an internal reference pm being currently 
            // changed by the other thread !
            boost::unique_lock<boost::mutex> lk(_mx);

            // Then you are assured that everything is coherent at this point
            // the stack frame holding the exception stuffs won't be corrupted

            fu.get();
        

        assert(false);
    

【讨论】:

以上是关于跨线程复制 boost::exception 崩溃的主要内容,如果未能解决你的问题,请参考以下文章

发送字符串时提升消息队列崩溃

boost exception

使用 boost::asio 获取 UDP 套接字远程地址

有多少线程可以在单个 CPU 中或跨所有 CPU 并行执行? [复制]

DAY7-剖析复制线程

为啥我的 .Net/WinForms 应用程序仅在 Visual Studio 中获得跨线程异常? [复制]