-#ifdef WIN32
-#define _WIN32_WINNT 0x0501
-#include <ws2tcpip.h>
-#else
-#include <netdb.h>
-#endif
+#include "resolve.h"
#include <msp/core/systemerror.h>
#include <msp/strings/format.h>
+#include "platform_api.h"
#include "sockaddr_private.h"
#include "socket.h"
-#include "resolve.h"
using namespace std;
+namespace {
+
+void parse_host_serv(const string &str, string &host, string &serv)
+{
+ if(str[0]=='[')
+ {
+ string::size_type bracket = str.find(']');
+ host = str.substr(1, bracket-1);
+ string::size_type colon = str.find(':', bracket);
+ if(colon!=string::npos)
+ serv = str.substr(colon+1);
+ }
+ else
+ {
+ string::size_type colon = str.find(':');
+ if(colon!=string::npos)
+ {
+ host = str.substr(0, colon);
+ serv = str.substr(colon+1);
+ }
+ else
+ host = str;
+ }
+}
+
+}
+
+
namespace Msp {
namespace Net {
SockAddr *resolve(const string &host, const string &serv, Family family)
{
- const char *chost = (host.empty() ? 0 : host.c_str());
- const char *cserv = (serv.empty() ? 0 : serv.c_str());
+ const char *chost = (host.empty() ? nullptr : host.c_str());
+ const char *cserv = (serv.empty() ? nullptr : serv.c_str());
int flags = 0;
if(host=="*")
{
flags = AI_PASSIVE;
- chost = 0;
+ chost = nullptr;
}
- addrinfo hints = { flags, family_to_sys(family), 0, 0, 0, 0, 0, 0 };
+ addrinfo hints = { flags, family_to_sys(family), 0, 0, 0, nullptr, nullptr, nullptr };
addrinfo *res;
int err = getaddrinfo(chost, cserv, &hints, &res);
return addr;
}
else
-#ifdef WIN32
+#ifdef _WIN32
throw system_error("getaddrinfo", WSAGetLastError());
#else
throw system_error("getaddrinfo", gai_strerror(err));
SockAddr *resolve(const string &str, Family family)
{
string host, serv;
- if(str[0]=='[')
+ parse_host_serv(str, host, serv);
+
+ return resolve(host, serv, family);
+}
+
+
+Resolver::Resolver()
+{
+ thread.get_notify_pipe().signal_data_available.connect(sigc::mem_fun(this, &Resolver::task_done));
+}
+
+void Resolver::use_event_dispatcher(IO::EventDispatcher *ed)
+{
+ if(event_disp)
+ event_disp->remove(thread.get_notify_pipe());
+ event_disp = ed;
+ if(event_disp)
+ event_disp->add(thread.get_notify_pipe());
+}
+
+unsigned Resolver::resolve(const string &host, const string &serv, Family family)
+{
+ Task task;
+ task.tag = next_tag++;
+ task.host = host;
+ task.serv = serv;
+ task.family = family;
+ thread.add_task(move(task));
+ return task.tag;
+}
+
+unsigned Resolver::resolve(const string &str, Family family)
+{
+ string host, serv;
+ parse_host_serv(str, host, serv);
+
+ return resolve(host, serv, family);
+}
+
+void Resolver::tick()
+{
+ if(IO::poll(thread.get_notify_pipe(), IO::P_INPUT, Time::zero))
+ task_done();
+}
+
+void Resolver::task_done()
+{
+ char buf[64];
+ thread.get_notify_pipe().read(buf, sizeof(buf));
+
+ while(Task *task = thread.get_complete_task())
{
- string::size_type bracket = str.find(']');
- host = str.substr(1, bracket-1);
- string::size_type colon = str.find(':', bracket);
- if(colon!=string::npos)
- serv = str.substr(colon+1);
+ if(task->addr)
+ signal_address_resolved.emit(task->tag, *task->addr);
+ else if(task->error)
+ {
+ if(signal_resolve_failed.empty())
+ {
+ unique_ptr<runtime_error> err = move(task->error);
+ thread.pop_complete_task();
+ throw *err;
+ }
+ signal_resolve_failed.emit(task->tag, *task->error);
+ }
+ thread.pop_complete_task();
}
+}
+
+
+Resolver::WorkerThread::WorkerThread():
+ Thread("Resolver"),
+ sem(1)
+{
+ launch();
+}
+
+Resolver::WorkerThread::~WorkerThread()
+{
+ done = true;
+ sem.signal();
+ join();
+}
+
+void Resolver::WorkerThread::add_task(Task &&t)
+{
+ MutexLock lock(queue_mutex);
+ bool was_starved = (queue.empty() || queue.back().is_complete());
+ queue.push_back(move(t));
+ if(was_starved)
+ sem.signal();
+}
+
+Resolver::Task *Resolver::WorkerThread::get_complete_task()
+{
+ MutexLock lock(queue_mutex);
+ if(!queue.empty() && queue.front().is_complete())
+ return &queue.front();
else
+ return nullptr;
+}
+
+void Resolver::WorkerThread::pop_complete_task()
+{
+ MutexLock lock(queue_mutex);
+ if(!queue.empty() && queue.front().is_complete())
+ queue.pop_front();
+}
+
+void Resolver::WorkerThread::main()
+{
+ bool wait = true;
+ while(!done)
{
- string::size_type colon = str.find(':');
- if(colon!=string::npos)
+ if(wait)
+ sem.wait();
+ wait = false;
+
+ Task *task = nullptr;
{
- host = str.substr(0, colon);
- serv = str.substr(colon+1);
+ MutexLock lock(queue_mutex);
+ for(auto i=queue.begin(); (!task && i!=queue.end()); ++i)
+ if(!i->is_complete())
+ task = &*i;
+ }
+
+ if(task)
+ {
+ try
+ {
+ unique_ptr<SockAddr> addr(Net::resolve(task->host, task->serv, task->family));
+ {
+ MutexLock lock(queue_mutex);
+ task->addr = move(addr);
+ }
+ }
+ catch(const runtime_error &e)
+ {
+ MutexLock lock(queue_mutex);
+ task->error = make_unique<runtime_error>(e);
+ }
+ notify_pipe.put(1);
}
else
- host = str;
+ wait = true;
}
-
- return resolve(host, serv, family);
}
- /*sockaddr sa;
- unsigned size = fill_sockaddr(sa);
- char hst[128];
- char srv[128];
- int err = getnameinfo(&sa, size, hst, 128, srv, 128, 0);
- if(err==0)
- {
- host = hst;
- serv = srv;
- }*/
-
} // namespace Net
} // namespace Msp