From 46655156dcc37509dcb69fcd0717c110eb1c624a Mon Sep 17 00:00:00 2001 From: "NARUSE, Yui" Date: Sat, 22 May 2021 21:36:27 +0900 Subject: [PATCH] Add Thread#native_thread_id [Feature #17853] --- test/ruby/test_thread.rb | 21 +++++++++++++++++++++ thread.c | 31 +++++++++++++++++++++++++++++++ thread_pthread.c | 37 +++++++++++++++++++++++++++++++++++++ thread_win32.c | 8 ++++++++ vm_core.h | 7 +++++++ 5 files changed, 104 insertions(+) diff --git a/test/ruby/test_thread.rb b/test/ruby/test_thread.rb index 6005e5b001..d87160a2a8 100644 --- a/test/ruby/test_thread.rb +++ b/test/ruby/test_thread.rb @@ -1334,6 +1334,27 @@ q.pop assert_equal("foo", c.new {Thread.current.name}.value, bug12290) end + def test_thread_native_thread_id + skip "don't support native_thread_id" unless (Thread.main.native_thread_id rescue nil) + assert_instance_of Integer, Thread.main.native_thread_id + + th1 = Thread.start{sleep} + + # newly created thread which doesn't run yet returns nil or integer + assert_include [NilClass, Integer], th1.native_thread_id.class + + Thread.pass until th1.stop? + + # After a thread starts (and execute `sleep`), it returns native_thread_id + assert_instance_of Integer, th1.native_thread_id + + th1.wakeup + Thread.pass while th1.alive? + + # dead thread returns nil + assert_nil th1.native_thread_id + end + def test_thread_interrupt_for_killed_thread opts = { timeout: 5, timeout_error: nil } diff --git a/thread.c b/thread.c index 4f2debdaf2..e36ea6110b 100644 --- a/thread.c +++ b/thread.c @@ -3402,6 +3402,36 @@ rb_thread_setname(VALUE thread, VALUE name) return name; } +/* + * call-seq: + * thr.native_thread_id -> integer + * + * Return the native thread ID which is used by the Ruby thread. + * + * The ID depends on the OS. (not POSIX thread ID returned by pthread_self(3)) + * * On Linux it is TID returned by gettid(2). + * * On macOS it is the system-wide unique integral ID of thread returned + * by pthread_threadid_np(3). + * * On FreeBSD it is the unique integral ID of the thread returned by + * pthread_getthreadid_np(3). + * * On Windows it is the thread identifier returned by GetThreadId(). + * * On other platforms, it raises NotImplementedError. + * + * NOTE: + * If the thread is not associated yet or already deassociated with a native + * thread, it returns _nil_. + * If the Ruby implementation uses M:N thread model, the ID may change + * depending on the timing. + */ + +static VALUE +rb_thread_native_thread_id(VALUE thread) +{ + rb_thread_t *target_th = rb_thread_ptr(thread); + if (rb_threadptr_dead(target_th)) return Qnil; + return native_thread_native_thread_id(target_th); +} + /* * call-seq: * thr.to_s -> string @@ -5494,6 +5524,7 @@ Init_Thread(void) rb_define_method(rb_cThread, "name", rb_thread_getname, 0); rb_define_method(rb_cThread, "name=", rb_thread_setname, 1); + rb_define_method(rb_cThread, "native_thread_id", rb_thread_native_thread_id, 0); rb_define_method(rb_cThread, "to_s", rb_thread_to_s, 0); rb_define_alias(rb_cThread, "inspect", "to_s"); diff --git a/thread_pthread.c b/thread_pthread.c index 3e0c78b256..87db59e638 100644 --- a/thread_pthread.c +++ b/thread_pthread.c @@ -34,6 +34,9 @@ #if defined(__HAIKU__) #include #endif +#ifdef __linux__ +#include /* for SYS_gettid */ +#endif #include #include @@ -659,11 +662,26 @@ Init_native_thread(rb_thread_t *th) posix_signal(SIGVTALRM, null_func); } +#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(rb_thread_t *th) { native_thread_data_t *nd = &th->native_thread_data; +#ifdef RB_THREAD_T_HAS_NATIVE_ID + th->tid = get_native_thread_id(); +#endif #ifdef USE_UBF_LIST list_node_init(&nd->node.ubf); #endif @@ -1708,6 +1726,25 @@ native_set_another_thread_name(rb_nativethread_id_t thread_id, VALUE name) #endif } +static VALUE +native_thread_native_thread_id(rb_thread_t *target_th) +{ +#if !defined(RB_THREAD_T_HAS_NATIVE_ID) && !defined(__APPLE__) + rb_notimplement(); +#endif + +#ifdef RB_THREAD_T_HAS_NATIVE_ID + int tid = target_th->tid; + if (tid == 0) return Qnil; + return INT2FIX(tid); +#elif defined(__APPLE__) + uint64_t tid; + int e = pthread_threadid_np(target_th->thread_id, &tid); + if (e != 0) rb_syserr_fail(e, "pthread_threadid_np"); + return ULL2NUM((unsigned long long)tid); +#endif +} + static void ubf_timer_invalidate(void) { diff --git a/thread_win32.c b/thread_win32.c index 8b5120be3c..fc31c09ec3 100644 --- a/thread_win32.c +++ b/thread_win32.c @@ -835,6 +835,14 @@ native_set_thread_name(rb_thread_t *th) { } +static VALUE +native_thread_native_thread_id(rb_thread_t *th) +{ + DWORD tid = GetThreadId(th->thread_id); + if (tid == 0) rb_sys_fail("GetThreadId"); + return ULONG2NUM(tid); +} + #if USE_MJIT static unsigned long __stdcall mjit_worker(void *arg) diff --git a/vm_core.h b/vm_core.h index 2d92d9b9f8..72e2bcb6fb 100644 --- a/vm_core.h +++ b/vm_core.h @@ -942,6 +942,10 @@ struct rb_ext_config { typedef struct rb_ractor_struct rb_ractor_t; +#if defined(__linux__) || defined(__FreeBSD__) +# define RB_THREAD_T_HAS_NATIVE_ID +#endif + typedef struct rb_thread_struct { struct list_node lt_node; // managed by a ractor VALUE self; @@ -963,6 +967,9 @@ typedef struct rb_thread_struct { rb_nativethread_id_t thread_id; #ifdef NON_SCALAR_THREAD_ID rb_thread_id_string_t thread_id_string; +#endif +#ifdef RB_THREAD_T_HAS_NATIVE_ID + int tid; #endif BITFIELD(enum rb_thread_status, status, 2); /* bit flags */