Отмена потока с заблокированным мьютексом не разблокирует мьютекс

помощь клиенту с проблемой, которая у них есть. Я больше парень sysadmin / DBA, поэтому я борюсь с тем, чтобы помочь им. Они говорят, что это ошибка в ядре/среде, я пытаюсь либо доказать, либо опровергнуть это, прежде чем настаивать, что это в их коде, или искать поддержку поставщика для ОС.

происходит на Red Hat и Oracle Enterprise Linux 5.7 (и 5.8), приложение написано на C++

проблема, которую они испытывают, заключается в том, что поток запускает отдельный поток для выполнения потенциально долгосрочного TCP connect () [клиентское подключение к серверу]. Если "длительный" аспект занимает слишком много времени, они отменяют поток и начинают другой.

Это делается потому, что мы не знаем состояние серверной программы:

  • программа-сервер и работает --> подключение сразу приняли
  • серверная программа не работает, машина и сеть в порядке -- > подключение сразу ошибка 'соединение отказано'
  • машина или сеть разбились или вниз -- > подключение занимает много времени с ошибкой-нет маршрута к хосту'

проблема в том, что отмена потока, который мьютекс заблокирован (с обработчиками очистки, настроенными для разблокировки мьютекса) иногда не разблокирует мьютекс.

Это оставляет основную нить висеть на попытке заблокировать мьютекс.

подробная среды info:

  • glibc-2.5-65
  • glibc-2.5-65
  • libcap-1.10-26
  • kernel-debug-2.6.18-274.el5
  • glibc-заголовки-2.5-65
  • glibc-common-2.5-65
  • libcap-1.10-26
  • kernel-doc-2.6.18-274.el5
  • kernel-2.6.18-274.el5
  • kernel-headers-2.6.18-274.el5
  • в glibc-Devel в-2.5-65

код был построен с: c++ - g3 tst2.C-lpthread - o tst2

любые советы и рекомендации с благодарностью

2 ответов


правильно, что отмененные потоки не разблокируют мьютексы, которые они держат, вам нужно организовать это вручную, что может быть сложно, поскольку вам нужно быть очень осторожным, чтобы использовать правильные обработчики очистки вокруг каждой возможной точки отмены. Предполагая, что вы используете pthread_cancel для отмены потока и установки обработчиков очистки с помощью pthread_cleanup_push чтобы разблокировать мьютексы, есть несколько альтернатив, которые вы можете попробовать, которые могут быть проще, чтобы получить право, и поэтому может быть больше надежный.

используя RAII чтобы разблокировать мьютекс будет быть более надежным. На GNU / Linux pthread_cancel реализовано со специальным исключением типа __cxxabi::__forced_unwind, поэтому, когда поток отменяется исключение и стек. Если мьютекс заблокирован типом RAII, то его деструктор будет гарантированно запущен, если стек размотан __forced_unwind исключения. Boost Thread предоставляет портативную библиотеку C++, которая обертывает Pthreads и гораздо проще в использовании. Он обеспечивает тип RAII boost::mutex и другие полезные абстракции. Boost Thread также предоставляет свой собственный механизм "прерывания потока", который похож на отмену Pthread, но не то же самое, и точки отмены Pthread (например,connect) не стимулировать точки прерывания потока, что может быть полезно для некоторых приложений. Однако в случае вашего клиента, так как точка отмены должна прервать connect вызов они, вероятно, хотят придерживаться Pthread отмена. (Непереносимый) способ GNU / Linux реализует отмену в качестве исключения означает, что он будет хорошо работать с boost::mutex.

на самом деле нет никаких оправданий для явной блокировки и разблокировки мьютексов, когда вы пишете на C++, IMHO самый важный и самый полезный особенностью C++ являются деструкторы, которые идеально подходят для автоматического освобождения ресурсов, таких как блокировки мьютекса.

другой вариант-использовать надежный мьютекс, который создается зову pthread_mutexattr_setrobust на pthread_mutexattr_t прежде, чем инициализация мьютекса. Если поток умирает, удерживая надежный мьютекс, ядро запишет его, чтобы следующий поток, который пытается заблокировать мьютекс, получил специальный код ошибки EOWNERDEAD. Если это возможно, новый поток может сделать данные, защищенные потоком, согласованными снова и взять на себя ответственность за мьютекс. Это намного сложнее использовать правильно, чем просто использовать тип RAII для блокировки и разблокировки мьютекса.

A совершенно другим подходом было бы решить, действительно ли вам нужно удерживать блокировку мьютекса при вызове connect. Проведение мьютексов во время медленных операций-не очень хорошая идея. Не могли бы вы позвонить connect затем, если успешная блокировка мьютекса и обновление любых общих данных защищаются мьютексом?

я бы предпочел использовать поток Boost и избегать удержания мьютекса в течение длительного времени.


проблема, которую они испытывают, заключается в том, что основной поток запускает отдельный поток для выполнения потенциально долгосрочного TCP connect() [клиент, подключающийся к серверу]. Если "длительный" аспект занимает слишком много времени, они отменяют поток и начинают другой.

Trivial fix -- не отменяйте поток. Это не повредит? При необходимости проверьте поток (когда connect наконец-то завершено) требуется ли соединение и, если нет, закрыть это, отпустите мьютекс и прекратите. Это можно сделать с помощью булевой переменной, защищенной мьютексом.

кроме того, поток не должен содержать мьютекс во время ожидания сетевого ввода-вывода.мьютексы должны использоваться только для вещей, которые быстры и в основном ограничены процессором или, возможно, ограничены локальным диском.

наконец, если вы чувствуете, что вам нужно войти извне и заставить нить что-то сделать, отступите. вы написал код для этого потока. Если ты чувствуешь это ... нужно, это означает, что вы не закодировали этот поток, чтобы сделать то, что вы действительно хотели. Исправление состоит в том, чтобы изменить поток, чтобы сделать то, что и только то, что вы действительно хотите. Тогда вам не придется "толкать его" извне.