1
0

cNetwork: Fixed race conditions with lookups; proper shutdown.

This commit is contained in:
Mattes D 2015-01-18 12:35:02 +01:00
parent c0cb787c10
commit d4682463a1
7 changed files with 130 additions and 88 deletions

View File

@ -15,10 +15,22 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// cHostnameLookup: // cHostnameLookup:
cHostnameLookup::cHostnameLookup(const AString & a_Hostname, cNetwork::cResolveNameCallbacksPtr a_Callbacks): cHostnameLookup::cHostnameLookup(cNetwork::cResolveNameCallbacksPtr a_Callbacks):
m_Callbacks(a_Callbacks), m_Callbacks(a_Callbacks)
m_Hostname(a_Hostname)
{ {
}
void cHostnameLookup::Lookup(const AString & a_Hostname)
{
// Store the hostname for the callback:
m_Hostname = a_Hostname;
// Start the lookup:
// Note that we don't have to store the LibEvent lookup handle, LibEvent will free it on its own.
evutil_addrinfo hints; evutil_addrinfo hints;
memset(&hints, 0, sizeof(hints)); memset(&hints, 0, sizeof(hints));
hints.ai_protocol = IPPROTO_TCP; hints.ai_protocol = IPPROTO_TCP;
@ -79,7 +91,7 @@ void cHostnameLookup::Callback(int a_ErrCode, evutil_addrinfo * a_Addr, void * a
// If only unsupported families were reported, call the Error handler: // If only unsupported families were reported, call the Error handler:
if (!HasResolved) if (!HasResolved)
{ {
Self->m_Callbacks->OnError(1); Self->m_Callbacks->OnError(DNS_ERR_NODATA);
} }
else else
{ {
@ -101,7 +113,10 @@ bool cNetwork::HostnameToIP(
cNetwork::cResolveNameCallbacksPtr a_Callbacks cNetwork::cResolveNameCallbacksPtr a_Callbacks
) )
{ {
return cNetworkSingleton::Get().HostnameToIP(a_Hostname, a_Callbacks); auto Lookup = std::make_shared<cHostnameLookup>(a_Callbacks);
cNetworkSingleton::Get().AddHostnameLookup(Lookup);
Lookup->Lookup(a_Hostname);
return true;
} }

View File

@ -21,6 +21,15 @@
/** Holds information about an in-progress Hostname-to-IP lookup. */ /** Holds information about an in-progress Hostname-to-IP lookup. */
class cHostnameLookup class cHostnameLookup
{ {
public:
/** Creates the lookup object. Doesn't start the lookup yet. */
cHostnameLookup(cNetwork::cResolveNameCallbacksPtr a_Callbacks);
/** Starts the lookup. */
void Lookup(const AString & a_Hostname);
protected:
/** The callbacks to call for resolved names / errors. */ /** The callbacks to call for resolved names / errors. */
cNetwork::cResolveNameCallbacksPtr m_Callbacks; cNetwork::cResolveNameCallbacksPtr m_Callbacks;
@ -28,9 +37,6 @@ class cHostnameLookup
AString m_Hostname; AString m_Hostname;
static void Callback(int a_ErrCode, struct evutil_addrinfo * a_Addr, void * a_Self); static void Callback(int a_ErrCode, struct evutil_addrinfo * a_Addr, void * a_Self);
public:
cHostnameLookup(const AString & a_Hostname, cNetwork::cResolveNameCallbacksPtr a_Callbacks);
}; };
typedef SharedPtr<cHostnameLookup> cHostnameLookupPtr; typedef SharedPtr<cHostnameLookup> cHostnameLookupPtr;
typedef std::vector<cHostnameLookupPtr> cHostnameLookupPtrs; typedef std::vector<cHostnameLookupPtr> cHostnameLookupPtrs;

View File

@ -15,14 +15,31 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// cIPLookup: // cIPLookup:
cIPLookup::cIPLookup(const AString & a_IP, cNetwork::cResolveNameCallbacksPtr a_Callbacks): cIPLookup::cIPLookup(cNetwork::cResolveNameCallbacksPtr a_Callbacks):
m_Callbacks(a_Callbacks), m_Callbacks(a_Callbacks)
m_IP(a_IP)
{ {
ASSERT(a_Callbacks != nullptr);
}
bool cIPLookup::Lookup(const AString & a_IP)
{
// Parse the IP address string into a sockaddr structure:
m_IP = a_IP;
sockaddr_storage sa; sockaddr_storage sa;
int salen = static_cast<int>(sizeof(sa)); int salen = static_cast<int>(sizeof(sa));
memset(&sa, 0, sizeof(sa)); memset(&sa, 0, sizeof(sa));
evutil_parse_sockaddr_port(a_IP.c_str(), reinterpret_cast<sockaddr *>(&sa), &salen); if (evutil_parse_sockaddr_port(a_IP.c_str(), reinterpret_cast<sockaddr *>(&sa), &salen) != 0)
{
LOGD("Failed to parse IP address \"%s\".", a_IP.c_str());
return false;
}
// Call the proper resolver based on the address family:
// Note that there's no need to store the evdns_request handle returned, LibEvent frees it on its own.
switch (sa.ss_family) switch (sa.ss_family)
{ {
case AF_INET: case AF_INET:
@ -41,9 +58,10 @@ cIPLookup::cIPLookup(const AString & a_IP, cNetwork::cResolveNameCallbacksPtr a_
{ {
LOGWARNING("%s: Unknown address family: %d", __FUNCTION__, sa.ss_family); LOGWARNING("%s: Unknown address family: %d", __FUNCTION__, sa.ss_family);
ASSERT(!"Unknown address family"); ASSERT(!"Unknown address family");
break; return false;
} }
} // switch (address family) } // switch (address family)
return true;
} }
@ -83,7 +101,9 @@ bool cNetwork::IPToHostName(
cNetwork::cResolveNameCallbacksPtr a_Callbacks cNetwork::cResolveNameCallbacksPtr a_Callbacks
) )
{ {
return cNetworkSingleton::Get().IPToHostName(a_IP, a_Callbacks); auto res = std::make_shared<cIPLookup>(a_Callbacks);
cNetworkSingleton::Get().AddIPLookup(res);
return res->Lookup(a_IP);
} }

View File

@ -20,16 +20,25 @@
/** Holds information about an in-progress IP-to-Hostname lookup. */ /** Holds information about an in-progress IP-to-Hostname lookup. */
class cIPLookup class cIPLookup
{ {
public:
/** Creates the lookup object. Doesn't start the lookup yet. */
cIPLookup(cNetwork::cResolveNameCallbacksPtr a_Callbacks);
/** Starts the lookup.
Returns true if lookup started successfully, false on failure (invalid IP format etc.) */
bool Lookup(const AString & a_IP);
protected:
/** The callbacks to call for resolved names / errors. */ /** The callbacks to call for resolved names / errors. */
cNetwork::cResolveNameCallbacksPtr m_Callbacks; cNetwork::cResolveNameCallbacksPtr m_Callbacks;
/** The IP that was queried (needed for the callbacks). */ /** The IP that was queried (needed for the callbacks). */
AString m_IP; AString m_IP;
static void Callback(int a_Result, char a_Type, int a_Count, int a_Ttl, void * a_Addresses, void * a_Self);
public: /** Callback that is called by LibEvent when there's an event for the request. */
cIPLookup(const AString & a_IP, cNetwork::cResolveNameCallbacksPtr a_Callbacks); static void Callback(int a_Result, char a_Type, int a_Count, int a_Ttl, void * a_Addresses, void * a_Self);
}; };
typedef SharedPtr<cIPLookup> cIPLookupPtr; typedef SharedPtr<cIPLookup> cIPLookupPtr;
typedef std::vector<cIPLookupPtr> cIPLookupPtrs; typedef std::vector<cIPLookupPtr> cIPLookupPtrs;

View File

@ -70,6 +70,30 @@ cNetworkSingleton::cNetworkSingleton(void)
cNetworkSingleton::~cNetworkSingleton()
{
// Wait for the LibEvent event loop to terminate:
event_base_loopbreak(m_EventBase);
m_EventLoopTerminated.Wait();
// Remove all objects:
{
cCSLock Lock(m_CS);
m_Connections.clear();
m_Servers.clear();
m_HostnameLookups.clear();
m_IPLookups.clear();
}
// Free the underlying LibEvent objects:
evdns_base_free(m_DNSBase, true);
event_base_free(m_EventBase);
}
cNetworkSingleton & cNetworkSingleton::Get(void) cNetworkSingleton & cNetworkSingleton::Get(void)
{ {
static cNetworkSingleton Instance; static cNetworkSingleton Instance;
@ -80,50 +104,6 @@ cNetworkSingleton & cNetworkSingleton::Get(void)
bool cNetworkSingleton::HostnameToIP(
const AString & a_Hostname,
cNetwork::cResolveNameCallbacksPtr a_Callbacks
)
{
try
{
// TODO: This has a race condition with possible memory leak:
// If a lookup finishes immediately, the constructor calls the removal before this addition
cCSLock Lock(m_CS);
m_HostnameLookups.push_back(std::make_shared<cHostnameLookup>(a_Hostname, a_Callbacks));
}
catch (...)
{
return false;
}
return true;
}
bool cNetworkSingleton::IPToHostName(
const AString & a_IP,
cNetwork::cResolveNameCallbacksPtr a_Callbacks
)
{
try
{
// TODO: This has a race condition with possible memory leak:
// If a lookup finishes immediately, the constructor calls the removal before this addition
cCSLock Lock(m_CS);
m_IPLookups.push_back(std::make_shared<cIPLookup>(a_IP, a_Callbacks));
}
catch (...)
{
return false;
}
return true;
}
void cNetworkSingleton::LogCallback(int a_Severity, const char * a_Msg) void cNetworkSingleton::LogCallback(int a_Severity, const char * a_Msg)
{ {
switch (a_Severity) switch (a_Severity)
@ -147,6 +127,17 @@ void cNetworkSingleton::LogCallback(int a_Severity, const char * a_Msg)
void cNetworkSingleton::RunEventLoop(cNetworkSingleton * a_Self) void cNetworkSingleton::RunEventLoop(cNetworkSingleton * a_Self)
{ {
event_base_loop(a_Self->m_EventBase, EVLOOP_NO_EXIT_ON_EMPTY); event_base_loop(a_Self->m_EventBase, EVLOOP_NO_EXIT_ON_EMPTY);
a_Self->m_EventLoopTerminated.Set();
}
void cNetworkSingleton::AddHostnameLookup(cHostnameLookupPtr a_HostnameLookup)
{
cCSLock Lock(m_CS);
m_HostnameLookups.push_back(a_HostnameLookup);
} }
@ -170,6 +161,16 @@ void cNetworkSingleton::RemoveHostnameLookup(const cHostnameLookup * a_HostnameL
void cNetworkSingleton::AddIPLookup(cIPLookupPtr a_IPLookup)
{
cCSLock Lock(m_CS);
m_IPLookups.push_back(a_IPLookup);
}
void cNetworkSingleton::RemoveIPLookup(const cIPLookup * a_IPLookup) void cNetworkSingleton::RemoveIPLookup(const cIPLookup * a_IPLookup)
{ {
cCSLock Lock(m_CS); cCSLock Lock(m_CS);

View File

@ -14,6 +14,7 @@
#include "Network.h" #include "Network.h"
#include "CriticalSection.h" #include "CriticalSection.h"
#include "Event.h"
@ -22,16 +23,16 @@
// fwd: // fwd:
struct event_base; struct event_base;
struct evdns_base; struct evdns_base;
class cServerHandleImpl;
class cTCPLinkImpl; class cTCPLinkImpl;
class cHostnameLookup;
class cIPLookup;
typedef SharedPtr<cTCPLinkImpl> cTCPLinkImplPtr; typedef SharedPtr<cTCPLinkImpl> cTCPLinkImplPtr;
typedef std::vector<cTCPLinkImplPtr> cTCPLinkImplPtrs; typedef std::vector<cTCPLinkImplPtr> cTCPLinkImplPtrs;
class cServerHandleImpl;
typedef SharedPtr<cServerHandleImpl> cServerHandleImplPtr; typedef SharedPtr<cServerHandleImpl> cServerHandleImplPtr;
typedef std::vector<cServerHandleImplPtr> cServerHandleImplPtrs; typedef std::vector<cServerHandleImplPtr> cServerHandleImplPtrs;
class cHostnameLookup;
typedef SharedPtr<cHostnameLookup> cHostnameLookupPtr; typedef SharedPtr<cHostnameLookup> cHostnameLookupPtr;
typedef std::vector<cHostnameLookupPtr> cHostnameLookupPtrs; typedef std::vector<cHostnameLookupPtr> cHostnameLookupPtrs;
class cIPLookup;
typedef SharedPtr<cIPLookup> cIPLookupPtr; typedef SharedPtr<cIPLookup> cIPLookupPtr;
typedef std::vector<cIPLookupPtr> cIPLookupPtrs; typedef std::vector<cIPLookupPtr> cIPLookupPtrs;
@ -42,43 +43,29 @@ typedef std::vector<cIPLookupPtr> cIPLookupPtrs;
class cNetworkSingleton class cNetworkSingleton
{ {
public: public:
~cNetworkSingleton();
/** Returns the singleton instance of this class */ /** Returns the singleton instance of this class */
static cNetworkSingleton & Get(void); static cNetworkSingleton & Get(void);
// The following functions are implementations for the cNetwork class
/** Queues a DNS query to resolve the specified hostname to IP address.
Calls one of the callbacks when the resolving succeeds, or when it fails.
Returns true if queueing was successful, false if not.
Note that the return value doesn't report the success of the actual lookup; the lookup happens asynchronously on the background.
TODO: Move this out into a separate file with cHostnameLookup. */
bool HostnameToIP(
const AString & a_Hostname,
cNetwork::cResolveNameCallbacksPtr a_Callbacks
);
/** Queues a DNS query to resolve the specified IP address to a hostname.
Calls one of the callbacks when the resolving succeeds, or when it fails.
Returns true if queueing was successful, false if not.
Note that the return value doesn't report the success of the actual lookup; the lookup happens asynchronously on the background.
TODO: Move this out into a separate file with cIPLookup. */
bool IPToHostName(
const AString & a_IP,
cNetwork::cResolveNameCallbacksPtr a_Callbacks
);
/** Returns the main LibEvent handle for event registering. */ /** Returns the main LibEvent handle for event registering. */
event_base * GetEventBase(void) { return m_EventBase; } event_base * GetEventBase(void) { return m_EventBase; }
/** Returns the LibEvent handle for DNS lookups. */ /** Returns the LibEvent handle for DNS lookups. */
evdns_base * GetDNSBase(void) { return m_DNSBase; } evdns_base * GetDNSBase(void) { return m_DNSBase; }
/** Adds the specified hostname lookup to m_HostnameLookups.
Used by the underlying lookup implementation when a new lookup is initiated. */
void AddHostnameLookup(cHostnameLookupPtr a_HostnameLookup);
/** Removes the specified hostname lookup from m_HostnameLookups. /** Removes the specified hostname lookup from m_HostnameLookups.
Used by the underlying lookup implementation when the lookup is finished. */ Used by the underlying lookup implementation when the lookup is finished. */
void RemoveHostnameLookup(const cHostnameLookup * a_HostnameLookup); void RemoveHostnameLookup(const cHostnameLookup * a_HostnameLookup);
/** Adds the specified IP lookup to M_IPLookups.
Used by the underlying lookup implementation when a new lookup is initiated. */
void AddIPLookup(cIPLookupPtr a_IPLookup);
/** Removes the specified IP lookup from m_IPLookups. /** Removes the specified IP lookup from m_IPLookups.
Used by the underlying lookup implementation when the lookup is finished. */ Used by the underlying lookup implementation when the lookup is finished. */
void RemoveIPLookup(const cIPLookup * a_IPLookup); void RemoveIPLookup(const cIPLookup * a_IPLookup);
@ -123,6 +110,9 @@ protected:
/** Mutex protecting all containers against multithreaded access. */ /** Mutex protecting all containers against multithreaded access. */
cCriticalSection m_CS; cCriticalSection m_CS;
/** Event that gets signalled when the event loop terminates. */
cEvent m_EventLoopTerminated;
/** Initializes the LibEvent internals. */ /** Initializes the LibEvent internals. */
cNetworkSingleton(void); cNetworkSingleton(void);

View File

@ -95,6 +95,7 @@ int main()
evtFinish.Wait(); evtFinish.Wait();
LOGD("Network test finished"); LOGD("Network test finished");
return 0;
} }