works
Some checks failed
Book / test (pull_request) Has been cancelled
Rust / build (pull_request) Has been cancelled

This commit is contained in:
Jaroslaw Konik 2025-05-30 07:00:00 +02:00
parent 8a3661a77a
commit dfd96746fe
6 changed files with 50 additions and 34 deletions

View file

@ -1,5 +1,8 @@
a = get_player_name a = get_player_name
b = a b = a
puts '0'
puts a.await puts a.await
puts '1'
puts b.await puts b.await
puts '2'
quit quit

View file

@ -10,18 +10,18 @@ pub struct CallbackSystem<R: Runtime> {
pub(crate) arg_types: Vec<TypeId>, pub(crate) arg_types: Vec<TypeId>,
} }
pub(crate) struct FunctionCallEvent<C: Send, V: Send, F: Send> { pub(crate) struct FunctionCallEvent<C: Send, V: Send> {
pub(crate) params: Vec<V>, pub(crate) params: Vec<V>,
pub(crate) promise: Promise<C, V, F>, pub(crate) promise: Promise<C, V>,
} }
type Calls<C, V, F> = Arc<Mutex<Vec<FunctionCallEvent<C, V, F>>>>; type Calls<C, V> = Arc<Mutex<Vec<FunctionCallEvent<C, V>>>>;
/// A struct representing a Bevy system that can be called from a script. /// A struct representing a Bevy system that can be called from a script.
pub(crate) struct Callback<R: Runtime> { pub(crate) struct Callback<R: Runtime> {
pub(crate) name: String, pub(crate) name: String,
pub(crate) system: Arc<Mutex<CallbackSystem<R>>>, pub(crate) system: Arc<Mutex<CallbackSystem<R>>>,
pub(crate) calls: Calls<R::CallContext, R::Value, R::Value>, pub(crate) calls: Calls<R::CallContext, R::Value>,
} }
impl<R: Runtime> Clone for Callback<R> { impl<R: Runtime> Clone for Callback<R> {
@ -37,7 +37,7 @@ impl<R: Runtime> Clone for Callback<R> {
impl<R: Runtime> CallbackSystem<R> { impl<R: Runtime> CallbackSystem<R> {
pub(crate) fn call( pub(crate) fn call(
&mut self, &mut self,
call: &FunctionCallEvent<R::CallContext, R::Value, R::Value>, call: &FunctionCallEvent<R::CallContext, R::Value>,
world: &mut World, world: &mut World,
) -> R::Value { ) -> R::Value {
self.system.run(call.params.clone(), world) self.system.run(call.params.clone(), world)

View file

@ -345,8 +345,7 @@ pub trait Runtime: Resource + Default {
f: impl Fn( f: impl Fn(
Self::CallContext, Self::CallContext,
Vec<Self::Value>, Vec<Self::Value>,
) ) -> Result<Promise<Self::CallContext, Self::Value>, ScriptingError>
-> Result<Promise<Self::CallContext, Self::Value, Self::Value>, ScriptingError>
+ Send + Send
+ Sync + Sync
+ 'static, + 'static,
@ -376,6 +375,8 @@ pub trait Runtime: Resource + Default {
fn needs_rdynamic_linking() -> bool { fn needs_rdynamic_linking() -> bool {
false false
} }
fn resume(&self, fiber: &Self::Value, value: &Self::Value);
} }
pub trait FuncArgs<'a, V, R: Runtime> { pub trait FuncArgs<'a, V, R: Runtime> {

View file

@ -1,31 +1,29 @@
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use magnus::Fiber;
use crate::{Runtime, ScriptingError}; use crate::{Runtime, ScriptingError};
/// A struct that represents a function that will get called when the Promise is resolved. /// A struct that represents a function that will get called when the Promise is resolved.
pub(crate) struct PromiseCallback<C: Send, V: Send, F: Send> { pub(crate) struct PromiseCallback<C: Send, V: Send> {
callback: V, callback: V,
following_promise: Arc<Mutex<PromiseInner<C, V, F>>>, following_promise: Arc<Mutex<PromiseInner<C, V>>>,
} }
/// Internal representation of a Promise. /// Internal representation of a Promise.
pub(crate) struct PromiseInner<C: Send, V: Send, F: Send> { pub(crate) struct PromiseInner<C: Send, V: Send> {
pub(crate) callbacks: Vec<PromiseCallback<C, V, F>>, pub(crate) callbacks: Vec<PromiseCallback<C, V>>,
#[allow(deprecated)] #[allow(deprecated)]
pub(crate) context: C, pub(crate) context: C,
pub(crate) resolved_value: Option<V>, pub(crate) resolved_value: Option<V>,
pub(crate) fibers: Vec<F>, // TODO: should htis be vec or option pub(crate) fibers: Vec<V>, // TODO: should htis be vec or option
} }
/// A struct that represents a Promise. /// A struct that represents a Promise.
#[derive(Clone)] #[derive(Clone)]
pub struct Promise<C: Send, V: Send, F: Send> { pub struct Promise<C: Send, V: Send> {
pub(crate) inner: Arc<Mutex<PromiseInner<C, V, F>>>, pub(crate) inner: Arc<Mutex<PromiseInner<C, V>>>,
} }
impl<C: Send, V: Send + Clone, F: Send + Clone> PromiseInner<C, V, F> { impl<C: Send, V: Send + Clone> PromiseInner<C, V> {
/// Resolve the Promise. This will call all the callbacks that were added to the Promise. /// Resolve the Promise. This will call all the callbacks that were added to the Promise.
fn resolve<R>(&mut self, runtime: &mut R, val: R::Value) -> Result<(), ScriptingError> fn resolve<R>(&mut self, runtime: &mut R, val: R::Value) -> Result<(), ScriptingError>
where where
@ -45,7 +43,7 @@ impl<C: Send, V: Send + Clone, F: Send + Clone> PromiseInner<C, V, F> {
} }
} }
impl<C: Clone + Send + 'static, V: Send + Clone, F: Send + Clone> Promise<C, V, F> { impl<C: Clone + Send + 'static, V: Send + Clone> Promise<C, V> {
/// Acquire [Mutex] for writing the promise and resolve it. Call will be forwarded to [PromiseInner::resolve]. /// Acquire [Mutex] for writing the promise and resolve it. Call will be forwarded to [PromiseInner::resolve].
pub(crate) fn resolve<R>( pub(crate) fn resolve<R>(
&mut self, &mut self,
@ -55,19 +53,24 @@ impl<C: Clone + Send + 'static, V: Send + Clone, F: Send + Clone> Promise<C, V,
where where
R: Runtime<Value = V, CallContext = C>, R: Runtime<Value = V, CallContext = C>,
{ {
let mut fibers: Vec<V> = vec![];
if let Ok(mut inner) = self.inner.lock() { if let Ok(mut inner) = self.inner.lock() {
inner.resolved_value = Some(val.clone()); inner.resolved_value = Some(val.clone());
inner.resolve(runtime, val)?; inner.resolve(runtime, val.clone())?;
for fiber in inner.fibers.drain(..) { for fiber in inner.fibers.drain(..) {
println!("resume"); fibers.push(fiber);
} }
} }
for fiber in fibers {
runtime.resume(&fiber, &val.clone());
}
Ok(()) Ok(())
} }
/// Register a fiber that will be resumed when the [Promise] is resolved. /// Register a fiber that will be resumed when the [Promise] is resolved.
#[cfg(any(feature = "rhai", feature = "lua", feature = "ruby"))] #[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 let mut inner = self
.inner .inner
.lock() .lock()

View file

@ -7,6 +7,7 @@ use std::{
}; };
use ::magnus::{ use ::magnus::{
Fiber,
typed_data::Inspect, typed_data::Inspect,
value::{self, Opaque}, 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 { fn class(ruby: &Ruby) -> magnus::RClass {
static CLASS: Lazy<RClass> = Lazy::new(|ruby| { static CLASS: Lazy<RClass> = Lazy::new(|ruby| {
let class = ruby let class = ruby
@ -147,12 +148,12 @@ unsafe impl TypedData for Promise<(), RubyValue, RubyValue> {
fn data_type() -> &'static magnus::DataType { fn data_type() -> &'static magnus::DataType {
static DATA_TYPE: DataType = static DATA_TYPE: DataType =
data_type_builder!(Promise<(), RubyValue, RubyValue>, "Bevy::Promise").build(); data_type_builder!(Promise<(), RubyValue>, "Bevy::Promise").build();
&DATA_TYPE &DATA_TYPE
} }
} }
impl TryConvert for Promise<(), RubyValue, RubyValue> { impl TryConvert for Promise<(), RubyValue> {
fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> { fn try_convert(val: magnus::Value) -> Result<Self, magnus::Error> {
let result: Result<&Self, _> = TryConvert::try_convert(val); let result: Result<&Self, _> = TryConvert::try_convert(val);
result.cloned() result.cloned()
@ -160,7 +161,7 @@ impl TryConvert for Promise<(), RubyValue, RubyValue> {
} }
fn then(r_self: magnus::Value) -> magnus::Value { 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"); TryConvert::try_convert(r_self).expect("Couldn't convert self to Promise");
let ruby = let ruby =
Ruby::get().expect("Failed to get a handle to Ruby API when registering Promise callback"); 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 { 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"); TryConvert::try_convert(r_self).expect("Couldn't convert self to Promise");
let ruby = let ruby =
Ruby::get().expect("Failed to get a handle to Ruby API when registering Promise callback"); Ruby::get().expect("Failed to get a handle to Ruby API when registering Promise callback");
let fiber = Opaque::from(ruby.fiber_current().as_value()); let fiber = Opaque::from(ruby.fiber_current().as_value());
if let Some(value) = &promise.inner.try_lock().unwrap().resolved_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(); promise.clone().await_promise(RubyValue(fiber)).into_value();
ruby.fiber_yield::<_, magnus::Value>(()).unwrap() ruby.fiber_yield::<_, magnus::Value>(()).unwrap()
@ -451,7 +452,7 @@ impl Runtime for RubyRuntime {
Self::CallContext, Self::CallContext,
Vec<Self::Value>, Vec<Self::Value>,
) -> Result< ) -> Result<
crate::promise::Promise<Self::CallContext, Self::Value, Self::Value>, crate::promise::Promise<Self::CallContext, Self::Value>,
crate::ScriptingError, crate::ScriptingError,
> + Send > + Send
+ Sync + Sync
@ -461,10 +462,9 @@ impl Runtime for RubyRuntime {
dyn Fn( dyn Fn(
(), (),
Vec<RubyValue>, Vec<RubyValue>,
) -> Result< )
crate::promise::Promise<(), RubyValue, RubyValue>, -> Result<crate::promise::Promise<(), RubyValue>, crate::ScriptingError>
crate::ScriptingError, + Send,
> + Send,
>; >;
static RUBY_CALLBACKS: LazyLock<Mutex<HashMap<String, CallbackClosure>>> = static RUBY_CALLBACKS: LazyLock<Mutex<HashMap<String, CallbackClosure>>> =
LazyLock::new(|| Mutex::new(HashMap::new())); LazyLock::new(|| Mutex::new(HashMap::new()));
@ -553,6 +553,15 @@ impl Runtime for RubyRuntime {
fn needs_rdynamic_linking() -> bool { fn needs_rdynamic_linking() -> bool {
true 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 { pub mod magnus {

View file

@ -144,7 +144,7 @@ pub(crate) fn process_calls<R: Runtime>(world: &mut World) -> Result<(), Scripti
.lock() .lock()
.expect("Failed to lock callback calls mutex") .expect("Failed to lock callback calls mutex")
.drain(..) .drain(..)
.collect::<Vec<FunctionCallEvent<R::CallContext, R::Value, R::Value>>>(); .collect::<Vec<FunctionCallEvent<R::CallContext, R::Value>>>();
for mut call in calls { for mut call in calls {
tracing::trace!("process_calls: calling '{}'", callback.name); tracing::trace!("process_calls: calling '{}'", callback.name);
let mut system = callback let mut system = callback