IXDNSLookup. Uses weak pointer + smart_ptr + shared_from_this instead of static sets + mutex to handle object going away before dns lookup has resolved
This commit is contained in:
parent
dbd62b8622
commit
b26e9d0338
@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
## [unreleased] - 2019-06-09
|
## [unreleased] - 2019-06-09
|
||||||
### Changed
|
### Changed
|
||||||
|
- IXDNSLookup. Uses weak pointer + smart_ptr + shared_from_this instead of static sets + mutex to handle object going away before dns lookup has resolved
|
||||||
- cobra_to_sentry / backtraces are reversed and line number is not extracted correctly
|
- cobra_to_sentry / backtraces are reversed and line number is not extracted correctly
|
||||||
- mbedtls and zlib are searched with find_package, and we use the vendored version if nothing is found
|
- mbedtls and zlib are searched with find_package, and we use the vendored version if nothing is found
|
||||||
- travis CI uses g++ on Linux
|
- travis CI uses g++ on Linux
|
||||||
|
@ -14,27 +14,15 @@ namespace ix
|
|||||||
{
|
{
|
||||||
const int64_t DNSLookup::kDefaultWait = 10; // ms
|
const int64_t DNSLookup::kDefaultWait = 10; // ms
|
||||||
|
|
||||||
std::atomic<uint64_t> DNSLookup::_nextId(0);
|
|
||||||
std::set<uint64_t> DNSLookup::_activeJobs;
|
|
||||||
std::mutex DNSLookup::_activeJobsMutex;
|
|
||||||
|
|
||||||
DNSLookup::DNSLookup(const std::string& hostname, int port, int64_t wait) :
|
DNSLookup::DNSLookup(const std::string& hostname, int port, int64_t wait) :
|
||||||
_port(port),
|
_port(port),
|
||||||
_wait(wait),
|
_wait(wait),
|
||||||
_res(nullptr),
|
_res(nullptr),
|
||||||
_done(false),
|
_done(false)
|
||||||
_id(_nextId++)
|
|
||||||
{
|
{
|
||||||
setHostname(hostname);
|
setHostname(hostname);
|
||||||
}
|
}
|
||||||
|
|
||||||
DNSLookup::~DNSLookup()
|
|
||||||
{
|
|
||||||
// Remove this job from the active jobs list
|
|
||||||
std::lock_guard<std::mutex> lock(_activeJobsMutex);
|
|
||||||
_activeJobs.erase(_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct addrinfo* DNSLookup::getAddrInfo(const std::string& hostname,
|
struct addrinfo* DNSLookup::getAddrInfo(const std::string& hostname,
|
||||||
int port,
|
int port,
|
||||||
std::string& errMsg)
|
std::string& errMsg)
|
||||||
@ -94,17 +82,14 @@ namespace ix
|
|||||||
// if you need a second lookup.
|
// if you need a second lookup.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record job in the active Job set
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(_activeJobsMutex);
|
|
||||||
_activeJobs.insert(_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Good resource on thread forced termination
|
// Good resource on thread forced termination
|
||||||
// https://www.bo-yang.net/2017/11/19/cpp-kill-detached-thread
|
// https://www.bo-yang.net/2017/11/19/cpp-kill-detached-thread
|
||||||
//
|
//
|
||||||
_thread = std::thread(&DNSLookup::run, this, _id, getHostname(), _port);
|
auto ptr = shared_from_this();
|
||||||
|
std::weak_ptr<DNSLookup> self(ptr);
|
||||||
|
|
||||||
|
_thread = std::thread(&DNSLookup::run, this, self, getHostname(), _port);
|
||||||
_thread.detach();
|
_thread.detach();
|
||||||
|
|
||||||
std::unique_lock<std::mutex> lock(_conditionVariableMutex);
|
std::unique_lock<std::mutex> lock(_conditionVariableMutex);
|
||||||
@ -138,7 +123,7 @@ namespace ix
|
|||||||
return getRes();
|
return getRes();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DNSLookup::run(uint64_t id, const std::string& hostname, int port) // thread runner
|
void DNSLookup::run(std::weak_ptr<DNSLookup> self, const std::string& hostname, int port) // thread runner
|
||||||
{
|
{
|
||||||
// We don't want to read or write into members variables of an object that could be
|
// We don't want to read or write into members variables of an object that could be
|
||||||
// gone, so we use temporary variables (res) or we pass in by copy everything that
|
// gone, so we use temporary variables (res) or we pass in by copy everything that
|
||||||
@ -146,21 +131,15 @@ namespace ix
|
|||||||
std::string errMsg;
|
std::string errMsg;
|
||||||
struct addrinfo* res = getAddrInfo(hostname, port, errMsg);
|
struct addrinfo* res = getAddrInfo(hostname, port, errMsg);
|
||||||
|
|
||||||
// if this isn't an active job, and the control thread is gone
|
if (self.lock())
|
||||||
// there is nothing to do, and we don't want to touch the defunct
|
|
||||||
// object data structure such as _errMsg or _condition
|
|
||||||
std::lock_guard<std::mutex> lock(_activeJobsMutex);
|
|
||||||
if (_activeJobs.count(id) == 0)
|
|
||||||
{
|
{
|
||||||
return;
|
// Copy result into the member variables
|
||||||
|
setRes(res);
|
||||||
|
setErrMsg(errMsg);
|
||||||
|
|
||||||
|
_condition.notify_one();
|
||||||
|
_done = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy result into the member variables
|
|
||||||
setRes(res);
|
|
||||||
setErrMsg(errMsg);
|
|
||||||
|
|
||||||
_condition.notify_one();
|
|
||||||
_done = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DNSLookup::setHostname(const std::string& hostname)
|
void DNSLookup::setHostname(const std::string& hostname)
|
||||||
|
@ -16,16 +16,17 @@
|
|||||||
#include <set>
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
struct addrinfo;
|
struct addrinfo;
|
||||||
|
|
||||||
namespace ix
|
namespace ix
|
||||||
{
|
{
|
||||||
class DNSLookup
|
class DNSLookup : public std::enable_shared_from_this<DNSLookup>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DNSLookup(const std::string& hostname, int port, int64_t wait = DNSLookup::kDefaultWait);
|
DNSLookup(const std::string& hostname, int port, int64_t wait = DNSLookup::kDefaultWait);
|
||||||
~DNSLookup();
|
~DNSLookup() = default;
|
||||||
|
|
||||||
struct addrinfo* resolve(std::string& errMsg,
|
struct addrinfo* resolve(std::string& errMsg,
|
||||||
const CancellationRequest& isCancellationRequested,
|
const CancellationRequest& isCancellationRequested,
|
||||||
@ -41,7 +42,7 @@ namespace ix
|
|||||||
int port,
|
int port,
|
||||||
std::string& errMsg);
|
std::string& errMsg);
|
||||||
|
|
||||||
void run(uint64_t id, const std::string& hostname, int port); // thread runner
|
void run(std::weak_ptr<DNSLookup> self, const std::string& hostname, int port); // thread runner
|
||||||
|
|
||||||
void setHostname(const std::string& hostname);
|
void setHostname(const std::string& hostname);
|
||||||
const std::string& getHostname();
|
const std::string& getHostname();
|
||||||
@ -69,11 +70,6 @@ namespace ix
|
|||||||
std::condition_variable _condition;
|
std::condition_variable _condition;
|
||||||
std::mutex _conditionVariableMutex;
|
std::mutex _conditionVariableMutex;
|
||||||
|
|
||||||
uint64_t _id;
|
|
||||||
static std::atomic<uint64_t> _nextId;
|
|
||||||
static std::set<uint64_t> _activeJobs;
|
|
||||||
static std::mutex _activeJobsMutex;
|
|
||||||
|
|
||||||
const static int64_t kDefaultWait;
|
const static int64_t kDefaultWait;
|
||||||
};
|
};
|
||||||
} // namespace ix
|
} // namespace ix
|
||||||
|
@ -128,8 +128,8 @@ namespace ix
|
|||||||
//
|
//
|
||||||
// First do DNS resolution
|
// First do DNS resolution
|
||||||
//
|
//
|
||||||
DNSLookup dnsLookup(hostname, port);
|
auto dnsLookup = std::make_shared<DNSLookup>(hostname, port);
|
||||||
struct addrinfo *res = dnsLookup.resolve(errMsg, isCancellationRequested);
|
struct addrinfo *res = dnsLookup->resolve(errMsg, isCancellationRequested);
|
||||||
if (res == nullptr)
|
if (res == nullptr)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
|
4
makefile
4
makefile
@ -9,10 +9,10 @@ install: brew
|
|||||||
# on osx it is good practice to make /usr/local user writable
|
# on osx it is good practice to make /usr/local user writable
|
||||||
# sudo chown -R `whoami`/staff /usr/local
|
# sudo chown -R `whoami`/staff /usr/local
|
||||||
brew:
|
brew:
|
||||||
mkdir -p build && (cd build ; cmake -DUSE_TLS=1 -DUSE_WS=1 .. ; make -j install)
|
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 .. ; make -j install)
|
||||||
|
|
||||||
ws:
|
ws:
|
||||||
mkdir -p build && (cd build ; cmake -DUSE_TLS=1 -DUSE_WS=1 -DUSE_MBED_TLS=1 -DUSE_VENDORED_THIRD_PARTY=1 .. ; make -j)
|
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_WS=1 -DUSE_MBED_TLS=1 -DUSE_VENDORED_THIRD_PARTY=1 .. ; make -j)
|
||||||
|
|
||||||
uninstall:
|
uninstall:
|
||||||
xargs rm -fv < build/install_manifest.txt
|
xargs rm -fv < build/install_manifest.txt
|
||||||
|
@ -17,33 +17,33 @@ TEST_CASE("dns", "[net]")
|
|||||||
{
|
{
|
||||||
SECTION("Test resolving a known hostname")
|
SECTION("Test resolving a known hostname")
|
||||||
{
|
{
|
||||||
DNSLookup dnsLookup("www.google.com", 80);
|
auto dnsLookup = std::make_shared<DNSLookup>("www.google.com", 80);
|
||||||
|
|
||||||
std::string errMsg;
|
std::string errMsg;
|
||||||
struct addrinfo* res;
|
struct addrinfo* res;
|
||||||
|
|
||||||
res = dnsLookup.resolve(errMsg, [] { return false; });
|
res = dnsLookup->resolve(errMsg, [] { return false; });
|
||||||
std::cerr << "Error message: " << errMsg << std::endl;
|
std::cerr << "Error message: " << errMsg << std::endl;
|
||||||
REQUIRE(res != nullptr);
|
REQUIRE(res != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Test resolving a non-existing hostname")
|
SECTION("Test resolving a non-existing hostname")
|
||||||
{
|
{
|
||||||
DNSLookup dnsLookup("wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww", 80);
|
auto dnsLookup = std::make_shared<DNSLookup>("wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww", 80);
|
||||||
|
|
||||||
std::string errMsg;
|
std::string errMsg;
|
||||||
struct addrinfo* res = dnsLookup.resolve(errMsg, [] { return false; });
|
struct addrinfo* res = dnsLookup->resolve(errMsg, [] { return false; });
|
||||||
std::cerr << "Error message: " << errMsg << std::endl;
|
std::cerr << "Error message: " << errMsg << std::endl;
|
||||||
REQUIRE(res == nullptr);
|
REQUIRE(res == nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Test resolving a good hostname, with cancellation")
|
SECTION("Test resolving a good hostname, with cancellation")
|
||||||
{
|
{
|
||||||
DNSLookup dnsLookup("www.google.com", 80, 1);
|
auto dnsLookup = std::make_shared<DNSLookup>("www.google.com", 80, 1);
|
||||||
|
|
||||||
std::string errMsg;
|
std::string errMsg;
|
||||||
// The callback returning true means we are requesting cancellation
|
// The callback returning true means we are requesting cancellation
|
||||||
struct addrinfo* res = dnsLookup.resolve(errMsg, [] { return true; });
|
struct addrinfo* res = dnsLookup->resolve(errMsg, [] { return true; });
|
||||||
std::cerr << "Error message: " << errMsg << std::endl;
|
std::cerr << "Error message: " << errMsg << std::endl;
|
||||||
REQUIRE(res == nullptr);
|
REQUIRE(res == nullptr);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user