Skip to content

Commit a448f7e

Browse files
committed
[GR-40333] Adding Thread#native_thread_id method
PullRequest: truffleruby/3648
2 parents d48ea99 + 1657a4b commit a448f7e

File tree

9 files changed

+62
-15
lines changed

9 files changed

+62
-15
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ Compatibility:
7878
* `Module` methods `#private`, `#public`, `#protected`, `#module_function` now returns their arguments like in CRuby 3.1 (#2733, @horakivo)
7979
* `Kernel#exit!`, killing Fibers and internal errors do not run code in `ensure` clauses anymore, the same as CRuby (@eregon).
8080
* Implement `UnboundMethod#original_name` (@paracycle, @nirvdrum).
81+
* Implement `Thread#native_thread_id` method (#2733, @horakivo).
8182

8283
Performance:
8384

mx.truffleruby/mx_truffleruby.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,6 @@ def contents(self, result):
114114
ruby_options = [
115115
'--experimental-options',
116116
'--building-core-cexts',
117-
'--platform-native-interrupt=false', # no librubysignal in the ruby home yet
118117
'--launcher=' + result,
119118
'--disable-gems',
120119
'--disable-rubyopt',
Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,30 @@
11
require_relative '../../spec_helper'
22

3-
if ruby_version_is "3.1" and Thread.method_defined?(:native_thread_id)
4-
# This method is very platform specific
3+
ruby_version_is "3.1" do
4+
platform_is :linux, :darwin, :windows, :freebsd do
5+
describe "Thread#native_thread_id" do
6+
it "returns an integer when the thread is alive" do
7+
Thread.current.native_thread_id.should be_kind_of(Integer)
8+
end
59

6-
describe "Thread#native_thread_id" do
7-
it "returns an integer when the thread is alive" do
8-
Thread.current.native_thread_id.should be_kind_of(Integer)
9-
end
10+
it "returns nil when the thread is not running" do
11+
t = Thread.new {}
12+
t.join
13+
t.native_thread_id.should == nil
14+
end
15+
16+
it "each thread has different native thread id" do
17+
t = Thread.new { sleep }
18+
Thread.pass until t.stop?
19+
main_thread_id = Thread.current.native_thread_id
20+
t_thread_id = t.native_thread_id
1021

11-
it "returns nil when the thread is not running" do
12-
t = Thread.new {}
13-
t.join
14-
t.native_thread_id.should == nil
22+
t_thread_id.should be_kind_of(Integer)
23+
main_thread_id.should_not == t_thread_id
24+
t.run
25+
t.join
26+
t.native_thread_id.should == nil
27+
end
1528
end
1629
end
1730
end

spec/tags/core/thread/native_thread_id_tags.txt

Lines changed: 0 additions & 2 deletions
This file was deleted.

src/main/c/rubysignal/src/rubysignal.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#include "org_truffleruby_signal_LibRubySignal.h"
1111
#include <pthread.h>
1212
#include <signal.h>
13+
#include <unistd.h>
14+
#include <sys/syscall.h>
1315

1416
_Static_assert(sizeof(pthread_t) == sizeof(jlong), "Expected sizeof(pthread_t) == sizeof(jlong)");
1517

@@ -33,3 +35,13 @@ JNIEXPORT jint JNICALL Java_org_truffleruby_signal_LibRubySignal_sendSIGVTALRMTo
3335
pthread_t pthread_id = (pthread_t) threadID;
3436
return pthread_kill(pthread_id, SIGVTALRM);
3537
}
38+
39+
JNIEXPORT jlong JNICALL Java_org_truffleruby_signal_LibRubySignal_getNativeThreadID(JNIEnv *env, jclass clazz) {
40+
#ifdef __APPLE__
41+
uint64_t native_id;
42+
pthread_threadid_np(NULL, &native_id);
43+
#elif defined(__linux__)
44+
pid_t native_id = (pid_t) syscall(SYS_gettid);
45+
#endif
46+
return (jlong) native_id;
47+
}

src/main/java/org/truffleruby/core/thread/RubyThread.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ public final class RubyThread extends RubyDynamicObject implements ObjectGraphNo
5959
boolean reportOnException;
6060
boolean abortOnException;
6161
public volatile Thread thread = null;
62+
/** Either nil or long */
63+
public volatile Object nativeThreadId = Nil.INSTANCE;
6264
volatile RubyException exception = null;
6365
volatile Object value = null;
6466
public final AtomicBoolean wakeUp = new AtomicBoolean(false);

src/main/java/org/truffleruby/core/thread/ThreadManager.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ public class ThreadManager {
8585
private final ConcurrentWeakSet<Thread> rubyManagedThreads = new ConcurrentWeakSet<>();
8686

8787
private boolean nativeInterrupt;
88+
private boolean useLibRubySignal;
8889
private Timer nativeInterruptTimer;
8990
private ThreadLocal<Interrupter> nativeCallInterrupter;
9091

@@ -95,9 +96,13 @@ public ThreadManager(RubyContext context, RubyLanguage language) {
9596
}
9697

9798
public void initialize() {
98-
nativeInterrupt = context.getOptions().NATIVE_INTERRUPT && language.getRubyHome() != null;
99-
if (nativeInterrupt) {
99+
useLibRubySignal = context.getOptions().NATIVE_PLATFORM && !context.getOptions().BUILDING_CORE_CEXTS &&
100+
language.getRubyHome() != null;
101+
nativeInterrupt = context.getOptions().NATIVE_INTERRUPT && useLibRubySignal;
102+
if (useLibRubySignal) {
100103
LibRubySignal.loadLibrary(language.getRubyHome());
104+
}
105+
if (nativeInterrupt) {
101106
LibRubySignal.setupSIGVTALRMEmptySignalHandler();
102107

103108
nativeInterruptTimer = new Timer("Ruby-NativeCallInterrupt-Timer", true);
@@ -407,6 +412,11 @@ public void startForeignThread(RubyThread rubyThread, Thread javaThread) {
407412
}
408413

409414
public void start(RubyThread thread, Thread javaThread) {
415+
final var isSameThread = javaThread == Thread.currentThread();
416+
if (isSameThread && useLibRubySignal) {
417+
thread.nativeThreadId = LibRubySignal.getNativeThreadID();
418+
}
419+
410420
thread.thread = javaThread;
411421
thread.ioBuffer = context.getOptions().NATIVE_PLATFORM ? Pointer.getNullBuffer(context) : null;
412422
registerThread(thread);
@@ -424,6 +434,7 @@ public void cleanup(RubyThread thread, Thread javaThread) {
424434

425435
/** We cannot call this from {@link RubyLanguage#disposeThread} because that's called under a context lock. */
426436
private void cleanupKillOtherFibers(RubyThread thread) {
437+
thread.nativeThreadId = Nil.INSTANCE;
427438
thread.status = ThreadStatus.DEAD;
428439
context.fiberManager.killOtherFibers(thread);
429440
}

src/main/java/org/truffleruby/core/thread/ThreadNodes.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,15 @@ protected Object status(RubyThread self) {
580580

581581
}
582582

583+
@CoreMethod(names = "native_thread_id")
584+
public abstract static class NativeThreadIdNode extends CoreMethodArrayArgumentsNode {
585+
586+
@Specialization
587+
protected Object nativeThreadId(RubyThread self) {
588+
return self.nativeThreadId;
589+
}
590+
}
591+
583592
@CoreMethod(names = "stop?")
584593
public abstract static class StopNode extends CoreMethodArrayArgumentsNode {
585594

src/main/java/org/truffleruby/signal/LibRubySignal.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,6 @@ public static void loadLibrary(String rubyHome) {
2424

2525
public static native int sendSIGVTALRMToThread(long thread);
2626

27+
public static native long getNativeThreadID();
28+
2729
}

0 commit comments

Comments
 (0)