Что может вызвать исключение 16: "mutex: Ресурс занят" (с использованием Boost / BB10)?

Я перенес давно работающую стабильную библиотеку, написанную на C++ и Boost, на Blackberry 10. Библиотека передает файлы между устройствами. Библиотека хорошо компилируется и связывается, и работает просто отлично. Тем не менее, я постоянно сталкиваюсь с брошенным исключением на моем устройстве Blackberry 10 после передачи 1, 2 или 3 файлов. Поймать исключение как 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. Я искал в Stackru, Google и т. Д. Все, что связано с "mutex: Resource busy", но ничего не нашел. На данный момент мой код не обращается к мьютексам на уровне приложения, поэтому я предполагаю, что упомянутый мьютекс связан с Boost.

Может кто-нибудь сказать мне, что в основном означает сообщение, и почему выбрасывается исключение "ресурс занят"? Есть ли известная проблема на Blackberry 10, связанная с исключением?

Заранее спасибо!

1 ответ

Решение

После долгих отладок коллега наконец решил проблему.

Управляющее резюме

Исключение было вызвано pthread_mutex_init () после 55-65 вызовов конструктора boost::mutex, потому что объект производного класса уровня приложения, имеющий boost::mutex в качестве переменной-члена, не был полностью уничтожен, потому что деструктор базового класса не был -virtual. Это заставляло число boost::mutex-s расти до тех пор, пока не возникнет исключение mutex. Когда деструктор производного класса был правильно вызван, исключения мьютекса больше не генерировались.

Соответствующие / интересные факты, полученные по пути

(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) Прорыв произошел, когда тщательное изучение кода приложения нашло производный объект, деструктор базового класса которого был не виртуальным, что привело к утечке памяти. Создание виртуального деструктора базового класса устранило утечку памяти и разрешило исключения мьютекса.

(5) Даже после создания виртуального деструктора базового класса мы обнаружили, что деструктор производного класса не вызывался при компиляции для Blackberry 10 с использованием QNX® Momentics Tool Suite. Мы "взломали" эту проблему, указав как базовый, так и производные деструкторы как виртуальные. Только тогда вызванный деструктор был вызван. Это может указывать на ошибку в реализации компилятором QNX спецификации C++, в которой четко указывается, что виртуальность распространяется на производные классы ( рабочий проект, стандарт для языка программирования C++ (2012), стр. 250, сноска 9).

Редактировать: см. Этот пост переполнения стека для другого примера QNX бросая мяч в отношении виртуальных деструкторов.

Другие вопросы по тегам