Может ли вызов WSAStartup() из нескольких потоков вызвать взаимоблокировку?

Я разрабатываю приложение с одним TCP-сервером и несколькими UDP-серверами/слушателями. Каждый сервер представляет собой отдельный поток, такой же, как рабочие потоки для установленных TCP-соединений. Я вызываю WSAStartup() в каждом из потоков.

Иногда вызов WSAStartup() зависает (мне это кажется тупиком). Вот трассировка стека:

  ntdll.dll!_KiFastSystemCallRet@0()  
  ntdll.dll!_ZwWaitForSingleObject@12()  + 0xc bytes 
  ntdll.dll!_RtlpWaitForCriticalSection@4()  + 0x8c bytes 
  ntdll.dll!_RtlEnterCriticalSection@4()  + 0x46 bytes 
  ntdll.dll!_LdrpGetProcedureAddress@20()  + 0x17d bytes 
  ntdll.dll!_LdrGetProcedureAddress@16()  + 0x18 bytes 
  kernel32.dll!_GetProcAddress@8()  + 0x3e bytes 
  vld.dll!03203723()  
  [Frames below may be incorrect and/or missing, no symbols loaded for vld.dll] 
  ws2_32.dll!CheckForHookersOrChainers()  + 0x22 bytes 
  ws2_32.dll!_WSAStartup@8()  + 0xa7 bytes 

Этот тупик происходит во время фазы инициализации. Я вижу, что TCP-сервер запущен и установлено одно TCP-соединение, при этом запущен только один из UDP-серверов. Трассировка стека исходит от функции, которая должна инициировать остальные серверы UDP. Я предполагаю, что пока я пытаюсь инициализировать UDP-сервер и вызывать WSACStartup(), другой поток обрабатывает другую операцию сокета, например, новое TCP-соединение, и он также вызывает WSAStartup()?

Мой вопрос заключается в том, может ли вызов WSAStartup() из нескольких потоков вызвать этот тупик? Также я проверил, вызывается ли WSACleanup() до взаимоблокировки, и это не так. Выполнение никогда не достигает ни одного из WSACleanup().

Я знаю, что только одного вызова WSAStartup должно быть достаточно, но вызов WSAStartup() несколько раз не должен быть проблемой (MSDN]1): "Приложение может вызывать WSAStartup более одного раза, если ему необходимо получить информацию о структуре WSADATA более одного раза". Следовательно, я хотел бы установить, вызван ли этот тупик WSAStartup() или чем-то еще.


person Misko Mare    schedule 05.08.2010    source источник
comment
Это не ответ на ваш вопрос, но рассматривали ли вы возможность использования boost asio (boost.org/doc/libs/1_43_0/doc/html/boost_asio.html)? С этой библиотекой мне было намного легче решать такие проблемы, как твоя.   -  person nabulke    schedule 05.08.2010
comment
Николай, раньше пользовался бустом, понравилось. Поскольку я начал это приложение с WinSock, я хотел бы разобраться в этом вопросе. Просто для любопытства, я думаю :)   -  person Misko Mare    schedule 05.08.2010
comment
Вы смотрели на стеки вызовов других потоков?   -  person Collin Dauphinee    schedule 05.08.2010
comment
Похоже, у вас есть взаимоблокировка, связанная с блокировкой загрузчика; вы должны посмотреть на другие потоки и посмотреть, вызывают ли они LoadLibary, GetProcAddress и т. д., особенно если это происходит в одной из их функций DllMain.   -  person Luke    schedule 05.08.2010
comment
dauphic, я использую VS 2005. Я не знаю, как проверять стеки других потоков в VS 2005. Я считаю, что в более новых версиях VS должна быть такая возможность.   -  person Misko Mare    schedule 06.08.2010
comment
в VS2005 - Debug › Windows › Threads, мини дамп был бы даже лучше, но это было бы НАСТОЛЬКО похоже на поддержку клиентов :)   -  person Andrey    schedule 06.08.2010
comment
Тупик не вызван вызовом WSAStartup из нескольких потоков. Это вызвано тем, что вы вызываете его из DllMain (или из того, что вы решили назвать своей точкой входа в DLL). На это уже указывал @Luke, но, поскольку вы решили проигнорировать это, я подумал, что подниму этот вопрос снова.   -  person IInspectable    schedule 11.12.2014


Ответы (4)


Функция WSAStartup обычно приводит к загрузке зависимых от протокола вспомогательных библиотек DLL. В результате функцию WSAStartup не следует вызывать из функции DllMain в библиотеке DLL приложения. Это потенциально может привести к взаимоблокировкам. Dllmain вызывается в критической секции загрузчика DLL, что является основной причиной этой взаимоблокировки.
Подробнее: http://msdn.microsoft.com/en-us/library/windows/desktop/ms742213%28v=vs.85%29.aspx

person Eftiquar    schedule 03.10.2013

Вам вообще не нужно вызывать WSAStartup() несколько раз. Один раз на программу нормально.

person Warren Young    schedule 05.08.2010
comment
Имхо, в вопросе четко указано, что автор темы прочитал эту часть MSDN и вместо решения, чтобы избежать проблемы (что, безусловно, является хорошим решением), он пытается найти первопричину. - person Andrey; 06.08.2010
comment
Это как в старой шутке: Доктор, мне больно, когда я это делаю. Тогда не делай этого. - person Warren Young; 06.08.2010
comment
Если вы используете AppVerifier, вызова WSAStartup только один раз для каждого процесса будет недостаточно. Рекомендуется, чтобы каждый поток инициализировал сокеты Windows и деинициализировал их по завершении. Это особенно верно, если вы не можете заранее знать, какой поток будет последним для выхода. - person IInspectable; 26.12.2014

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

person wilx    schedule 05.08.2010
comment
Я работаю над автономным приложением и использую системные библиотеки DLL, поэтому у меня нет DLLMain(). Все мои вызовы WSAStartup() находятся в функциях потока: msdn.microsoft.com/en-us/library/ms686736(VS.85).aspx PS. вот список дополнительных библиотек: - ws2_32.lib - strsafe.lib - shell32.lib - person Misko Mare; 06.08.2010
comment
Внимательно изучите список DLL, загруженных в релизную версию вашего приложения (без профилировщиков, детекторов утечек и т. д.). Скорее всего, одна из этих DLL перехватывает функции Windows. Process Explorer от SysInternals вам очень поможет. - person Andrey; 06.08.2010

WSAStartup на самом деле не ведет ни к какому LoadLibrary, поэтому я не думаю, что это loader lock случай.

Вместо этого очевидно, что Windows API попадает в ловушку в вашем случае (термин trap здесь лучше, потому что hook имеет другое значение в Windows).

Таким образом, я считаю, что проблема заключается не в одновременном использовании WSAStartup, а в побочных эффектах сторонних ловушек по сравнению с исходными функциями Windows API в вашем процессе. Я думаю, вам нужно очистить свою среду от любого внешнего воздействия (ловушки API с вашей стороны или от антивирусного программного обеспечения, что угодно).

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

person Andrey    schedule 05.08.2010
comment
Вы уверены, что он не выполняет вызов LoadLibrary? - person notbad.jpeg; 31.07.2014
comment
Вы правы, я не помню, почему я так думал тогда, но на самом деле он может загружать вспомогательные библиотеки DLL в соответствии с MSDN, поэтому блокировка загрузчика может быть проблемой. Я все же думаю, глядя на стек, что первопричина кроется в ловушке, перехватившей WSAStartup. - person Andrey; 06.08.2014