task_local context over context objects
- interner impls logically separate from API in orchid-base (default host interner still in base for testing) - error reporting, logging, and a variety of other features passed down via context in extension, not yet in host to maintain library-ish profile, should consider options - no global spawn mechanic, the host has a spawn function but extensions only get a stash for enqueuing async work in sync callbacks which is then explicitly, manually, and with strict order popped and awaited - still deadlocks nondeterministically for some ungodly reason
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
use std::any::{Any, TypeId, type_name};
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::future::Future;
|
||||
use std::marker::PhantomData;
|
||||
use std::num::NonZero;
|
||||
use std::ops::Deref;
|
||||
use std::pin::Pin;
|
||||
use std::sync::atomic::AtomicU64;
|
||||
use std::rc::Rc;
|
||||
|
||||
use async_once_cell::OnceCell;
|
||||
use dyn_clone::{DynClone, clone_box};
|
||||
@@ -19,32 +20,33 @@ use orchid_api_traits::{Decode, Encode, enc_vec};
|
||||
use orchid_base::error::OrcRes;
|
||||
use orchid_base::format::{FmtCtx, FmtCtxImpl, FmtUnit, take_first};
|
||||
use orchid_base::name::Sym;
|
||||
use task_local::task_local;
|
||||
|
||||
use crate::api;
|
||||
use crate::atom::{
|
||||
AtomCard, AtomCtx, AtomDynfo, AtomFactory, Atomic, AtomicFeaturesImpl, AtomicVariant, MethodSet,
|
||||
MethodSetBuilder, TAtom, err_not_callable, err_not_command, get_info,
|
||||
};
|
||||
use crate::context::{SysCtxEntry, ctx, i};
|
||||
use crate::expr::Expr;
|
||||
use crate::gen_expr::{GExpr, bot};
|
||||
use crate::system_ctor::CtedObj;
|
||||
use crate::system::{cted, sys_id};
|
||||
|
||||
pub struct OwnedVariant;
|
||||
impl AtomicVariant for OwnedVariant {}
|
||||
impl<A: OwnedAtom + Atomic<Variant = OwnedVariant>> AtomicFeaturesImpl<OwnedVariant> for A {
|
||||
fn _factory(self) -> AtomFactory {
|
||||
AtomFactory::new(async move || {
|
||||
let serial = ctx()
|
||||
.get_or_default::<ObjStore>()
|
||||
.next_id
|
||||
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||
let atom_id = api::AtomId(NonZero::new(serial + 1).unwrap());
|
||||
let (typ_id, _) = get_info::<A>(ctx().get::<CtedObj>().inst().card());
|
||||
let mut data = enc_vec(&typ_id).await;
|
||||
let obj_store = get_obj_store();
|
||||
let atom_id = {
|
||||
let mut id = obj_store.next_id.borrow_mut();
|
||||
*id += 1;
|
||||
api::AtomId(NonZero::new(*id + 1).unwrap())
|
||||
};
|
||||
let (typ_id, _) = get_info::<A>(cted().inst().card());
|
||||
let mut data = enc_vec(&typ_id);
|
||||
self.encode(Pin::<&mut Vec<u8>>::new(&mut data)).await;
|
||||
ctx().get_or_default::<ObjStore>().objects.read().await.insert(atom_id, Box::new(self));
|
||||
api::Atom { drop: Some(atom_id), data: api::AtomData(data), owner: ctx().sys_id() }
|
||||
obj_store.objects.read().await.insert(atom_id, Box::new(self));
|
||||
api::Atom { drop: Some(atom_id), data: api::AtomData(data), owner: sys_id() }
|
||||
})
|
||||
}
|
||||
fn _info() -> Self::_Info { OwnedAtomDynfo { msbuild: A::reg_reqs(), ms: OnceCell::new() } }
|
||||
@@ -59,7 +61,7 @@ pub(crate) struct AtomReadGuard<'a> {
|
||||
}
|
||||
impl<'a> AtomReadGuard<'a> {
|
||||
async fn new(id: api::AtomId) -> Self {
|
||||
let guard = ctx().get_or_default::<ObjStore>().objects.read().await;
|
||||
let guard = get_obj_store().objects.read().await;
|
||||
if guard.get(&id).is_none() {
|
||||
panic!("Received invalid atom ID: {id:?}");
|
||||
}
|
||||
@@ -73,7 +75,7 @@ impl Deref for AtomReadGuard<'_> {
|
||||
|
||||
/// Remove an atom from the store
|
||||
pub(crate) async fn take_atom(id: api::AtomId) -> Box<dyn DynOwnedAtom> {
|
||||
let mut g = ctx().get_or_default::<ObjStore>().objects.write().await;
|
||||
let mut g = get_obj_store().objects.write().await;
|
||||
g.remove(&id).unwrap_or_else(|| panic!("Received invalid atom ID: {}", id.0))
|
||||
}
|
||||
|
||||
@@ -86,7 +88,7 @@ impl<T: OwnedAtom> AtomDynfo for OwnedAtomDynfo<T> {
|
||||
fn name(&self) -> &'static str { type_name::<T>() }
|
||||
fn decode<'a>(&'a self, AtomCtx(data, ..): AtomCtx<'a>) -> LocalBoxFuture<'a, Box<dyn Any>> {
|
||||
Box::pin(async {
|
||||
Box::new(<T as AtomCard>::Data::decode(Pin::new(&mut &data[..])).await) as Box<dyn Any>
|
||||
Box::new(<T as AtomCard>::Data::decode_slice(&mut &data[..])) as Box<dyn Any>
|
||||
})
|
||||
}
|
||||
fn call(&self, AtomCtx(_, id): AtomCtx, arg: Expr) -> LocalBoxFuture<'_, GExpr> {
|
||||
@@ -127,7 +129,7 @@ impl<T: OwnedAtom> AtomDynfo for OwnedAtomDynfo<T> {
|
||||
) -> LocalBoxFuture<'a, Option<Vec<Expr>>> {
|
||||
Box::pin(async move {
|
||||
let id = id.unwrap();
|
||||
id.encode(write.as_mut()).await;
|
||||
id.encode(write.as_mut()).await.unwrap();
|
||||
AtomReadGuard::new(id).await.dyn_serialize(write).await
|
||||
})
|
||||
}
|
||||
@@ -155,7 +157,7 @@ pub trait DeserializeCtx: Sized {
|
||||
|
||||
struct DeserCtxImpl<'a>(&'a [u8]);
|
||||
impl DeserializeCtx for DeserCtxImpl<'_> {
|
||||
async fn read<T: Decode>(&mut self) -> T { T::decode(Pin::new(&mut self.0)).await }
|
||||
async fn read<T: Decode>(&mut self) -> T { T::decode(Pin::new(&mut self.0)).await.unwrap() }
|
||||
fn is_empty(&self) -> bool { self.0.is_empty() }
|
||||
}
|
||||
|
||||
@@ -266,7 +268,7 @@ impl<T: OwnedAtom> DynOwnedAtom for T {
|
||||
fn atom_tid(&self) -> TypeId { TypeId::of::<T>() }
|
||||
fn as_any_ref(&self) -> &dyn Any { self }
|
||||
fn encode<'a>(&'a self, buffer: Pin<&'a mut dyn AsyncWrite>) -> LocalBoxFuture<'a, ()> {
|
||||
async { self.val().await.as_ref().encode(buffer).await }.boxed_local()
|
||||
async { self.val().await.as_ref().encode(buffer).await.unwrap() }.boxed_local()
|
||||
}
|
||||
fn dyn_call_ref(&self, arg: Expr) -> LocalBoxFuture<'_, GExpr> {
|
||||
self.call_ref(arg).boxed_local()
|
||||
@@ -279,7 +281,7 @@ impl<T: OwnedAtom> DynOwnedAtom for T {
|
||||
}
|
||||
fn dyn_free(self: Box<Self>) -> LocalBoxFuture<'static, ()> { self.free().boxed_local() }
|
||||
fn dyn_print(&self) -> LocalBoxFuture<'_, FmtUnit> {
|
||||
async move { self.print_atom(&FmtCtxImpl { i: &i() }).await }.boxed_local()
|
||||
async move { self.print_atom(&FmtCtxImpl::default()).await }.boxed_local()
|
||||
}
|
||||
fn dyn_serialize<'a>(
|
||||
&'a self,
|
||||
@@ -294,13 +296,24 @@ impl<T: OwnedAtom> DynOwnedAtom for T {
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct ObjStore {
|
||||
pub(crate) next_id: AtomicU64,
|
||||
pub(crate) next_id: RefCell<u64>,
|
||||
pub(crate) objects: RwLock<MemoMap<api::AtomId, Box<dyn DynOwnedAtom>>>,
|
||||
}
|
||||
impl SysCtxEntry for ObjStore {}
|
||||
|
||||
task_local! {
|
||||
static OBJ_STORE: Rc<ObjStore>;
|
||||
}
|
||||
|
||||
pub(crate) fn with_obj_store<'a>(fut: LocalBoxFuture<'a, ()>) -> LocalBoxFuture<'a, ()> {
|
||||
Box::pin(OBJ_STORE.scope(Rc::new(ObjStore::default()), fut))
|
||||
}
|
||||
|
||||
pub(crate) fn get_obj_store() -> Rc<ObjStore> {
|
||||
OBJ_STORE.try_with(|store| store.clone()).expect("Owned atom store not initialized")
|
||||
}
|
||||
|
||||
pub async fn own<A: OwnedAtom>(typ: &TAtom<A>) -> A {
|
||||
let g = ctx().get_or_default::<ObjStore>().objects.read().await;
|
||||
let g = get_obj_store().objects.read().await;
|
||||
let atom_id = typ.untyped.atom.drop.expect("Owned atoms always have a drop ID");
|
||||
let dyn_atom =
|
||||
g.get(&atom_id).expect("Atom ID invalid; atom type probably not owned by this crate");
|
||||
@@ -308,8 +321,7 @@ pub async fn own<A: OwnedAtom>(typ: &TAtom<A>) -> A {
|
||||
}
|
||||
|
||||
pub async fn debug_print_obj_store(show_atoms: bool) {
|
||||
let ctx = ctx();
|
||||
let store = ctx.get_or_default::<ObjStore>();
|
||||
let store = get_obj_store();
|
||||
let keys = store.objects.read().await.keys().cloned().collect_vec();
|
||||
let mut message = "Atoms in store:".to_string();
|
||||
if !show_atoms {
|
||||
|
||||
Reference in New Issue
Block a user