2

我已将一个用 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;
}

跟踪日志行是:

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

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

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

提前致谢!

4

1 回答 1

7

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

执行摘要

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

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

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

class mutex
{
private:
    . . .
public:
    mutex()
    {
        . . .
    }
    ~mutex()
    {
        . . .
    }
}

到:

class mutex
{
private:
    static int _nCount;
public:
    mutex()
    {
        ++_nCount;
        . . .
    }
    ~mutex()
    {
        . . .
        --_nCount;
    }
    static int getCount ()
    {
        return _nCount;
    }
    . . .
}

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

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

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

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

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

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

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

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

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

于 2013-08-11T08:23:54.250 回答