ruby thread ensure single access
This commit is contained in:
parent
c46391871f
commit
fcf2680824
1 changed files with 56 additions and 19 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
// TODO: make sure ruby is statically linked
|
// TODO: make sure ruby is statically linked
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
sync::{LazyLock, Mutex},
|
sync::{Arc, Condvar, LazyLock, Mutex, MutexGuard},
|
||||||
thread::{self, JoinHandle},
|
thread::{self, JoinHandle},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -17,11 +17,14 @@ use serde::Deserialize;
|
||||||
use crate::{
|
use crate::{
|
||||||
assets::GetExtensions,
|
assets::GetExtensions,
|
||||||
callback::{FromRuntimeValueWithEngine, IntoRuntimeValueWithEngine},
|
callback::{FromRuntimeValueWithEngine, IntoRuntimeValueWithEngine},
|
||||||
|
runtimes::ruby,
|
||||||
FuncArgs, Runtime,
|
FuncArgs, Runtime,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
pub struct RubyRuntime {}
|
pub struct RubyRuntime {
|
||||||
|
ruby_thread: Option<RubyThread>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(ScheduleLabel, Clone, PartialEq, Eq, Debug, Hash, Default)]
|
#[derive(ScheduleLabel, Clone, PartialEq, Eq, Debug, Hash, Default)]
|
||||||
pub struct RubySchedule;
|
pub struct RubySchedule;
|
||||||
|
|
@ -50,8 +53,8 @@ struct RubyThread {
|
||||||
handle: Option<JoinHandle<()>>,
|
handle: Option<JoinHandle<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Can we put references to those in runtime struct?
|
static RUBY_THREAD: LazyLock<Arc<(Mutex<Option<RubyThread>>, Condvar)>> =
|
||||||
static RUBY_THREAD: LazyLock<RubyThread> = LazyLock::new(|| RubyThread::spawn());
|
LazyLock::new(|| Arc::new((Mutex::new(Some(RubyThread::spawn())), Condvar::new())));
|
||||||
|
|
||||||
impl RubyThread {
|
impl RubyThread {
|
||||||
fn spawn() -> Self {
|
fn spawn() -> Self {
|
||||||
|
|
@ -94,7 +97,26 @@ impl Drop for RubyThread {
|
||||||
|
|
||||||
impl Default for RubyRuntime {
|
impl Default for RubyRuntime {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {}
|
let (lock, cvar) = &*Arc::clone(&RUBY_THREAD);
|
||||||
|
let mut ruby_thread = lock.lock().unwrap();
|
||||||
|
|
||||||
|
while ruby_thread.is_none() {
|
||||||
|
ruby_thread = cvar.wait(ruby_thread).unwrap();
|
||||||
|
}
|
||||||
|
let ruby_thread = ruby_thread.take().unwrap();
|
||||||
|
cvar.notify_all();
|
||||||
|
Self {
|
||||||
|
ruby_thread: Some(ruby_thread),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for RubyRuntime {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let (lock, cvar) = &*Arc::clone(&RUBY_THREAD);
|
||||||
|
let mut ruby_thread = lock.lock().unwrap();
|
||||||
|
*ruby_thread = self.ruby_thread.take();
|
||||||
|
cvar.notify_all();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -118,14 +140,20 @@ impl Runtime for RubyRuntime {
|
||||||
&mut self,
|
&mut self,
|
||||||
f: impl FnOnce(&mut Self::RawEngine) -> T + Send + 'static,
|
f: impl FnOnce(&mut Self::RawEngine) -> T + Send + 'static,
|
||||||
) -> T {
|
) -> T {
|
||||||
RUBY_THREAD.execute_in(Box::new(move |mut ruby| f(&mut ruby)))
|
self.ruby_thread
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.execute_in(Box::new(move |mut ruby| f(&mut ruby)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_engine<T: Send + 'static>(
|
fn with_engine<T: Send + 'static>(
|
||||||
&self,
|
&self,
|
||||||
f: impl FnOnce(&Self::RawEngine) -> T + Send + 'static,
|
f: impl FnOnce(&Self::RawEngine) -> T + Send + 'static,
|
||||||
) -> T {
|
) -> T {
|
||||||
RUBY_THREAD.execute_in(Box::new(move |ruby| f(&ruby)))
|
self.ruby_thread
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.execute_in(Box::new(move |ruby| f(&ruby)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval(
|
fn eval(
|
||||||
|
|
@ -134,10 +162,13 @@ 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();
|
||||||
RUBY_THREAD.execute_in(Box::new(move |ruby| {
|
self.ruby_thread
|
||||||
ruby.eval::<magnus::value::Value>(&script).unwrap();
|
.as_ref()
|
||||||
RubyValue(())
|
.unwrap()
|
||||||
}));
|
.execute_in(Box::new(move |ruby| {
|
||||||
|
ruby.eval::<magnus::value::Value>(&script).unwrap();
|
||||||
|
RubyValue(())
|
||||||
|
}));
|
||||||
Ok(RubyScriptData)
|
Ok(RubyScriptData)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -187,10 +218,13 @@ impl Runtime for RubyRuntime {
|
||||||
ruby.qnil().as_value()
|
ruby.qnil().as_value()
|
||||||
}
|
}
|
||||||
|
|
||||||
RUBY_THREAD.execute_in(Box::new(move |ruby| {
|
self.ruby_thread
|
||||||
ruby.define_global_function(&name, function!(callback, 0));
|
.as_ref()
|
||||||
RubyValue(())
|
.unwrap()
|
||||||
}));
|
.execute_in(Box::new(move |ruby| {
|
||||||
|
ruby.define_global_function(&name, function!(callback, 0));
|
||||||
|
RubyValue(())
|
||||||
|
}));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -203,10 +237,13 @@ 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();
|
||||||
RUBY_THREAD.execute_in(Box::new(move |ruby| {
|
self.ruby_thread
|
||||||
let _: magnus::Value = ruby.class_object().funcall(name, ()).unwrap();
|
.as_ref()
|
||||||
RubyValue(())
|
.unwrap()
|
||||||
}));
|
.execute_in(Box::new(move |ruby| {
|
||||||
|
let _: magnus::Value = ruby.class_object().funcall(name, ()).unwrap();
|
||||||
|
RubyValue(())
|
||||||
|
}));
|
||||||
|
|
||||||
Ok(RubyValue(()))
|
Ok(RubyValue(()))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue