From 0117a6d389c5b607eaec1b7f917132efa356f665 Mon Sep 17 00:00:00 2001 From: KJ Tsanaktsidis Date: Fri, 15 Sep 2023 11:33:32 +1000 Subject: [PATCH] Fix Thread#native_thread_id being cached across fork (#8418) The native thread ID can and does change on some operating systems (e.g. Linux) after forking, so it needs to be re-queried. [Bug #19873] --- test/ruby/test_thread.rb | 30 ++++++++++++++++++++++++++++++ thread_pthread.c | 27 +++++++++++++++------------ 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/test/ruby/test_thread.rb b/test/ruby/test_thread.rb index 703373b11e..bcd6590903 100644 --- a/test/ruby/test_thread.rb +++ b/test/ruby/test_thread.rb @@ -3,6 +3,7 @@ require 'test/unit' require "rbconfig/sizeof" require "timeout" +require "fiddle" class TestThread < Test::Unit::TestCase class Thread < ::Thread @@ -1443,6 +1444,35 @@ q.pop assert_nil th1.native_thread_id end + def test_thread_native_thread_id_across_fork_on_linux + rtld_default = Fiddle.dlopen(nil) + omit "this test is only for Linux" unless rtld_default.sym_defined?('gettid') + + gettid = Fiddle::Function.new(rtld_default['gettid'], [], Fiddle::TYPE_INT) + + parent_thread_id = Thread.main.native_thread_id + real_parent_thread_id = gettid.call + + assert_equal real_parent_thread_id, parent_thread_id + + child_lines = nil + IO.popen('-') do |pipe| + if pipe + # parent + child_lines = pipe.read.lines + else + # child + puts Thread.main.native_thread_id + puts gettid.call + end + end + child_thread_id = child_lines[0].chomp.to_i + real_child_thread_id = child_lines[1].chomp.to_i + + assert_equal real_child_thread_id, child_thread_id + refute_equal parent_thread_id, child_thread_id + end + def test_thread_interrupt_for_killed_thread opts = { timeout: 5, timeout_error: nil } diff --git a/thread_pthread.c b/thread_pthread.c index 057c50c124..7c53325240 100644 --- a/thread_pthread.c +++ b/thread_pthread.c @@ -637,6 +637,18 @@ rb_thread_sched_destroy(struct rb_thread_sched *sched) clear_thread_cache_altstack(); } +#ifdef RB_THREAD_T_HAS_NATIVE_ID +static int +get_native_thread_id(void) +{ +#ifdef __linux__ + return (int)syscall(SYS_gettid); +#elif defined(__FreeBSD__) + return pthread_getthreadid_np(); +#endif +} +#endif + #if defined(HAVE_WORKING_FORK) static void thread_cache_reset(void); static void @@ -646,6 +658,9 @@ thread_sched_atfork(struct rb_thread_sched *sched) thread_cache_reset(); rb_thread_sched_init(sched); thread_sched_to_running(sched, GET_THREAD()); +#ifdef RB_THREAD_T_HAS_NATIVE_ID + GET_THREAD()->nt->tid = get_native_thread_id(); +#endif } #endif @@ -693,18 +708,6 @@ ruby_thread_set_native(rb_thread_t *th) #endif } -#ifdef RB_THREAD_T_HAS_NATIVE_ID -static int -get_native_thread_id(void) -{ -#ifdef __linux__ - return (int)syscall(SYS_gettid); -#elif defined(__FreeBSD__) - return pthread_getthreadid_np(); -#endif -} -#endif - static void native_thread_init(struct rb_native_thread *nt) {