Программа обрабатывает поступающую к ней очередь задач, создаёт на каждую задачу по два три потока.
Всё собираю с /MDd, единственная либа что использует msvcrtd.dll — curllibd.dll, который загружает ws_2.dll, а сокеты — msvcrtd.dll.
В тестовом прогоне, что описан ниже curl специально не использовался.
После непродолжительной нагрузки все дедлочится, список потоков выглядит так:
3176 _initptd _getch_nolock Normal 0
3856 _threadstartex boost::thread::sleep Normal 0
2864 _threadstartex _initptd Normal 0
3156 _threadstartex _initptd Normal 0
> 1524 _threadstartex _endthreadex Normal 0
2704 _threadstartex _endthreadex Normal 0
368 _threadstartex _endthreadex Normal 0
3000 _threadstartex _initptd Normal 0
3408 _threadstartex _endthreadex Normal 0
1032 _threadstartex _endthreadex Normal 0
1556 _threadstartex _endthreadex Normal 0
3844 _threadstartex TasksQueue::Lock Normal 0
3344 _threadstartex _endthreadex Normal 0
3292 _threadstartex TasksQueue::Lock Normal 0
200 _threadstartex _endthreadex Normal 0
3672 _threadstartex TasksQueue::Lock Normal 0
216 _threadstartex _endthreadex Normal 0
Call stack для 1524:
ntdll.dll!KiFastSystemCallRet()
[Frames below may be incorrect and/or missing, no symbols loaded for ntdll.dll]
ntdll.dll!NtWaitForSingleObject() + 0xc bytes
ntdll.dll!RtlLockHeap() + 0x25c bytes
ntdll.dll!LdrShutdownThread() + 0x32 bytes
kernel32.dll!ExitThread() + 0x2f bytes
> msvcr80d.dll!_endthreadex(unsigned int retcode=0x00000000) Line 414 C
msvcr80d.dll!_callthreadstartex() Line 348 + 0x15 bytes C
msvcr80d.dll!_threadstartex(void * ptd=0x01046090) Line 331 C
kernel32.dll!GetModuleHandleA() + 0xdf bytes
Здравствуйте, Sergey Larionov, Вы писали:
SL>Здравствуйте, Sergey Larionov, Вы писали: SL>>В чём может быть дело? Из-за чего потоки могут не закрываться?
SL>Кстати, у меня в потоки передаются хендлы процессов, пайпов и евентов. SL>Правильно делать Duplicate вообще, правильно ли в вызывающем потоке?
В целом нормально, если всё правильно сделать.
В принципе, если основной поток ждёт завершения дочерних потоков, можно закрывать все хендлы в основном потоке после завершения дочернего. Тогда ничего дублицировать не надо.
Здравствуйте, Sergey Larionov, Вы писали:
SL>Программа обрабатывает поступающую к ней очередь задач, создаёт на каждую задачу по два три потока. SL>Всё собираю с /MDd, единственная либа что использует msvcrtd.dll — curllibd.dll, который загружает ws_2.dll, а сокеты — msvcrtd.dll. SL>В тестовом прогоне, что описан ниже curl специально не использовался.
SL>После непродолжительной нагрузки все дедлочится, список потоков выглядит так:
SL>Почти все потоки висят на RtlLockHeap, ни один дальше неё не ушёл, в своём коде никакого блокирования найти не могу.
SL>В чём может быть дело? Из-за чего потоки могут не закрываться?
Первое, что приходит на ум, что какой-то поток умер "внутри" залоченного хипа, так и не разлочив мьютекс.
Посмотри в окне Output, нету ли там каких-либо ошибок и сообщений о завершении потоков.
> После непродолжительной нагрузки все дедлочится, список потоков выглядит > так: >
> 3176 _initptd _getch_nolock Normal 0
> 3856 _threadstartex boost::thread::sleep Normal 0
> 2864 _threadstartex _initptd Normal 0
> 3156 _threadstartex _initptd Normal 0
>> 1524 _threadstartex _endthreadex Normal 0
> 2704 _threadstartex _endthreadex Normal 0
> 368 _threadstartex _endthreadex Normal 0
> 3000 _threadstartex _initptd Normal 0
> 3408 _threadstartex _endthreadex Normal 0
> 1032 _threadstartex _endthreadex Normal 0
> 1556 _threadstartex _endthreadex Normal 0
> 3844 _threadstartex TasksQueue::Lock Normal 0
> 3344 _threadstartex _endthreadex Normal 0
> 3292 _threadstartex TasksQueue::Lock Normal 0
> 200 _threadstartex _endthreadex Normal 0
> 3672 _threadstartex TasksQueue::Lock Normal 0
> 216 _threadstartex _endthreadex Normal 0
>
А завершения потоков вы случайно не в DllMain ждете? Если так, то скорее
всего не дождетесь.
А вообще, я с дедлоками обычно борюсь так — беру первый попавшийся поток,
смотрю на ожидании какого мьютекса он висит. Мьютексы у нас в проекте либо
CriticalSection, либо самописные — и у тех, и у других можно отладчиком
посмотреть, какой поток его занял. Результаты записываю на бумажку. Повторяю
итерацию для потока, занявшего мьютекс. Обычно в результате нескольких
итераций либо обнаруживаются "перекрещивающиеся" мьютексы, либо (редко)
выясняется, что какой-то поток забыл отпустить мьютекс и завершился.
Posted via RSDN NNTP Server 2.1 beta
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Здравствуйте, remark, Вы писали:
R>Здравствуйте, Sergey Larionov, Вы писали:
R>Первое, что приходит на ум, что какой-то поток умер "внутри" залоченного хипа, так и не разлочив мьютекс. R>Посмотри в окне Output, нету ли там каких-либо ошибок и сообщений о завершении потоков.
Вобщем сейчас поотлаживал, добился только того, что знаю когда всё встаёт.
Единственный повторяющийся параметр — одновременное выполнение функций _beginthreadex, _endthreadex в разных потоках.
Причём beginthreadex виснет так:
ntdll.dll!KiFastSystemCallRet()
[Frames below may be incorrect and/or missing, no symbols loaded for ntdll.dll]
ntdll.dll!NtWaitForSingleObject() + 0xc bytes
ntdll.dll!RtlLockHeap() + 0x25c bytes
ntdll.dll!RtlCreateAcl() + 0x62 bytes
ntdll.dll!LdrGetDllHandleEx() + 0x8a bytes
ntdll.dll!LdrGetDllHandle() + 0x18 bytes
kernel32.dll!GetModuleHandleW() + 0x4f bytes
kernel32.dll!GetModuleHandleW() + 0x159 bytes
kernel32.dll!GetModuleHandleW() + 0x1f bytes
kernel32.dll!GetModuleHandleA() + 0x1f bytes
> msvcr80d.dll!_initptd(_tiddata * ptd=0x00e45318, threadlocaleinfostruct * ptloci=0x003fed58) Line 479 + 0xb bytes C
msvcr80d.dll!_beginthreadex(void * security=0x00000000, unsigned int stacksize=0x00000000, unsigned int (void *)* initialcode=0x004943cc, void * argument=0x00001f48, unsigned int createflag=0x00000000, unsigned int * thrdaddr=0x0121ff68) Line 177 + 0x12 bytes C
pro_service.exe!WorkerProc(void * pParam=0x00000000) Line 265 + 0x19 bytes C++
msvcr80d.dll!_callthreadstartex() Line 348 + 0xf bytes C
msvcr80d.dll!_threadstartex(void * ptd=0x00e209d0) Line 331 C
kernel32.dll!GetModuleHandleA() + 0xdf bytes
а _endthreadex так:
ntdll.dll!KiFastSystemCallRet()
[Frames below may be incorrect and/or missing, no symbols loaded for ntdll.dll]
ntdll.dll!NtWaitForSingleObject() + 0xc bytes
ntdll.dll!RtlLockHeap() + 0x25c bytes
ntdll.dll!LdrShutdownThread() + 0x32 bytes
kernel32.dll!ExitThread() + 0x2f bytes
> msvcr80d.dll!_endthreadex(unsigned int retcode=0x00000000) Line 414 C
pro_service.exe!convertor_listener(void * lpvParam=0x00e4e3a0) Line 1770 + 0x8 bytes C++
msvcr80d.dll!_callthreadstartex() Line 348 + 0xf bytes C
msvcr80d.dll!_threadstartex(void * ptd=0x00e45248) Line 331 C
kernel32.dll!GetModuleHandleA() + 0xdf bytes
Эта _initpd -> GetModuleHandle(KERNEL32) — всегда повторяется, на ней и лочится.
--
"To get the brain out was an easy part.
The hard part was to get the brain OUT"
Здравствуйте, Sergey, Вы писали:
S>А завершения потоков вы случайно не в DllMain ждете? Если так, то скорее S>всего не дождетесь.
Я их завершения нигде не жду, они самодостаточные все. Структуры с параметрами прямо в них удаляю, эти данные только в том потоке и используются, а хендлы сразу закрываю.
S>А вообще, я с дедлоками обычно борюсь так — беру первый попавшийся поток, S>смотрю на ожидании какого мьютекса он висит. Мьютексы у нас в проекте либо S>CriticalSection, либо самописные — и у тех, и у других можно отладчиком S>посмотреть, какой поток его занял. Результаты записываю на бумажку. Повторяю S>итерацию для потока, занявшего мьютекс. Обычно в результате нескольких S>итераций либо обнаруживаются "перекрещивающиеся" мьютексы, либо (редко) S>выясняется, что какой-то поток забыл отпустить мьютекс и завершился.
Да, спасибо, один перекрёстный мьютекс нашёл, но дело было, к сожалению, не в нём
--
"To get the brain out was an easy part.
The hard part was to get the brain OUT"
Здравствуйте, Аноним, Вы писали:
А>TerminateThread или SuspendThread не юзаете?
Сейчас проверил — действительно юзаю, но я от него никак не ожидал.
Нужно избавиться?
--
"To get the brain out was an easy part.
The hard part was to get the brain OUT"
Здравствуйте, Sergey, Вы писали:
S>А завершения потоков вы случайно не в DllMain ждете? Если так, то скорее S>всего не дождетесь. S>А вообще, я с дедлоками обычно борюсь так — беру первый попавшийся поток, S>смотрю на ожидании какого мьютекса он висит. Мьютексы у нас в проекте либо S>CriticalSection, либо самописные — и у тех, и у других можно отладчиком S>посмотреть, какой поток его занял. Результаты записываю на бумажку. Повторяю S>итерацию для потока, занявшего мьютекс. Обычно в результате нескольких S>итераций либо обнаруживаются "перекрещивающиеся" мьютексы, либо (редко) S>выясняется, что какой-то поток забыл отпустить мьютекс и завершился.
Здравствуйте, Sergey Larionov, Вы писали:
SL>Здравствуйте, Аноним, Вы писали:
А>>TerminateThread или SuspendThread не юзаете? SL>Сейчас проверил — действительно юзаю, но я от него никак не ожидал. SL>Нужно избавиться?
Я имею ввиду TerminateThread
--
"To get the brain out was an easy part.
The hard part was to get the brain OUT"
Здравствуйте, Sergey Larionov, Вы писали:
SL>Здравствуйте, remark, Вы писали:
R>>Здравствуйте, Sergey Larionov, Вы писали:
R>>Первое, что приходит на ум, что какой-то поток умер "внутри" залоченного хипа, так и не разлочив мьютекс. R>>Посмотри в окне Output, нету ли там каких-либо ошибок и сообщений о завершении потоков.
SL>Вобщем сейчас поотлаживал, добился только того, что знаю когда всё встаёт.
SL>Единственный повторяющийся параметр — одновременное выполнение функций _beginthreadex, _endthreadex в разных потоках.
Ну они же оба висят на RtlLockHeap(), значит мьютекс кто-то раньше попортил.
Кстати, возможно просто идёт расстел памяти, и кто-то портит lock-word хипового мьютекса. Соотв. все потоки на нём виснут, т.к. ждут что его кто-то освободит, а его никто освобождать и не собирается.
Хммм... это вполне вероятно.
Можешь попробовать при старте программы найти адрес, где сидит lock-word хипового мьютекса, и поставить на него брик поинт, и дальше тупо и упорно смотреть, не модифицирует ли его кто-то левый.
SL>Эта _initpd -> GetModuleHandle(KERNEL32) — всегда повторяется, на ней и лочится.
GetModuleHandle это просто неправильный вывод отладчиком. Это не более чем стартовая точка всех потоков. Иногда она показывается как, например, 'strcmp'
Здравствуйте, Sergey Larionov, Вы писали:
SL>Здравствуйте, Аноним, Вы писали:
А>>TerminateThread или SuspendThread не юзаете? SL>Сейчас проверил — действительно юзаю, но я от него никак не ожидал. SL>Нужно избавиться?
TerminateThread() как минимум к серьёзным утечкам памяти приводит. Если убивать в произвольном месте, то дедлок как пить дать словить
Здравствуйте, remark, Вы писали:
R>Здравствуйте, Sergey Larionov, Вы писали:
SL>>Здравствуйте, Аноним, Вы писали:
А>>>TerminateThread или SuspendThread не юзаете? SL>>Сейчас проверил — действительно юзаю, но я от него никак не ожидал. SL>>Нужно избавиться?
R>
R>TerminateThread() как минимум к серьёзным утечкам памяти приводит. Если убивать в произвольном месте, то дедлок как пить дать словить
Точняк! Убрал TerminateThread — всё заработало.
Тут дело всё в том, что я в одно место его бездумно вставил, а при использовании CreateThread этот дедлок славливался только через несколько дней работы.
Вот он, кровавый экспириенс.
Мне нужно было читать из пайпа, в который писал дочерний процесс, причём ReadFile не всегда отпускался, вынес его в поток, и по евенту завершения процесса терминейтил поток чтения. Через два месяца настигло
Спасибо, коллеги!
--
"To get the brain out was an easy part.
The hard part was to get the brain OUT"
Здравствуйте, remark, Вы писали:
R>Здравствуйте, Sergey Larionov, Вы писали:
SL>>Эта _initpd -> GetModuleHandle(KERNEL32) — всегда повторяется, на ней и лочится.
R>GetModuleHandle это просто неправильный вывод отладчиком. Это не более чем стартовая точка всех потоков. Иногда она показывается как, например, 'strcmp'
Да да, точно, про strcmp до меня давно дошло, а про это — только догадка, но я сам себе не верил
--
"To get the brain out was an easy part.
The hard part was to get the brain OUT"
Здравствуйте, Sergey Larionov, Вы писали:
SL>Здравствуйте, Sergey Larionov, Вы писали: SL>>В чём может быть дело? Из-за чего потоки могут не закрываться?
SL>Кстати, у меня в потоки передаются хендлы процессов, пайпов и евентов. SL>я делаю это так: SL>
SL>Правильно делать Duplicate вообще, правильно ли в вызывающем потоке?
DuplicateHandle имеет смысл вызывать если тебе надо передать handle в другой процесс, если же нет, то не надо создавать их копии, просто надо вовремя их закрыть.
> S>А завершения потоков вы случайно не в DllMain ждете? Если так, то скорее > S>всего не дождетесь. > S>А вообще, я с дедлоками обычно борюсь так — беру первый попавшийся > поток, > S>смотрю на ожидании какого мьютекса он висит. Мьютексы у нас в проекте > либо > S>CriticalSection, либо самописные — и у тех, и у других можно отладчиком > S>посмотреть, какой поток его занял. Результаты записываю на бумажку. > Повторяю > S>итерацию для потока, занявшего мьютекс. Обычно в результате нескольких > S>итераций либо обнаруживаются "перекрещивающиеся" мьютексы, либо (редко) > S>выясняется, что какой-то поток забыл отпустить мьютекс и завершился. > > Теперь это можно делать автоматически, без бумажки: > http://msdn2.microsoft.com/en-us/library/ms681622(VS.85).aspx
Не, в моем случае наверное не получится. У нас read-write recursive mutex на
событиях реализован, а их WCT, насколько я понял, не умеет отслеживать. А
вообще полезная конечно штука.
Posted via RSDN NNTP Server 2.1 beta
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Здравствуйте, Sergey, Вы писали:
>> S>А завершения потоков вы случайно не в DllMain ждете? Если так, то скорее >> S>всего не дождетесь. >> S>А вообще, я с дедлоками обычно борюсь так — беру первый попавшийся >> поток, >> S>смотрю на ожидании какого мьютекса он висит. Мьютексы у нас в проекте >> либо >> S>CriticalSection, либо самописные — и у тех, и у других можно отладчиком >> S>посмотреть, какой поток его занял. Результаты записываю на бумажку. >> Повторяю >> S>итерацию для потока, занявшего мьютекс. Обычно в результате нескольких >> S>итераций либо обнаруживаются "перекрещивающиеся" мьютексы, либо (редко) >> S>выясняется, что какой-то поток забыл отпустить мьютекс и завершился. >> >> Теперь это можно делать автоматически, без бумажки: >> http://msdn2.microsoft.com/en-us/library/ms681622(VS.85).aspx
S>Не, в моем случае наверное не получится. У нас read-write recursive mutex на S>событиях реализован, а их WCT, насколько я понял, не умеет отслеживать. А S>вообще полезная конечно штука.
Хммм... Ну в итоге-то ты всё равно блокирешься на объекте ядра, т.ч. я не вижу причин, причему WCT не может это отследить.
Здравствуйте, Adriano, Вы писали:
A>Здравствуйте, Sergey Larionov, Вы писали: SL>>Правильно делать Duplicate вообще, правильно ли в вызывающем потоке?
A>DuplicateHandle имеет смысл вызывать если тебе надо передать handle в другой процесс, если же нет, то не надо создавать их копии, просто надо вовремя их закрыть.
Да, про это я знаю, но дело в том, что хэндлы у меня используются в двух дочерних тредах, в главном их завершение не отслеживается.
Нормально ли будет если я сделаю дубликат, и каждую копию закрою при завершении функции треда?
--
"To get the brain out was an easy part.
The hard part was to get the brain OUT"