From 57f865178af5110aa3243539cd3d084ce55dc139 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Fri, 30 May 2025 07:00:00 +0200 Subject: [PATCH 1/5] ruby fibers POC --- Cargo.toml | 1 + assets/examples/ruby/promises.rb | 5 ++- examples/ruby/promises.rs | 22 +++++++----- src/runtimes/ruby.rs | 61 ++++++++++++++++++++++++++++---- 4 files changed, 71 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 71cc86f..9dbd741 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,7 @@ magnus = { version = "0.7.1", optional = true } rb-sys = { version = "0.9", default-features = false, features = ["link-ruby", "ruby-static"], optional = true } crossbeam-channel = "0.5.15" libc = "0.2.172" +tempfile = "3.20.0" [[example]] name = "call_function_from_rust_rhai" diff --git a/assets/examples/ruby/promises.rb b/assets/examples/ruby/promises.rb index dd0ecbe..9aeacb0 100644 --- a/assets/examples/ruby/promises.rb +++ b/assets/examples/ruby/promises.rb @@ -1,3 +1,2 @@ -get_player_name.and_then do |name| - puts name -end +puts get_player_name.await +quit diff --git a/examples/ruby/promises.rs b/examples/ruby/promises.rs index 1b3ed62..5afe115 100644 --- a/examples/ruby/promises.rs +++ b/examples/ruby/promises.rs @@ -9,15 +9,19 @@ fn main() { App::new() .add_plugins(DefaultPlugins) .add_scripting::(|builder| { - builder.add_function( - String::from("get_player_name"), - |player_names: Query<&Name, With>| { - player_names - .single() - .expect("Missing player_names") - .to_string() - }, - ); + builder + .add_function( + String::from("get_player_name"), + |player_names: Query<&Name, With>| { + player_names + .single() + .expect("Missing player_names") + .to_string() + }, + ) + .add_function(String::from("quit"), |mut exit: EventWriter| { + exit.write(AppExit::Success); + }); }) .add_systems(Startup, startup) .run(); diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 1ccccdd..1646d7f 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -1,11 +1,15 @@ use std::{ collections::HashMap, ffi::CString, + io::Write, sync::{Arc, Condvar, LazyLock, Mutex}, thread::{self, JoinHandle}, }; -use ::magnus::{typed_data::Inspect, value::Opaque}; +use ::magnus::{ + typed_data::Inspect, + value::{self, Opaque}, +}; use bevy::{ asset::Asset, ecs::{component::Component, entity::Entity, resource::Resource, schedule::ScheduleLabel}, @@ -19,7 +23,7 @@ use magnus::{ value::{Lazy, ReprValue}, }; use magnus::{method, prelude::*}; -use rb_sys::{VALUE, ruby_init_stack}; +use rb_sys::{VALUE, rb_load, ruby_init_stack}; use serde::Deserialize; use crate::{ @@ -174,6 +178,25 @@ fn then(r_self: magnus::Value) -> magnus::Value { .into_value() } +fn await_promise(r_self: magnus::Value) -> magnus::Value { + let promise: &Promise<(), RubyValue> = + TryConvert::try_convert(r_self).expect("Couldn't convert self to Promise"); + let ruby = + Ruby::get().expect("Failed to get a handle to Ruby API when registering Promise callback"); + let fiber = Opaque::from(ruby.fiber_current()); + promise + .clone() + .then(RubyValue::new( + ruby.proc_from_fn(move |ruby, args, _| { + let fiber = ruby.get_inner(fiber); + fiber.resume::<_, magnus::Value>(args).unwrap(); + }) + .as_value(), + )) + .into_value(); + ruby.fiber_yield::<_, magnus::Value>(()).unwrap() +} + #[derive(Clone, Debug)] #[magnus::wrap(class = "Bevy::Entity")] pub struct BevyEntity(pub Entity); @@ -266,6 +289,7 @@ impl Default for RubyRuntime { let promise = module.define_class("Promise", ruby.class_object())?; promise.define_method("and_then", magnus::method!(then, 0))?; + promise.define_method("await", magnus::method!(await_promise, 0))?; let vec3 = module.define_class("Vec3", ruby.class_object())?; vec3.define_singleton_method("new", function!(BevyVec3::new, 3))?; @@ -392,10 +416,35 @@ impl Runtime for RubyRuntime { ) -> Result { let script = script.0.clone(); self.execute_in_thread(Box::new(move |ruby: &Ruby| { - Self::with_current_entity(ruby, entity, || { - ruby.eval::(&script) - .map_err(>::into) - })?; + let p = Opaque::from(ruby.proc_from_fn(move |ruby, _args, _block| { + Self::with_current_entity(ruby, entity, || { + let mut tmpfile = tempfile::NamedTempFile::new().unwrap(); + tmpfile.write(script.as_bytes()).unwrap(); + unsafe { + let file = rb_sys::rb_str_new_cstr( + CString::new(tmpfile.path().to_str().unwrap()) + .unwrap() + .into_raw(), + ); + rb_load(file, 1); + }; + // ruby.eval::(&script) + // .map_err(>::into) + Ok::<(), ScriptingError>(()) + }) + .unwrap(); + })); + let fiber = ruby + .fiber_new_from_fn(Default::default(), move |ruby, _args, _block| { + let p = ruby.get_inner(p); + + p.call::<_, value::Value>(()).unwrap(); + + Ok(()) + }) + .unwrap(); + fiber.resume::<_, value::Value>(()).unwrap(); + Ok::(RubyScriptData) })) } -- 2.45.2 From 8a3661a77a78d8c2b9678ab38bc878a8dbb83f76 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Fri, 30 May 2025 07:00:00 +0200 Subject: [PATCH 2/5] wip --- assets/examples/ruby/promises.rb | 5 ++++- src/callback.rs | 10 ++++----- src/lib.rs | 3 ++- src/promise.rs | 36 ++++++++++++++++++++++++------- src/runtimes/ruby.rs | 37 ++++++++++++++------------------ src/systems.rs | 6 ++++-- 6 files changed, 59 insertions(+), 38 deletions(-) diff --git a/assets/examples/ruby/promises.rb b/assets/examples/ruby/promises.rb index 9aeacb0..06de435 100644 --- a/assets/examples/ruby/promises.rb +++ b/assets/examples/ruby/promises.rb @@ -1,2 +1,5 @@ -puts get_player_name.await +a = get_player_name +b = a +puts a.await +puts b.await quit diff --git a/src/callback.rs b/src/callback.rs index f19a898..0880eaf 100644 --- a/src/callback.rs +++ b/src/callback.rs @@ -10,18 +10,18 @@ pub struct CallbackSystem { pub(crate) arg_types: Vec, } -pub(crate) struct FunctionCallEvent { +pub(crate) struct FunctionCallEvent { pub(crate) params: Vec, - pub(crate) promise: Promise, + pub(crate) promise: Promise, } -type Calls = Arc>>>; +type Calls = Arc>>>; /// A struct representing a Bevy system that can be called from a script. pub(crate) struct Callback { pub(crate) name: String, pub(crate) system: Arc>>, - pub(crate) calls: Calls, + pub(crate) calls: Calls, } impl Clone for Callback { @@ -37,7 +37,7 @@ impl Clone for Callback { impl CallbackSystem { pub(crate) fn call( &mut self, - call: &FunctionCallEvent, + call: &FunctionCallEvent, world: &mut World, ) -> R::Value { self.system.run(call.params.clone(), world) diff --git a/src/lib.rs b/src/lib.rs index 0b6f097..01b8f09 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -345,7 +345,8 @@ pub trait Runtime: Resource + Default { f: impl Fn( Self::CallContext, Vec, - ) -> Result, ScriptingError> + ) + -> Result, ScriptingError> + Send + Sync + 'static, diff --git a/src/promise.rs b/src/promise.rs index 038cc0c..ce821a3 100644 --- a/src/promise.rs +++ b/src/promise.rs @@ -1,27 +1,31 @@ use std::sync::{Arc, Mutex}; +use magnus::Fiber; + use crate::{Runtime, ScriptingError}; /// A struct that represents a function that will get called when the Promise is resolved. -pub(crate) struct PromiseCallback { +pub(crate) struct PromiseCallback { callback: V, - following_promise: Arc>>, + following_promise: Arc>>, } /// Internal representation of a Promise. -pub(crate) struct PromiseInner { - pub(crate) callbacks: Vec>, +pub(crate) struct PromiseInner { + pub(crate) callbacks: Vec>, #[allow(deprecated)] pub(crate) context: C, + pub(crate) resolved_value: Option, + pub(crate) fibers: Vec, // TODO: should htis be vec or option } /// A struct that represents a Promise. #[derive(Clone)] -pub struct Promise { - pub(crate) inner: Arc>>, +pub struct Promise { + pub(crate) inner: Arc>>, } -impl PromiseInner { +impl PromiseInner { /// Resolve the Promise. This will call all the callbacks that were added to the Promise. fn resolve(&mut self, runtime: &mut R, val: R::Value) -> Result<(), ScriptingError> where @@ -41,7 +45,7 @@ impl PromiseInner { } } -impl Promise { +impl Promise { /// Acquire [Mutex] for writing the promise and resolve it. Call will be forwarded to [PromiseInner::resolve]. pub(crate) fn resolve( &mut self, @@ -52,11 +56,25 @@ impl Promise { R: Runtime, { if let Ok(mut inner) = self.inner.lock() { + inner.resolved_value = Some(val.clone()); inner.resolve(runtime, val)?; + for fiber in inner.fibers.drain(..) { + println!("resume"); + } } Ok(()) } + /// Register a fiber that will be resumed when the [Promise] is resolved. + #[cfg(any(feature = "rhai", feature = "lua", feature = "ruby"))] + pub(crate) fn await_promise(&mut self, fiber: F) { + let mut inner = self + .inner + .lock() + .expect("Failed to lock inner promise mutex"); + inner.fibers.push(fiber); + } + /// Register a callback that will be called when the [Promise] is resolved. #[cfg(any(feature = "rhai", feature = "lua", feature = "ruby"))] pub(crate) fn then(&mut self, callback: V) -> Self { @@ -65,8 +83,10 @@ impl Promise { .lock() .expect("Failed to lock inner promise mutex"); let following_inner = Arc::new(Mutex::new(PromiseInner { + fibers: vec![], callbacks: vec![], context: inner.context.clone(), + resolved_value: None, })); inner.callbacks.push(PromiseCallback { diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 1646d7f..09072e2 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -129,9 +129,9 @@ impl Drop for RubyThread { } } -impl DataTypeFunctions for Promise<(), RubyValue> {} +impl DataTypeFunctions for Promise<(), RubyValue, RubyValue> {} -unsafe impl TypedData for Promise<(), RubyValue> { +unsafe impl TypedData for Promise<(), RubyValue, RubyValue> { fn class(ruby: &Ruby) -> magnus::RClass { static CLASS: Lazy = Lazy::new(|ruby| { let class = ruby @@ -147,12 +147,12 @@ unsafe impl TypedData for Promise<(), RubyValue> { fn data_type() -> &'static magnus::DataType { static DATA_TYPE: DataType = - data_type_builder!(Promise<(), RubyValue>, "Bevy::Promise").build(); + data_type_builder!(Promise<(), RubyValue, RubyValue>, "Bevy::Promise").build(); &DATA_TYPE } } -impl TryConvert for Promise<(), RubyValue> { +impl TryConvert for Promise<(), RubyValue, RubyValue> { fn try_convert(val: magnus::Value) -> Result { let result: Result<&Self, _> = TryConvert::try_convert(val); result.cloned() @@ -160,7 +160,7 @@ impl TryConvert for Promise<(), RubyValue> { } fn then(r_self: magnus::Value) -> magnus::Value { - let promise: &Promise<(), RubyValue> = + let promise: &Promise<(), RubyValue, RubyValue> = TryConvert::try_convert(r_self).expect("Couldn't convert self to Promise"); let ruby = Ruby::get().expect("Failed to get a handle to Ruby API when registering Promise callback"); @@ -179,21 +179,15 @@ fn then(r_self: magnus::Value) -> magnus::Value { } fn await_promise(r_self: magnus::Value) -> magnus::Value { - let promise: &Promise<(), RubyValue> = + let promise: &Promise<(), RubyValue, RubyValue> = TryConvert::try_convert(r_self).expect("Couldn't convert self to Promise"); let ruby = Ruby::get().expect("Failed to get a handle to Ruby API when registering Promise callback"); - let fiber = Opaque::from(ruby.fiber_current()); - promise - .clone() - .then(RubyValue::new( - ruby.proc_from_fn(move |ruby, args, _| { - let fiber = ruby.get_inner(fiber); - fiber.resume::<_, magnus::Value>(args).unwrap(); - }) - .as_value(), - )) - .into_value(); + let fiber = Opaque::from(ruby.fiber_current().as_value()); + if let Some(value) = &promise.inner.try_lock().unwrap().resolved_value { + panic!(); + } + promise.clone().await_promise(RubyValue(fiber)).into_value(); ruby.fiber_yield::<_, magnus::Value>(()).unwrap() } @@ -457,7 +451,7 @@ impl Runtime for RubyRuntime { Self::CallContext, Vec, ) -> Result< - crate::promise::Promise, + crate::promise::Promise, crate::ScriptingError, > + Send + Sync @@ -467,9 +461,10 @@ impl Runtime for RubyRuntime { dyn Fn( (), Vec, - ) - -> Result, crate::ScriptingError> - + Send, + ) -> Result< + crate::promise::Promise<(), RubyValue, RubyValue>, + crate::ScriptingError, + > + Send, >; static RUBY_CALLBACKS: LazyLock>> = LazyLock::new(|| Mutex::new(HashMap::new())); diff --git a/src/systems.rs b/src/systems.rs index 7cbf81b..09940c2 100644 --- a/src/systems.rs +++ b/src/systems.rs @@ -89,6 +89,8 @@ pub(crate) fn init_callbacks(world: &mut World) -> Result<(), Script move |context, params| { let promise = Promise { inner: Arc::new(Mutex::new(PromiseInner { + resolved_value: None, + fibers: vec![], callbacks: vec![], context, })), @@ -100,7 +102,7 @@ pub(crate) fn init_callbacks(world: &mut World) -> Result<(), Script .expect("Failed to lock callback calls mutex"); calls.push(FunctionCallEvent { - promise: promise.clone(), + promise: promise.clone(), // TODO: dont clone? params, }); Ok(promise) @@ -142,7 +144,7 @@ pub(crate) fn process_calls(world: &mut World) -> Result<(), Scripti .lock() .expect("Failed to lock callback calls mutex") .drain(..) - .collect::>>(); + .collect::>>(); for mut call in calls { tracing::trace!("process_calls: calling '{}'", callback.name); let mut system = callback -- 2.45.2 From dfd96746fe94f8d93e1aca6b529f3601210d3aae Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Fri, 30 May 2025 07:00:00 +0200 Subject: [PATCH 3/5] works --- assets/examples/ruby/promises.rb | 3 +++ src/callback.rs | 10 +++++----- src/lib.rs | 5 +++-- src/promise.rs | 31 ++++++++++++++++-------------- src/runtimes/ruby.rs | 33 ++++++++++++++++++++------------ src/systems.rs | 2 +- 6 files changed, 50 insertions(+), 34 deletions(-) diff --git a/assets/examples/ruby/promises.rb b/assets/examples/ruby/promises.rb index 06de435..8698919 100644 --- a/assets/examples/ruby/promises.rb +++ b/assets/examples/ruby/promises.rb @@ -1,5 +1,8 @@ a = get_player_name b = a +puts '0' puts a.await +puts '1' puts b.await +puts '2' quit diff --git a/src/callback.rs b/src/callback.rs index 0880eaf..f19a898 100644 --- a/src/callback.rs +++ b/src/callback.rs @@ -10,18 +10,18 @@ pub struct CallbackSystem { pub(crate) arg_types: Vec, } -pub(crate) struct FunctionCallEvent { +pub(crate) struct FunctionCallEvent { pub(crate) params: Vec, - pub(crate) promise: Promise, + pub(crate) promise: Promise, } -type Calls = Arc>>>; +type Calls = Arc>>>; /// A struct representing a Bevy system that can be called from a script. pub(crate) struct Callback { pub(crate) name: String, pub(crate) system: Arc>>, - pub(crate) calls: Calls, + pub(crate) calls: Calls, } impl Clone for Callback { @@ -37,7 +37,7 @@ impl Clone for Callback { impl CallbackSystem { pub(crate) fn call( &mut self, - call: &FunctionCallEvent, + call: &FunctionCallEvent, world: &mut World, ) -> R::Value { self.system.run(call.params.clone(), world) diff --git a/src/lib.rs b/src/lib.rs index 01b8f09..5ee510c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -345,8 +345,7 @@ pub trait Runtime: Resource + Default { f: impl Fn( Self::CallContext, Vec, - ) - -> Result, ScriptingError> + ) -> Result, ScriptingError> + Send + Sync + 'static, @@ -376,6 +375,8 @@ pub trait Runtime: Resource + Default { fn needs_rdynamic_linking() -> bool { false } + + fn resume(&self, fiber: &Self::Value, value: &Self::Value); } pub trait FuncArgs<'a, V, R: Runtime> { diff --git a/src/promise.rs b/src/promise.rs index ce821a3..3425521 100644 --- a/src/promise.rs +++ b/src/promise.rs @@ -1,31 +1,29 @@ use std::sync::{Arc, Mutex}; -use magnus::Fiber; - use crate::{Runtime, ScriptingError}; /// A struct that represents a function that will get called when the Promise is resolved. -pub(crate) struct PromiseCallback { +pub(crate) struct PromiseCallback { callback: V, - following_promise: Arc>>, + following_promise: Arc>>, } /// Internal representation of a Promise. -pub(crate) struct PromiseInner { - pub(crate) callbacks: Vec>, +pub(crate) struct PromiseInner { + pub(crate) callbacks: Vec>, #[allow(deprecated)] pub(crate) context: C, pub(crate) resolved_value: Option, - pub(crate) fibers: Vec, // TODO: should htis be vec or option + pub(crate) fibers: Vec, // TODO: should htis be vec or option } /// A struct that represents a Promise. #[derive(Clone)] -pub struct Promise { - pub(crate) inner: Arc>>, +pub struct Promise { + pub(crate) inner: Arc>>, } -impl PromiseInner { +impl PromiseInner { /// Resolve the Promise. This will call all the callbacks that were added to the Promise. fn resolve(&mut self, runtime: &mut R, val: R::Value) -> Result<(), ScriptingError> where @@ -45,7 +43,7 @@ impl PromiseInner { } } -impl Promise { +impl Promise { /// Acquire [Mutex] for writing the promise and resolve it. Call will be forwarded to [PromiseInner::resolve]. pub(crate) fn resolve( &mut self, @@ -55,19 +53,24 @@ impl Promise, { + let mut fibers: Vec = vec![]; if let Ok(mut inner) = self.inner.lock() { inner.resolved_value = Some(val.clone()); - inner.resolve(runtime, val)?; + inner.resolve(runtime, val.clone())?; + for fiber in inner.fibers.drain(..) { - println!("resume"); + fibers.push(fiber); } } + for fiber in fibers { + runtime.resume(&fiber, &val.clone()); + } Ok(()) } /// Register a fiber that will be resumed when the [Promise] is resolved. #[cfg(any(feature = "rhai", feature = "lua", feature = "ruby"))] - pub(crate) fn await_promise(&mut self, fiber: F) { + pub(crate) fn await_promise(&mut self, fiber: V) { let mut inner = self .inner .lock() diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 09072e2..1272684 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -7,6 +7,7 @@ use std::{ }; use ::magnus::{ + Fiber, typed_data::Inspect, value::{self, Opaque}, }; @@ -129,9 +130,9 @@ impl Drop for RubyThread { } } -impl DataTypeFunctions for Promise<(), RubyValue, RubyValue> {} +impl DataTypeFunctions for Promise<(), RubyValue> {} -unsafe impl TypedData for Promise<(), RubyValue, RubyValue> { +unsafe impl TypedData for Promise<(), RubyValue> { fn class(ruby: &Ruby) -> magnus::RClass { static CLASS: Lazy = Lazy::new(|ruby| { let class = ruby @@ -147,12 +148,12 @@ unsafe impl TypedData for Promise<(), RubyValue, RubyValue> { fn data_type() -> &'static magnus::DataType { static DATA_TYPE: DataType = - data_type_builder!(Promise<(), RubyValue, RubyValue>, "Bevy::Promise").build(); + data_type_builder!(Promise<(), RubyValue>, "Bevy::Promise").build(); &DATA_TYPE } } -impl TryConvert for Promise<(), RubyValue, RubyValue> { +impl TryConvert for Promise<(), RubyValue> { fn try_convert(val: magnus::Value) -> Result { let result: Result<&Self, _> = TryConvert::try_convert(val); result.cloned() @@ -160,7 +161,7 @@ impl TryConvert for Promise<(), RubyValue, RubyValue> { } fn then(r_self: magnus::Value) -> magnus::Value { - let promise: &Promise<(), RubyValue, RubyValue> = + let promise: &Promise<(), RubyValue> = TryConvert::try_convert(r_self).expect("Couldn't convert self to Promise"); let ruby = Ruby::get().expect("Failed to get a handle to Ruby API when registering Promise callback"); @@ -179,13 +180,13 @@ fn then(r_self: magnus::Value) -> magnus::Value { } fn await_promise(r_self: magnus::Value) -> magnus::Value { - let promise: &Promise<(), RubyValue, RubyValue> = + let promise: &Promise<(), RubyValue> = TryConvert::try_convert(r_self).expect("Couldn't convert self to Promise"); let ruby = Ruby::get().expect("Failed to get a handle to Ruby API when registering Promise callback"); let fiber = Opaque::from(ruby.fiber_current().as_value()); if let Some(value) = &promise.inner.try_lock().unwrap().resolved_value { - panic!(); + return ruby.get_inner(value.0); } promise.clone().await_promise(RubyValue(fiber)).into_value(); ruby.fiber_yield::<_, magnus::Value>(()).unwrap() @@ -451,7 +452,7 @@ impl Runtime for RubyRuntime { Self::CallContext, Vec, ) -> Result< - crate::promise::Promise, + crate::promise::Promise, crate::ScriptingError, > + Send + Sync @@ -461,10 +462,9 @@ impl Runtime for RubyRuntime { dyn Fn( (), Vec, - ) -> Result< - crate::promise::Promise<(), RubyValue, RubyValue>, - crate::ScriptingError, - > + Send, + ) + -> Result, crate::ScriptingError> + + Send, >; static RUBY_CALLBACKS: LazyLock>> = LazyLock::new(|| Mutex::new(HashMap::new())); @@ -553,6 +553,15 @@ impl Runtime for RubyRuntime { fn needs_rdynamic_linking() -> bool { true } + + fn resume(&self, fiber: &Self::Value, value: &Self::Value) { + let fiber = fiber.clone(); + let value = value.clone(); + self.execute_in_thread(move |ruby| { + let fiber: Fiber = TryConvert::try_convert(ruby.get_inner(fiber.0)).unwrap(); + fiber.resume::<_, magnus::Value>((value.0,)); + }); + } } pub mod magnus { diff --git a/src/systems.rs b/src/systems.rs index 09940c2..db2c3a5 100644 --- a/src/systems.rs +++ b/src/systems.rs @@ -144,7 +144,7 @@ pub(crate) fn process_calls(world: &mut World) -> Result<(), Scripti .lock() .expect("Failed to lock callback calls mutex") .drain(..) - .collect::>>(); + .collect::>>(); for mut call in calls { tracing::trace!("process_calls: calling '{}'", callback.name); let mut system = callback -- 2.45.2 From bd0c5e184e750076bfcfe5b1369edd090358c6e0 Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Fri, 30 May 2025 07:00:00 +0200 Subject: [PATCH 4/5] fix --- assets/examples/ruby/promises.rb | 5 +++++ src/runtimes/ruby.rs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/assets/examples/ruby/promises.rb b/assets/examples/ruby/promises.rb index 8698919..a3910af 100644 --- a/assets/examples/ruby/promises.rb +++ b/assets/examples/ruby/promises.rb @@ -3,6 +3,11 @@ b = a puts '0' puts a.await puts '1' +u = get_player_name puts b.await puts '2' +z = get_player_name +puts z +puts z.await +puts "end" quit diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 1272684..075e036 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -421,7 +421,7 @@ impl Runtime for RubyRuntime { .unwrap() .into_raw(), ); - rb_load(file, 1); + rb_load(file, 0); }; // ruby.eval::(&script) // .map_err(>::into) -- 2.45.2 From d572faf95a9e0bba1d3d471588fd50660ea6fd1d Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Fri, 30 May 2025 18:45:48 +0200 Subject: [PATCH 5/5] async --- assets/examples/ruby/promises.rb | 32 ++++++++++++++++++++------------ src/runtimes/ruby.rs | 15 +++++++++++++++ 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/assets/examples/ruby/promises.rb b/assets/examples/ruby/promises.rb index a3910af..54560e3 100644 --- a/assets/examples/ruby/promises.rb +++ b/assets/examples/ruby/promises.rb @@ -1,13 +1,21 @@ -a = get_player_name -b = a -puts '0' -puts a.await -puts '1' -u = get_player_name -puts b.await -puts '2' -z = get_player_name -puts z -puts z.await -puts "end" +def async_fun + async do + a = get_player_name + b = a + puts '0' + puts a.await + puts '1' + u = get_player_name + puts b.await + puts '2' + z = get_player_name + puts z + puts z.await + puts "end" + end +end + +async_fun.await +puts "after await" + quit diff --git a/src/runtimes/ruby.rs b/src/runtimes/ruby.rs index 075e036..7477cb0 100644 --- a/src/runtimes/ruby.rs +++ b/src/runtimes/ruby.rs @@ -213,6 +213,19 @@ impl TryConvert for BevyEntity { #[magnus::wrap(class = "Bevy::Vec3")] pub struct BevyVec3(pub Vec3); +pub fn async_function() { + let ruby = Ruby::get().unwrap(); + let fiber = ruby + .fiber_new_from_fn(Default::default(), move |ruby, _args, _block| { + let p = ruby.block_proc().unwrap(); + p.call::<_, value::Value>(()).unwrap(); + + Ok(()) + }) + .unwrap(); + fiber.resume::<_, magnus::Value>(()).unwrap(); +} + impl BevyVec3 { pub fn new(x: f32, y: f32, z: f32) -> Self { Self(Vec3::new(x, y, z)) @@ -291,6 +304,8 @@ impl Default for RubyRuntime { vec3.define_method("x", method!(BevyVec3::x, 0))?; vec3.define_method("y", method!(BevyVec3::y, 0))?; vec3.define_method("z", method!(BevyVec3::z, 0))?; + + ruby.define_global_function("async", function!(async_function, 0)); Ok::<(), ScriptingError>(()) })) .expect("Failed to define builtin types"); -- 2.45.2