New macro system and stdlib additions

This commit is contained in:
2025-11-21 14:25:03 +01:00
parent b77653f841
commit 603efef28e
230 changed files with 3033 additions and 16640 deletions

View File

@@ -13,8 +13,8 @@ use orchid_base::reqnot::Requester;
use crate::api;
use crate::atom::ForeignAtom;
use crate::context::{ctx, i};
use crate::gen_expr::{GExpr, GExprKind};
use crate::system::SysCtx;
pub struct BorrowedExprStore(RefCell<Option<HashSet<Rc<ExprHandle>>>>);
impl BorrowedExprStore {
@@ -22,7 +22,7 @@ impl BorrowedExprStore {
pub async fn dispose(self) {
let elements = self.0.borrow_mut().take().unwrap();
for handle in elements {
handle.drop_one().await
handle.on_borrow_expire().await
}
}
}
@@ -34,58 +34,67 @@ impl Drop for BorrowedExprStore {
}
}
#[derive(destructure)]
pub struct ExprHandle {
pub tk: api::ExprTicket,
pub ctx: SysCtx,
}
#[derive(destructure, PartialEq, Eq, Hash)]
pub struct ExprHandle(api::ExprTicket);
impl ExprHandle {
/// This function does not signal to take ownership of the expr.
pub fn borrowed(ctx: SysCtx, tk: api::ExprTicket, store: &BorrowedExprStore) -> Rc<Self> {
let this = Rc::new(Self { ctx, tk });
/// Do not signal to take ownership of the expr. Instead, the
/// [BorrowedExprStore] signifies the lifetime of the borrow, and when it is
/// freed, it signals to take ownership of any exprs that ended up outliving
/// it. It is used to receive exprs sent via [ExprHandle::ticket] as an
/// optimization over [ExprHandle::from_ticket]
pub fn borrowed(tk: api::ExprTicket, store: &BorrowedExprStore) -> Rc<Self> {
let this = Rc::new(Self(tk));
store.0.borrow_mut().as_mut().unwrap().insert(this.clone());
this
}
pub fn deserialize(ctx: SysCtx, tk: api::ExprTicket) -> Rc<Self> { Rc::new(Self { ctx, tk }) }
pub fn get_ctx(&self) -> SysCtx { self.ctx.clone() }
/// Drop one instance of the handle silently; if it's the last one, do
/// nothing, otherwise send an Acquire
pub async fn drop_one(self: Rc<Self>) {
match Rc::try_unwrap(self) {
Err(rc) => rc.ctx.reqnot().notify(api::Acquire(rc.ctx.sys_id(), rc.tk)).await,
Ok(hand) => {
// avoid calling destructor
hand.destructure();
},
}
/// This function takes over the loose reference pre-created via
/// [ExprHandle::serialize] in the sender. It must therefore pair up with a
/// corresponding call to that function.
pub fn deserialize(tk: api::ExprTicket) -> Rc<Self> { Rc::new(Self(tk)) }
/// This function takes ownership of a borrowed expr sent via
/// [ExprHandle::ticket] and signals immediately to record that ownership. It
/// is used in place of [ExprHandle::borrowed] when it's impractical to
/// determine how long the borrow will live.
///
/// # Safety
///
/// You need to ensure that the [api::Acquire] sent by this function arrives
/// before the borrow expires, so you still need a borrow delimited by some
/// message you will send in the future.
pub async fn from_ticket(tk: api::ExprTicket) -> Rc<Self> {
let store = BorrowedExprStore::new();
let expr = Self::borrowed(tk, &store);
store.dispose().await;
expr
}
/// The raw ticket used in messages. If you want to transfer ownership via the
/// ticket, you should use [ExprHandle::serialize]. Only send this if you want
/// to lend the expr, and you expect the receiver to use
/// [ExprHandle::borrowed] or [ExprHandle::from_ticket]
pub fn ticket(&self) -> api::ExprTicket { self.0 }
async fn send_acq(&self) { ctx().reqnot().notify(api::Acquire(ctx().sys_id(), self.0)).await }
/// If this is the last one reference, do nothing, otherwise send an Acquire
pub async fn on_borrow_expire(self: Rc<Self>) { self.serialize().await; }
/// Drop the handle and get the ticket without a release notification.
/// Use this with messages that imply ownership transfer. This function is
/// safe because abusing it is a memory leak.
pub fn serialize(self) -> api::ExprTicket { self.destructure().0 }
}
impl Eq for ExprHandle {}
impl PartialEq for ExprHandle {
fn eq(&self, other: &Self) -> bool {
self.ctx.sys_id() == other.ctx.sys_id() && self.tk == other.tk
}
}
impl Hash for ExprHandle {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.ctx.sys_id().hash(state);
self.tk.hash(state);
pub async fn serialize(self: Rc<Self>) -> api::ExprTicket {
match Rc::try_unwrap(self) {
Err(rc) => {
rc.send_acq().await;
rc.0
},
Ok(hand) => hand.destructure().0,
}
}
}
impl fmt::Debug for ExprHandle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "ExprHandle({})", self.tk.0)
}
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "ExprHandle({})", self.0.0) }
}
impl Drop for ExprHandle {
fn drop(&mut self) {
let notif = api::Release(self.ctx.sys_id(), self.tk);
let reqnot = self.ctx.reqnot().clone();
self.ctx.spawner()(Box::pin(async move { reqnot.notify(notif).await }))
let notif = api::Release(ctx().sys_id(), self.0);
ctx().spawn(async move { ctx().reqnot().clone().notify(notif).await })
}
}
@@ -96,19 +105,23 @@ pub struct Expr {
}
impl Expr {
pub fn from_handle(handle: Rc<ExprHandle>) -> Self { Self { handle, data: Rc::default() } }
pub fn new(handle: Rc<ExprHandle>, d: ExprData) -> Self {
pub fn from_data(handle: Rc<ExprHandle>, d: ExprData) -> Self {
Self { handle, data: Rc::new(OnceCell::from(d)) }
}
/// Creates an instance without incrementing the reference count. This is
/// only safe to be called on a reference created with an [Expr::serialize]
/// call which created the loose reference it can take ownership of.
pub async fn deserialize(tk: api::ExprTicket) -> Self {
Self::from_handle(ExprHandle::deserialize(tk))
}
pub async fn data(&self) -> &ExprData {
(self.data.get_or_init(async {
let details = self.handle.ctx.reqnot().request(api::Inspect { target: self.handle.tk }).await;
let pos = Pos::from_api(&details.location, self.handle.ctx.i()).await;
let details = ctx().reqnot().request(api::Inspect { target: self.handle.ticket() }).await;
let pos = Pos::from_api(&details.location, &i()).await;
let kind = match details.kind {
api::InspectedKind::Atom(a) =>
ExprKind::Atom(ForeignAtom::new(self.handle.clone(), a, pos.clone())),
api::InspectedKind::Bottom(b) =>
ExprKind::Bottom(OrcErrv::from_api(&b, self.handle.ctx.i()).await),
api::InspectedKind::Bottom(b) => ExprKind::Bottom(OrcErrv::from_api(&b, &i()).await),
api::InspectedKind::Opaque => ExprKind::Opaque,
};
ExprData { pos, kind }
@@ -122,20 +135,22 @@ impl Expr {
}
}
pub fn handle(&self) -> Rc<ExprHandle> { self.handle.clone() }
pub fn ctx(&self) -> SysCtx { self.handle.ctx.clone() }
pub fn slot(&self) -> GExpr {
GExpr { pos: Pos::SlotTarget, kind: GExprKind::Slot(self.clone()) }
}
/// Increments the refcount to ensure that the ticket remains valid even if
/// the handle is freed. To avoid a leak, [Expr::deserialize] must eventually
/// be called.
pub async fn serialize(self) -> api::ExprTicket { self.handle.serialize().await }
}
impl Format for Expr {
async fn print<'a>(&'a self, _c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
match &self.data().await.kind {
ExprKind::Opaque => "OPAQUE".to_string().into(),
ExprKind::Bottom(b) => format!("Bottom({b})").into(),
ExprKind::Atom(a) => FmtUnit::from_api(
&self.handle.ctx.reqnot().request(api::ExtAtomPrint(a.atom.clone())).await,
),
ExprKind::Atom(a) =>
FmtUnit::from_api(&ctx().reqnot().request(api::ExtAtomPrint(a.atom.clone())).await),
}
}
}