summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas 'Pixel' Noble <pixel@nobis-crew.org>2013-01-17 01:23:09 -0800
committerNicolas 'Pixel' Noble <pixel@nobis-crew.org>2013-01-17 01:23:09 -0800
commitfcf7b16cbc380b2e4c3a0fdc2e4291fd31b81ba9 (patch)
treef642bfe310364e09b0407772614ac31d1db60031
parent9320d6bd005afb4beb551cac2b3963255cff5e37 (diff)
Making name resolution in setLocal() and connect() interruptible...
-rw-r--r--includes/Socket.h4
-rw-r--r--src/Socket.cc68
2 files changed, 48 insertions, 24 deletions
diff --git a/includes/Socket.h b/includes/Socket.h
index e5a9f21..2a6a95d 100644
--- a/includes/Socket.h
+++ b/includes/Socket.h
@@ -14,6 +14,8 @@
namespace Balau {
+struct DNSRequest;
+
class Socket : public Handle {
public:
@@ -33,6 +35,7 @@ class Socket : public Handle {
bool gotW() { return m_evtW->gotSignal(); }
IO<Socket> accept() throw (GeneralException);
bool listen();
+ bool resolved();
private:
Socket(int fd);
class SocketEvent : public Events::BaseEvent {
@@ -55,6 +58,7 @@ class Socket : public Handle {
bool m_listening = false;
sockaddr_in6 m_localAddr, m_remoteAddr;
SocketEvent * m_evtR, * m_evtW;
+ DNSRequest * m_req = NULL;
};
class ListenerBase : public Task {
diff --git a/src/Socket.cc b/src/Socket.cc
index a953941..05c5e83 100644
--- a/src/Socket.cc
+++ b/src/Socket.cc
@@ -186,20 +186,24 @@ static const char * inet_ntop(int af, const void * src, char * dst, socklen_t si
#endif
-namespace {
+namespace Balau {
struct DNSRequest {
struct addrinfo * res;
int error;
+ Balau::Events::Custom evt;
+};
+
};
+namespace {
+
class AsyncOpResolv : public Balau::AsyncOperation {
public:
- AsyncOpResolv(const char * name, const char * service, struct addrinfo * hints, Balau::Events::Custom * evt, struct DNSRequest * request)
+ AsyncOpResolv(const char * name, const char * service, struct addrinfo * hints, Balau::DNSRequest * request)
: m_name(name)
, m_service(service)
, m_hints(hints)
- , m_evt(evt)
, m_request(request)
{ }
virtual bool needsMainQueue() { return false; }
@@ -208,27 +212,21 @@ class AsyncOpResolv : public Balau::AsyncOperation {
m_request->error = getaddrinfo(m_name, m_service, m_hints, &m_request->res);
}
virtual void done() {
- m_evt->doSignal();
+ m_request->evt.doSignal();
delete this;
}
private:
const char * m_name;
const char * m_service;
struct addrinfo * m_hints;
- Balau::Events::Custom * m_evt;
- struct DNSRequest * m_request;
+ Balau::DNSRequest * m_request;
};
};
-static DNSRequest resolveName(const char * name, const char * service = NULL, struct addrinfo * hints = NULL) {
- DNSRequest req;
- Balau::Events::Custom evt;
- memset(&req, 0, sizeof(req));
-
- createAsyncOp(new AsyncOpResolv(name, service, hints, &evt, &req));
- Balau::Task::operationYield(&evt);
-
+static Balau::DNSRequest * resolveName(const char * name, const char * service = NULL, struct addrinfo * hints = NULL) {
+ Balau::DNSRequest * req = new Balau::DNSRequest();
+ createAsyncOp(new AsyncOpResolv(name, service, hints, req));
return req;
}
@@ -311,21 +309,32 @@ bool Balau::Socket::canRead() { return true; }
bool Balau::Socket::canWrite() { return true; }
const char * Balau::Socket::getName() { return m_name.to_charp(); }
+bool Balau::Socket::resolved() {
+ return m_req && m_req->evt.gotSignal();
+}
+
bool Balau::Socket::setLocal(const char * hostname, int port) {
AAssert(m_localAddr.sin6_family == 0, "Can't call setLocal twice");
- if (hostname && hostname[0]) {
+ if (hostname && hostname[0] && !m_req) {
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET6;
hints.ai_flags = AI_V4MAPPED;
- DNSRequest req = resolveName(hostname, NULL, &hints);
- struct addrinfo * res = req.res;
- if (req.error != 0) {
- Printer::elog(E_SOCKET, "Got a resolution error for host %s: %s (%i)", hostname, gai_strerror(req.error), req.error);
+ m_req = resolveName(hostname, NULL, &hints);
+ Task::operationYield(&m_req->evt, Task::INTERRUPTIBLE);
+ }
+
+ if (m_req) {
+ AAssert(m_req->evt.gotSignal(), "Please don't call setLocal after a EAgain without checking its resolution status first.");
+ struct addrinfo * res = m_req->res;
+ if (m_req->error != 0) {
+ Printer::elog(E_SOCKET, "Got a resolution error for host %s: %s (%i)", hostname, gai_strerror(m_req->error), m_req->error);
if (res)
freeaddrinfo(res);
+ delete m_req;
+ m_req = NULL;
return false;
}
IAssert(res, "That really shouldn't happen...");
@@ -334,6 +343,8 @@ bool Balau::Socket::setLocal(const char * hostname, int port) {
EAssert(res->ai_addrlen == sizeof(sockaddr_in6), "getaddrinfo returned an addrlen which isn't that of sizeof(sockaddr_in6); %i", res->ai_addrlen);
memcpy(&m_localAddr.sin6_addr, &((sockaddr_in6 *) res->ai_addr)->sin6_addr, sizeof(struct in6_addr));
freeaddrinfo(res);
+ delete m_req;
+ m_req = NULL;
} else {
m_localAddr.sin6_addr = in6addr_any;
}
@@ -359,7 +370,7 @@ bool Balau::Socket::connect(const char * hostname, int port) {
AAssert(hostname, "You can't call Socket::connect() without a hostname");
AAssert(m_fd >= 0, "You can't call Socket::connect() on a closed socket");
- if (!m_connecting) {
+ if (!m_connecting && !m_req) {
Printer::elog(E_SOCKET, "Resolving %s", hostname);
IAssert(m_remoteAddr.sin6_family == 0, "That shouldn't happen...; family = %i", m_remoteAddr.sin6_family);
@@ -368,12 +379,19 @@ bool Balau::Socket::connect(const char * hostname, int port) {
hints.ai_family = AF_INET6;
hints.ai_flags = AI_V4MAPPED;
- DNSRequest req = resolveName(hostname, NULL, &hints);
- struct addrinfo * res = req.res;
- if (req.error != 0) {
- Printer::elog(E_SOCKET, "Got a resolution error for host %s: %s (%i)", hostname, gai_strerror(req.error), req.error);
+ m_req = resolveName(hostname, NULL, &hints);
+ Task::operationYield(&m_req->evt, Task::INTERRUPTIBLE);
+ }
+
+ if (!m_connecting && m_req) {
+ AAssert(m_req->evt.gotSignal(), "Please don't call connect after a EAgain without checking its resolution status first.");
+ struct addrinfo * res = m_req->res;
+ if (m_req->error != 0) {
+ Printer::elog(E_SOCKET, "Got a resolution error for host %s: %s (%i)", hostname, gai_strerror(m_req->error), m_req->error);
if (res)
freeaddrinfo(res);
+ delete m_req;
+ m_req = NULL;
return false;
}
IAssert(res, "That really shouldn't happen...");
@@ -389,6 +407,8 @@ bool Balau::Socket::connect(const char * hostname, int port) {
m_connecting = true;
freeaddrinfo(res);
+ delete m_req;
+ m_req = NULL;
} else {
// if we end up there, it means our yield earlier threw an EAgain exception.
AAssert(m_evtR->gotSignal(), "Please don't call connect after a EAgain without checking its signal first.");