Что может вызвать исключение 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 бросая мяч в отношении виртуальных деструкторов.