什么会导致异常 16: "mutex: Resource busy" 被抛出(使用 Boost / BB10)?

Mos*_*bin 2 c++ boost exception boost-asio blackberry-10

我已将用 C++ 和 Boost 编写的长期稳定库移植到 Blackberry 10。该库在设备之间传输文件。该库编译和链接良好,并且运行良好。但是,在传输 1、2 或 3 个文件后,我始终在我的 Blackberry 10 设备上遇到抛出异常。在源代码中将异常作为 boost::system::system_error 捕获显示它是异常 16,文本为“mutex: Resource busy”。

这是发生异常的源代码:

try
{
    . . .

    // Find DtpFunctionData for the operation ID, use it to invoke handling function
    std::map<int, FunctionData>::iterator iter = _vecFunctionData.find (operationId);
    if (iter == _vecDtpClientFunctionData.end ())
        return EC_GENERAL_FAILURE;

    HANDLINGFUNC_1 handlingFunc = (*iter).second._clientHandlingFunc;
    POSTOPFUNC_1 postOpFunc = (*iter).second._clientPostOpFunc;
    bool callPostOpOnSuccess = (*iter).second._callPostOpOnSuccess;

    // Open a socket opposite the remote peer's TcpPortListener
    /* Start: ----- EXCEPTION 16: "mutex: Resource busy" ----- */
    boost::asio::io_service io_service;
    /* End: ----- EXCEPTION 16: "mutex: Resource busy" ----- */

    boost::asio::ip::tcp::socket socket (io_service);
    . . .
}
catch (boost::system::system_error& err)
{
    LOGLINE (("error", "Boost exception (%d / \"%s\") caught in HandleQueueOperation",  err.code ().value(), err.what()));
       return EC_EXCEPTION_CAUGHT;
}
Run Code Online (Sandbox Code Playgroud)

跟踪日志行是:

18:37:04 ( 149077264) [error] Boost exception (16 / "mutex: Resource busy") caught in HandleQueueOperation
Run Code Online (Sandbox Code Playgroud)

异常是在上面的“开始”和“结束”注释之间的某个地方抛出的,其中定义了 boost::asio::io_service 对象。我在 StackOverflow、Google 等中搜索了与“互斥:资源繁忙”相关的任何内容,但一无所获。我的代码此时没有访问任何应用程序级别的互斥锁,因此我假设所指的互斥锁是与 Boost 相关的互斥锁。

有人能告诉我这条消息的基本含义是什么,为什么会抛出“资源繁忙”异常?Blackberry 10 上是否存在与异常相关的已知问题?

提前致谢!

Mos*_*bin 7

经过多次调试,一位同事终于解决了这个问题。

执行摘要

pthread_mutex_init () 在 55-65 次boost::mutex构造函数调用后抛出异常,因为应用程序级派生类对象(具有 boost::mutex 作为成员变量)没有完全析构,因为基类析构函数是非-虚拟的。这导致 boost::mutex-s 的数量增加,直到抛出互斥体异常。当正确调用派生类的析构函数时,不再抛出互斥异常。

沿途收集的相关/有趣的事实

(1) 一个早期的理论被提出,系统中有太多的互斥锁,并且应用程序超出了允许的最大同步对象数量的一些未知限制(尽管 QNX 文档明确指出此类对象的数量是无限的)。为了测试这一点,我们修改了boost::mutex类:

class mutex
{
private:
    . . .
public:
    mutex()
    {
        . . .
    }
    ~mutex()
    {
        . . .
    }
}
Run Code Online (Sandbox Code Playgroud)

到:

class mutex
{
private:
    static int _nCount;
public:
    mutex()
    {
        ++_nCount;
        . . .
    }
    ~mutex()
    {
        . . .
        --_nCount;
    }
    static int getCount ()
    {
        return _nCount;
    }
    . . .
}
Run Code Online (Sandbox Code Playgroud)

请注意,对 _nCount 变量的访问不是同步的(为此我们需要一个互斥对象!),但是从应用程序调用调试boost::mutex::getCount()函数让我们确信互斥的数量是异常时低(平均 55-65 个活动互斥体)。

这种通过添加静态访问函数在最低级别监视对象(例如,Boost 中的互斥锁)的技术是调试棘手问题时需要考虑的好工具。

(2) 我们偶尔会收到 ENOMEM 异常,表示内存问题(“系统无法分配创建互斥锁所需的资源”)。

(3)三个月前发布的 FreeBSD 站点与我们的症状非常相似:

我遇到了似乎无法解决的麻烦。我的程序可重复地创建和销毁互斥锁​​(并且,显然,在两者之间使用它们)。在创建第 60 个锁时,我总是得到 ENOMEM。我有空闲内存,很多。所有锁都被正确释放。

不幸的是,该线程并没有为我们指明建设性的方向。

(4) 当仔细研究应用程序的代码时,发现一个派生对象的基类析构函数是非虚拟的,从而泄漏了一些内存,从而取得了突破。使基类析构函数虚拟修复了内存泄漏并解决了互斥异常。

(5) 即使在将基类的析构函数设为虚拟之后,我们发现在使用 QNX® Momentics Tool Suite 为 Blackberry 10 编译时,派生类的析构函数并未被调用。我们通过将基础析构函数和派生析构函数都指定为 virtual来“破解”这个问题。只有这样,派生的析构函数才被调用。这可能表明 QNX 编译器对 C++ 规范的实现存在错误,该规范明确指出虚拟性会传播到派生类(工作草案,C++ 编程语言标准(2012 年),第 250 页,脚注 9)。

编辑:请参阅此 Stack Overflow 帖子,了解 QNX 放弃有关虚拟析构函数的另一个示例。