Work at SourceForge, help us to make it a better place! We have an immediate need for a Support Technician in our San Francisco or Denver office.

Close

#494 Threading bug in initializeNetwork() on Windows

Platform_Specific
closed
nobody
Net (141)
5
2012-11-10
2012-01-17
Alan Stokes
No

If two threads simultaneously call initializeNetwork() on Windows, one will call WSAStartup(), while the other will return immediately - potentially before the call to WSAStartup() has completed. This can then cause attempts to use WinSock functions to throw.

As an example, I have one thread at this stage in the call to WSAStartup:
ntdll.dll!_KiFastSystemCallRet@0()
ntdll.dll!_NtQueryValueKey@24() + 0xc bytes
advapi32.dll!_LocalBaseRegQueryValue@24() + 0x111 bytes
advapi32.dll!_RegQueryValueExA@24() + 0xad bytes
ws2_32.dll!ReadRegistryEntry() + 0x4b bytes
ws2_32.dll!DCATALOG::OpenCatalog() + 0x70 bytes
ws2_32.dll!DCATALOG::InitializeFromRegistry() + 0x10 bytes
ws2_32.dll!DPROCESS::Initialize() + 0x82 bytes
ws2_32.dll!DPROCESS::DProcessClassInitialize() + 0x24 bytes
ws2_32.dll!_WSAStartup@8() + 0x13c2 bytes
NativeClient.dll!Poco::Net::initializeNetwork() Line 286 + 0x12 bytes C++
NativeClient.dll!Poco::Net::SocketImpl::SocketImpl() Line 66 C++
NativeClient.dll!Poco::Net::StreamSocketImpl::StreamSocketImpl() Line 47 + 0xf bytes C++
NativeClient.dll!Poco::Net::StreamSocket::StreamSocket() Line 50 + 0x40 bytes C++
NativeClient.dll!Poco::Net::HTTPSession::HTTPSession() Line 57 + 0x32 bytes C++
NativeClient.dll!Poco::Net::HTTPClientSession::HTTPClientSession(const std::basic_string<char,std::char_traits<char>,std::allocator<char> > & host="localhost", unsigned short port=8080) Line 110 + 0x2f bytes C++

And another thread busy throwing an exception due to a POCO_ENOTINIT:
kernel32.dll!_RaiseException@16() + 0x52 bytes
NativeClient.dll!_CxxThrowException(void * pExceptionObject=0x00d4f43c, const _s__ThrowInfo * pThrowInfo=0x10350d7c) Line 161 C++

NativeClient.dll!Poco::Net::DNS::error(int code=10093, const std::basic_string<char,std::char_traits<char>,std::allocator<char> > & arg="localhost") Line 228 + 0xa7 bytes C++
NativeClient.dll!Poco::Net::DNS::hostByName(const std::basic_string<char,std::char_traits<char>,std::allocator<char> > & hostname="localhost") Line 107 + 0xf bytes C++
NativeClient.dll!Poco::Net::SocketAddress::init(const std::basic_string<char,std::char_traits<char>,std::allocator<char> > & host="localhost", unsigned short port=8080) Line 385 + 0xd bytes C++
NativeClient.dll!Poco::Net::SocketAddress::SocketAddress(const std::basic_string<char,std::char_traits<char>,std::allocator<char> > & addr="localhost", unsigned short port=8080) Line 239 C++
NativeClient.dll!Poco::Net::HTTPClientSession::reconnect() Line 338 + 0x17 bytes C++
NativeClient.dll!Poco::Net::HTTPClientSession::sendRequest(Poco::Net::HTTPRequest & request={...}) Line 208 C++

The solution would be for initializeNetwork() to always call WSAStartup() even if initializeCount is not 1. (Multiple calls are allowed, and fairly cheap, as long as they are balanced by the same number of WSACleanup calls - so obviously uninitializeNetwork() would need to change similarly.) This would work because if you make multiple simultaneous calls to WSAStartup, none of them will return until the initialisation is complete.

Or you could implement the same sort of synchronisation in POCO, but that would be more work.

Discussion

  • fixed in 1.4.4, rev. 1927
    Removed the reference counting and just call WSAStartup()/WSACleanup().

     
  • Alan Stokes
    Alan Stokes
    2012-08-06

    Thanks.

     
    • status: open --> closed