From dfd96746fe94f8d93e1aca6b529f3601210d3aae Mon Sep 17 00:00:00 2001 From: Jaroslaw Konik Date: Fri, 30 May 2025 07:00:00 +0200 Subject: [PATCH] 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