Завершение глобальных потоков в DLL во время завершения процесса - PullRequest
0 голосов
/ 06 ноября 2018

Существует эта мультиплатформенная (Windows, Linux, Cygwin) динамическая библиотека, которая загружается во время выполнения исполняемым файлом Cygwin. В какой-то момент, во время нормального рабочего процесса, DLL выделяет пул потоков для использования. Эти потоки управляются как глобальные переменные (подсчет ссылок). Поэтому, когда клиентский процесс завершает работу, он начинает освобождать глобальные объекты, потоки также должны быть освобождены.

Проблема, как я понимаю, заключается в том, что во время завершения процесса блокировка загрузчика получается, и дальше по улице потоки хотят установить такую ​​же блокировку, и теперь у нас тупик.

Теперь я прошу совета, как мы можем сделать хорошее отключение?

DLL не имеет методов init() или uninit() для вызова. В лучшем случае клиент может быть дополнен некоторым кодом до конца main () (то есть до завершения процесса).

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

Кстати, под Linux я не вижу таких проблем.

DLL - это только C ++ 14, клиент - C99 (Cygwin).

Я пытался прояснить ситуацию, но дайте мне знать, если у вас есть дополнительные вопросы. Заранее спасибо за любые идеи.

Ответы [ 2 ]

0 голосов
/ 09 ноября 2018

На предыдущей работе я боролся с этой проблемой довольно долго. В конечном итоге все сводится к двум возможным решениям:

  1. Очистите каждый ресурс, на который претендует нить, затем TerminateThread. Это жестокий и уродливый, но он работает вокруг проблемы THREAD_DETACH, и я на самом деле нашел его рекомендованным в Интернете.
  2. Если у вас есть возможность получить предварительное уведомление до PROCESS_DETACH - очистите все на этом раннем этапе, включая упорядоченное отключение ваших потоков. Тогда непременно не делайте абсолютно ничего во время PROCESS_DETACH - да, даже не освобождайте какие-либо затяжные объекты кучи, так как вы можете подвергнуть себя риску взаимоблокировки или аварийного завершения, и процесс все равно останавливается и освобождает все свои ресурсы.

В качестве дополнительного примечания я также научился любой ценой избегать привязки глобальных переменных к времени жизни DLL. Их конструкторы и деструкторы будут выполняться в контексте DllMain, не говоря уже о том, чтобы ... Если вам нужны глобальные синглеты в DLL, убедитесь, что у них есть ручное управление временем их жизни (с обоих концов, поэтому не нужно автоматически уничтожать умные указатели). либо).

0 голосов
/ 06 ноября 2018

Исправление заключается в добавлении метода uninit в DLL. Возможно, еще нет, но он нужен. Вы выяснили почему: хотя операционная система будет вызывать DllMain при выгрузке DLL, она делает это при блокировке загрузчика. Вам нужно сделать то, что невозможно при блокировке загрузчика, поэтому вам нужно сделать дополнительный вызов до DllMain. Называть этот метод uninit() достаточно разумно.

C ++ 14 здесь не проблема; это механизм ОС. Замок погрузчика существует с древних времен.

...