summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNARUSE, Yui <naruse@airemix.jp>2021-05-22 21:36:27 +0900
committerNARUSE, Yui <naruse@airemix.jp>2021-05-26 15:14:11 +0900
commit46655156dcc37509dcb69fcd0717c110eb1c624a (patch)
treee21a75ebde64d8e7f05627fc990b62c47c8099c6
parent88e3848fca69915a24657bcc26da1a65b659c6f3 (diff)
Add Thread#native_thread_id [Feature #17853]
-rw-r--r--test/ruby/test_thread.rb21
-rw-r--r--thread.c31
-rw-r--r--thread_pthread.c37
-rw-r--r--thread_win32.c8
-rw-r--r--vm_core.h7
5 files changed, 104 insertions, 0 deletions
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
@@ -3404,6 +3404,36 @@ rb_thread_setname(VALUE thread, VALUE 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
*
* Dump the name, id, and status of _thr_ to a 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 <kernel/OS.h>
#endif
+#ifdef __linux__
+#include <sys/syscall.h> /* for SYS_gettid */
+#endif
#include <time.h>
#include <signal.h>
@@ -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;
@@ -964,6 +968,9 @@ typedef struct rb_thread_struct {
#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 */
unsigned int to_kill : 1;