mirror of
https://github.com/lune-org/mlua-luau-scheduler.git
synced 2025-04-03 01:50:57 +01:00
378 lines
10 KiB
Rust
378 lines
10 KiB
Rust
#![allow(unused_imports)]
|
|
#![allow(clippy::missing_errors_doc)]
|
|
|
|
use std::{
|
|
cell::Cell, future::Future, process::ExitCode, rc::Weak as WeakRc, sync::Weak as WeakArc,
|
|
};
|
|
|
|
use async_executor::{Executor, Task};
|
|
use mlua::prelude::*;
|
|
use tracing::trace;
|
|
|
|
use crate::{
|
|
exit::Exit,
|
|
queue::{DeferredThreadQueue, FuturesQueue, SpawnedThreadQueue},
|
|
result_map::ThreadResultMap,
|
|
scheduler::Scheduler,
|
|
thread_id::ThreadId,
|
|
};
|
|
|
|
/**
|
|
Trait for any struct that can be turned into an [`LuaThread`]
|
|
and passed to the scheduler, implemented for the following types:
|
|
|
|
- Lua threads ([`LuaThread`])
|
|
- Lua functions ([`LuaFunction`])
|
|
- Lua chunks ([`LuaChunk`])
|
|
*/
|
|
pub trait IntoLuaThread<'lua> {
|
|
/**
|
|
Converts the value into a Lua thread.
|
|
|
|
# Errors
|
|
|
|
Errors when out of memory.
|
|
*/
|
|
fn into_lua_thread(self, lua: &'lua Lua) -> LuaResult<LuaThread<'lua>>;
|
|
}
|
|
|
|
impl<'lua> IntoLuaThread<'lua> for LuaThread<'lua> {
|
|
fn into_lua_thread(self, _: &'lua Lua) -> LuaResult<LuaThread<'lua>> {
|
|
Ok(self)
|
|
}
|
|
}
|
|
|
|
impl<'lua> IntoLuaThread<'lua> for LuaFunction<'lua> {
|
|
fn into_lua_thread(self, lua: &'lua Lua) -> LuaResult<LuaThread<'lua>> {
|
|
lua.create_thread(self)
|
|
}
|
|
}
|
|
|
|
impl<'lua> IntoLuaThread<'lua> for LuaChunk<'lua, '_> {
|
|
fn into_lua_thread(self, lua: &'lua Lua) -> LuaResult<LuaThread<'lua>> {
|
|
lua.create_thread(self.into_function()?)
|
|
}
|
|
}
|
|
|
|
impl<'lua, T> IntoLuaThread<'lua> for &T
|
|
where
|
|
T: IntoLuaThread<'lua> + Clone,
|
|
{
|
|
fn into_lua_thread(self, lua: &'lua Lua) -> LuaResult<LuaThread<'lua>> {
|
|
self.clone().into_lua_thread(lua)
|
|
}
|
|
}
|
|
|
|
/**
|
|
Trait for interacting with the current [`Scheduler`].
|
|
|
|
Provides extra methods on the [`Lua`] struct for:
|
|
|
|
- Setting the exit code and forcibly stopping the scheduler
|
|
- Pushing (spawning) and deferring (pushing to the back) lua threads
|
|
- Tracking and getting the result of lua threads
|
|
*/
|
|
pub trait LuaSchedulerExt<'lua> {
|
|
/**
|
|
Sets the exit code of the current scheduler.
|
|
|
|
See [`Scheduler::set_exit_code`] for more information.
|
|
|
|
# Panics
|
|
|
|
Panics if called outside of a running [`Scheduler`].
|
|
*/
|
|
fn set_exit_code(&self, code: ExitCode);
|
|
|
|
/**
|
|
Pushes (spawns) a lua thread to the **front** of the current scheduler.
|
|
|
|
See [`Scheduler::push_thread_front`] for more information.
|
|
|
|
# Panics
|
|
|
|
Panics if called outside of a running [`Scheduler`].
|
|
*/
|
|
fn push_thread_front(
|
|
&'lua self,
|
|
thread: impl IntoLuaThread<'lua>,
|
|
args: impl IntoLuaMulti<'lua>,
|
|
) -> LuaResult<ThreadId>;
|
|
|
|
/**
|
|
Pushes (defers) a lua thread to the **back** of the current scheduler.
|
|
|
|
See [`Scheduler::push_thread_back`] for more information.
|
|
|
|
# Panics
|
|
|
|
Panics if called outside of a running [`Scheduler`].
|
|
*/
|
|
fn push_thread_back(
|
|
&'lua self,
|
|
thread: impl IntoLuaThread<'lua>,
|
|
args: impl IntoLuaMulti<'lua>,
|
|
) -> LuaResult<ThreadId>;
|
|
|
|
/**
|
|
Registers the given thread to be tracked within the current scheduler.
|
|
|
|
Must be called before waiting for a thread to complete or getting its result.
|
|
*/
|
|
fn track_thread(&'lua self, id: ThreadId);
|
|
|
|
/**
|
|
Gets the result of the given thread.
|
|
|
|
See [`Scheduler::get_thread_result`] for more information.
|
|
|
|
# Panics
|
|
|
|
Panics if called outside of a running [`Scheduler`].
|
|
*/
|
|
fn get_thread_result(&'lua self, id: ThreadId) -> Option<LuaResult<LuaMultiValue<'lua>>>;
|
|
|
|
/**
|
|
Waits for the given thread to complete.
|
|
|
|
See [`Scheduler::wait_for_thread`] for more information.
|
|
|
|
# Panics
|
|
|
|
Panics if called outside of a running [`Scheduler`].
|
|
*/
|
|
fn wait_for_thread(&'lua self, id: ThreadId) -> impl Future<Output = ()>;
|
|
}
|
|
|
|
/**
|
|
Trait for interacting with the [`Executor`] for the current [`Scheduler`].
|
|
|
|
Provides extra methods on the [`Lua`] struct for:
|
|
|
|
- Spawning thread-local (`!Send`) futures on the current executor
|
|
- Spawning background (`Send`) futures on the current executor
|
|
- Spawning blocking tasks on a separate thread pool
|
|
*/
|
|
pub trait LuaSpawnExt<'lua> {
|
|
/**
|
|
Spawns the given future on the current executor and returns its [`Task`].
|
|
|
|
# Panics
|
|
|
|
Panics if called outside of a running [`Scheduler`].
|
|
|
|
# Example usage
|
|
|
|
```rust
|
|
use async_io::block_on;
|
|
|
|
use mlua::prelude::*;
|
|
use mlua_luau_scheduler::*;
|
|
|
|
fn main() -> LuaResult<()> {
|
|
let lua = Lua::new();
|
|
|
|
lua.globals().set(
|
|
"spawnBackgroundTask",
|
|
lua.create_async_function(|lua, ()| async move {
|
|
lua.spawn(async move {
|
|
println!("Hello from background task!");
|
|
}).await;
|
|
Ok(())
|
|
})?
|
|
)?;
|
|
|
|
let sched = Scheduler::new(&lua);
|
|
sched.push_thread_front(lua.load("spawnBackgroundTask()"), ());
|
|
block_on(sched.run());
|
|
|
|
Ok(())
|
|
}
|
|
```
|
|
*/
|
|
fn spawn<F, T>(&self, fut: F) -> Task<T>
|
|
where
|
|
F: Future<Output = T> + Send + 'static,
|
|
T: Send + 'static;
|
|
|
|
/**
|
|
Spawns the given thread-local future on the current executor.
|
|
|
|
Note that this future will run detached and always to completion,
|
|
preventing the [`Scheduler`] was spawned on from completing until done.
|
|
|
|
# Panics
|
|
|
|
Panics if called outside of a running [`Scheduler`].
|
|
|
|
# Example usage
|
|
|
|
```rust
|
|
use async_io::block_on;
|
|
|
|
use mlua::prelude::*;
|
|
use mlua_luau_scheduler::*;
|
|
|
|
fn main() -> LuaResult<()> {
|
|
let lua = Lua::new();
|
|
|
|
lua.globals().set(
|
|
"spawnLocalTask",
|
|
lua.create_async_function(|lua, ()| async move {
|
|
lua.spawn_local(async move {
|
|
println!("Hello from local task!");
|
|
});
|
|
Ok(())
|
|
})?
|
|
)?;
|
|
|
|
let sched = Scheduler::new(&lua);
|
|
sched.push_thread_front(lua.load("spawnLocalTask()"), ());
|
|
block_on(sched.run());
|
|
|
|
Ok(())
|
|
}
|
|
```
|
|
*/
|
|
fn spawn_local<F>(&self, fut: F)
|
|
where
|
|
F: Future<Output = ()> + 'static;
|
|
|
|
/**
|
|
Spawns the given blocking function and returns its [`Task`].
|
|
|
|
This function will run on a separate thread pool and not block the current executor.
|
|
|
|
# Panics
|
|
|
|
Panics if called outside of a running [`Scheduler`].
|
|
|
|
# Example usage
|
|
|
|
```rust
|
|
use async_io::block_on;
|
|
|
|
use mlua::prelude::*;
|
|
use mlua_luau_scheduler::*;
|
|
|
|
fn main() -> LuaResult<()> {
|
|
let lua = Lua::new();
|
|
|
|
lua.globals().set(
|
|
"spawnBlockingTask",
|
|
lua.create_async_function(|lua, ()| async move {
|
|
lua.spawn_blocking(|| {
|
|
println!("Hello from blocking task!");
|
|
}).await;
|
|
Ok(())
|
|
})?
|
|
)?;
|
|
|
|
let sched = Scheduler::new(&lua);
|
|
sched.push_thread_front(lua.load("spawnBlockingTask()"), ());
|
|
block_on(sched.run());
|
|
|
|
Ok(())
|
|
}
|
|
```
|
|
*/
|
|
fn spawn_blocking<F, T>(&self, f: F) -> Task<T>
|
|
where
|
|
F: FnOnce() -> T + Send + 'static,
|
|
T: Send + 'static;
|
|
}
|
|
|
|
impl<'lua> LuaSchedulerExt<'lua> for Lua {
|
|
fn set_exit_code(&self, code: ExitCode) {
|
|
let exit = self
|
|
.app_data_ref::<Exit>()
|
|
.expect("exit code can only be set from within an active scheduler");
|
|
exit.set(code);
|
|
}
|
|
|
|
fn push_thread_front(
|
|
&'lua self,
|
|
thread: impl IntoLuaThread<'lua>,
|
|
args: impl IntoLuaMulti<'lua>,
|
|
) -> LuaResult<ThreadId> {
|
|
let queue = self
|
|
.app_data_ref::<SpawnedThreadQueue>()
|
|
.expect("lua threads can only be pushed from within an active scheduler");
|
|
queue.push_item(self, thread, args)
|
|
}
|
|
|
|
fn push_thread_back(
|
|
&'lua self,
|
|
thread: impl IntoLuaThread<'lua>,
|
|
args: impl IntoLuaMulti<'lua>,
|
|
) -> LuaResult<ThreadId> {
|
|
let queue = self
|
|
.app_data_ref::<DeferredThreadQueue>()
|
|
.expect("lua threads can only be pushed from within an active scheduler");
|
|
queue.push_item(self, thread, args)
|
|
}
|
|
|
|
fn track_thread(&'lua self, id: ThreadId) {
|
|
let map = self
|
|
.app_data_ref::<ThreadResultMap>()
|
|
.expect("lua threads can only be tracked from within an active scheduler");
|
|
map.track(id);
|
|
}
|
|
|
|
fn get_thread_result(&'lua self, id: ThreadId) -> Option<LuaResult<LuaMultiValue<'lua>>> {
|
|
let map = self
|
|
.app_data_ref::<ThreadResultMap>()
|
|
.expect("lua threads results can only be retrieved from within an active scheduler");
|
|
map.remove(id).map(|r| r.value(self))
|
|
}
|
|
|
|
fn wait_for_thread(&'lua self, id: ThreadId) -> impl Future<Output = ()> {
|
|
let map = self
|
|
.app_data_ref::<ThreadResultMap>()
|
|
.expect("lua threads results can only be retrieved from within an active scheduler");
|
|
async move { map.listen(id).await }
|
|
}
|
|
}
|
|
|
|
impl<'lua> LuaSpawnExt<'lua> for Lua {
|
|
fn spawn<F, T>(&self, fut: F) -> Task<T>
|
|
where
|
|
F: Future<Output = T> + Send + 'static,
|
|
T: Send + 'static,
|
|
{
|
|
let exec = self
|
|
.app_data_ref::<WeakArc<Executor>>()
|
|
.expect("tasks can only be spawned within an active scheduler")
|
|
.upgrade()
|
|
.expect("executor was dropped");
|
|
trace!("spawning future on executor");
|
|
exec.spawn(fut)
|
|
}
|
|
|
|
fn spawn_local<F>(&self, fut: F)
|
|
where
|
|
F: Future<Output = ()> + 'static,
|
|
{
|
|
let queue = self
|
|
.app_data_ref::<WeakRc<FuturesQueue>>()
|
|
.expect("tasks can only be spawned within an active scheduler")
|
|
.upgrade()
|
|
.expect("executor was dropped");
|
|
trace!("spawning local task on executor");
|
|
queue.push_item(fut);
|
|
}
|
|
|
|
fn spawn_blocking<F, T>(&self, f: F) -> Task<T>
|
|
where
|
|
F: FnOnce() -> T + Send + 'static,
|
|
T: Send + 'static,
|
|
{
|
|
let exec = self
|
|
.app_data_ref::<WeakArc<Executor>>()
|
|
.expect("tasks can only be spawned within an active scheduler")
|
|
.upgrade()
|
|
.expect("executor was dropped");
|
|
trace!("spawning blocking task on executor");
|
|
exec.spawn(blocking::unblock(f))
|
|
}
|
|
}
|