summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Gruber <luke.gruber@shopify.com>2025-11-13 16:42:38 -0500
committerGitHub <noreply@github.com>2025-11-13 16:42:38 -0500
commitf100298e28b3f3db93956a563a11c5cc1dbcb0a7 (patch)
tree7ac3013ef04f8f8bdb6e8af041b8d292a0ecf401
parentd58960a912e8146d228fd3a86c886db4457a5482 (diff)
ext/socket: Set raddrinfo thread as detached before thread start (#15142)
We were seeing segfaults when calling `pthread_detach`. Apparently in some versions of glibc there is a race between when this is called (usually right after starting a thread) and a short-lived thread's shutdown routine. The bug has been reported to glibc: https://sourceware.org/bugzilla/show_bug.cgi?id=19951 I haven't been able to reproduce it on my Linux desktop but apparently it's easier to reproduce on certain kinds of servers. As a workaround, we can set the thread's detach state before thread start. I don't know of a platform that doesn't have `pthread_attr_setdetachstate`, but to be safe we check for it in `extconf.rb` and use `pthread_detach` as a backup if it isn't available. Fixes [Bug #21679]
-rw-r--r--ext/socket/extconf.rb1
-rw-r--r--ext/socket/ipsocket.c1
-rw-r--r--ext/socket/raddrinfo.c40
3 files changed, 38 insertions, 4 deletions
diff --git a/ext/socket/extconf.rb b/ext/socket/extconf.rb
index d44ce31b0a..a814e21c3a 100644
--- a/ext/socket/extconf.rb
+++ b/ext/socket/extconf.rb
@@ -704,6 +704,7 @@ SRC
have_func("pthread_create")
have_func("pthread_detach")
+ have_func("pthread_attr_setdetachstate")
$VPATH << '$(topdir)' << '$(top_srcdir)'
create_makefile("socket")
diff --git a/ext/socket/ipsocket.c b/ext/socket/ipsocket.c
index fa41d89936..a95f5767d2 100644
--- a/ext/socket/ipsocket.c
+++ b/ext/socket/ipsocket.c
@@ -705,7 +705,6 @@ init_fast_fallback_inetsock_internal(VALUE v)
if (raddrinfo_pthread_create(&threads[i], fork_safe_do_fast_fallback_getaddrinfo, arg->getaddrinfo_entries[i]) != 0) {
rsock_raise_resolution_error("getaddrinfo(3)", EAI_AGAIN);
}
- pthread_detach(threads[i]);
}
if (NIL_P(resolv_timeout)) {
diff --git a/ext/socket/raddrinfo.c b/ext/socket/raddrinfo.c
index 22ab34a073..e4e40e591d 100644
--- a/ext/socket/raddrinfo.c
+++ b/ext/socket/raddrinfo.c
@@ -496,13 +496,49 @@ int
raddrinfo_pthread_create(pthread_t *th, void *(*start_routine) (void *), void *arg)
{
int limit = 3, ret;
+ int saved_errno;
+#ifdef HAVE_PTHREAD_ATTR_SETDETACHSTATE
+ pthread_attr_t attr;
+ pthread_attr_t *attr_p = &attr;
+ int err;
+ int init_retries = 0;
+ int init_retries_max = 3;
+retry_attr_init:
+ if ((err = pthread_attr_init(attr_p)) != 0) {
+ if (err == ENOMEM && init_retries < init_retries_max) {
+ init_retries++;
+ rb_gc();
+ goto retry_attr_init;
+ }
+ return err;
+ }
+ if ((err = pthread_attr_setdetachstate(attr_p, PTHREAD_CREATE_DETACHED)) != 0) {
+ saved_errno = errno;
+ pthread_attr_destroy(attr_p);
+ errno = saved_errno;
+ return err; // EINVAL - shouldn't happen
+ }
+#else
+ pthread_attr_t *attr_p = NULL;
+#endif
do {
// It is said that pthread_create may fail spuriously, so we follow the JDK and retry several times.
//
// https://bugs.openjdk.org/browse/JDK-8268605
// https://github.com/openjdk/jdk/commit/e35005d5ce383ddd108096a3079b17cb0bcf76f1
- ret = pthread_create(th, 0, start_routine, arg);
+ ret = pthread_create(th, attr_p, start_routine, arg);
} while (ret == EAGAIN && limit-- > 0);
+#ifdef HAVE_PTHREAD_ATTR_SETDETACHSTATE
+ saved_errno = errno;
+ pthread_attr_destroy(attr_p);
+ if (ret != 0) {
+ errno = saved_errno;
+ }
+#else
+ if (ret == 0) {
+ pthread_detach(th); // this can race with shutdown routine of thread in some glibc versions
+ }
+#endif
return ret;
}
@@ -534,7 +570,6 @@ start:
errno = err;
return EAI_SYSTEM;
}
- pthread_detach(th);
rb_thread_call_without_gvl2(wait_getaddrinfo, arg, cancel_getaddrinfo, arg);
@@ -770,7 +805,6 @@ start:
errno = err;
return EAI_SYSTEM;
}
- pthread_detach(th);
rb_thread_call_without_gvl2(wait_getnameinfo, arg, cancel_getnameinfo, arg);