summaryrefslogtreecommitdiff
path: root/ext/socket
diff options
context:
space:
mode:
authorYusuke Endoh <mame@ruby-lang.org>2023-10-18 19:46:40 +0900
committerYusuke Endoh <mame@ruby-lang.org>2023-10-24 12:22:53 +0900
commit16d6a22757e064806284d86337997762a50bf6a6 (patch)
tree30530e28ca73ef8e0807de518522813c0f8c7058 /ext/socket
parent3dc311bdc8badb680267f5a10e0c467ddd9dfe4c (diff)
Make rb_getnameinfo interruptible
Same as previous commit for rb_getnameinfo.
Diffstat (limited to 'ext/socket')
-rw-r--r--ext/socket/raddrinfo.c170
1 files changed, 169 insertions, 1 deletions
diff --git a/ext/socket/raddrinfo.c b/ext/socket/raddrinfo.c
index fa7510371b..8184aa0423 100644
--- a/ext/socket/raddrinfo.c
+++ b/ext/socket/raddrinfo.c
@@ -527,7 +527,7 @@ rb_getnameinfo(const struct sockaddr *sa, socklen_t salen,
return getnameinfo(sa, salen, host, hostlen, serv, servlen, flags);
}
-#elif GETADDRINFO_IMPL == 1 || GETADDRINFO_IMPL == 2 // tmp
+#elif GETADDRINFO_IMPL == 1
struct getnameinfo_arg
{
@@ -567,6 +567,174 @@ rb_getnameinfo(const struct sockaddr *sa, socklen_t salen,
return ret;
}
+#elif GETADDRINFO_IMPL == 2
+
+struct getnameinfo_arg
+{
+ struct sockaddr *sa;
+ socklen_t salen;
+ int flags;
+ char *host;
+ size_t hostlen;
+ char *serv;
+ size_t servlen;
+ int err, refcount, done, cancelled;
+ rb_nativethread_lock_t lock;
+ rb_nativethread_cond_t cond;
+};
+
+static struct getnameinfo_arg *
+allocate_getnameinfo_arg(const struct sockaddr *sa, socklen_t salen, size_t hostlen, size_t servlen, int flags)
+{
+ size_t sa_offset = sizeof(struct getnameinfo_arg);
+ size_t host_offset = sa_offset + salen;
+ size_t serv_offset = host_offset + hostlen;
+ size_t bufsize = serv_offset + servlen;
+
+ char *buf = malloc(bufsize);
+ if (!buf) {
+ rb_gc();
+ buf = malloc(bufsize);
+ if (!buf) return NULL;
+ }
+ struct getnameinfo_arg *arg = (struct getnameinfo_arg *)buf;
+
+ arg->sa = (struct sockaddr *)(buf + sa_offset);
+ memcpy(arg->sa, sa, salen);
+ arg->salen = salen;
+ arg->host = buf + host_offset;
+ arg->hostlen = hostlen;
+ arg->serv = buf + serv_offset;
+ arg->servlen = servlen;
+ arg->flags = flags;
+
+ arg->refcount = 2;
+ arg->done = arg->cancelled = 0;
+
+ rb_nativethread_lock_initialize(&arg->lock);
+ rb_native_cond_initialize(&arg->cond);
+
+ return arg;
+}
+
+static void
+free_getnameinfo_arg(struct getnameinfo_arg *arg)
+{
+ rb_native_cond_destroy(&arg->cond);
+ rb_nativethread_lock_destroy(&arg->lock);
+
+ free(arg);
+}
+
+static void *
+do_getnameinfo(void *ptr)
+{
+ struct getnameinfo_arg *arg = (struct getnameinfo_arg *)ptr;
+
+ int err;
+ err = getnameinfo(arg->sa, arg->salen, arg->host, (socklen_t)arg->hostlen, arg->serv, (socklen_t)arg->servlen, arg->flags);
+
+ int need_free = 0;
+ rb_nativethread_lock_lock(&arg->lock);
+ arg->err = err;
+ if (!arg->cancelled) {
+ arg->done = 1;
+ rb_native_cond_signal(&arg->cond);
+ }
+ if (--arg->refcount == 0) need_free = 1;
+ rb_nativethread_lock_unlock(&arg->lock);
+
+ if (need_free) free_getnameinfo_arg(arg);
+
+ return 0;
+}
+
+static void *
+wait_getnameinfo(void *ptr)
+{
+ struct getnameinfo_arg *arg = (struct getnameinfo_arg *)ptr;
+ rb_nativethread_lock_lock(&arg->lock);
+ while (!arg->done && !arg->cancelled) {
+ rb_native_cond_wait(&arg->cond, &arg->lock);
+ }
+ rb_nativethread_lock_unlock(&arg->lock);
+ return 0;
+}
+
+static void
+cancel_getnameinfo(void *ptr)
+{
+ struct getnameinfo_arg *arg = (struct getnameinfo_arg *)ptr;
+ rb_nativethread_lock_lock(&arg->lock);
+ arg->cancelled = 1;
+ rb_native_cond_signal(&arg->cond);
+ rb_nativethread_lock_unlock(&arg->lock);
+}
+
+int
+rb_getnameinfo(const struct sockaddr *sa, socklen_t salen,
+ char *host, size_t hostlen,
+ char *serv, size_t servlen, int flags)
+{
+ int retry;
+ struct getnameinfo_arg *arg = allocate_getnameinfo_arg(sa, salen, hostlen, servlen, flags);
+ int err;
+
+start:
+ retry = 0;
+
+ arg = allocate_getnameinfo_arg(sa, salen, hostlen, servlen, flags);
+ if (!arg) {
+ return ENOMEM;
+ }
+
+ pthread_t th;
+ if (pthread_create(&th, 0, do_getnameinfo, arg) != 0) {
+ free_getnameinfo_arg(arg);
+ return EAGAIN;
+ }
+
+ pthread_detach(th);
+#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(th, sizeof(cpu_set_t), &tmp_cpu_set);
+#endif
+
+ rb_thread_call_without_gvl2(wait_getnameinfo, arg, cancel_getnameinfo, arg);
+
+ int need_free = 0;
+ rb_nativethread_lock_lock(&arg->lock);
+ if (arg->done) {
+ err = arg->err;
+ if (err == 0) {
+ if (host) memcpy(host, arg->host, hostlen);
+ if (serv) memcpy(serv, arg->serv, servlen);
+ }
+ }
+ else if (arg->cancelled) {
+ err = EAGAIN;
+ }
+ else {
+ // If already interrupted, rb_thread_call_without_gvl2 may return without calling wait_getnameinfo.
+ // In this case, it could be !arg->done && !arg->cancelled.
+ arg->cancelled = 1;
+ retry = 1;
+ }
+ if (--arg->refcount == 0) need_free = 1;
+ rb_nativethread_lock_unlock(&arg->lock);
+
+ if (need_free) free_getnameinfo_arg(arg);
+
+ // If the current thread is interrupted by asynchronous exception, the following raises the exception.
+ // But if the current thread is interrupted by timer thread, the following returns; we need to manually retry.
+ rb_thread_check_ints();
+ if (retry) goto start;
+
+ return err;
+}
+
#endif
static void