Skip to content

Commit b23f25e

Browse files
authored
Add Jvm object monitor and remove rust_object_field from Object/Thread (#138)
* Add object monitor to Jvm with object_wait, object_listen and object_notify * Replace rust_object_field events with Jvm monitor in Object and Thread
1 parent 37e91dc commit b23f25e

File tree

6 files changed

+73
-60
lines changed

6 files changed

+73
-60
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

java_runtime/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ tracing = { workspace = true }
1414

1515
chrono = { version = "^0.4", default-features = false }
1616
encoding_rs = { version = "^0.8", features = ["alloc"], default-features = false }
17-
event-listener = { version = "^5.4", default-features = false }
1817
zip = { version = "^6.0", features = ["deflate"], default-features = false }
1918
url = { version = "^2.5", default-features = false }
2019

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

Lines changed: 16 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
use core::{hash::BuildHasher, time::Duration};
22

3-
use alloc::{boxed::Box, format, sync::Arc, vec};
3+
use alloc::{boxed::Box, format, vec};
44

55
use dyn_clone::clone_box;
6-
use event_listener::Event;
76
use hashbrown::DefaultHashBuilder;
8-
use java_class_proto::{JavaFieldProto, JavaMethodProto};
7+
use java_class_proto::JavaMethodProto;
98
use java_constants::MethodAccessFlags;
10-
use jvm::{Array, ClassInstance, ClassInstanceRef, Jvm, Result, runtime::JavaLangString};
9+
use jvm::{ClassInstance, ClassInstanceRef, Jvm, Result, runtime::JavaLangString};
1110

1211
use crate::{Runtime, RuntimeClassProto, RuntimeContext, SpawnCallback, classes::java::lang::String};
1312

@@ -34,7 +33,7 @@ impl Object {
3433
JavaMethodProto::new("wait", "()V", Self::wait, Default::default()),
3534
JavaMethodProto::new("finalize", "()V", Self::finalize, Default::default()),
3635
],
37-
fields: vec![JavaFieldProto::new("waitEvent", "[B", Default::default())],
36+
fields: vec![],
3837
access_flags: Default::default(),
3938
}
4039
}
@@ -107,27 +106,15 @@ impl Object {
107106
async fn notify(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>) -> Result<()> {
108107
tracing::debug!("java.lang.Object::notify({:?})", &this);
109108

110-
let wait_event: ClassInstanceRef<Array<i8>> = jvm.get_field(&this, "waitEvent", "[B").await?;
111-
if wait_event.is_null() {
112-
return Ok(());
113-
}
114-
115-
let wait_event: Arc<Event> = jvm.get_rust_object_field(&this, "waitEvent").await?;
116-
wait_event.notify(1);
109+
jvm.object_notify(&this, 1);
117110

118111
Ok(())
119112
}
120113

121114
async fn notify_all(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>) -> Result<()> {
122115
tracing::debug!("java.lang.Object::notifyAll({:?})", &this);
123116

124-
let wait_event: ClassInstanceRef<Array<i8>> = jvm.get_field(&this, "waitEvent", "[B").await?;
125-
if wait_event.is_null() {
126-
return Ok(());
127-
}
128-
129-
let wait_event: Arc<Event> = jvm.get_rust_object_field(&this, "waitEvent").await?;
130-
wait_event.notify(usize::MAX);
117+
jvm.object_notify(&this, usize::MAX);
131118

132119
Ok(())
133120
}
@@ -140,24 +127,21 @@ impl Object {
140127
Ok(())
141128
}
142129

143-
async fn wait_long_int(jvm: &Jvm, context: &mut RuntimeContext, mut this: ClassInstanceRef<Self>, millis: i64, nanos: i32) -> Result<()> {
130+
async fn wait_long_int(jvm: &Jvm, context: &mut RuntimeContext, this: ClassInstanceRef<Self>, millis: i64, nanos: i32) -> Result<()> {
144131
tracing::debug!("java.lang.Object::wait({:?}, {:?}, {:?})", &this, millis, nanos);
145132

146-
let wait_event = Arc::new(Event::new());
147-
jvm.put_rust_object_field(&mut this, "waitEvent", wait_event.clone()).await?;
148-
149-
struct Waiter {
133+
struct TimeoutNotifier {
150134
timeout: i64,
151-
wait_event: Arc<Event>,
135+
jvm: Jvm,
136+
this: Box<dyn ClassInstance>,
152137
context: Box<dyn Runtime>,
153138
}
154139

155140
#[async_trait::async_trait]
156-
impl SpawnCallback for Waiter {
141+
impl SpawnCallback for TimeoutNotifier {
157142
async fn call(&self) -> Result<()> {
158143
self.context.sleep(Duration::from_millis(self.timeout as _)).await;
159-
160-
self.wait_event.notify(1); // TODO this would notify other waiter
144+
self.jvm.object_notify(&self.this, 1); // TODO this may wake an unrelated waiter
161145
Ok(())
162146
}
163147
}
@@ -166,15 +150,16 @@ impl Object {
166150
if timeout != 0 {
167151
context.spawn(
168152
jvm,
169-
Box::new(Waiter {
153+
Box::new(TimeoutNotifier {
170154
timeout,
171-
wait_event: wait_event.clone(),
155+
jvm: jvm.clone(),
156+
this: this.clone().into(),
172157
context: clone_box(context),
173158
}),
174159
);
175160
}
176161

177-
wait_event.listen().await;
162+
jvm.object_wait(&this).await?;
178163

179164
Ok(())
180165
}

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

Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
use alloc::{boxed::Box, sync::Arc, vec};
1+
use alloc::{boxed::Box, vec};
22
use core::time::Duration;
33

4-
use event_listener::Event;
5-
64
use java_class_proto::{JavaFieldProto, JavaMethodProto};
75
use java_constants::MethodAccessFlags;
8-
use jvm::{Array, ClassInstanceRef, Jvm, Result, runtime::JavaLangString};
6+
use jvm::{ClassInstanceRef, Jvm, Result, runtime::JavaLangString};
97

108
use crate::{RuntimeClassProto, RuntimeContext, SpawnCallback, classes::java::lang::Runnable};
119

@@ -40,7 +38,7 @@ impl Thread {
4038
fields: vec![
4139
JavaFieldProto::new("id", "J", Default::default()),
4240
JavaFieldProto::new("target", "Ljava/lang/Runnable;", Default::default()),
43-
JavaFieldProto::new("joinEvent", "[B", Default::default()),
41+
JavaFieldProto::new("alive", "Z", Default::default()),
4442
],
4543
access_flags: Default::default(),
4644
}
@@ -84,7 +82,6 @@ impl Thread {
8482
struct ThreadStartProxy {
8583
jvm: Jvm,
8684
thread_id: i32,
87-
join_event: Arc<Event>,
8885
this: ClassInstanceRef<Thread>,
8986
}
9087

@@ -129,14 +126,15 @@ impl Thread {
129126

130127
self.jvm.detach_thread()?;
131128

132-
self.join_event.notify(usize::MAX);
129+
let mut this = self.this.clone();
130+
self.jvm.put_field(&mut this, "alive", "Z", false).await.unwrap();
131+
self.jvm.object_notify(&self.this, usize::MAX);
133132

134133
Ok(())
135134
}
136135
}
137136

138-
let join_event = Arc::new(Event::new());
139-
jvm.put_rust_object_field(&mut this, "joinEvent", join_event.clone()).await?;
137+
jvm.put_field(&mut this, "alive", "Z", true).await?;
140138

141139
let id: i32 = jvm.invoke_virtual(&this, "hashCode", "()I", ()).await?;
142140

@@ -145,7 +143,6 @@ impl Thread {
145143
Box::new(ThreadStartProxy {
146144
jvm: jvm.clone(),
147145
thread_id: id,
148-
join_event,
149146
this: this.clone(),
150147
}),
151148
);
@@ -164,27 +161,23 @@ impl Thread {
164161
Ok(())
165162
}
166163

167-
async fn join(jvm: &Jvm, _context: &mut RuntimeContext, mut this: ClassInstanceRef<Self>) -> Result<()> {
164+
async fn join(jvm: &Jvm, _context: &mut RuntimeContext, this: ClassInstanceRef<Self>) -> Result<()> {
168165
tracing::debug!("java.lang.Thread::join({:?})", &this);
169166

170-
// TODO we don't have get same field twice
171-
let raw_join_event: ClassInstanceRef<Array<i8>> = jvm.get_field(&this, "joinEvent", "[B").await?;
172-
if raw_join_event.is_null() {
173-
return Ok(()); // already joined or not started
167+
loop {
168+
let listener = jvm.object_listen(&this);
169+
let alive: bool = jvm.get_field(&this, "alive", "Z").await?;
170+
if !alive {
171+
return Ok(());
172+
}
173+
listener.await;
174174
}
175-
176-
let join_event: Arc<Event> = jvm.get_rust_object_field(&this, "joinEvent").await?;
177-
join_event.listen().await;
178-
179-
jvm.put_field(&mut this, "joinEvent", "[B", None).await?;
180-
181-
Ok(())
182175
}
183176

184-
async fn is_alive(_jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>) -> Result<bool> {
185-
tracing::warn!("stub java.lang.Thread::isAlive({:?})", &this);
186-
187-
Ok(true)
177+
async fn is_alive(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>) -> Result<bool> {
178+
tracing::debug!("java.lang.Thread::isAlive({:?})", &this);
179+
let alive: bool = jvm.get_field(&this, "alive", "Z").await?;
180+
Ok(alive)
188181
}
189182

190183
async fn sleep(_: &Jvm, context: &mut RuntimeContext, duration: i64) -> Result<()> {

jvm/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ nom = { workspace = true }
1616
parking_lot = { workspace = true }
1717
tracing = { workspace = true }
1818

19+
event-listener = { version = "^5.4", default-features = false }
20+
1921
java_constants = { workspace = true }
2022

2123
[dev-dependencies]

jvm/src/jvm.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,16 @@
33
use alloc::{borrow::ToOwned, boxed::Box, collections::BTreeMap, format, string::String, sync::Arc, vec::Vec};
44
use core::{
55
fmt::Debug,
6+
hash::BuildHasher,
67
iter,
78
mem::{forget, size_of_val},
89
sync::atomic::{AtomicBool, Ordering},
910
};
1011

1112
use bytemuck::cast_slice;
1213
use dyn_clone::clone_box;
13-
use hashbrown::HashSet;
14+
use event_listener::{Event, EventListener};
15+
use hashbrown::{DefaultHashBuilder, HashSet};
1416
use parking_lot::RwLock;
1517

1618
use java_constants::{ClassAccessFlags, MethodAccessFlags};
@@ -36,6 +38,8 @@ struct JvmInner {
3638
classes: RwLock<BTreeMap<String, Class>>,
3739
threads: RwLock<BTreeMap<u64, JvmThread>>,
3840
all_objects: RwLock<HashSet<Box<dyn ClassInstance>>>,
41+
monitors: RwLock<BTreeMap<u64, Arc<Event>>>,
42+
monitor_hasher: DefaultHashBuilder,
3943
get_current_thread_id: Box<dyn Fn() -> u64 + Sync + Send>,
4044
bootstrap_class_loader: Box<dyn BootstrapClassLoader>,
4145
bootstrapping: AtomicBool,
@@ -57,6 +61,8 @@ impl Jvm {
5761
classes: RwLock::new(BTreeMap::new()),
5862
threads: RwLock::new(BTreeMap::new()),
5963
all_objects: RwLock::new(HashSet::new()),
64+
monitors: RwLock::new(BTreeMap::new()),
65+
monitor_hasher: DefaultHashBuilder::default(),
6066
get_current_thread_id: Box::new(get_current_thread_id),
6167
bootstrap_class_loader: Box::new(bootstrap_class_loader),
6268
bootstrapping: AtomicBool::new(true),
@@ -464,6 +470,21 @@ impl Jvm {
464470
self.inner.classes.read().get(class_name).cloned()
465471
}
466472

473+
pub fn object_listen(&self, obj: &Box<dyn ClassInstance>) -> EventListener {
474+
self.get_or_create_monitor(obj).listen()
475+
}
476+
477+
pub async fn object_wait(&self, obj: &Box<dyn ClassInstance>) -> Result<()> {
478+
self.object_listen(obj).await;
479+
480+
Ok(())
481+
}
482+
483+
pub fn object_notify(&self, obj: &Box<dyn ClassInstance>, count: usize) {
484+
let monitor = self.get_or_create_monitor(obj);
485+
monitor.notify(count);
486+
}
487+
467488
#[async_recursion::async_recursion]
468489
pub async fn resolve_class(&self, class_name: &str) -> Result<Class> {
469490
self.resolve_class_internal(class_name, None).await
@@ -720,6 +741,19 @@ impl Jvm {
720741
}
721742
}
722743

744+
fn get_or_create_monitor(&self, obj: &Box<dyn ClassInstance>) -> Arc<Event> {
745+
let key = self.inner.monitor_hasher.hash_one(obj);
746+
747+
let monitors = self.inner.monitors.read();
748+
if let Some(monitor) = monitors.get(&key) {
749+
return monitor.clone();
750+
}
751+
drop(monitors);
752+
753+
let mut monitors = self.inner.monitors.write();
754+
monitors.entry(key).or_insert_with(|| Arc::new(Event::new())).clone()
755+
}
756+
723757
pub(crate) fn find_field(&self, class: &dyn ClassDefinition, name: &str, descriptor: &str) -> Result<Option<Box<dyn Field>>> {
724758
let field = class.field(name, descriptor, false);
725759

0 commit comments

Comments
 (0)