-
-
Save mame/d7b4d8eaa3b0c3016838c33eea752cbb to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/ext/socket/extconf.rb b/ext/socket/extconf.rb | |
index 93bd850c34..6eec869d69 100644 | |
--- a/ext/socket/extconf.rb | |
+++ b/ext/socket/extconf.rb | |
@@ -327,6 +327,8 @@ def test_recvmsg_with_msg_peek_creates_fds(headers) | |
net/if_dl.h | |
arpa/nameser.h | |
resolv.h | |
+ pthread.h | |
+ sched.h | |
].each {|h| | |
if have_header(h, headers) | |
headers << h | |
@@ -700,6 +702,11 @@ def %(s) s || self end | |
end | |
end | |
+ have_func("pthread_create") | |
+ have_func("pthread_detach") | |
+ have_func("pthread_setaffinity_np") | |
+ have_func("sched_getcpu") | |
+ | |
$VPATH << '$(topdir)' << '$(top_srcdir)' | |
create_makefile("socket") | |
end | |
diff --git a/ext/socket/raddrinfo.c b/ext/socket/raddrinfo.c | |
index 269edc4dad..9f62b1f82c 100644 | |
--- a/ext/socket/raddrinfo.c | |
+++ b/ext/socket/raddrinfo.c | |
@@ -10,6 +10,13 @@ | |
#include "rubysocket.h" | |
+#if !defined(GETADDRINFO_EMU) && defined(HAVE_PTHREAD_CREATE) && defined(HAVE_PTHREAD_DETACH) | |
+// If pthread is available, we run getaddrinfo (or getnameinfo) in a dedicated pthread | |
+// to allow the resolution interruptible. | |
+# define GETADDRINFO_IN_DEDICATED_PTHREAD | |
+# include "ruby/thread_native.h" | |
+#endif | |
+ | |
#if defined(INET6) && (defined(LOOKUP_ORDER_HACK_INET) || defined(LOOKUP_ORDER_HACK_INET6)) | |
#define LOOKUP_ORDERS (sizeof(lookup_order_table) / sizeof(lookup_order_table[0])) | |
static const int lookup_order_table[] = { | |
@@ -176,18 +183,83 @@ parse_numeric_port(const char *service, int *portp) | |
#ifndef GETADDRINFO_EMU | |
struct getaddrinfo_arg | |
{ | |
- const char *node; | |
- const char *service; | |
- const struct addrinfo *hints; | |
- struct addrinfo **res; | |
+ char *node; | |
+ char *service; | |
+ struct addrinfo hints; | |
+ struct addrinfo *res; | |
+ int executed; | |
+#ifdef GETADDRINFO_IN_DEDICATED_PTHREAD | |
+ int wait, done, ret, refcount; | |
+ rb_nativethread_lock_t mutex; | |
+ rb_nativethread_cond_t cond; | |
+#endif | |
}; | |
+struct getaddrinfo_arg * | |
+init_getaddrinfo(char *hostp, char *portp, struct addrinfo *hints) | |
+{ | |
+ struct getaddrinfo_arg *arg = malloc(sizeof(struct getaddrinfo_arg)); | |
+ if (!arg) return NULL; | |
+ MEMZERO(arg, struct getaddrinfo_arg, 1); | |
+#undef strdup | |
+ if (hostp) { | |
+ arg->node = strdup(hostp); | |
+ if (!arg->node) { | |
+ free(arg); | |
+ return NULL; | |
+ } | |
+ } | |
+ else { | |
+ arg->node = 0; | |
+ } | |
+ if (portp) { | |
+ arg->service = strdup(portp); | |
+ if (!arg->service) { | |
+ free(arg->node); | |
+ free(arg); | |
+ return NULL; | |
+ } | |
+ } | |
+ else { | |
+ arg->service = 0; | |
+ } | |
+ arg->hints = *hints; | |
+ return arg; | |
+} | |
+ | |
+static void | |
+finish_getaddrinfo(struct getaddrinfo_arg *arg, int need_lock, int need_free_addrinfo) | |
+{ | |
+#ifdef GETADDRINFO_IN_DEDICATED_PTHREAD | |
+ if (need_lock) rb_native_mutex_lock(&arg->mutex); | |
+ if (0 == --arg->refcount) { | |
+ rb_native_mutex_unlock(&arg->mutex); | |
+ rb_native_cond_destroy(&arg->cond); | |
+ rb_native_mutex_destroy(&arg->mutex); | |
+ if (arg->node) free(arg->node); | |
+ if (arg->service) free(arg->service); | |
+ if (need_free_addrinfo) { | |
+ freeaddrinfo(arg->res); | |
+ } | |
+ free(arg); | |
+ } | |
+ else { | |
+ rb_native_mutex_unlock(&arg->mutex); | |
+ } | |
+#else | |
+ free(arg->node); | |
+ free(arg->service); | |
+ free(arg); | |
+#endif // GETADDRINFO_IN_DEDICATED_PTHREAD | |
+} | |
+ | |
static void * | |
nogvl_getaddrinfo(void *arg) | |
{ | |
int ret; | |
struct getaddrinfo_arg *ptr = arg; | |
- ret = getaddrinfo(ptr->node, ptr->service, ptr->hints, ptr->res); | |
+ ptr->executed = true; | |
+ ret = getaddrinfo(ptr->node, ptr->service, &ptr->hints, &ptr->res); | |
#ifdef __linux__ | |
/* On Linux (mainly Ubuntu 13.04) /etc/nsswitch.conf has mdns4 and | |
* it cause getaddrinfo to return EAI_SYSTEM/ENOENT. [ruby-list:49420] | |
@@ -197,7 +269,54 @@ nogvl_getaddrinfo(void *arg) | |
#endif | |
return (void *)(VALUE)ret; | |
} | |
+ | |
+#ifdef GETADDRINFO_IN_DEDICATED_PTHREAD | |
+static void * | |
+do_getaddrinfo(void *arg) | |
+{ | |
+ struct getaddrinfo_arg *ptr = arg; | |
+ ptr->ret = (int)(VALUE)nogvl_getaddrinfo(arg); | |
+#ifdef __linux__ | |
+ /* On Linux (mainly Ubuntu 13.04) /etc/nsswitch.conf has mdns4 and | |
+ * it cause getaddrinfo to return EAI_SYSTEM/ENOENT. [ruby-list:49420] | |
+ */ | |
+ if (ptr->ret == EAI_SYSTEM && errno == ENOENT) | |
+ ptr->ret = EAI_NONAME; | |
#endif | |
+ rb_native_mutex_lock(&ptr->mutex); | |
+ ptr->done = 1; | |
+ rb_native_cond_signal(&ptr->cond); | |
+ finish_getaddrinfo(ptr, 0, ptr->wait == 2); | |
+ return 0; | |
+} | |
+ | |
+static void | |
+cancel_getaddrinfo(void *arg) | |
+{ | |
+ struct getaddrinfo_arg *ptr = arg; | |
+ rb_native_mutex_lock(&ptr->mutex); | |
+ ptr->done = 2; | |
+ rb_native_cond_signal(&ptr->cond); | |
+ rb_native_mutex_unlock(&ptr->mutex); | |
+} | |
+ | |
+static void * | |
+wait_getaddrinfo(void *arg) | |
+{ | |
+ struct getaddrinfo_arg *ptr = arg; | |
+ rb_native_mutex_lock(&ptr->mutex); | |
+ ptr->wait = 1; | |
+ while (!ptr->done) { | |
+ rb_native_cond_wait(&ptr->cond, &ptr->mutex); | |
+ } | |
+ ptr->wait = 2; | |
+ rb_native_mutex_unlock(&ptr->mutex); | |
+ return (void*)(VALUE)ptr->ret; | |
+} | |
+ | |
+#endif // GETADDRINFO_IN_DEDICATED_PTHREAD | |
+ | |
+#endif // GETADDRINFO_EMU | |
static int | |
numeric_getaddrinfo(const char *node, const char *service, | |
@@ -305,25 +424,116 @@ rb_freeaddrinfo(struct rb_addrinfo *ai) | |
#ifndef GETADDRINFO_EMU | |
struct getnameinfo_arg | |
{ | |
- const struct sockaddr *sa; | |
+ struct sockaddr *sa; | |
socklen_t salen; | |
int flags; | |
char *host; | |
size_t hostlen; | |
char *serv; | |
size_t servlen; | |
+ int executed; | |
+#ifdef GETADDRINFO_IN_DEDICATED_PTHREAD | |
+ int wait, done, ret, refcount; | |
+ rb_nativethread_lock_t mutex; | |
+ rb_nativethread_cond_t cond; | |
+#endif | |
}; | |
+struct getnameinfo_arg * | |
+init_getnameinfo_arg(const struct sockaddr *sa, socklen_t salen, char *host, size_t hostlen, char *serv, size_t servlen, int flags) | |
+{ | |
+ struct getnameinfo_arg *arg = malloc(sizeof(struct getnameinfo_arg)); | |
+ MEMZERO(arg, struct getnameinfo_arg, 1); | |
+ if (!arg) return NULL; | |
+ char *mem = (char*) malloc(salen + hostlen + servlen); | |
+ if (!mem) { | |
+ free(arg); | |
+ return NULL; | |
+ } | |
+ arg->sa = (struct sockaddr *)mem; | |
+ memcpy(mem, sa, salen); | |
+ arg->salen = salen; | |
+ arg->host = mem + salen; | |
+ arg->hostlen = hostlen; | |
+ arg->serv = mem + salen + hostlen; | |
+ arg->servlen = servlen; | |
+ arg->flags = flags; | |
+ arg->executed = 0; | |
+ return arg; | |
+} | |
+ | |
+static void | |
+finish_getnameinfo(struct getnameinfo_arg *ptr) | |
+{ | |
+#ifdef GETADDRINFO_IN_DEDICATED_PTHREAD | |
+ rb_native_mutex_lock(&ptr->mutex); | |
+ if (0 == --ptr->refcount) { | |
+ rb_native_mutex_unlock(&ptr->mutex); | |
+ rb_native_cond_destroy(&ptr->cond); | |
+ rb_native_mutex_destroy(&ptr->mutex); | |
+ free(ptr->sa); | |
+ free(ptr); | |
+ } | |
+ else { | |
+ rb_native_mutex_unlock(&ptr->mutex); | |
+ } | |
+#else | |
+ free(ptr->sa); | |
+ free(ptr); | |
+#endif | |
+} | |
+ | |
static void * | |
nogvl_getnameinfo(void *arg) | |
{ | |
struct getnameinfo_arg *ptr = arg; | |
+ ptr->executed = 1; | |
return (void *)(VALUE)getnameinfo(ptr->sa, ptr->salen, | |
ptr->host, (socklen_t)ptr->hostlen, | |
ptr->serv, (socklen_t)ptr->servlen, | |
ptr->flags); | |
} | |
-#endif | |
+ | |
+#ifdef GETADDRINFO_IN_DEDICATED_PTHREAD | |
+static void * | |
+do_getnameinfo(void *arg) | |
+{ | |
+ struct getnameinfo_arg *ptr = arg; | |
+ ptr->ret = (int)(VALUE)nogvl_getnameinfo(arg); | |
+ rb_native_mutex_lock(&ptr->mutex); | |
+ ptr->done = 1; | |
+ rb_native_cond_signal(&ptr->cond); | |
+ rb_native_mutex_unlock(&ptr->mutex); | |
+ finish_getnameinfo(ptr); | |
+ return 0; | |
+} | |
+ | |
+static void * | |
+wait_getnameinfo(void *arg) | |
+{ | |
+ struct getnameinfo_arg *ptr = arg; | |
+ rb_native_mutex_lock(&ptr->mutex); | |
+ ptr->wait = 1; | |
+ while (!ptr->done) { | |
+ rb_native_cond_wait(&ptr->cond, &ptr->mutex); | |
+ } | |
+ rb_native_mutex_unlock(&ptr->mutex); | |
+ return (void*)(VALUE)ptr->ret; | |
+} | |
+ | |
+static void | |
+cancel_getnameinfo(void *arg) | |
+{ | |
+ struct getnameinfo_arg *ptr = arg; | |
+ rb_native_mutex_lock(&ptr->mutex); | |
+ ptr->done = 2; | |
+ rb_native_cond_signal(&ptr->cond); | |
+ rb_native_mutex_unlock(&ptr->mutex); | |
+} | |
+ | |
+#endif // GETADDRINFO_IN_DEDICATED_PTHREAD | |
+ | |
+#endif // GETADDRINFO_EMU | |
int | |
rb_getnameinfo(const struct sockaddr *sa, socklen_t salen, | |
@@ -333,16 +543,53 @@ rb_getnameinfo(const struct sockaddr *sa, socklen_t salen, | |
#ifdef GETADDRINFO_EMU | |
return getnameinfo(sa, salen, host, hostlen, serv, servlen, flags); | |
#else | |
- struct getnameinfo_arg arg; | |
+start: | |
+ int retry = 0; | |
+ struct getnameinfo_arg *arg = init_getnameinfo_arg(sa, salen, host, hostlen, serv, servlen, flags); | |
+ if (!arg) return ENOMEM; | |
+ | |
int ret; | |
- arg.sa = sa; | |
- arg.salen = salen; | |
- arg.host = host; | |
- arg.hostlen = hostlen; | |
- arg.serv = serv; | |
- arg.servlen = servlen; | |
- arg.flags = flags; | |
- ret = (int)(VALUE)rb_thread_call_without_gvl(nogvl_getnameinfo, &arg, RUBY_UBF_IO, 0); | |
+#ifdef GETADDRINFO_IN_DEDICATED_PTHREAD | |
+ arg->done = 0; | |
+ arg->ret = 0; | |
+ arg->refcount = 2; | |
+ rb_native_mutex_initialize(&arg->mutex); | |
+ rb_native_cond_initialize(&arg->cond); | |
+ | |
+ pthread_t t; | |
+ if (pthread_create(&t, 0, do_getnameinfo, arg) != 0) { | |
+ ret = EAGAIN; | |
+ } | |
+ else { | |
+ pthread_detach(t); | |
+#if defined(HAVE_PTHREAD_SETAFFINITY_NP) && defined(HAVE_SCHED_GETCPU) | |
+ cpu_set_t tmp_cpu_set; | |
+ CPU_ZERO(&tmp_cpu_set); | |
+ CPU_SET(sched_getcpu(), &tmp_cpu_set); | |
+ pthread_setaffinity_np(t, sizeof(cpu_set_t), &tmp_cpu_set); | |
+#endif | |
+ ret = (int)(VALUE)rb_thread_call_without_gvl2(wait_getnameinfo, arg, cancel_getnameinfo, arg); | |
+ if (arg->wait == 0) { | |
+ ret = EAGAIN; | |
+ retry = 1; | |
+ } | |
+ } | |
+#else | |
+ ret = (int)(VALUE)rb_thread_call_without_gvl2(nogvl_getnameinfo, arg, RUBY_UBF_IO, 0); | |
+ if (arg->executed == 0) { | |
+ ret = EAGAIN; | |
+ retry = 1; | |
+ } | |
+ | |
+#endif | |
+ if (ret == 0) { | |
+ memcpy(host, (char*)(arg->sa) + salen, hostlen); | |
+ memcpy(serv, (char*)(arg->sa) + salen + hostlen, servlen); | |
+ } | |
+ finish_getnameinfo(arg); | |
+ rb_thread_check_ints(); | |
+ if (retry) goto start; | |
+ | |
return ret; | |
#endif | |
} | |
@@ -532,6 +779,9 @@ rsock_getaddrinfo(VALUE host, VALUE port, struct addrinfo *hints, int socktype_h | |
} | |
hints->ai_flags |= additional_flags; | |
+start: | |
+ int retry = 0; | |
+ | |
error = numeric_getaddrinfo(hostp, portp, hints, &ai); | |
if (error == 0) { | |
res = (struct rb_addrinfo *)xmalloc(sizeof(struct rb_addrinfo)); | |
@@ -553,22 +803,50 @@ rsock_getaddrinfo(VALUE host, VALUE port, struct addrinfo *hints, int socktype_h | |
#ifdef GETADDRINFO_EMU | |
error = getaddrinfo(hostp, portp, hints, &ai); | |
#else | |
- struct getaddrinfo_arg arg; | |
- MEMZERO(&arg, struct getaddrinfo_arg, 1); | |
- arg.node = hostp; | |
- arg.service = portp; | |
- arg.hints = hints; | |
- arg.res = &ai; | |
- error = (int)(VALUE)rb_thread_call_without_gvl(nogvl_getaddrinfo, &arg, RUBY_UBF_IO, 0); | |
+ struct getaddrinfo_arg *arg = init_getaddrinfo(hostp, portp, hints); | |
+ if (!arg) { | |
+ error = ENOMEM; | |
+ goto end; | |
+ } | |
+#ifdef GETADDRINFO_IN_DEDICATED_PTHREAD | |
+ arg->done = 0; | |
+ arg->ret = 0; | |
+ arg->refcount = 2; | |
+ rb_native_mutex_initialize(&arg->mutex); | |
+ rb_native_cond_initialize(&arg->cond); | |
+ | |
+ pthread_t t; | |
+ if (pthread_create(&t, 0, do_getaddrinfo, arg) != 0) { | |
+ error = EAGAIN; | |
+ goto end; | |
+ } | |
+ else { | |
+ pthread_detach(t); | |
+ error = (int)(VALUE)rb_thread_call_without_gvl2(wait_getaddrinfo, arg, cancel_getaddrinfo, arg); | |
+ if (arg->wait == 0) { | |
+ error = EAGAIN; | |
+ retry = 1; | |
+ } | |
+ } | |
+#else | |
+ error = (int)(VALUE)rb_thread_call_without_gvl2(nogvl_getaddrinfo, arg, RUBY_UBF_IO, 0); | |
+ if (!arg->executed) retry = 1; | |
#endif | |
+ ai = arg->res; | |
+#endif // GETADDRINFO_EMU | |
if (error == 0) { | |
res = (struct rb_addrinfo *)xmalloc(sizeof(struct rb_addrinfo)); | |
res->allocated_by_malloc = 0; | |
res->ai = ai; | |
} | |
+end: | |
+ if (arg) finish_getaddrinfo(arg, 1, 0); | |
} | |
} | |
+ rb_thread_check_ints(); | |
+ if (retry) goto start; | |
+ | |
if (error) { | |
if (hostp && hostp[strlen(hostp)-1] == '\n') { | |
rb_raise(rb_eSocket, "newline at the end of hostname"); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment