From de410e4cfedc007cd142b2e9a41fbc6e3bcb1a3c Mon Sep 17 00:00:00 2001 From: Sahas Subramanian Date: Sat, 13 Dec 2025 23:36:17 +0100 Subject: [PATCH 1/5] Replace Cell with RefCell for span in TulispObject Spans are retrieved only when there is an error, so it should be fine to remove that optimization for more maintainable code. --- src/object.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/object.rs b/src/object.rs index 5563203..5626b12 100644 --- a/src/object.rs +++ b/src/object.rs @@ -6,7 +6,7 @@ use crate::{ }; use std::{ any::Any, - cell::{Cell, Ref, RefCell}, + cell::{Ref, RefCell}, rc::Rc, }; @@ -31,7 +31,7 @@ impl Span { #[derive(Debug, Clone)] pub struct TulispObject { rc: Rc>, - span: Rc>>, + span: Rc>>, } impl Default for TulispObject { @@ -373,7 +373,7 @@ impl TulispObject { pub(crate) fn new(vv: TulispValue, span: Option) -> TulispObject { Self { rc: Rc::new(RefCell::new(vv)), - span: Rc::new(Cell::new(span)), + span: Rc::new(RefCell::new(span)), } } @@ -411,7 +411,7 @@ impl TulispObject { pub(crate) fn clone_without_span(&self) -> Self { Self { rc: Rc::clone(&self.rc), - span: Rc::new(Cell::new(None)), + span: Rc::new(RefCell::new(None)), } } @@ -448,7 +448,7 @@ impl TulispObject { } pub(crate) fn with_span(&self, in_span: Option) -> Self { - self.span.set(in_span); + *self.span.borrow_mut() = in_span; self.clone() } pub(crate) fn take(&self) -> TulispValue { @@ -458,7 +458,7 @@ impl TulispObject { #[doc(hidden)] #[inline(always)] pub fn span(&self) -> Option { - self.span.get() + self.span.borrow().clone() } #[doc(hidden)] From 76d5455124910c6244f469a54b366cb4b5d52630 Mon Sep 17 00:00:00 2001 From: Sahas Subramanian Date: Sat, 13 Dec 2025 23:45:42 +0100 Subject: [PATCH 2/5] Convert TulispFn from a type alias to a trait Also add a blanket impl and replace all `impl Fn(..)` with it. --- src/context.rs | 13 +++---------- src/object.rs | 2 ++ src/object/wrappers.rs | 14 ++++++++++++++ src/value.rs | 10 ++++------ 4 files changed, 23 insertions(+), 16 deletions(-) create mode 100644 src/object/wrappers.rs diff --git a/src/context.rs b/src/context.rs index e83c585..17f193b 100644 --- a/src/context.rs +++ b/src/context.rs @@ -11,6 +11,7 @@ use crate::{ error::Error, eval::{DummyEval, eval, eval_and_then, eval_basic, funcall}, list, + object::wrappers::generic::TulispFn, parse::parse, }; @@ -86,11 +87,7 @@ impl TulispContext { } #[inline(always)] - pub fn add_special_form( - &mut self, - name: &str, - func: impl Fn(&mut TulispContext, &TulispObject) -> Result + 'static, - ) { + pub fn add_special_form(&mut self, name: &str, func: impl TulispFn) { self.intern(name) .set_global(TulispValue::Func(Rc::new(func)).into_ref(None)) .unwrap(); @@ -125,11 +122,7 @@ impl TulispContext { } #[inline(always)] - pub fn add_macro( - &mut self, - name: &str, - func: impl Fn(&mut TulispContext, &TulispObject) -> Result + 'static, - ) { + pub fn add_macro(&mut self, name: &str, func: impl TulispFn) { self.intern(name) .set_global(TulispValue::Macro(Rc::new(func)).into_ref(None)) .unwrap(); diff --git a/src/object.rs b/src/object.rs index 5626b12..d8bb54e 100644 --- a/src/object.rs +++ b/src/object.rs @@ -1,3 +1,5 @@ +pub(crate) mod wrappers; + use crate::{ TulispValue, cons::{self, Cons}, diff --git a/src/object/wrappers.rs b/src/object/wrappers.rs new file mode 100644 index 0000000..09b68ea --- /dev/null +++ b/src/object/wrappers.rs @@ -0,0 +1,14 @@ +use crate::{Error, TulispContext, TulispObject}; + +pub(crate) mod generic { + use super::*; + + pub trait TulispFn: + Fn(&mut TulispContext, &TulispObject) -> Result + 'static + { + } + impl TulispFn for T where + T: Fn(&mut TulispContext, &TulispObject) -> Result + 'static + { + } +} diff --git a/src/value.rs b/src/value.rs index c36d6f8..6a580c4 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,9 +1,9 @@ use crate::{ - TulispContext, TulispObject, + TulispObject, cons::{self, Cons}, context::Scope, error::Error, - object::Span, + object::{Span, wrappers::generic::TulispFn}, }; use std::{ any::Any, @@ -98,8 +98,6 @@ impl DefunParams { } } -type TulispFn = dyn Fn(&mut TulispContext, &TulispObject) -> Result; - #[derive(Default, Clone, Debug)] pub struct SymbolBindings { name: String, @@ -244,8 +242,8 @@ pub enum TulispValue { value: TulispObject, }, Any(Rc), - Func(Rc), - Macro(Rc), + Func(Rc), + Macro(Rc), Defmacro { params: DefunParams, body: TulispObject, From 78a13fab0c08759b110a49ff2d58c8ced1d97f1c Mon Sep 17 00:00:00 2001 From: Sahas Subramanian Date: Tue, 23 Dec 2025 23:56:51 +0100 Subject: [PATCH 3/5] Wrap Rc/RefCell usage in Shared and SharedMut abstractions --- src/builtin/functions/hash_table.rs | 13 ++-- src/context.rs | 10 +-- src/lib.rs | 2 +- src/object.rs | 36 +++++----- src/object/wrappers.rs | 105 +++++++++++++++++++++++++++- src/value.rs | 27 ++++--- tests/tests.rs | 8 +-- 7 files changed, 156 insertions(+), 45 deletions(-) diff --git a/src/builtin/functions/hash_table.rs b/src/builtin/functions/hash_table.rs index ba966ed..10d2d4f 100644 --- a/src/builtin/functions/hash_table.rs +++ b/src/builtin/functions/hash_table.rs @@ -1,5 +1,8 @@ -use crate::{Error, TulispAny, TulispContext, TulispObject, destruct_eval_bind}; -use std::{cell::RefCell, collections::HashMap, rc::Rc}; +use crate::{ + Error, TulispContext, TulispObject, destruct_eval_bind, + object::wrappers::generic::{Shared, SharedMut}, +}; +use std::collections::HashMap; struct TulispObjectEql(TulispObject); @@ -29,7 +32,7 @@ impl From for TulispObjectEql { } pub(crate) struct HashTable { - inner: RefCell>, + inner: SharedMut>, } impl std::fmt::Display for HashTable { @@ -46,8 +49,8 @@ pub(crate) fn add(ctx: &mut TulispContext) { ) .with_trace(args.clone())); } - let table: Rc = Rc::new(HashTable { - inner: RefCell::new(HashMap::new()), + let table = Shared::new_any(HashTable { + inner: SharedMut::new(HashMap::new()), }); Ok(table.into()) }); diff --git a/src/context.rs b/src/context.rs index 17f193b..fc1d463 100644 --- a/src/context.rs +++ b/src/context.rs @@ -3,7 +3,7 @@ mod add_function; mod rest; pub use rest::Rest; -use std::{collections::HashMap, fs, rc::Rc}; +use std::{collections::HashMap, fs}; use crate::{ TulispObject, TulispValue, builtin, @@ -11,7 +11,7 @@ use crate::{ error::Error, eval::{DummyEval, eval, eval_and_then, eval_basic, funcall}, list, - object::wrappers::generic::TulispFn, + object::wrappers::generic::{Shared, TulispFn}, parse::parse, }; @@ -87,9 +87,9 @@ impl TulispContext { } #[inline(always)] - pub fn add_special_form(&mut self, name: &str, func: impl TulispFn) { + pub fn add_special_form(&mut self, name: &str, func: impl TulispFn + std::any::Any) { self.intern(name) - .set_global(TulispValue::Func(Rc::new(func)).into_ref(None)) + .set_global(TulispValue::Func(Shared::new_tulisp_fn(func)).into_ref(None)) .unwrap(); } @@ -124,7 +124,7 @@ impl TulispContext { #[inline(always)] pub fn add_macro(&mut self, name: &str, func: impl TulispFn) { self.intern(name) - .set_global(TulispValue::Macro(Rc::new(func)).into_ref(None)) + .set_global(TulispValue::Macro(Shared::new_tulisp_fn(func)).into_ref(None)) .unwrap(); } diff --git a/src/lib.rs b/src/lib.rs index 003d508..207a1ac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -99,7 +99,7 @@ pub use value::TulispAny; pub use value::TulispValue; mod object; -pub use object::TulispObject; +pub use {object::TulispObject, object::wrappers::generic::Shared}; #[cfg(test)] mod test_utils { diff --git a/src/object.rs b/src/object.rs index d8bb54e..43246a4 100644 --- a/src/object.rs +++ b/src/object.rs @@ -1,16 +1,13 @@ -pub(crate) mod wrappers; +pub mod wrappers; use crate::{ TulispValue, cons::{self, Cons}, error::Error, + object::wrappers::generic::{Shared, SharedMut}, value::TulispAny, }; -use std::{ - any::Any, - cell::{Ref, RefCell}, - rc::Rc, -}; +use std::cell::Ref; #[derive(Debug, Clone, PartialEq, Eq, Copy)] pub struct Span { @@ -32,8 +29,8 @@ impl Span { /// A type for representing tulisp objects. #[derive(Debug, Clone)] pub struct TulispObject { - rc: Rc>, - span: Rc>>, + rc: SharedMut, + span: SharedMut>, } impl Default for TulispObject { @@ -283,7 +280,7 @@ impl TulispObject { "Returns a string if `self` contains a string, and an Error otherwise." ); extractor_fn_with_err!( - Rc, + Shared, as_any, r#"Returns a boxed value if `self` contains a boxed value, and an Error otherwise. @@ -292,8 +289,7 @@ with `as_any`, and downcast to desired types. ## Example ```rust -# use tulisp::{TulispContext, destruct_bind, Error, TulispAny}; -# use std::rc::Rc; +# use tulisp::{TulispContext, destruct_bind, Error, TulispAny, Shared}; # # fn main() -> Result<(), Error> { let mut ctx = TulispContext::new(); @@ -312,7 +308,7 @@ ctx.add_special_form("make_any", |_ctx, args| { destruct_bind!((inp) = args); let inp: i64 = inp.try_into()?; - let any_obj: Rc = Rc::new(TestStruct { value: inp }); + let any_obj = Shared::new_any(TestStruct { value: inp }); Ok(any_obj.into()) }); @@ -374,8 +370,8 @@ impl TulispObject { pub(crate) fn new(vv: TulispValue, span: Option) -> TulispObject { Self { - rc: Rc::new(RefCell::new(vv)), - span: Rc::new(RefCell::new(span)), + rc: SharedMut::new(vv), + span: SharedMut::new(span), } } @@ -398,7 +394,7 @@ impl TulispObject { #[inline(always)] pub(crate) fn eq_ptr(&self, other: &TulispObject) -> bool { - Rc::ptr_eq(&self.rc, &other.rc) + self.rc.ptr_eq(&other.rc) } #[inline(always)] @@ -412,14 +408,14 @@ impl TulispObject { pub(crate) fn clone_without_span(&self) -> Self { Self { - rc: Rc::clone(&self.rc), - span: Rc::new(RefCell::new(None)), + rc: self.rc.clone(), + span: SharedMut::new(None), } } #[inline(always)] pub(crate) fn strong_count(&self) -> usize { - Rc::strong_count(&self.rc) + self.rc.strong_count() } #[inline(always)] @@ -554,7 +550,7 @@ impl From for bool { } } -impl TryFrom for Rc { +impl TryFrom for Shared { type Error = Error; fn try_from(value: TulispObject) -> Result { @@ -577,7 +573,7 @@ tulisp_object_from!(f64); tulisp_object_from!(&str); tulisp_object_from!(String); tulisp_object_from!(bool); -tulisp_object_from!(Rc); +tulisp_object_from!(Shared); impl FromIterator for TulispObject { fn from_iter>(iter: T) -> Self { diff --git a/src/object/wrappers.rs b/src/object/wrappers.rs index 09b68ea..a71cfc6 100644 --- a/src/object/wrappers.rs +++ b/src/object/wrappers.rs @@ -1,8 +1,111 @@ use crate::{Error, TulispContext, TulispObject}; -pub(crate) mod generic { +pub mod generic { + use std::ops::Deref; + + use crate::TulispAny; + use super::*; + #[repr(transparent)] + #[derive(Debug)] + pub struct Shared(std::rc::Rc); + + impl std::fmt::Display for Shared { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Shared({})", self.0) + } + } + + impl Clone for Shared { + fn clone(&self) -> Self { + Self(self.0.clone()) + } + } + + impl Shared { + pub fn new_tulisp_fn(val: impl TulispFn) -> Shared { + Shared(std::rc::Rc::new(val)) + } + + pub fn new_any(val: impl TulispAny) -> Shared { + Shared(std::rc::Rc::new(val)) + } + + pub fn downcast_ref(&self) -> Option<&U> { + let a: &dyn std::any::Any = &*self.0; + a.downcast_ref::() + } + + pub fn downcast(self) -> Result, Shared> { + match std::rc::Rc::downcast::(self.0.clone()) { + Ok(v) => Ok(Shared(v)), + Err(_) => Err(Shared(self.0)), + } + } + } + + impl Shared { + pub fn new(val: T) -> Self { + Shared(std::rc::Rc::new(val)) + } + + pub fn ptr_eq(&self, other: &Self) -> bool { + std::rc::Rc::ptr_eq(&self.0, &other.0) + } + } + + impl Deref for Shared { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl std::ops::DerefMut for Shared { + fn deref_mut(&mut self) -> &mut Self::Target { + std::rc::Rc::get_mut(&mut self.0).expect("Multiple references exist") + } + } + + #[repr(transparent)] + #[derive(Clone, Debug)] + pub struct SharedMut(std::rc::Rc>); + + impl SharedMut { + pub fn new(val: T) -> Self { + SharedMut(std::rc::Rc::new(std::cell::RefCell::new(val))) + } + pub fn ptr_eq(&self, other: &Self) -> bool { + std::rc::Rc::ptr_eq(&self.0, &other.0) + } + + pub fn borrow(&self) -> std::cell::Ref<'_, T> { + self.0.borrow() + } + + pub fn borrow_mut(&self) -> std::cell::RefMut<'_, T> { + self.0.borrow_mut() + } + + pub fn as_ptr(&self) -> *const T { + self.0.as_ptr() + } + + pub fn strong_count(&self) -> usize { + std::rc::Rc::strong_count(&self.0) + } + } + + impl Deref for SharedMut { + type Target = T; + + fn deref(&self) -> &Self::Target { + todo!() + } + } + pub trait TulispFn: Fn(&mut TulispContext, &TulispObject) -> Result + 'static { diff --git a/src/value.rs b/src/value.rs index 6a580c4..71d15d8 100644 --- a/src/value.rs +++ b/src/value.rs @@ -3,13 +3,15 @@ use crate::{ cons::{self, Cons}, context::Scope, error::Error, - object::{Span, wrappers::generic::TulispFn}, + object::{ + Span, + wrappers::generic::{Shared, TulispFn}, + }, }; use std::{ any::Any, convert::TryInto, fmt::{Display, Write}, - rc::Rc, }; #[doc(hidden)] @@ -198,6 +200,13 @@ impl SymbolBindings { } pub trait TulispAny: Any + Display {} + +impl std::fmt::Debug for dyn TulispAny { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "TulispAny({})", self) + } +} + impl TulispAny for T {} #[doc(hidden)] @@ -241,9 +250,9 @@ pub enum TulispValue { Splice { value: TulispObject, }, - Any(Rc), - Func(Rc), - Macro(Rc), + Any(Shared), + Func(Shared), + Macro(Shared), Defmacro { params: DefunParams, body: TulispObject, @@ -720,11 +729,11 @@ impl TulispValue { } #[inline(always)] - pub(crate) fn as_any(&self) -> Result, Error> { + pub(crate) fn as_any(&self) -> Result, Error> { match self { TulispValue::Any(value) => Ok(value.clone()), _ => Err(Error::type_mismatch(format!( - "Expected Any(Rc), got: {}", + "Expected Any(Shared), got: {}", self ))), } @@ -819,8 +828,8 @@ impl From for TulispValue { } } -impl From> for TulispValue { - fn from(value: Rc) -> Self { +impl From> for TulispValue { + fn from(value: Shared) -> Self { TulispValue::Any(value) } } diff --git a/tests/tests.rs b/tests/tests.rs index 8a8c29b..45242fa 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -1,5 +1,5 @@ -use std::{fmt::Display, rc::Rc}; -use tulisp::{Error, Iter, TulispAny, TulispContext, TulispObject, destruct_eval_bind}; +use std::fmt::Display; +use tulisp::{Error, Iter, Shared, TulispAny, TulispContext, TulispObject, destruct_eval_bind}; macro_rules! tulisp_assert { (@impl $ctx: expr, program:$input:expr, result:$result:expr $(,)?) => { @@ -1165,7 +1165,7 @@ fn test_any() -> Result<(), Error> { ctx.add_special_form("make_any", |ctx, args| { destruct_eval_bind!(ctx, (inp) = args); - let res: Rc = Rc::new(TestStruct { + let res: Shared = Shared::new_any(TestStruct { value: inp.try_into()?, }); @@ -1190,7 +1190,7 @@ fn test_any() -> Result<(), Error> { tulisp_assert! { ctx: ctx, program: "(get_int 55)", - error: r#"ERR TypeMismatch: Expected Any(Rc), got: 55 + error: r#"ERR TypeMismatch: Expected Any(Shared), got: 55 :1.10-1.11: at 55 :1.1-1.12: at (get_int 55) "# From 948a8cfa436703210901ca66e3cb063febef6b39 Mon Sep 17 00:00:00 2001 From: Sahas Subramanian Date: Wed, 24 Dec 2025 21:54:06 +0100 Subject: [PATCH 4/5] Replace direct pointer cast with `SharedMut::addr_as_usize` method --- src/object.rs | 2 +- src/object/wrappers.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/object.rs b/src/object.rs index 43246a4..1ae510a 100644 --- a/src/object.rs +++ b/src/object.rs @@ -403,7 +403,7 @@ impl TulispObject { } pub(crate) fn addr_as_usize(&self) -> usize { - self.rc.as_ptr() as usize + self.rc.addr_as_usize() } pub(crate) fn clone_without_span(&self) -> Self { diff --git a/src/object/wrappers.rs b/src/object/wrappers.rs index a71cfc6..5e47e8f 100644 --- a/src/object/wrappers.rs +++ b/src/object/wrappers.rs @@ -89,8 +89,8 @@ pub mod generic { self.0.borrow_mut() } - pub fn as_ptr(&self) -> *const T { - self.0.as_ptr() + pub fn addr_as_usize(&self) -> usize { + self.0.as_ptr() as usize } pub fn strong_count(&self) -> usize { From baf16ada8572ce8c842ec9fe185e163c8fe71e16 Mon Sep 17 00:00:00 2001 From: Sahas Subramanian Date: Wed, 24 Dec 2025 21:56:25 +0100 Subject: [PATCH 5/5] Add `sync` feature for thread-safe use --- .github/workflows/ci.yaml | 3 + Cargo.toml | 1 + src/context/add_function.rs | 25 ++++---- src/object.rs | 5 +- src/object/wrappers.rs | 114 ++++++++++++++++++++++++++++++++++++ src/value.rs | 6 +- 6 files changed, 136 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 25c2d37..5697cdf 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -13,3 +13,6 @@ jobs: - uses: actions/checkout@v3 - name: Run tests run: cargo test + + - name: Run sync tests + run: cargo test --features sync diff --git a/Cargo.toml b/Cargo.toml index 29e993d..ccdc20f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,4 +20,5 @@ name = "tulisp" path = "src/lib.rs" [features] +sync = [] big_functions = [] diff --git a/src/context/add_function.rs b/src/context/add_function.rs index e87cc18..127f7b9 100644 --- a/src/context/add_function.rs +++ b/src/context/add_function.rs @@ -1,3 +1,4 @@ +use crate::object::wrappers::generic::SyncSend; use crate::{Error, Rest, TulispContext, TulispObject, destruct_bind, destruct_eval_bind}; pub trait TulispCallable< @@ -25,7 +26,7 @@ macro_rules! impl_tulisp_callable { impl TulispCallable<($($arg,)* $($opt,)*) , OutT, false, $args_count, $opts_count, false, true, false> for FnT where - FnT: Fn($($arg,)* $(Option<$opt>),*) -> OutT + 'static, + FnT: Fn($($arg,)* $(Option<$opt>),*) -> OutT + 'static + SyncSend, $($arg: TryFrom + 'static,)* $($opt: TryFrom + 'static,)* OutT: Into + 'static, @@ -51,7 +52,7 @@ macro_rules! impl_tulisp_callable { impl TulispCallable<($($arg,)* $($opt,)*) , (), false, $args_count, $opts_count, false, false, false> for FnT where - FnT: Fn($($arg,)* $(Option<$opt>),*) + 'static, + FnT: Fn($($arg,)* $(Option<$opt>),*) + 'static + SyncSend, $($arg: TryFrom + 'static,)* $($opt: TryFrom + 'static,)* { @@ -76,7 +77,7 @@ macro_rules! impl_tulisp_callable { impl TulispCallable<($($arg,)* $($opt,)*) , OutT, true, $args_count, $opts_count, false, true,false> for FnT where - FnT: Fn(&mut TulispContext, $($arg,)* $(Option<$opt>),*) -> OutT + 'static, + FnT: Fn(&mut TulispContext, $($arg,)* $(Option<$opt>),*) -> OutT + 'static + SyncSend, $($arg: TryFrom + 'static,)* $($opt: TryFrom + 'static,)* OutT: Into + 'static, @@ -102,7 +103,7 @@ macro_rules! impl_tulisp_callable { impl TulispCallable<($($arg,)* $($opt,)*), (), true, $args_count, $opts_count, false, false,false> for FnT where - FnT: Fn(&mut TulispContext, $($arg,)* $(Option<$opt>),*) + 'static, + FnT: Fn(&mut TulispContext, $($arg,)* $(Option<$opt>),*) + 'static + SyncSend, $($arg: TryFrom + 'static,)* $($opt: TryFrom + 'static,)* { @@ -127,7 +128,7 @@ macro_rules! impl_tulisp_callable { impl TulispCallable<($($arg,)* $($opt,)*) , OutT, false, $args_count, $opts_count, false, true, true> for FnT where - FnT: Fn($($arg,)* $(Option<$opt>),*) -> Result + 'static, + FnT: Fn($($arg,)* $(Option<$opt>),*) -> Result + 'static + SyncSend, $($arg: TryFrom + 'static,)* $($opt: TryFrom + 'static,)* OutT: Into + 'static, @@ -153,7 +154,7 @@ macro_rules! impl_tulisp_callable { impl TulispCallable<($($arg,)* $($opt,)*) , OutT, true, $args_count, $opts_count, false, true, true> for FnT where - FnT: Fn(&mut TulispContext, $($arg,)* $(Option<$opt>),*) -> Result + 'static, + FnT: Fn(&mut TulispContext, $($arg,)* $(Option<$opt>),*) -> Result + 'static + SyncSend, $($arg: TryFrom + 'static,)* $($opt: TryFrom + 'static,)* OutT: Into + 'static, @@ -179,7 +180,7 @@ macro_rules! impl_tulisp_callable { impl TulispCallable<($($arg,)* $($opt,)* RestT,), OutT, false, $args_count, $opts_count, true, true, false> for FnT where - FnT: Fn($($arg,)* $(Option<$opt>,)* Rest) -> OutT + 'static, + FnT: Fn($($arg,)* $(Option<$opt>,)* Rest) -> OutT + 'static + SyncSend, $($arg: TryFrom + 'static,)* $($opt: TryFrom + 'static,)* RestT: TryFrom + Into + 'static, @@ -216,7 +217,7 @@ macro_rules! impl_tulisp_callable { impl TulispCallable<($($arg,)* $($opt,)* RestT,), (), false, $args_count, $opts_count, true, false, false> for FnT where - FnT: Fn($($arg,)* $(Option<$opt>,)* Rest) + 'static, + FnT: Fn($($arg,)* $(Option<$opt>,)* Rest) + 'static + SyncSend, $($arg: TryFrom + 'static,)* $($opt: TryFrom + 'static,)* RestT: TryFrom + Into + 'static, @@ -252,7 +253,7 @@ macro_rules! impl_tulisp_callable { impl TulispCallable<($($arg,)* $($opt,)* RestT,), OutT, true, $args_count, $opts_count, true, true, false> for FnT where - FnT: Fn(&mut TulispContext, $($arg,)* $(Option<$opt>,)* Rest) -> OutT + 'static, + FnT: Fn(&mut TulispContext, $($arg,)* $(Option<$opt>,)* Rest) -> OutT + 'static + SyncSend, $($arg: TryFrom + 'static,)* $($opt: TryFrom + 'static,)* RestT: TryFrom + Into + 'static, @@ -290,7 +291,7 @@ macro_rules! impl_tulisp_callable { impl TulispCallable<($($arg,)* $($opt,)* RestT,), (), true, $args_count, $opts_count, true, false, false> for FnT where - FnT: Fn(&mut TulispContext, $($arg,)* $(Option<$opt>,)* Rest) + 'static, + FnT: Fn(&mut TulispContext, $($arg,)* $(Option<$opt>,)* Rest) + 'static + SyncSend, $($arg: TryFrom + 'static,)* $($opt: TryFrom + 'static,)* RestT: TryFrom + Into + 'static, @@ -327,7 +328,7 @@ macro_rules! impl_tulisp_callable { impl TulispCallable<($($arg,)* $($opt,)* RestT,), OutT, false, $args_count, $opts_count, true, true, true> for FnT where - FnT: Fn($($arg,)* $(Option<$opt>,)* Rest) -> Result + 'static, + FnT: Fn($($arg,)* $(Option<$opt>,)* Rest) -> Result + 'static + SyncSend, $($arg: TryFrom + 'static,)* $($opt: TryFrom + 'static,)* RestT: TryFrom + Into + 'static, @@ -364,7 +365,7 @@ macro_rules! impl_tulisp_callable { impl TulispCallable<($($arg,)* $($opt,)* RestT,), OutT, true, $args_count, $opts_count, true, true, true> for FnT where - FnT: Fn(&mut TulispContext, $($arg,)* $(Option<$opt>,)* Rest) -> Result + 'static, + FnT: Fn(&mut TulispContext, $($arg,)* $(Option<$opt>,)* Rest) -> Result + 'static + SyncSend, $($arg: TryFrom + 'static,)* $($opt: TryFrom + 'static,)* RestT: TryFrom + Into + 'static, diff --git a/src/object.rs b/src/object.rs index 1ae510a..af1e86b 100644 --- a/src/object.rs +++ b/src/object.rs @@ -4,10 +4,9 @@ use crate::{ TulispValue, cons::{self, Cons}, error::Error, - object::wrappers::generic::{Shared, SharedMut}, + object::wrappers::generic::{Shared, SharedMut, SharedRef}, value::TulispAny, }; -use std::cell::Ref; #[derive(Debug, Clone, PartialEq, Eq, Copy)] pub struct Span { @@ -428,7 +427,7 @@ impl TulispObject { } #[inline(always)] - pub(crate) fn inner_ref(&self) -> Ref<'_, TulispValue> { + pub(crate) fn inner_ref(&self) -> SharedRef<'_, TulispValue> { self.rc.borrow() } diff --git a/src/object/wrappers.rs b/src/object/wrappers.rs index 5e47e8f..b9ce14b 100644 --- a/src/object/wrappers.rs +++ b/src/object/wrappers.rs @@ -1,5 +1,6 @@ use crate::{Error, TulispContext, TulispObject}; +#[cfg(not(feature = "sync"))] pub mod generic { use std::ops::Deref; @@ -7,10 +8,15 @@ pub mod generic { use super::*; + pub trait SyncSend {} + impl SyncSend for T {} + #[repr(transparent)] #[derive(Debug)] pub struct Shared(std::rc::Rc); + pub type SharedRef<'a, T> = std::cell::Ref<'a, T>; + impl std::fmt::Display for Shared { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "Shared({})", self.0) @@ -115,3 +121,111 @@ pub mod generic { { } } + +#[cfg(feature = "sync")] +pub mod generic { + use std::ops::Deref; + + use crate::TulispAny; + + use super::*; + + pub trait SyncSend: Sync + Send {} + impl SyncSend for T where T: Send + Sync {} + + #[repr(transparent)] + #[derive(Debug)] + pub struct Shared(std::sync::Arc); + + pub type SharedRef<'a, T> = std::sync::RwLockReadGuard<'a, T>; + + impl std::fmt::Display for Shared { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Shared({})", self.0) + } + } + + impl Clone for Shared { + fn clone(&self) -> Self { + Self(self.0.clone()) + } + } + + impl Shared { + pub fn new_tulisp_fn(val: impl TulispFn) -> Shared { + Shared(std::sync::Arc::new(val)) + } + + pub fn new_any(val: impl TulispAny) -> Shared { + Shared(std::sync::Arc::new(val)) + } + + pub fn downcast_ref(&self) -> Option<&U> { + let a: &dyn std::any::Any = &*self.0; + a.downcast_ref::() + } + + pub fn downcast(self) -> Result, Shared> { + match std::sync::Arc::downcast::(self.0.clone()) { + Ok(v) => Ok(Shared(v)), + Err(_) => Err(Shared(self.0)), + } + } + } + + impl Shared { + pub fn new(val: T) -> Self { + Shared(std::sync::Arc::new(val)) + } + + pub fn ptr_eq(&self, other: &Self) -> bool { + std::sync::Arc::ptr_eq(&self.0, &other.0) + } + } + + impl Deref for Shared { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + #[repr(transparent)] + #[derive(Clone, Debug)] + pub struct SharedMut(std::sync::Arc>); + impl SharedMut { + pub fn new(val: T) -> Self { + SharedMut(std::sync::Arc::new(std::sync::RwLock::new(val))) + } + pub fn ptr_eq(&self, other: &Self) -> bool { + std::sync::Arc::ptr_eq(&self.0, &other.0) + } + + pub fn borrow(&self) -> std::sync::RwLockReadGuard<'_, T> { + self.0.read().unwrap() + } + + pub fn borrow_mut(&self) -> std::sync::RwLockWriteGuard<'_, T> { + self.0.write().unwrap() + } + + pub fn addr_as_usize(&self) -> usize { + std::sync::Arc::as_ptr(&self.0) as usize + } + + pub fn strong_count(&self) -> usize { + std::sync::Arc::strong_count(&self.0) + } + } + pub trait TulispFn: + Fn(&mut TulispContext, &TulispObject) -> Result + SyncSend + 'static + { + } + impl TulispFn for T where + T: Fn(&mut TulispContext, &TulispObject) -> Result + + SyncSend + + 'static + { + } +} diff --git a/src/value.rs b/src/value.rs index 71d15d8..ae63d83 100644 --- a/src/value.rs +++ b/src/value.rs @@ -5,7 +5,7 @@ use crate::{ error::Error, object::{ Span, - wrappers::generic::{Shared, TulispFn}, + wrappers::generic::{Shared, SyncSend, TulispFn}, }, }; use std::{ @@ -199,7 +199,7 @@ impl SymbolBindings { } } -pub trait TulispAny: Any + Display {} +pub trait TulispAny: Any + Display + SyncSend {} impl std::fmt::Debug for dyn TulispAny { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -207,7 +207,7 @@ impl std::fmt::Debug for dyn TulispAny { } } -impl TulispAny for T {} +impl TulispAny for T {} #[doc(hidden)] #[derive(Clone)]