Skip to content

Commit f9a315e

Browse files
authored
Return the same Thread object from Thread.currentThread() (#175)
Every attached thread now owns its java/lang/Thread instance: attach takes the instance for threads started via Thread.start (so currentThread() inside run() is the started Thread object) and creates one otherwise (bootstrap, external attachers). currentThread() returns the stored instance, and the GC roots it per thread. Also parse unrecognized classfile attributes as an opaque Unknown variant instead of failing — JVMS 4.7.1 requires silently ignoring them, and the anonymous-class fixture carries EnclosingMethod and Signature attributes the parser rejected. Expected output for the fixture is generated by a real JVM.
1 parent fe5d116 commit f9a315e

10 files changed

Lines changed: 62 additions & 11 deletions

File tree

classfile/src/attribute.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ pub enum AttributeInfo {
146146
MethodParameters(Vec<u8>), // TODO
147147
NestMembers(Vec<u8>), // TODO
148148
NestHost(Vec<u8>), // TODO
149+
Unknown(Arc<String>, Vec<u8>),
149150
}
150151

151152
impl AttributeInfo {
@@ -170,7 +171,8 @@ impl AttributeInfo {
170171
"MethodParameters" => AttributeInfo::MethodParameters(info.to_vec()),
171172
"NestMembers" => AttributeInfo::NestMembers(info.to_vec()),
172173
"NestHost" => AttributeInfo::NestHost(info.to_vec()),
173-
_ => return Err(nom::Err::Error(nom::error_position!(info, nom::error::ErrorKind::Switch))),
174+
// unrecognized attributes must be silently ignored (JVMS 4.7.1)
175+
_ => AttributeInfo::Unknown(name.clone(), info.to_vec()),
174176
})
175177
},
176178
)

java_runtime/src/classes/java/lang/thread.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ impl Thread {
9191
async fn call(&self) -> Result<()> {
9292
tracing::trace!("Thread start");
9393

94-
self.jvm.attach_thread()?;
94+
self.jvm.attach_thread(self.this.instance.clone()).await?;
9595

9696
let result: Result<()> = self.jvm.invoke_virtual(&self.this, "run", "()V", []).await;
9797

@@ -202,10 +202,8 @@ impl Thread {
202202
}
203203

204204
async fn current_thread(jvm: &Jvm, _: &mut RuntimeContext) -> Result<ClassInstanceRef<Self>> {
205-
tracing::warn!("stub java.lang.Thread::currentThread()");
205+
tracing::debug!("java.lang.Thread::currentThread()");
206206

207-
let thread = jvm.new_class("java/lang/Thread", "(Z)V", (true,)).await?;
208-
209-
Ok(thread.into())
207+
Ok(jvm.current_java_thread().into())
210208
}
211209
}

java_runtime/tests/classes/java/lang/test_object.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ async fn test_wait() -> Result<()> {
3030
#[async_trait::async_trait]
3131
impl SpawnCallback for Notifier {
3232
async fn call(&self) -> Result<()> {
33-
self.jvm.attach_thread()?;
33+
self.jvm.attach_thread(None).await?;
3434

3535
self.runtime.sleep(Duration::from_millis(100)).await;
3636
self.notified.store(true, Ordering::Relaxed);
@@ -77,7 +77,7 @@ async fn test_wait_timeout() -> Result<()> {
7777
#[async_trait::async_trait]
7878
impl SpawnCallback for Notifier {
7979
async fn call(&self) -> Result<()> {
80-
self.jvm.attach_thread()?;
80+
self.jvm.attach_thread(None).await?;
8181

8282
self.runtime.sleep(Duration::from_millis(1000)).await;
8383
self.notified.store(true, Ordering::Relaxed);

jvm/src/garbage_collector.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ pub fn determine_garbage(
2626
find_reachable_objects(jvm, x, &mut reachable_objects);
2727
});
2828

29+
threads.values().filter_map(|thread| thread.java_thread()).for_each(|x| {
30+
find_reachable_objects(jvm, x, &mut reachable_objects);
31+
});
32+
2933
interned_strings.iter().for_each(|x| {
3034
find_reachable_objects(jvm, x, &mut reachable_objects);
3135
});

jvm/src/jvm.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ impl Jvm {
7979
}
8080

8181
// init startup thread
82-
jvm.attach_thread()?;
82+
jvm.attach_thread(None).await?;
8383

8484
// set java class for bootstrap classes
8585
let classes = jvm.inner.classes.read().values().cloned().collect::<Vec<_>>();
@@ -822,11 +822,19 @@ impl Jvm {
822822
Ok(())
823823
}
824824

825-
pub fn attach_thread(&self) -> Result<()> {
825+
// every attached thread owns a java/lang/Thread instance; pass the instance for threads
826+
// started from java (Thread.start), or None to create one
827+
pub async fn attach_thread(&self, java_thread: Option<Box<dyn ClassInstance>>) -> Result<()> {
826828
let thread_id = (self.inner.get_current_thread_id)();
827829
self.inner.threads.write().insert(thread_id, JvmThread::new());
828830
self.push_native_frame();
829831

832+
let java_thread = match java_thread {
833+
Some(x) => x,
834+
None => self.new_class("java/lang/Thread", "(Z)V", (true,)).await?,
835+
};
836+
self.inner.threads.write().get_mut(&thread_id).unwrap().set_java_thread(java_thread);
837+
830838
Ok(())
831839
}
832840

@@ -837,6 +845,11 @@ impl Jvm {
837845
Ok(())
838846
}
839847

848+
pub fn current_java_thread(&self) -> Box<dyn ClassInstance> {
849+
let thread_id = (self.inner.get_current_thread_id)();
850+
self.inner.threads.read().get(&thread_id).unwrap().java_thread().unwrap().clone()
851+
}
852+
840853
// TODO we need safe, ergonomic api..
841854
pub fn push_native_frame(&self) {
842855
let thread_id = (self.inner.get_current_thread_id)();

jvm/src/thread.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,24 @@ impl StackFrame {
2929

3030
pub struct JvmThread {
3131
stack: Vec<StackFrame>,
32+
java_thread: Option<Box<dyn ClassInstance>>,
3233
}
3334

3435
impl JvmThread {
3536
pub fn new() -> Self {
36-
Self { stack: Vec::new() }
37+
Self {
38+
stack: Vec::new(),
39+
java_thread: None,
40+
}
41+
}
42+
43+
#[allow(clippy::borrowed_box)] // same as jvm.rs; callers pass it to &Box-taking apis
44+
pub fn java_thread(&self) -> Option<&Box<dyn ClassInstance>> {
45+
self.java_thread.as_ref()
46+
}
47+
48+
pub fn set_java_thread(&mut self, java_thread: Box<dyn ClassInstance>) {
49+
self.java_thread = Some(java_thread);
3750
}
3851

3952
pub fn push_java_frame(&mut self, class: &Class, class_instance: Option<Box<dyn ClassInstance>>, method: &str) {

test_data/CurrentThread$1.class

592 Bytes
Binary file not shown.

test_data/CurrentThread.class

1009 Bytes
Binary file not shown.

test_data/CurrentThread.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
true
2+
true
3+
false

test_data/src/CurrentThread.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
public class CurrentThread {
2+
public static void main(String[] args) throws Exception {
3+
Thread a = Thread.currentThread();
4+
Thread b = Thread.currentThread();
5+
System.out.println(a == b);
6+
7+
final Thread[] seen = new Thread[1];
8+
Thread t = new Thread(new Runnable() {
9+
public void run() {
10+
seen[0] = Thread.currentThread();
11+
}
12+
});
13+
t.start();
14+
t.join();
15+
System.out.println(seen[0] == t);
16+
System.out.println(seen[0] == a);
17+
}
18+
}

0 commit comments

Comments
 (0)