mirror of
https://github.com/lune-org/mlua-luau-scheduler.git
synced 2025-04-10 21:40:55 +01:00
Organize lua functions created by runtime a bit better
This commit is contained in:
parent
b03a0101e2
commit
7b2e15c676
9 changed files with 122 additions and 94 deletions
|
@ -74,8 +74,8 @@ let sleepThread = lua.load("sleep(0.1)");
|
||||||
let fileThread = lua.load("readFile(\"Cargo.toml\")");
|
let fileThread = lua.load("readFile(\"Cargo.toml\")");
|
||||||
|
|
||||||
// ... spawn them both onto the runtime ...
|
// ... spawn them both onto the runtime ...
|
||||||
rt.spawn_thread(sleepThread, ());
|
rt.push_thread_front(sleepThread, ());
|
||||||
rt.spawn_thread(fileThread, ());
|
rt.push_thread_front(fileThread, ());
|
||||||
|
|
||||||
// ... and run until they finish
|
// ... and run until they finish
|
||||||
block_on(rt.run());
|
block_on(rt.run());
|
||||||
|
|
|
@ -26,7 +26,7 @@ pub fn main() -> LuaResult<()> {
|
||||||
// Load the main script into a runtime
|
// Load the main script into a runtime
|
||||||
let rt = Runtime::new(&lua);
|
let rt = Runtime::new(&lua);
|
||||||
let main = lua.load(MAIN_SCRIPT);
|
let main = lua.load(MAIN_SCRIPT);
|
||||||
rt.spawn_thread(main, ())?;
|
rt.push_thread_front(main, ())?;
|
||||||
|
|
||||||
// Run until completion
|
// Run until completion
|
||||||
block_on(rt.run());
|
block_on(rt.run());
|
||||||
|
|
|
@ -19,7 +19,7 @@ pub fn main() -> LuaResult<()> {
|
||||||
"readFile",
|
"readFile",
|
||||||
lua.create_async_function(|lua, path: String| async move {
|
lua.create_async_function(|lua, path: String| async move {
|
||||||
// Spawn background task that does not take up resources on the Lua thread
|
// Spawn background task that does not take up resources on the Lua thread
|
||||||
let task = lua.spawn_future(async move {
|
let task = lua.spawn(async move {
|
||||||
match read_to_string(path).await {
|
match read_to_string(path).await {
|
||||||
Ok(s) => Ok(Some(s)),
|
Ok(s) => Ok(Some(s)),
|
||||||
Err(e) if e.kind() == ErrorKind::NotFound => Ok(None),
|
Err(e) if e.kind() == ErrorKind::NotFound => Ok(None),
|
||||||
|
@ -33,7 +33,7 @@ pub fn main() -> LuaResult<()> {
|
||||||
// Load the main script into a runtime
|
// Load the main script into a runtime
|
||||||
let rt = Runtime::new(&lua);
|
let rt = Runtime::new(&lua);
|
||||||
let main = lua.load(MAIN_SCRIPT);
|
let main = lua.load(MAIN_SCRIPT);
|
||||||
rt.spawn_thread(main, ())?;
|
rt.push_thread_front(main, ())?;
|
||||||
|
|
||||||
// Run until completion
|
// Run until completion
|
||||||
block_on(rt.run());
|
block_on(rt.run());
|
||||||
|
|
|
@ -26,7 +26,7 @@ pub fn main() -> LuaResult<()> {
|
||||||
|
|
||||||
// Load the main script into the runtime, and keep track of the thread we spawn
|
// Load the main script into the runtime, and keep track of the thread we spawn
|
||||||
let main = lua.load(MAIN_SCRIPT);
|
let main = lua.load(MAIN_SCRIPT);
|
||||||
let handle = rt.spawn_thread(main, ())?;
|
let handle = rt.push_thread_front(main, ())?;
|
||||||
|
|
||||||
// Run until completion
|
// Run until completion
|
||||||
block_on(rt.run());
|
block_on(rt.run());
|
||||||
|
|
|
@ -18,7 +18,8 @@ pub fn main() -> LuaResult<()> {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
let rt = Runtime::new(&lua);
|
let rt = Runtime::new(&lua);
|
||||||
|
|
||||||
lua.globals().set("spawn", rt.create_spawn_function()?)?;
|
let rt_fns = rt.create_functions()?;
|
||||||
|
lua.globals().set("spawn", rt_fns.spawn)?;
|
||||||
lua.globals().set(
|
lua.globals().set(
|
||||||
"sleep",
|
"sleep",
|
||||||
lua.create_async_function(|_, ()| async move {
|
lua.create_async_function(|_, ()| async move {
|
||||||
|
@ -31,7 +32,7 @@ pub fn main() -> LuaResult<()> {
|
||||||
|
|
||||||
// Load the main script into the runtime
|
// Load the main script into the runtime
|
||||||
let main = lua.load(MAIN_SCRIPT);
|
let main = lua.load(MAIN_SCRIPT);
|
||||||
rt.spawn_thread(main, ())?;
|
rt.push_thread_front(main, ())?;
|
||||||
|
|
||||||
// Run until completion
|
// Run until completion
|
||||||
block_on(rt.run());
|
block_on(rt.run());
|
||||||
|
|
|
@ -17,8 +17,9 @@ pub fn main() -> LuaResult<()> {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
let rt = Runtime::new(&lua);
|
let rt = Runtime::new(&lua);
|
||||||
|
|
||||||
lua.globals().set("spawn", rt.create_spawn_function()?)?;
|
let rt_fns = rt.create_functions()?;
|
||||||
lua.globals().set("defer", rt.create_defer_function()?)?;
|
lua.globals().set("spawn", rt_fns.spawn)?;
|
||||||
|
lua.globals().set("defer", rt_fns.defer)?;
|
||||||
lua.globals().set(
|
lua.globals().set(
|
||||||
"sleep",
|
"sleep",
|
||||||
lua.create_async_function(|_, duration: Option<f64>| async move {
|
lua.create_async_function(|_, duration: Option<f64>| async move {
|
||||||
|
@ -31,7 +32,7 @@ pub fn main() -> LuaResult<()> {
|
||||||
|
|
||||||
// Load the main script into the runtime, and keep track of the thread we spawn
|
// Load the main script into the runtime, and keep track of the thread we spawn
|
||||||
let main = lua.load(MAIN_SCRIPT);
|
let main = lua.load(MAIN_SCRIPT);
|
||||||
let handle = rt.spawn_thread(main, ())?;
|
let handle = rt.push_thread_front(main, ())?;
|
||||||
|
|
||||||
// Run until completion
|
// Run until completion
|
||||||
block_on(rt.run());
|
block_on(rt.run());
|
||||||
|
|
|
@ -7,6 +7,6 @@ mod traits;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
pub use handle::Handle;
|
pub use handle::Handle;
|
||||||
pub use runtime::Runtime;
|
pub use runtime::{Functions, Runtime};
|
||||||
pub use status::Status;
|
pub use status::Status;
|
||||||
pub use traits::{IntoLuaThread, LuaRuntimeExt};
|
pub use traits::{IntoLuaThread, LuaRuntimeExt};
|
||||||
|
|
158
lib/runtime.rs
158
lib/runtime.rs
|
@ -92,6 +92,17 @@ impl<'lua> Runtime<'lua> {
|
||||||
self.error_callback.clear();
|
self.error_callback.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Creates a collection of lua functions that may be called to interact with the runtime.
|
||||||
|
|
||||||
|
# Errors
|
||||||
|
|
||||||
|
Errors when out of memory.
|
||||||
|
*/
|
||||||
|
pub fn create_functions(&self) -> LuaResult<Functions> {
|
||||||
|
Functions::new(self)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Spawns a chunk / function / thread onto the runtime queue.
|
Spawns a chunk / function / thread onto the runtime queue.
|
||||||
|
|
||||||
|
@ -107,7 +118,7 @@ impl<'lua> Runtime<'lua> {
|
||||||
|
|
||||||
Errors when out of memory.
|
Errors when out of memory.
|
||||||
*/
|
*/
|
||||||
pub fn spawn_thread(
|
pub fn push_thread_front(
|
||||||
&self,
|
&self,
|
||||||
thread: impl IntoLuaThread<'lua>,
|
thread: impl IntoLuaThread<'lua>,
|
||||||
args: impl IntoLuaMulti<'lua>,
|
args: impl IntoLuaMulti<'lua>,
|
||||||
|
@ -134,7 +145,7 @@ impl<'lua> Runtime<'lua> {
|
||||||
|
|
||||||
Errors when out of memory.
|
Errors when out of memory.
|
||||||
*/
|
*/
|
||||||
pub fn defer_thread(
|
pub fn push_thread_back(
|
||||||
&self,
|
&self,
|
||||||
thread: impl IntoLuaThread<'lua>,
|
thread: impl IntoLuaThread<'lua>,
|
||||||
args: impl IntoLuaMulti<'lua>,
|
args: impl IntoLuaMulti<'lua>,
|
||||||
|
@ -144,67 +155,6 @@ impl<'lua> Runtime<'lua> {
|
||||||
.push_item_with_handle(self.lua, thread, args)
|
.push_item_with_handle(self.lua, thread, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
Creates a Lua function that can be used to spawn threads / functions onto the runtime queue.
|
|
||||||
|
|
||||||
The function takes a thread or function as the first argument, and any variadic arguments as the rest.
|
|
||||||
|
|
||||||
# Errors
|
|
||||||
|
|
||||||
Errors when out of memory.
|
|
||||||
*/
|
|
||||||
pub fn create_spawn_function(&self) -> LuaResult<LuaFunction<'lua>> {
|
|
||||||
let error_callback = self.error_callback.clone();
|
|
||||||
let spawn_queue = self.queue_spawn.clone();
|
|
||||||
self.lua.create_function(
|
|
||||||
move |lua, (tof, args): (LuaThreadOrFunction, LuaMultiValue)| {
|
|
||||||
let thread = tof.into_thread(lua)?;
|
|
||||||
if thread.status() == LuaThreadStatus::Resumable {
|
|
||||||
// NOTE: We need to resume the thread once instantly for correct behavior,
|
|
||||||
// and only if we get the pending value back we can spawn to async executor
|
|
||||||
match thread.resume::<_, LuaValue>(args.clone()) {
|
|
||||||
Ok(v) => {
|
|
||||||
if v.as_light_userdata()
|
|
||||||
.map(|l| l == Lua::poll_pending())
|
|
||||||
.unwrap_or_default()
|
|
||||||
{
|
|
||||||
spawn_queue.push_item(lua, &thread, args)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
error_callback.call(&e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Ok(thread)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Creates a Lua function that can be used to defer threads / functions onto the runtime queue.
|
|
||||||
|
|
||||||
The function takes a thread or function as the first argument, and any variadic arguments as the rest.
|
|
||||||
|
|
||||||
Deferred threads are guaranteed to run after all spawned threads either yield or complete.
|
|
||||||
|
|
||||||
# Errors
|
|
||||||
|
|
||||||
Errors when out of memory.
|
|
||||||
*/
|
|
||||||
pub fn create_defer_function(&self) -> LuaResult<LuaFunction<'lua>> {
|
|
||||||
let defer_queue = self.queue_defer.clone();
|
|
||||||
self.lua.create_function(
|
|
||||||
move |lua, (tof, args): (LuaThreadOrFunction, LuaMultiValue)| {
|
|
||||||
let thread = tof.into_thread(lua)?;
|
|
||||||
if thread.status() == LuaThreadStatus::Resumable {
|
|
||||||
defer_queue.push_item(lua, &thread, args)?;
|
|
||||||
}
|
|
||||||
Ok(thread)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Runs the runtime until all Lua threads have completed.
|
Runs the runtime until all Lua threads have completed.
|
||||||
|
|
||||||
|
@ -350,3 +300,85 @@ impl<'lua> Runtime<'lua> {
|
||||||
.expect(ERR_METADATA_REMOVED);
|
.expect(ERR_METADATA_REMOVED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
A collection of lua functions that may be called to interact with a [`Runtime`].
|
||||||
|
*/
|
||||||
|
pub struct Functions<'lua> {
|
||||||
|
/**
|
||||||
|
Spawns a function / thread onto the runtime queue.
|
||||||
|
Resumes once instantly, and runs until first yield.
|
||||||
|
Adds to the queue if not completed.
|
||||||
|
*/
|
||||||
|
pub spawn: LuaFunction<'lua>,
|
||||||
|
/**
|
||||||
|
Defers a function / thread onto the runtime queue.
|
||||||
|
Does not resume instantly, only adds to the queue.
|
||||||
|
*/
|
||||||
|
pub defer: LuaFunction<'lua>,
|
||||||
|
/**
|
||||||
|
Cancels a function / thread, removing it from the queue.
|
||||||
|
*/
|
||||||
|
pub cancel: LuaFunction<'lua>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua> Functions<'lua> {
|
||||||
|
fn new(rt: &Runtime<'lua>) -> LuaResult<Self> {
|
||||||
|
let error_callback = rt.error_callback.clone();
|
||||||
|
let spawn_queue = rt.queue_spawn.clone();
|
||||||
|
let spawn = rt.lua.create_function(
|
||||||
|
move |lua, (tof, args): (LuaThreadOrFunction, LuaMultiValue)| {
|
||||||
|
let thread = tof.into_thread(lua)?;
|
||||||
|
if thread.status() == LuaThreadStatus::Resumable {
|
||||||
|
// NOTE: We need to resume the thread once instantly for correct behavior,
|
||||||
|
// and only if we get the pending value back we can spawn to async executor
|
||||||
|
match thread.resume::<_, LuaValue>(args.clone()) {
|
||||||
|
Ok(v) => {
|
||||||
|
if v.as_light_userdata()
|
||||||
|
.map(|l| l == Lua::poll_pending())
|
||||||
|
.unwrap_or_default()
|
||||||
|
{
|
||||||
|
spawn_queue.push_item(lua, &thread, args)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error_callback.call(&e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Ok(thread)
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let defer_queue = rt.queue_defer.clone();
|
||||||
|
let defer = rt.lua.create_function(
|
||||||
|
move |lua, (tof, args): (LuaThreadOrFunction, LuaMultiValue)| {
|
||||||
|
let thread = tof.into_thread(lua)?;
|
||||||
|
if thread.status() == LuaThreadStatus::Resumable {
|
||||||
|
defer_queue.push_item(lua, &thread, args)?;
|
||||||
|
}
|
||||||
|
Ok(thread)
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let close = rt
|
||||||
|
.lua
|
||||||
|
.globals()
|
||||||
|
.get::<_, LuaTable>("coroutine")?
|
||||||
|
.get::<_, LuaFunction>("close")?;
|
||||||
|
let close_key = rt.lua.create_registry_value(close)?;
|
||||||
|
let cancel = rt.lua.create_function(move |lua, thread: LuaThread| {
|
||||||
|
let close: LuaFunction = lua.registry_value(&close_key)?;
|
||||||
|
match close.call(thread) {
|
||||||
|
Err(LuaError::CoroutineInactive) | Ok(()) => Ok(()),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
spawn,
|
||||||
|
defer,
|
||||||
|
cancel,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -65,30 +65,30 @@ where
|
||||||
*/
|
*/
|
||||||
pub trait LuaRuntimeExt<'lua> {
|
pub trait LuaRuntimeExt<'lua> {
|
||||||
/**
|
/**
|
||||||
Spawns a lua thread onto the current runtime.
|
Pushes (spawns) a lua thread to the **front** of the current runtime.
|
||||||
|
|
||||||
See [`Runtime::spawn_thread`] for more information.
|
See [`Runtime::push_thread_front`] for more information.
|
||||||
|
|
||||||
# Panics
|
# Panics
|
||||||
|
|
||||||
Panics if called outside of a running [`Runtime`].
|
Panics if called outside of a running [`Runtime`].
|
||||||
*/
|
*/
|
||||||
fn spawn_thread(
|
fn push_thread_front(
|
||||||
&'lua self,
|
&'lua self,
|
||||||
thread: impl IntoLuaThread<'lua>,
|
thread: impl IntoLuaThread<'lua>,
|
||||||
args: impl IntoLuaMulti<'lua>,
|
args: impl IntoLuaMulti<'lua>,
|
||||||
) -> LuaResult<Handle>;
|
) -> LuaResult<Handle>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Defers a lua thread onto the current runtime.
|
Pushes (defers) a lua thread to the **back** of the current runtime.
|
||||||
|
|
||||||
See [`Runtime::defer_thread`] for more information.
|
See [`Runtime::push_thread_back`] for more information.
|
||||||
|
|
||||||
# Panics
|
# Panics
|
||||||
|
|
||||||
Panics if called outside of a running [`Runtime`].
|
Panics if called outside of a running [`Runtime`].
|
||||||
*/
|
*/
|
||||||
fn defer_thread(
|
fn push_thread_back(
|
||||||
&'lua self,
|
&'lua self,
|
||||||
thread: impl IntoLuaThread<'lua>,
|
thread: impl IntoLuaThread<'lua>,
|
||||||
args: impl IntoLuaMulti<'lua>,
|
args: impl IntoLuaMulti<'lua>,
|
||||||
|
@ -123,7 +123,7 @@ pub trait LuaRuntimeExt<'lua> {
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let rt = Runtime::new(&lua);
|
let rt = Runtime::new(&lua);
|
||||||
rt.spawn_thread(lua.load("spawnBackgroundTask()"), ());
|
rt.push_thread_front(lua.load("spawnBackgroundTask()"), ());
|
||||||
block_on(rt.run());
|
block_on(rt.run());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -132,39 +132,33 @@ pub trait LuaRuntimeExt<'lua> {
|
||||||
|
|
||||||
[`Runtime`]: crate::Runtime
|
[`Runtime`]: crate::Runtime
|
||||||
*/
|
*/
|
||||||
fn spawn_future<T: Send + 'static>(
|
fn spawn<T: Send + 'static>(&self, fut: impl Future<Output = T> + Send + 'static) -> Task<T>;
|
||||||
&self,
|
|
||||||
fut: impl Future<Output = T> + Send + 'static,
|
|
||||||
) -> Task<T>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua> LuaRuntimeExt<'lua> for Lua {
|
impl<'lua> LuaRuntimeExt<'lua> for Lua {
|
||||||
fn spawn_thread(
|
fn push_thread_front(
|
||||||
&'lua self,
|
&'lua self,
|
||||||
thread: impl IntoLuaThread<'lua>,
|
thread: impl IntoLuaThread<'lua>,
|
||||||
args: impl IntoLuaMulti<'lua>,
|
args: impl IntoLuaMulti<'lua>,
|
||||||
) -> LuaResult<Handle> {
|
) -> LuaResult<Handle> {
|
||||||
let queue = self
|
let queue = self
|
||||||
.app_data_ref::<SpawnedThreadQueue>()
|
.app_data_ref::<SpawnedThreadQueue>()
|
||||||
.expect("lua threads can only be spawned within a runtime");
|
.expect("lua threads can only be pushed within a runtime");
|
||||||
queue.push_item_with_handle(self, thread, args)
|
queue.push_item_with_handle(self, thread, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn defer_thread(
|
fn push_thread_back(
|
||||||
&'lua self,
|
&'lua self,
|
||||||
thread: impl IntoLuaThread<'lua>,
|
thread: impl IntoLuaThread<'lua>,
|
||||||
args: impl IntoLuaMulti<'lua>,
|
args: impl IntoLuaMulti<'lua>,
|
||||||
) -> LuaResult<Handle> {
|
) -> LuaResult<Handle> {
|
||||||
let queue = self
|
let queue = self
|
||||||
.app_data_ref::<DeferredThreadQueue>()
|
.app_data_ref::<DeferredThreadQueue>()
|
||||||
.expect("lua threads can only be deferred within a runtime");
|
.expect("lua threads can only be pushed within a runtime");
|
||||||
queue.push_item_with_handle(self, thread, args)
|
queue.push_item_with_handle(self, thread, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn_future<T: Send + 'static>(
|
fn spawn<T: Send + 'static>(&self, fut: impl Future<Output = T> + Send + 'static) -> Task<T> {
|
||||||
&self,
|
|
||||||
fut: impl Future<Output = T> + Send + 'static,
|
|
||||||
) -> Task<T> {
|
|
||||||
let exec = self
|
let exec = self
|
||||||
.app_data_ref::<Weak<Executor>>()
|
.app_data_ref::<Weak<Executor>>()
|
||||||
.expect("futures can only be spawned within a runtime")
|
.expect("futures can only be spawned within a runtime")
|
||||||
|
|
Loading…
Add table
Reference in a new issue