fibers #2
7 changed files with 137 additions and 19 deletions
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,21 @@
|
|||
get_player_name.and_then do |name|
|
||||
puts name
|
||||
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
|
||||
|
|
|
|||
|
|
@ -9,15 +9,19 @@ fn main() {
|
|||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_scripting::<RubyRuntime>(|builder| {
|
||||
builder.add_function(
|
||||
String::from("get_player_name"),
|
||||
|player_names: Query<&Name, With<Player>>| {
|
||||
player_names
|
||||
.single()
|
||||
.expect("Missing player_names")
|
||||
.to_string()
|
||||
},
|
||||
);
|
||||
builder
|
||||
.add_function(
|
||||
String::from("get_player_name"),
|
||||
|player_names: Query<&Name, With<Player>>| {
|
||||
player_names
|
||||
.single()
|
||||
.expect("Missing player_names")
|
||||
.to_string()
|
||||
},
|
||||
)
|
||||
.add_function(String::from("quit"), |mut exit: EventWriter<AppExit>| {
|
||||
exit.write(AppExit::Success);
|
||||
});
|
||||
})
|
||||
.add_systems(Startup, startup)
|
||||
.run();
|
||||
|
|
|
|||
|
|
@ -375,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> {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ pub(crate) struct PromiseInner<C: Send, V: Send> {
|
|||
pub(crate) callbacks: Vec<PromiseCallback<C, V>>,
|
||||
#[allow(deprecated)]
|
||||
pub(crate) context: C,
|
||||
pub(crate) resolved_value: Option<V>,
|
||||
pub(crate) fibers: Vec<V>, // TODO: should htis be vec or option
|
||||
}
|
||||
|
||||
/// A struct that represents a Promise.
|
||||
|
|
@ -51,12 +53,31 @@ impl<C: Clone + Send + 'static, V: Send + Clone> Promise<C, V> {
|
|||
where
|
||||
R: Runtime<Value = V, CallContext = C>,
|
||||
{
|
||||
let mut fibers: Vec<V> = vec![];
|
||||
if let Ok(mut inner) = self.inner.lock() {
|
||||
inner.resolve(runtime, val)?;
|
||||
inner.resolved_value = Some(val.clone());
|
||||
inner.resolve(runtime, val.clone())?;
|
||||
|
||||
for fiber in inner.fibers.drain(..) {
|
||||
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: V) {
|
||||
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 +86,10 @@ impl<C: Clone + Send + 'static, V: Send + Clone> Promise<C, V> {
|
|||
.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 {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,16 @@
|
|||
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::{
|
||||
Fiber,
|
||||
typed_data::Inspect,
|
||||
value::{self, Opaque},
|
||||
};
|
||||
use bevy::{
|
||||
asset::Asset,
|
||||
ecs::{component::Component, entity::Entity, resource::Resource, schedule::ScheduleLabel},
|
||||
|
|
@ -19,7 +24,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 +179,19 @@ 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().as_value());
|
||||
if let Some(value) = &promise.inner.try_lock().unwrap().resolved_value {
|
||||
return ruby.get_inner(value.0);
|
||||
}
|
||||
promise.clone().await_promise(RubyValue(fiber)).into_value();
|
||||
ruby.fiber_yield::<_, magnus::Value>(()).unwrap()
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[magnus::wrap(class = "Bevy::Entity")]
|
||||
pub struct BevyEntity(pub Entity);
|
||||
|
|
@ -195,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))
|
||||
|
|
@ -266,12 +297,15 @@ 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))?;
|
||||
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");
|
||||
|
|
@ -392,10 +426,35 @@ impl Runtime for RubyRuntime {
|
|||
) -> Result<Self::ScriptData, crate::ScriptingError> {
|
||||
let script = script.0.clone();
|
||||
self.execute_in_thread(Box::new(move |ruby: &Ruby| {
|
||||
Self::with_current_entity(ruby, entity, || {
|
||||
ruby.eval::<magnus::value::Value>(&script)
|
||||
.map_err(<magnus::Error as Into<ScriptingError>>::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, 0);
|
||||
};
|
||||
// ruby.eval::<magnus::value::Value>(&script)
|
||||
// .map_err(<magnus::Error as Into<ScriptingError>>::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::<Self::ScriptData, ScriptingError>(RubyScriptData)
|
||||
}))
|
||||
}
|
||||
|
|
@ -509,6 +568,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 {
|
||||
|
|
|
|||
|
|
@ -89,6 +89,8 @@ pub(crate) fn init_callbacks<R: Runtime>(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<R: Runtime>(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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue