regsvr32.exe проблемы с потоками (WaitForMultipleObjects () и SetEvent ()) - PullRequest
0 голосов
/ 16 марта 2011

У меня проблемы с зависанием regsvr32.exe во время установки.DLL, назовем ее common.dll, регистрируется как часть процесса установки с использованием regsvr32.exe.Common.dll использует другую DLL, utility.dll.

Часть utility.dll содержит функции ведения журнала.Эта функция ведения журнала использует статический объект «Таймер» для периодической проверки размеров файла журнала и соответствующего разделения.Объект Timer включает свой собственный поток, который он использует для запуска таймера.Объект таймера внутри регистратора является статическим, поэтому он используется в нескольких экземплярах регистратора, которые используют статические потоки для указания на один и тот же файл.

Таймер имеет два события, таймер (созданный с помощью CreateWaitableTimer ())и стандартное событие синхронизации (CreateEvent ()) для запуска отключения потока.Поток запускается в конструкторе (_beginthreadex ()).Внутри функции потока есть ожидание вызова WaitForMultipleObjects () как по таймеру, так и по событию завершения работы.Wait ... () имеет значение INFINITE, и функция потока возвращается, когда установлено событие выключения (SetEvent ()).

(Выше приведено в качестве фона, ни одна его часть не может быть изменена как частьрешения и все DLL-файлы, регистратор и таймер работают правильно).

Проблема возникает во время работы regsvr32.exe.Он загружает файл common.dll, который загружает файл utility.dll, который инициализирует объект потока статического таймера.Поток запускается правильно, и он достигает вызова WaitForMultipleObjects () внутри функции потока.Как только регистрация завершается (почти мгновенно), вызывается деструктор таймера.Деструктор вызывает SetEvent () для события shutdown, , но WaitForMultipleObjects () никогда не возвращается. В качестве части попытки выяснить эту проблему я поставил вызов WaitForSingleObject () сразу после вызова SetEvent (),ожидание события отключенияЭто также никогда не возвращается, что приводит меня к мысли, что есть проблема с самим событием.У меня были следующие возможные объяснения:

  1. Проблема синхронизации.Процесс регистрации заканчивается очень быстро, и, возможно, поток переходит в состояние, в котором он не готов к закрытию?Тем не менее поток достигает вызова WaitForMultipleObjects (), что заставляет меня поверить, что это не проблема.
  2. Utility.dll выгружается программой regsvr32.exe.Я не совсем понимаю, как все это работает, но с помощью ProcessExplorer похоже, что regsvr32.exe все еще загружает dll, пока он висит, поэтому я не думаю, что это проблема.
  3. Тесныйцикл внутри regsvr32.exe во время выключения.Если процесс уничтожения для regsvr32.exe происходит в тесном цикле (то есть, в то время как (NotShutdown ()) и т. Д.), Возможно, это не уменьшает время процессора для потока таймера, что объясняет зависание.

Есть еще мысли по этому поводу?Я искал в интернете и не могу найти ничего, что удаленно связано с этой проблемой.


PS Я знаю, что решение проблемы заключается в использовании статического указателя и инициализации таймера, когда это действительно необходимои это решение, с которым я собираюсь.Однако я все еще хотел бы понять , почему это происходит, поскольку мне кажется совершенно нелепым, что SetEvent () не будет работать.


Вывод команды windbg! Locks:

0:000> !locks

CritSec ntdll!LdrpLoaderLock+0 at 7c97e178
LockCount 2
RecursionCount 1
OwningThread d8
EntryCount 4
ContentionCount 4
*** Locked

Scanned 253 critical sections
0:000> ~*kv

. 0 Id: a40.d8 Suspend: 0 Teb: 7ffdf000 Unfrozen
ChildEBP RetAddr Args to Child 
0007e5ec 7c90df5a 7c8025db 00000764 00000000 ntdll!KiFastSystemCallRet (FPO: [0,0,0])
0007e5f0 7c8025db 00000764 00000000 00000000 ntdll!ZwWaitForSingleObject+0xc (FPO: [3,0,0])
0007e654 7c802542 00000764 ffffffff 00000000 kernel32!WaitForSingleObjectEx+0xa8 (FPO: [Non-Fpo])
*** WARNING: Unable to verify checksum for Utilityd.dll
0007e668 00a84e37 00000764 ffffffff 0007e71c kernel32!WaitForSingleObject+0x12 (FPO: [Non-Fpo])
0007e6c8 00a2e5af 0007e798 0007e754 00aa02e0 Utilityd!CThreadTimer::~CThreadTimer+0x97 [C:\xxx\ThreadTimer.cpp @ 49]
0007e71c 00aa02ae 00fe7a18 0007e740 00aa039b Utilityd!$E177+0x3f
0007e728 00aa039b 00a10000 00000000 00000000 Utilityd!_CRT_INIT+0xde [crtdll.c @ 236]
0007e740 7c90118a 00a10000 00000000 00000000 Utilityd!_DllMainCRTStartup+0xbb [crtdll.c @ 289]
0007e760 7c91e044 00aa02e0 00a10000 00000000 ntdll!LdrpCallInitRoutine+0x14
0007e858 7c80ac97 00950000 00000000 0003415e ntdll!LdrUnloadDll+0x41c (FPO: [Non-Fpo])
0007e86c 0100214e 00950000 00000000 00020bca kernel32!FreeLibrary+0x3f (FPO: [Non-Fpo])
0007ff1c 010024bf 01000000 00000000 00020bca regsvr32!wWinMain+0xad1 (FPO: [Non-Fpo])
0007ffc0 7c817077 00000018 00000000 7ffd4000 regsvr32!wWinMainCRTStartup+0x198 (FPO: [Non-Fpo])
0007fff0 00000000 01002327 00000000 78746341 kernel32!BaseProcessStart+0x23 (FPO: [Non-Fpo])

   1 Id: a40.e98 Suspend: 0 Teb: 7ffde000 Unfrozen
ChildEBP RetAddr Args to Child 
0104fe34 7c90df5a 7c91b24b 00000760 00000000 ntdll!KiFastSystemCallRet (FPO: [0,0,0])
0104fe38 7c91b24b 00000760 00000000 00000000 ntdll!ZwWaitForSingleObject+0xc (FPO: [3,0,0])
0104fec0 7c901046 0197e178 7c913978 7c97e178 ntdll!RtlpWaitForCriticalSection+0x132 (FPO: [Non-Fpo])
0104fec8 7c913978 7c97e178 00000000 7ffde000 ntdll!RtlEnterCriticalSection+0x46 (FPO: [1,0,0])
0104ff34 7c80c136 006e0065 00560074 00fe43d8 ntdll!LdrShutdownThread+0x22 (FPO: [Non-Fpo])
*** ERROR: Symbol file could not be found. Defaulted to export symbols for MSVCRTD.DLL - 
0104ff6c 1020c061 00000000 00fe43d8 0104ffb4 kernel32!ExitThread+0x3e (FPO: [Non-Fpo])
WARNING: Stack unwind information not available. Following frames may be wrong.
0104ff7c 1020bfd8 00000000 006e0065 00560074 MSVCRTD!endthreadex+0x41
0104ffb4 7c80b729 00fe43d8 006e0065 00560074 MSVCRTD!beginthreadex+0x178
0104ffec 00000000 1020bf20 00fe43d8 00000000 kernel32!BaseThreadStart+0x37 (FPO: [Non-Fpo])

   2 Id: a40.1708 Suspend: 0 Teb: 7ffdd000 Unfrozen
ChildEBP RetAddr Args to Child 
0136fc0c 7c90df5a 7c91b24b 00000760 00000000 ntdll!KiFastSystemCallRet (FPO: [0,0,0])
0136fc10 7c91b24b 00000760 00000000 00000000 ntdll!ZwWaitForSingleObject+0xc (FPO: [3,0,0])
0136fc98 7c901046 0197e178 7c91e3b5 7c97e178 ntdll!RtlpWaitForCriticalSection+0x132 (FPO: [Non-Fpo])
0136fca0 7c91e3b5 7c97e178 0136fd2c 00000004 ntdll!RtlEnterCriticalSection+0x46 (FPO: [1,0,0])
0136fd18 7c90e457 0136fd2c 7c900000 00000000 ntdll!_LdrpInitialize+0xf0 (FPO: [Non-Fpo])
00000000 00000000 00000000 00000000 00000000 ntdll!KiUserApcDispatcher+0x7

1 Ответ

0 голосов
/ 17 марта 2011

Глобальные деструкторы и конструкторы вызываются из DllMain с блокировкой загрузчика, как вы можете видеть из трассировки стека.Поток, вызывающий ~ CThreadTimer, имеет блокировку загрузчика и ожидает установки события.Если для продолжения блокировки другому потоку потребуется блокировка загрузчика, он будет заблокирован до тех пор, пока не будет установлено событие.Если поток, который устанавливает событие, является одним из потоков, которым требуется блокировка загрузчика, вы получите тупик, подобный этому.Блокировка загрузчика выполняется при загрузке библиотеки DLL, создании или уничтожении потоков, загрузке библиотеки DLL, при выходе из процесса и запуске, а также в нескольких других местах (например, GetModuleHandle).

Простой способ созданиявот такой тупик:

 static Foo { Foo() { HANDLE h = CreateThread(...); WaitForSingleObject(h, INFINITE); } g_foo;

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

Вы можете использовать !handle, чтобы взглянуть на событие, которого вы ждете, а также посмотреть, сможете ли вы получить некоторое представление.Кроме того, я снова попробую запустить ApplicationVerifier, это может привести к проблеме.

...