From 275919694debc4d474ac8b05dbb387b302f767aa Mon Sep 17 00:00:00 2001 From: Noah <33094578+coolreader18@users.noreply.github.com> Date: Thu, 22 Oct 2020 14:06:05 -0500 Subject: [PATCH] Make StaticCell less complex to use --- common/src/static_cell.rs | 134 ++++++++++++++++++++++++------------- derive/src/pyclass.rs | 3 +- vm/src/pyobject.rs | 137 +++++++++++++++++++------------------- 3 files changed, 156 insertions(+), 118 deletions(-) diff --git a/common/src/static_cell.rs b/common/src/static_cell.rs index 8e86e9aca..01a54db29 100644 --- a/common/src/static_cell.rs +++ b/common/src/static_cell.rs @@ -1,16 +1,66 @@ #[cfg(not(feature = "threading"))] mod non_threading { use crate::lock::OnceCell; - pub type StaticCell = std::thread::LocalKey>; + use std::thread::LocalKey; + + pub struct StaticCell { + inner: &'static LocalKey>, + } + + fn leak(x: T) -> &'static T { + Box::leak(Box::new(x)) + } + + impl StaticCell { + #[doc(hidden)] + pub const fn _from_localkey(inner: &'static LocalKey>) -> Self { + Self { inner } + } + + pub fn get(&'static self) -> Option<&'static T> { + self.inner.with(|x| x.get().copied()) + } + + pub fn set(&'static self, value: T) -> Result<(), T> { + // thread-safe because it's a unsync::OnceCell + self.inner.with(|x| { + if x.get().is_some() { + Err(value) + } else { + // will never fail + let _ = x.set(leak(value)); + Ok(()) + } + }) + } + + pub fn get_or_init(&'static self, f: F) -> &'static T + where + F: FnOnce() -> T, + { + self.inner.with(|x| *x.get_or_init(|| leak(f()))) + } + + pub fn get_or_try_init(&'static self, f: F) -> Result<&'static T, E> + where + F: FnOnce() -> Result, + { + self.inner + .with(|x| x.get_or_try_init(|| f().map(leak)).map(|&x| x)) + } + } #[macro_export] - macro_rules! static_cells { - // process multiple declarations - ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty;) => ( - std::thread_local! { - $(#[$attr])* $vis static $name: $crate::lock::OnceCell<&'static $t> = $crate::lock::OnceCell::new(); - } - ); + macro_rules! static_cell { + ($($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty;)+) => { + $($(#[$attr])* + $vis static $name: $crate::static_cell::StaticCell<$t> = { + ::std::thread_local! { + $vis static $name: $crate::lock::OnceCell<&'static $t> = $crate::lock::OnceCell::new(); + } + $crate::static_cell::StaticCell::_from_localkey(&$name) + };)+ + }; } } #[cfg(not(feature = "threading"))] @@ -20,57 +70,47 @@ pub use non_threading::*; mod threading { use crate::lock::OnceCell; - pub struct StaticKey { - inner: T, + pub struct StaticCell { + inner: OnceCell, } - impl StaticKey { - pub const fn new(inner: T) -> Self { + impl StaticCell { + #[doc(hidden)] + pub const fn _from_oncecell(inner: OnceCell) -> Self { Self { inner } } - pub fn with(&self, f: F) -> R + pub fn get(&'static self) -> Option<&'static T> { + self.inner.get() + } + + pub fn set(&'static self, value: T) -> Result<(), T> { + self.inner.set(value) + } + + pub fn get_or_init(&'static self, f: F) -> &'static T where - F: FnOnce(&T) -> R, + F: FnOnce() -> T, { - f(&self.inner) + self.inner.get_or_init(f) + } + + pub fn get_or_try_init(&'static self, f: F) -> Result<&'static T, E> + where + F: FnOnce() -> Result, + { + self.inner.get_or_try_init(f) } } - pub type StaticCell = StaticKey>; - #[macro_export] - macro_rules! static_cells { - // process multiple declarations - ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty;) => ( - $(#[$attr])* $vis static $name: $crate::static_cell::StaticKey<$crate::lock::OnceCell<&'static $t>> = $crate::static_cell::StaticKey::new($crate::lock::OnceCell::new()); - ); + macro_rules! static_cell { + ($($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty;)+) => { + $($(#[$attr])* + $vis static $name: $crate::static_cell::StaticCell<$t> = + $crate::static_cell::StaticCell::_from_oncecell($crate::lock::OnceCell::new());)+ + }; } } #[cfg(feature = "threading")] pub use threading::*; - -pub fn get(cell: &'static StaticCell) -> Option<&'static T> { - cell.with(|cell| cell.get().copied()) -} - -pub fn init_expect(cell: &'static StaticCell, value: T, msg: &'static str) -> &'static T { - cell.with(|cell| { - let static_ref = Box::leak(Box::new(value)) as &_; - cell.set(static_ref) - .unwrap_or_else(|_| panic!("double initializing '{}'", msg)); - static_ref - }) -} - -pub fn get_or_init(cell: &'static StaticCell, f: F) -> &'static T -where - F: FnOnce() -> T, -{ - cell.with(|cell| { - *cell.get_or_init(|| { - let value = f(); - Box::leak(Box::new(value)) - }) - }) -} diff --git a/derive/src/pyclass.rs b/derive/src/pyclass.rs index 99fe8c88f..5272778f0 100644 --- a/derive/src/pyclass.rs +++ b/derive/src/pyclass.rs @@ -197,8 +197,7 @@ fn generate_class_def( impl ::rustpython_vm::pyobject::StaticType for #ident { fn static_cell() -> &'static ::rustpython_common::static_cell::StaticCell<::rustpython_vm::builtins::PyTypeRef> { - use ::rustpython_common::static_cells; - static_cells! { + ::rustpython_common::static_cell! { static CELL: ::rustpython_vm::builtins::PyTypeRef; } &CELL diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index cbaa57c0c..a80496769 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -111,63 +111,59 @@ impl PyContext { pub const INT_CACHE_POOL_MIN: i32 = -5; pub const INT_CACHE_POOL_MAX: i32 = 256; + fn init() -> Self { + flame_guard!("init PyContext"); + let types = TypeZoo::init(); + let exceptions = exceptions::ExceptionZoo::init(); + + fn create_object(payload: T, cls: &PyTypeRef) -> PyRef { + PyRef::new_ref(payload, cls.clone(), None) + } + + let none = create_object(PyNone, PyNone::static_type()); + let ellipsis = create_object(PyEllipsis, PyEllipsis::static_type()); + let not_implemented = create_object(PyNotImplemented, PyNotImplemented::static_type()); + + let int_cache_pool = (Self::INT_CACHE_POOL_MIN..=Self::INT_CACHE_POOL_MAX) + .map(|v| PyRef::new_ref(PyInt::from(BigInt::from(v)), types.int_type.clone(), None)) + .collect(); + + let true_value = create_object(PyInt::from(1), &types.bool_type); + let false_value = create_object(PyInt::from(0), &types.bool_type); + + let empty_tuple = create_object( + PyTuple::_new(Vec::new().into_boxed_slice()), + &types.tuple_type, + ); + + let tp_new_wrapper = create_object( + PyNativeFuncDef::from(pytype::tp_new_wrapper.into_func()).into_function(), + &types.builtin_function_or_method_type, + ) + .into_object(); + + let context = PyContext { + true_value, + false_value, + not_implemented, + none, + empty_tuple, + ellipsis, + + types, + exceptions, + int_cache_pool, + tp_new_wrapper, + }; + TypeZoo::extend(&context); + exceptions::ExceptionZoo::extend(&context); + context + } pub fn new() -> Self { - use rustpython_common::static_cells; - static_cells! { + rustpython_common::static_cell! { static CONTEXT: PyContext; } - static_cell::get_or_init(&CONTEXT, || { - flame_guard!("init PyContext"); - let types = TypeZoo::init(); - let exceptions = exceptions::ExceptionZoo::init(); - - fn create_object( - payload: T, - cls: &PyTypeRef, - ) -> PyRef { - PyRef::new_ref(payload, cls.clone(), None) - } - - let none = create_object(PyNone, PyNone::static_type()); - let ellipsis = create_object(PyEllipsis, PyEllipsis::static_type()); - let not_implemented = create_object(PyNotImplemented, PyNotImplemented::static_type()); - - let int_cache_pool = (Self::INT_CACHE_POOL_MIN..=Self::INT_CACHE_POOL_MAX) - .map(|v| PyRef::new_ref(PyInt::from(BigInt::from(v)), types.int_type.clone(), None)) - .collect(); - - let true_value = create_object(PyInt::from(1), &types.bool_type); - let false_value = create_object(PyInt::from(0), &types.bool_type); - - let empty_tuple = create_object( - PyTuple::_new(Vec::new().into_boxed_slice()), - &types.tuple_type, - ); - - let tp_new_wrapper = create_object( - PyNativeFuncDef::from(pytype::tp_new_wrapper.into_func()).into_function(), - &types.builtin_function_or_method_type, - ) - .into_object(); - - let context = PyContext { - true_value, - false_value, - not_implemented, - none, - empty_tuple, - ellipsis, - - types, - exceptions, - int_cache_pool, - tp_new_wrapper, - }; - TypeZoo::extend(&context); - exceptions::ExceptionZoo::extend(&context); - context - }) - .clone() + CONTEXT.get_or_init(Self::init).clone() } pub fn none(&self) -> PyObjectRef { @@ -1242,23 +1238,25 @@ pub trait StaticType { crate::builtins::object::PyBaseObject::static_type() } fn static_type() -> &'static PyTypeRef { - static_cell::get(Self::static_cell()).unwrap_or_else(|| unsafe { - // SAFETY: object must be initialized by init_* method. - // So this is actually not safe as itself. - // But easy to find out when it happened in debug build - std::hint::unreachable_unchecked() - }) + Self::static_cell() + .get() + .expect("static type has not been initialized") } - fn init_manually(typ: PyTypeRef) -> &'static PyTypeRef { - static_cell::init_expect(Self::static_cell(), typ, "init_manually") + let cell = Self::static_cell(); + cell.set(typ) + .unwrap_or_else(|_| panic!("double initialization from init_manually")); + cell.get().unwrap() } fn init_bare_type() -> &'static PyTypeRef where Self: PyClassImpl, { let typ = Self::create_bare_type(); - static_cell::init_expect(Self::static_cell(), typ, Self::NAME) + let cell = Self::static_cell(); + cell.set(typ) + .unwrap_or_else(|_| panic!("double initialization of {}", Self::NAME)); + cell.get().unwrap() } fn create_bare_type() -> PyTypeRef where @@ -1313,12 +1311,13 @@ pub trait PyClassImpl: PyClassDef { where Self: StaticType, { - static_cell::get_or_init(Self::static_cell(), || { - let typ = Self::create_bare_type(); - Self::extend_class(ctx, &typ); - typ - }) - .clone() + Self::static_cell() + .get_or_init(|| { + let typ = Self::create_bare_type(); + Self::extend_class(ctx, &typ); + typ + }) + .clone() } fn extend_slots(slots: &mut PyTypeSlots);