luby/crates/lb/src/runtime.rs

80 lines
1.9 KiB
Rust

use derive_more::{Deref, DerefMut};
use luaffi::{Registry, Type};
use luajit::{Chunk, State};
use std::fmt::Display;
use tokio::{
task::{JoinHandle, LocalSet, futures::TaskLocalFuture, spawn_local},
task_local,
};
#[derive(Debug, Default)]
pub struct Builder {
registry: Registry,
}
impl Builder {
pub fn new() -> Self {
Self {
registry: Registry::new(),
}
}
pub fn registry(&self) -> &Registry {
&self.registry
}
pub fn module<T: Type>(&mut self, name: impl Display) -> &mut Self {
self.registry.preload::<T>(name);
self
}
pub fn build(&self) -> luajit::Result<Runtime> {
Ok(Runtime {
state: {
let mut s = State::new()?;
s.eval(Chunk::new(self.registry.build()).path("[luby]"), 0, 0)?;
s
},
tasks: LocalSet::new(),
})
}
}
#[derive(Debug, Deref, DerefMut)]
pub struct Runtime {
#[deref]
#[deref_mut]
state: State,
tasks: LocalSet,
}
task_local! {
static STATE: State;
}
impl Runtime {
pub fn spawn<T: 'static>(
&self,
f: impl AsyncFnOnce(&mut State) -> T + 'static,
) -> JoinHandle<T> {
self.tasks
.spawn_local(async move { f(&mut STATE.with(|s| s.new_thread())).await })
}
}
pub fn spawn<T: 'static>(f: impl AsyncFnOnce(&mut State) -> T + 'static) -> JoinHandle<T> {
// SAFETY: `new_thread` must be called inside `spawn_local` because this free-standing spawn
// function may be called via ffi from lua, and it is not safe to access the lua state within
// ffi calls.
spawn_local(async move { f(&mut STATE.with(|s| s.new_thread())).await })
}
impl IntoFuture for Runtime {
type Output = ();
type IntoFuture = TaskLocalFuture<State, LocalSet>;
fn into_future(self) -> Self::IntoFuture {
STATE.scope(self.state, self.tasks)
}
}