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