use std::cell::{Cell, RefCell};
use std::rc::Rc;
use mlua::prelude::*;
use mlua::{Function, UserData, UserDataMethods, UserDataRegistry};
use tokio::task::JoinHandle;
use super::cancel::CancelToken;
use super::driver::{spawn_into, Handle};
use super::LOCAL_SCOPE;
pub(super) struct Scope {
pub(super) name: Option<String>,
pub(super) token: CancelToken,
pub(super) children: Vec<JoinHandle<()>>,
}
impl Scope {
pub(super) fn new(name: Option<String>) -> Rc<RefCell<Self>> {
Rc::new(RefCell::new(Self {
name,
token: CancelToken::new(),
children: Vec::new(),
}))
}
pub(super) fn attach(&mut self, h: JoinHandle<()>) {
if self.children.len() >= 32 {
self.children.retain(|h| !h.is_finished());
}
self.children.push(h);
}
}
impl Drop for Scope {
fn drop(&mut self) {
for h in &self.children {
h.abort();
}
}
}
pub(super) struct ScopeGuard {
scope: Rc<RefCell<Scope>>,
armed: Cell<bool>,
}
impl ScopeGuard {
pub(super) fn new(scope: Rc<RefCell<Scope>>) -> Self {
Self {
scope,
armed: Cell::new(true),
}
}
pub(super) fn disarm(&self) {
self.armed.set(false);
}
}
impl Drop for ScopeGuard {
fn drop(&mut self) {
if !self.armed.get() {
return;
}
let s = self.scope.borrow();
s.token.cancel();
for h in &s.children {
h.abort();
}
}
}
pub(super) async fn drain_scope(scope: &Rc<RefCell<Scope>>) {
loop {
let next = { scope.borrow_mut().children.pop() };
match next {
Some(h) => {
let _ = h.await;
}
None => break,
}
}
}
pub(super) fn abort_all(scope: &Rc<RefCell<Scope>>) {
for h in &scope.borrow().children {
h.abort();
}
}
#[derive(Clone)]
pub(super) struct ScopeHandle(pub(super) Rc<RefCell<Scope>>);
impl UserData for ScopeHandle {
fn register(reg: &mut UserDataRegistry<Self>) {
reg.add_field_method_get("name", |_, this| Ok(this.0.borrow().name.clone()));
reg.add_method("token", |_, this, ()| Ok(this.0.borrow().token.clone()));
reg.add_method("cancel", |_, this, ()| {
this.0.borrow().token.cancel();
Ok(())
});
reg.add_method(
"spawn",
|lua, this, (func, opts): (Function, Option<LuaTable>)| -> LuaResult<Handle> {
spawn_into(lua, &this.0, func, opts)
},
);
}
}
pub(super) fn root_scope(lua: &Lua) -> LuaResult<Rc<RefCell<Scope>>> {
lua.app_data_ref::<Rc<RefCell<Scope>>>()
.map(|r| r.clone())
.ok_or_else(|| LuaError::external("std.task not initialised"))
}
pub(super) fn current_scope(lua: &Lua) -> LuaResult<Rc<RefCell<Scope>>> {
if let Ok(s) = LOCAL_SCOPE.try_with(|s| s.clone()) {
return Ok(s);
}
root_scope(lua)
}