c++ - boot::exception 消息上的第一次和第二次访问冲突

标签 c++ exception memory-management boost windows-8

我在调试第一次访问冲突时遇到问题,这导致我在 Windows 8 上的桌面应用程序崩溃。这尤其令人沮丧,因为崩溃不会发生在我用于开发的 Windows 8 VM 上,只发生在平板电脑设备上它应该在其上运行。

我有 boost::exception 是这样定义的:

struct ExceptionBase: virtual std::exception, virtual boost::exception {};
struct DEVICES_COMMUNICATION_API CDeviceException: virtual ExceptionBase {};
typedef boost::error_info<struct tag_device_message, std::string> device_message;

还有一个内联函数来设置错误信息:

inline void throwDeviceException(std::string message)
{
  CDeviceException de = CDeviceException()
     << (device_message(std::string(message)));
  BOOST_THROW_EXCEPTION(de);
}

还有另一个用于检索错误消息的内联函数:

inline std::string retrieveDeviceExceptionMessage(CDeviceException de)
{
    return *(boost::get_error_info<device_message, CDeviceException>(de));
}

异常在我的应用程序(它是一个服务器)中被捕获,然后被分配到一个用 protobuf 生成的类中,然后被序列化。捕获异常并将其存储在类中的代码是在 boost 线程中运行的仿函数:

struct invoke_fn
{
   void operator()(devices::server::CDeviceServer& server,
                   boost::promise<void>&           boostPromise,
                   CDeviceClientRequest&           request,
                   CDeviceServerResponse&          response)
   {
      try
      {
         server.invoke(request, response);
         boostPromise.set_value();
      }
      catch (devices::util::CDeviceException &e)
      {
         std::string message = devices::util::retrieveDeviceExceptionMessage(e);
         response.set_errormessage(message);
      }
      catch (std::exception &e)
      {
         std::string message(e.what());
         response.set_errormessage(message);
      }
   }
};

在由 protobuf 自动生成的响应对象上设置错误消息的代码是:

inline void CDeviceServerResponse::set_errormessage(const ::std::string& value) {
  set_has_errormessage();
  if (errormessage_ == &::google::protobuf::internal::kEmptyString) {
    errormessage_ = new ::std::string;
  }
  errormessage_->assign(value);
}

这使用 assign() 将传入的字符串复制到 protobuf 类。服务器请求在它自己的 boost::thread 中运行,如下所示:

boost::promise<void>        boostPromise;
boost::unique_future<void>  boostFuture = boostPromise.get_future();
boost::thread               boostThread([&]()
                            {
                               invoke_fn functor;
                               functor(*this,
                                       boostPromise,
                                       request,
                                       response);
                            });

return (boostFuture.wait_for(boost::chrono::milliseconds(timeout))
        ==
        boost::future_status::ready);

据我所知,这段代码看起来还不错。但是,已观察到以下堆栈跟踪:

来自内存博士:

> ~~3868~~
> ~~3868~~ Error #1: UNADDRESSABLE ACCESS: reading 0x03233e70-0x03233e74 4 byte(s)
> ~~3868~~ # 0 amalgam-devices-communication.dl!std::basic_string<char,std::char_traits<char>,std::allocator<char> >::_Grow [c:\program files (x86)\microsoft visual studio 11.0\vc\include\xstring:2226]
> ~~3868~~ # 1 amalgam-devices-communication.dl!std::basic_string<char,std::char_traits<char>,std::allocator<char> >::assign [c:\program files (x86)\microsoft visual studio 11.0\vc\include\xstring:1113]
> ~~3868~~ # 2 amalgam-devices-communication.dl!std::basic_string<char,std::char_traits<char>,std::allocator<char> >::assign [c:\program files (x86)\microsoft visual studio 11.0\vc\include\xstring:1099]
> ~~3868~~ # 3 amalgam-devices-communication.dl!CDeviceServerResponse::set_errormessage   [c:\workspace\bitbbucket\1edit_amalgam\devices\communication\src\main\c++\protobuf\src\main\cdeviceserverresponse.pb.h:294]
> ~~3868~~ # 4 amalgam-devices-communication.dl!invoke_fn::operator()                     [c:\workspace\bitbbucket\1edit_amalgam\devices\communication\src\main\c++\server\cdeviceserver.cc:156]
> ~~3868~~ # 5 amalgam-devices-communication.dl!<lambda_e4b2ef864a7f24dec10bd7126eadbce1>::operator() [c:\workspace\bitbbucket\1edit_amalgam\devices\communication\src\main\c++\server\cdeviceserver.cc:184]
> ~~3868~~ # 6 amalgam-devices-communication.dl!boost::detail::thread_data<<lambda_e4b2ef864a7f24dec10bd7126eadbce1> >::run [c:\workspace\bitbbucket\1edit_precompiledheaders\desktopdevices\external-sources\boost\thread\detail\thread.hpp:74]
> ~~3868~~ # 7 amalgam-devices-communication.dl!boost::`anonymous namespace'::thread_start_function [c:\users\marcusma\source\1edit-prism\boost_1_51_0\libs\thread\src\win32\thread.cpp:190]
> ~~3868~~ # 8 MSVCR110D.dll!beginthreadex                                               +0x1a0    (0x73dce001 <MSVCR110D.dll+0x5e001>)
> ~~3868~~ # 9 MSVCR110D.dll!endthreadex                                                 +0x170    (0x73dce1d1 <MSVCR110D.dll+0x5e1d1>)
> ~~3868~~ #10 KERNEL32.dll!BaseThreadInitThunk                                          +0xd      (0x7735850d <KERNEL32.dll+0x2850d>)
> ~~3868~~ Note: @0:00:44.759 in thread 3868
> ~~3868~~ Note: next higher malloc: 0x03234000-0x03234030
> ~~3868~~ Note: 0x03233e70-0x03233e74 overlaps memory 0x03233e58-0x03233e74 that was freed
> ~~3868~~ Note: instruction: mov    0x18(%eax) -> %ecx
> ~~3868~~

第一次和第二次访问冲突发生在第一个异常处理 catch block 的末尾大括号中:

catch (devices::util::CDeviceException &e)
{
    std::string message = devices::util::retrieveDeviceExceptionMessage(e);
    response.set_errormessage(message);
} // HERE

Visual Studio 调试器堆栈跟踪表明异常发生在析构函数 ~CDeviceException() 中,同时试图释放在对象上设置的 error_info(device_message)。

来自 Visual Studio:

First-chance exception at 0x76DA4B32 in amalgam-devices-server.exe: Microsoft C++ exception: boost::exception_detail::clone_impl<boost::broken_promise> at memory location 0x030EF55C.
First-chance exception at 0x76DA4B32 in amalgam-devices-server.exe: Microsoft C++ exception: boost::exception_detail::clone_impl<boost::broken_promise> at memory location 0x030EF55C.
First-chance exception at 0x76DA4B32 in amalgam-devices-server.exe: Microsoft C++ exception: [rethrow] at memory location 0x00000000.
First-chance exception at 0x76DA4B32 in amalgam-devices-server.exe: Microsoft C++ exception: [rethrow] at memory location 0x00000000.
First-chance exception at 0x76DA4B32 in amalgam-devices-server.exe: Microsoft C++ exception: boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::system::system_error> > at memory location 0x031EF6C0.
First-chance exception at 0x76DA4B32 in amalgam-devices-server.exe: Microsoft C++ exception: boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::system::system_error> > at memory location 0x031EF6C0.
First-chance exception at 0x76DA4B32 in amalgam-devices-server.exe: Microsoft C++ exception: boost::exception_detail::clone_impl<devices::util::CDeviceException> at memory location 0x031EF6A8.
First-chance exception at 0x76DA4B32 in amalgam-devices-server.exe: Microsoft C++ exception: boost::exception_detail::clone_impl<devices::util::CDeviceException> at memory location 0x031EF6A8.
First-chance exception at 0x76DA4B32 in amalgam-devices-server.exe: Microsoft C++ exception: devices::util::CDeviceException at memory location 0x031EF894.
First-chance exception at 0x76DA4B32 in amalgam-devices-server.exe: Microsoft C++ exception: devices::util::CDeviceException at memory location 0x031EF894.
Critical error detected c0000374

调用栈是这样的:

ntdll.dll!77a6a9ff()    Unknown
[Frames below may be incorrect and/or missing, no symbols loaded for ntdll.dll] 
ntdll.dll!77a6bcf5()    Unknown
ntdll.dll!77a6ad4a()    Unknown
ntdll.dll!77a2186c()    Unknown
ntdll.dll!779e26e8()    Unknown
ntdll.dll!779e1edb()    Unknown
ntdll.dll!779e217e()    Unknown
ntdll.dll!779e2664()    Unknown
ntdll.dll!779e2664()    Unknown
msvcr110d.dll!_free_base(void * pBlock) Line 50 C
msvcr110d.dll!_free_dbg_nolock(void * pUserData, int nBlockUse) Line 1431   C++
msvcr110d.dll!_free_dbg(void * pUserData, int nBlockUse) Line 1265  C++
msvcr110d.dll!operator delete(void * pUserData) Line 54 C++
amalgam-devices-communication.dll!std::allocator<char>::deallocate(char * _Ptr, unsigned int __formal) Line 586 C++
amalgam-devices-communication.dll!std::_Wrap_alloc<std::allocator<char> >::deallocate(char * _Ptr, unsigned int _Count) Line 888    C++
amalgam-devices-communication.dll!std::basic_string<char,std::char_traits<char>,std::allocator<char> >::_Tidy(bool _Built, unsigned int _Newsize) Line 2265 C++
amalgam-devices-communication.dll!std::basic_string<char,std::char_traits<char>,std::allocator<char> >::~basic_string<char,std::char_traits<char>,std::allocator<char> >() Line 965 C++
amalgam-devices-communication.dll!invoke_fn::operator()(devices::server::CDeviceServer & server, boost::promise<void> & boostPromise, CDeviceClientRequest & request, CDeviceServerResponse & response) Line 157    C++
>   amalgam-devices-communication.dll!<lambda_e4b2ef864a7f24dec10bd7126eadbce1>::operator()() Line 185  C++
amalgam-devices-communication.dll!boost::detail::thread_data<<lambda_e4b2ef864a7f24dec10bd7126eadbce1> >::run() Line 75 C++
amalgam-devices-communication.dll!boost::`anonymous namespace'::thread_start_function(void * param) Line 191    C++
msvcr110d.dll!_callthreadstartex() Line 354 C
msvcr110d.dll!_threadstartex(void * ptd) Line 337   C
kernel32.dll!7735850d() Unknown
ntdll.dll!779ebf39()    Unknown
ntdll.dll!779ebf0c()    Unknown

非常感谢任何帮助,我们已经尝试追踪这几天了。

最佳答案

我已经找出问题所在。 CDeviceResponse 对象通过引用传递 到 boost::thread 中的仿函数。如果线程超时,主线程接管 对象超出范围并被销毁。

然而,包含服务器调用的线程并没有被杀死,它继续运行, 直到最终它抛出自己的异常,并杀死线程。此时的 对象再次被销毁,导致访问冲突。

有 2 种可能的修复方法。

  1. 发现 invokeWithTimeout() 已返回 false 并显式终止线程。这将涉及记住有关线程的详细信息,并且我们希望代码更易于维护,因此我们选择了选项 2。

  2. 为请求和响应定义共享指针并将它们传递给仿函数和 而是调用。这样当主线程存在并继续移动时,只有引用 计数递减。当实际线程终止时,它成功地销毁了对象。

然而,如果有人知道一种更简洁的方法来杀死被调用的超时线程 使用下面的代码,然后请告诉我:

boost::promise<void>        boostPromise;
boost::unique_future<void>  boostFuture = boostPromise.get_future();
boost::thread               boostThread([&]()
                            {
                               invoke_fn functor;
                               functor(*this,
                                       boostPromise,
                                       request,
                                       response);
                            });

bool result = (boostFuture.wait_for(boost::chrono::milliseconds(timeout))
               ==
               boost::future_status::ready);
>> if result false kill the thread here
return result;

关于c++ - boot::exception 消息上的第一次和第二次访问冲突,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20000677/

相关文章:

c++ - 如何在QTabWidget标题中设置QWidget?

c++ - 在 SAPI 中说出进度事件

c++ - 将无符号变量的差异存储到有符号变量中

c# - 如何判断 .NET 调用抛出哪些异常?

c++ - 如何避免将 std::string 放入异常类中?

ios - 在基于ARC的APP中释放内存?

c - malloc 和 free 在 C 中是如何实现的?

c++ - 如何从 CWinThread 访问 AfxGetMainWnd()?

Java - 使用 catch block 内的方法返回语句和抛出的异常?

ios - 执行 Push Segue 时是否应该释放 UIViewController