Implement proper error handlign in spawned tasks
This commit is contained in:
@@ -1,20 +1,27 @@
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use luaffi::{Module, Registry};
|
||||
use luajit::{Chunk, State};
|
||||
use std::rc::Rc;
|
||||
use tokio::{
|
||||
task::{JoinHandle, LocalSet, futures::TaskLocalFuture, spawn_local},
|
||||
task_local,
|
||||
};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub type ErrorFn = dyn Fn(&luajit::Error);
|
||||
|
||||
pub struct Builder {
|
||||
registry: Registry,
|
||||
report_err: Rc<ErrorFn>,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
registry: Registry::new(),
|
||||
report_err: Rc::new(|err| match err.trace() {
|
||||
Some(trace) => eprintln!("unhandled lua error: {err}\n{trace}"),
|
||||
None => eprintln!("unhandled lua error: {err}"),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +29,11 @@ impl Builder {
|
||||
&self.registry
|
||||
}
|
||||
|
||||
pub fn unhandled_error(&mut self, handler: impl Fn(&luajit::Error) + 'static) -> &mut Self {
|
||||
self.report_err = Rc::new(handler);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn module<T: Module>(&mut self) -> &mut Self {
|
||||
self.registry.preload::<T>();
|
||||
self
|
||||
@@ -29,50 +41,74 @@ impl Builder {
|
||||
|
||||
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
|
||||
cx: Context {
|
||||
state: {
|
||||
let mut s = State::new()?;
|
||||
s.eval(Chunk::new(self.registry.build()).path("[luby]"), 0, 0)?;
|
||||
s
|
||||
},
|
||||
report_err: self.report_err.clone(),
|
||||
},
|
||||
tasks: LocalSet::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deref, DerefMut)]
|
||||
#[derive(Deref, DerefMut)]
|
||||
pub struct Runtime {
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
state: State,
|
||||
cx: Context,
|
||||
tasks: LocalSet,
|
||||
}
|
||||
|
||||
task_local! {
|
||||
static STATE: State;
|
||||
}
|
||||
|
||||
impl Runtime {
|
||||
pub fn spawn<T: 'static>(
|
||||
&self,
|
||||
f: impl AsyncFnOnce(&mut State) -> T + 'static,
|
||||
f: impl AsyncFnOnce(&mut Context) -> T + 'static,
|
||||
) -> JoinHandle<T> {
|
||||
self.tasks
|
||||
.spawn_local(async move { f(&mut STATE.with(|s| s.new_thread())).await })
|
||||
.spawn_local(async move { f(&mut CURRENT.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>;
|
||||
type IntoFuture = TaskLocalFuture<Context, LocalSet>;
|
||||
|
||||
fn into_future(self) -> Self::IntoFuture {
|
||||
STATE.scope(self.state, self.tasks)
|
||||
CURRENT.scope(self.cx, self.tasks)
|
||||
}
|
||||
}
|
||||
|
||||
task_local! {
|
||||
static CURRENT: Context;
|
||||
}
|
||||
|
||||
#[derive(Deref, DerefMut)]
|
||||
pub struct Context {
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
state: State,
|
||||
report_err: Rc<ErrorFn>,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
pub fn new_thread(&self) -> Self {
|
||||
Self {
|
||||
state: self.state.new_thread(),
|
||||
report_err: self.report_err.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn report_error(&self, err: &luajit::Error) {
|
||||
(self.report_err)(&err);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spawn<T: 'static>(f: impl AsyncFnOnce(&mut Context) -> 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 CURRENT.with(|s| s.new_thread())).await })
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::runtime::spawn;
|
||||
use luaffi::{cdef, metatype};
|
||||
use std::{ffi::c_int, process};
|
||||
use tokio::task::JoinHandle;
|
||||
use std::{ffi::c_int, time::Duration};
|
||||
use tokio::{task::JoinHandle, time::sleep};
|
||||
|
||||
#[cdef(module = "lb:task")]
|
||||
pub struct lb_tasklib;
|
||||
@@ -20,17 +20,17 @@ impl lb_tasklib {
|
||||
}
|
||||
|
||||
extern "Lua-C" fn __spawn(&self, key: c_int) -> lb_task {
|
||||
let handle = spawn(async move |s| {
|
||||
let handle = spawn(async move |cx| {
|
||||
// SAFETY: key is always unique, created by __ref above
|
||||
let arg = unsafe { s.new_ref_unchecked(key) };
|
||||
let arg = unsafe { cx.new_ref_unchecked(key) };
|
||||
let mut s = cx.guard();
|
||||
s.resize(0);
|
||||
s.push(arg);
|
||||
let narg = s.unpack(1, 1, None) - 1;
|
||||
println!("{s:?}");
|
||||
if let Err(_err) = s.call_async(narg, 0).await {
|
||||
process::exit(1)
|
||||
let narg = s.unpack(1, 1, None) - 1; // unpack the table containing the function to call and its args
|
||||
if let Err(err) = s.call_async(narg, 0).await {
|
||||
drop(s);
|
||||
cx.report_error(&err);
|
||||
}
|
||||
println!("{s:?}");
|
||||
});
|
||||
|
||||
lb_task { handle }
|
||||
|
||||
Reference in New Issue
Block a user