IXWebSocket/ixwebsocket/IXDNSLookup.cpp

165 lines
5.2 KiB
C++
Raw Normal View History

2018-12-15 01:28:11 +01:00
/*
* IXDNSLookup.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
*/
#include "IXDNSLookup.h"
2019-01-06 05:38:43 +01:00
#include "IXNetSystem.h"
2019-01-05 02:28:13 +01:00
2018-12-15 01:28:11 +01:00
#include <string.h>
#include <chrono>
namespace ix
{
2019-01-03 21:53:44 +01:00
const int64_t DNSLookup::kDefaultWait = 10; // ms
2018-12-15 01:28:11 +01:00
2018-12-15 02:49:42 +01:00
std::atomic<uint64_t> DNSLookup::_nextId(0);
std::set<uint64_t> DNSLookup::_activeJobs;
std::mutex DNSLookup::_activeJobsMutex;
2018-12-23 23:14:38 +01:00
DNSLookup::DNSLookup(const std::string& hostname, int port, int64_t wait) :
2018-12-15 01:28:11 +01:00
_hostname(hostname),
_port(port),
_wait(wait),
2018-12-15 01:28:11 +01:00
_res(nullptr),
_done(false),
2018-12-15 02:49:42 +01:00
_id(_nextId++)
2018-12-15 01:28:11 +01:00
{
}
DNSLookup::~DNSLookup()
{
2018-12-15 02:49:42 +01:00
// Remove this job from the active jobs list
std::unique_lock<std::mutex> lock(_activeJobsMutex);
_activeJobs.erase(_id);
2018-12-15 01:28:11 +01:00
}
struct addrinfo* DNSLookup::getAddrInfo(const std::string& hostname,
int port,
std::string& errMsg)
{
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
std::string sport = std::to_string(port);
struct addrinfo* res;
int getaddrinfo_result = getaddrinfo(hostname.c_str(), sport.c_str(),
&hints, &res);
if (getaddrinfo_result)
{
errMsg = gai_strerror(getaddrinfo_result);
res = nullptr;
}
return res;
}
struct addrinfo* DNSLookup::resolve(std::string& errMsg,
const CancellationRequest& isCancellationRequested,
bool blocking)
{
return blocking ? resolveBlocking(errMsg, isCancellationRequested)
: resolveAsync(errMsg, isCancellationRequested);
}
struct addrinfo* DNSLookup::resolveBlocking(std::string& errMsg,
const CancellationRequest& isCancellationRequested)
{
errMsg = "no error";
// Maybe a cancellation request got in before the background thread terminated ?
if (isCancellationRequested())
{
errMsg = "cancellation requested";
return nullptr;
}
return getAddrInfo(_hostname, _port, errMsg);
}
struct addrinfo* DNSLookup::resolveAsync(std::string& errMsg,
const CancellationRequest& isCancellationRequested)
{
errMsg = "no error";
// Can only be called once, otherwise we would have to manage a pool
// of background thread which is overkill for our usage.
if (_done)
{
return nullptr; // programming error, create a second DNSLookup instance
// if you need a second lookup.
}
2018-12-15 02:49:42 +01:00
// Record job in the active Job set
{
std::unique_lock<std::mutex> lock(_activeJobsMutex);
_activeJobs.insert(_id);
}
2018-12-15 01:28:11 +01:00
//
// Good resource on thread forced termination
// https://www.bo-yang.net/2017/11/19/cpp-kill-detached-thread
//
2019-01-07 03:27:26 +01:00
_thread = std::thread(&DNSLookup::run, this, _id, _hostname, _port);
2018-12-15 01:28:11 +01:00
_thread.detach();
2018-12-15 02:49:42 +01:00
std::unique_lock<std::mutex> lock(_conditionVariableMutex);
2018-12-15 01:28:11 +01:00
while (!_done)
{
// Wait for 10 milliseconds on the condition variable, to see
// if the bg thread has terminated.
if (_condition.wait_for(lock, std::chrono::milliseconds(_wait)) == std::cv_status::no_timeout)
{
// Background thread has terminated, so we can break of this loop
break;
}
// Were we cancelled ?
if (isCancellationRequested())
{
errMsg = "cancellation requested";
return nullptr;
}
}
// Maybe a cancellation request got in before the bg terminated ?
if (isCancellationRequested())
{
errMsg = "cancellation requested";
return nullptr;
}
return _res;
}
2019-01-07 03:27:26 +01:00
void DNSLookup::run(uint64_t id, const std::string& hostname, int port) // thread runner
2018-12-15 01:28:11 +01:00
{
2019-01-07 03:27:26 +01:00
// 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
// getAddrInfo needs to work.
2018-12-15 02:49:42 +01:00
std::string errMsg;
2019-01-07 03:27:26 +01:00
struct addrinfo* res = getAddrInfo(hostname, port, errMsg);
2018-12-15 02:49:42 +01:00
// if this isn't an active job, and the control thread is gone
// there is not thing to do, and we don't want to touch the defunct
// object data structure such as _errMsg or _condition
std::unique_lock<std::mutex> lock(_activeJobsMutex);
if (_activeJobs.count(id) == 0)
{
return;
}
2019-01-07 03:27:26 +01:00
// Copy result into the member variables
_res = res;
2018-12-15 02:49:42 +01:00
_errMsg = errMsg;
2018-12-15 01:28:11 +01:00
_condition.notify_one();
_done = true;
}
}