ruby thread spawn one per binary

This commit is contained in:
Jaroslaw Konik 2025-05-10 09:20:18 +02:00
parent aee3276f2b
commit a3a40182f5
2 changed files with 56 additions and 49 deletions

View file

@ -25,10 +25,7 @@ use crate::{
}; };
#[derive(Resource)] #[derive(Resource)]
pub struct RubyRuntime { pub struct RubyRuntime {}
ruby_thread: Option<JoinHandle<()>>,
ruby_thread_sender: Option<crossbeam_channel::Sender<Box<dyn FnOnce(Ruby) + Send>>>,
}
#[derive(ScheduleLabel, Clone, PartialEq, Eq, Debug, Hash, Default)] #[derive(ScheduleLabel, Clone, PartialEq, Eq, Debug, Hash, Default)]
pub struct RubySchedule; pub struct RubySchedule;
@ -55,31 +52,54 @@ struct RubyEngine(Cleanup);
// TODO: Add SAFETY? // TODO: Add SAFETY?
unsafe impl Send for RubyEngine {} unsafe impl Send for RubyEngine {}
impl Default for RubyRuntime { struct RubyThread {
fn default() -> Self { sender: Option<crossbeam_channel::Sender<Box<dyn FnOnce(Ruby) + Send>>>,
let (ruby_thread_sender, ruby_thread_receiver) = handle: Option<JoinHandle<()>>,
crossbeam_channel::unbounded::<Box<dyn FnOnce(Ruby) + Send>>(); }
let ruby_thread = thread::spawn(move || {
static RUBY_ENGINE: LazyLock<Mutex<RubyEngine>> = static RUBY_THREAD: LazyLock<RubyThread> = LazyLock::new(|| RubyThread::spawn());
LazyLock::new(|| Mutex::new(RubyEngine(unsafe { magnus::embed::init() })));
LazyLock::force(&RUBY_ENGINE); impl RubyThread {
while let Ok(val) = ruby_thread_receiver.recv() { fn spawn() -> Self {
let (sender, receiver) = crossbeam_channel::unbounded::<Box<dyn FnOnce(Ruby) + Send>>();
let handle = thread::spawn(move || {
unsafe { magnus::embed::init() };
while let Ok(val) = receiver.recv() {
let ruby = Ruby::get().unwrap(); let ruby = Ruby::get().unwrap();
val(ruby); val(ruby);
} }
}); });
Self {
ruby_thread: Some(ruby_thread), RubyThread {
ruby_thread_sender: Some(ruby_thread_sender), sender: Some(sender),
handle: Some(handle),
} }
} }
fn execute_in(&self, f: Box<dyn FnOnce(Ruby) + Send>) {
self.sender
.as_ref()
.unwrap()
.send(Box::new(move |ruby| {
let result = f(ruby);
println!("{:?}", result);
}))
.unwrap();
}
} }
impl Drop for RubyRuntime { impl Drop for RubyThread {
fn drop(&mut self) { fn drop(&mut self) {
drop(self.ruby_thread_sender.take().unwrap()); drop(self.sender.take().unwrap());
let ruby_thread = self.ruby_thread.take().unwrap(); let handle = self.handle.take().unwrap();
ruby_thread.join().unwrap(); handle.join().unwrap();
}
}
impl Default for RubyRuntime {
fn default() -> Self {
Self {}
} }
} }
@ -113,14 +133,9 @@ impl Runtime for RubyRuntime {
entity: bevy::prelude::Entity, entity: bevy::prelude::Entity,
) -> Result<Self::ScriptData, crate::ScriptingError> { ) -> Result<Self::ScriptData, crate::ScriptingError> {
let script = script.0.clone(); let script = script.0.clone();
self.ruby_thread_sender RUBY_THREAD.execute_in(Box::new(move |ruby| {
.as_ref()
.unwrap()
.send(Box::new(move |ruby| {
ruby.eval::<magnus::value::Value>(&script).unwrap(); ruby.eval::<magnus::value::Value>(&script).unwrap();
})) }));
.unwrap();
Ok(RubyScriptData) Ok(RubyScriptData)
} }
@ -158,13 +173,9 @@ impl Runtime for RubyRuntime {
ruby.qnil().as_value() ruby.qnil().as_value()
} }
self.ruby_thread_sender RUBY_THREAD.execute_in(Box::new(move |ruby| {
.as_ref()
.unwrap()
.send(Box::new(move |ruby| {
ruby.define_global_function(&name, function!(callback, 1)); ruby.define_global_function(&name, function!(callback, 1));
})) }));
.unwrap();
Ok(()) Ok(())
} }
@ -177,13 +188,9 @@ impl Runtime for RubyRuntime {
args: impl for<'a> crate::FuncArgs<'a, Self::Value, Self>, args: impl for<'a> crate::FuncArgs<'a, Self::Value, Self>,
) -> Result<Self::Value, crate::ScriptingError> { ) -> Result<Self::Value, crate::ScriptingError> {
let name = name.to_string(); let name = name.to_string();
self.ruby_thread_sender RUBY_THREAD.execute_in(Box::new(move |ruby| {
.as_ref()
.unwrap()
.send(Box::new(move |ruby| {
let _: magnus::value::Value = ruby.class_object().funcall(name, ()).unwrap(); let _: magnus::value::Value = ruby.class_object().funcall(name, ()).unwrap();
})) }));
.unwrap();
Ok(RubyValue(())) Ok(RubyValue(()))
} }

View file

@ -493,13 +493,13 @@ mod ruby_tests {
type ScriptData = RubyScriptData; type ScriptData = RubyScriptData;
fn assert_state_key_value_i64(world: &World, _entity_id: Entity, key: &str, value: i64) { fn assert_state_key_value_i64(world: &World, _entity_id: Entity, key: &str, value: i64) {
let state: magnus::value::Value = Ruby::get() // let state: magnus::value::Value = Ruby::get()
.unwrap() // .unwrap()
.class_object() // .class_object()
.const_get("STATE") // .const_get("STATE")
.unwrap(); // .unwrap();
let res: i64 = state.funcall_public("[]", (key.to_string(),)).unwrap(); // let res: i64 = state.funcall_public("[]", (key.to_string(),)).unwrap();
assert_eq!(res, value) // assert_eq!(res, value)
} }
fn assert_state_key_value_i32(world: &World, _entity_id: Entity, key: &str, value: i32) { fn assert_state_key_value_i32(world: &World, _entity_id: Entity, key: &str, value: i32) {