Skip to content

Commit 370b2b3

Browse files
committed
static object
1 parent 3dabaa8 commit 370b2b3

File tree

4 files changed

+80
-21
lines changed

4 files changed

+80
-21
lines changed

common/src/refcount.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::atomic::{Ordering::*, PyAtomic, Radium};
55
///
66
/// Going above this limit will abort your program (although not
77
/// necessarily) at _exactly_ `MAX_REFCOUNT + 1` references.
8-
const MAX_REFCOUNT: usize = (isize::MAX) as usize;
8+
const MAX_REFCOUNT: usize = isize::MAX as usize;
99

1010
pub struct RefCount {
1111
strong: PyAtomic<usize>,
@@ -18,6 +18,8 @@ impl Default for RefCount {
1818
}
1919

2020
impl RefCount {
21+
const MASK: usize = MAX_REFCOUNT;
22+
2123
pub fn new() -> Self {
2224
RefCount {
2325
strong: Radium::new(1),
@@ -33,7 +35,7 @@ impl RefCount {
3335
pub fn inc(&self) {
3436
let old_size = self.strong.fetch_add(1, Relaxed);
3537

36-
if old_size > MAX_REFCOUNT {
38+
if old_size & Self::MASK == Self::MASK {
3739
std::process::abort();
3840
}
3941
}
@@ -58,3 +60,19 @@ impl RefCount {
5860
true
5961
}
6062
}
63+
64+
impl RefCount {
65+
// move these functions out and give separated type once type range is stabilized
66+
67+
pub fn leak(&self) {
68+
debug_assert!(!self.is_leaked());
69+
const BIT_MARKER: usize = (std::isize::MAX as usize) + 1;
70+
debug_assert_eq!(BIT_MARKER.count_ones(), 1);
71+
debug_assert_eq!(BIT_MARKER.leading_zeros(), 0);
72+
self.strong.fetch_add(BIT_MARKER, Relaxed);
73+
}
74+
75+
pub fn is_leaked(&self) -> bool {
76+
(self.strong.load(Acquire) as isize) < 0
77+
}
78+
}

vm/src/intern.rs

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::{
22
builtins::{PyStr, PyTypeRef},
33
common::lock::PyRwLock,
44
convert::ToPyObject,
5-
Py, PyExact, PyObject, PyObjectRef, PyRef, PyRefExact,
5+
AsObject, Py, PyExact, PyObject, PyObjectRef, PyRef, PyRefExact,
66
};
77
use std::{
88
borrow::{Borrow, ToOwned},
@@ -43,14 +43,17 @@ impl StringPool {
4343
let inserted = zelf.inner.write().insert(cache.clone());
4444
if inserted {
4545
let interned = unsafe { PyStrInterned::borrow_cache(&cache) };
46-
// unsafe { interned.as_object().mark_intern() };
46+
unsafe { interned.as_object().mark_intern() };
4747
interned
4848
} else {
49-
zelf.inner
50-
.read()
51-
.get(cache.as_str())
52-
.map(|cached| unsafe { PyStrInterned::borrow_cache(cached) })
53-
.expect("")
49+
unsafe {
50+
PyStrInterned::borrow_cache(
51+
zelf.inner
52+
.read()
53+
.get(cache.as_ref())
54+
.expect("inserted is false"),
55+
)
56+
}
5457
}
5558
}
5659
let str_ref = s.into_pyref_exact(typ);
@@ -152,18 +155,20 @@ impl Borrow<PyObject> for PyStrInterned {
152155
}
153156
}
154157

155-
impl Deref for PyStrInterned {
156-
type Target = Py<PyStr>;
158+
// NOTE: std::hash::Hash of Self and Self::Borrowed *must* be the same
159+
// This is ok only because PyObject doesn't implement Hash
160+
impl std::hash::Hash for PyStrInterned {
157161
#[inline(always)]
158-
fn deref(&self) -> &Self::Target {
159-
&self.inner
162+
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
163+
self.get_id().hash(state)
160164
}
161165
}
162166

163-
impl std::hash::Hash for PyStrInterned {
167+
impl Deref for PyStrInterned {
168+
type Target = Py<PyStr>;
164169
#[inline(always)]
165-
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
166-
std::hash::Hash::hash(&(self as *const _), state)
170+
fn deref(&self) -> &Self::Target {
171+
&self.inner
167172
}
168173
}
169174

@@ -177,7 +182,7 @@ impl PartialEq for PyStrInterned {
177182
impl Eq for PyStrInterned {}
178183

179184
impl AsRef<str> for PyStrInterned {
180-
#[inline]
185+
#[inline(always)]
181186
fn as_ref(&self) -> &str {
182187
self.as_str()
183188
}
@@ -216,8 +221,12 @@ mod sealed {
216221
}
217222

218223
/// A sealed marker trait for `DictKey` types that always become an exact instance of `str`
219-
pub trait Internable: sealed::SealedInternable + ToPyObject + AsRef<Self::Interned> {
220-
type Interned: ?Sized + MaybeInterned;
224+
pub trait Internable
225+
where
226+
Self: sealed::SealedInternable + ToPyObject + AsRef<Self::Interned>,
227+
Self::Interned: MaybeInterned,
228+
{
229+
type Interned: ?Sized;
221230
fn into_pyref_exact(self, str_type: PyTypeRef) -> PyRefExact<PyStr>;
222231
}
223232

@@ -269,6 +278,24 @@ impl MaybeInterned for PyExact<PyStr> {
269278
impl MaybeInterned for Py<PyStr> {
270279
#[inline(always)]
271280
fn as_interned(&self) -> Option<&'static PyStrInterned> {
272-
None
281+
if self.as_object().is_interned() {
282+
Some(unsafe { std::mem::transmute(self) })
283+
} else {
284+
None
285+
}
286+
}
287+
}
288+
289+
impl PyObject {
290+
#[inline]
291+
pub fn as_interned_str(&self, vm: &crate::VirtualMachine) -> Option<&'static PyStrInterned> {
292+
let s: Option<&Py<PyStr>> = self.downcast_ref();
293+
if self.is_interned() {
294+
s.unwrap().as_interned()
295+
} else if let Some(s) = s {
296+
vm.ctx.interned_str(s.as_str())
297+
} else {
298+
None
299+
}
273300
}
274301
}

vm/src/object/core.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -783,6 +783,16 @@ impl PyObject {
783783
// call drop only when there are no references in scope - stacked borrows stuff
784784
drop_dealloc(ptr.as_ptr())
785785
}
786+
787+
/// # Safety
788+
/// This call will make the object live forever.
789+
pub(crate) unsafe fn mark_intern(&self) {
790+
self.0.ref_count.leak();
791+
}
792+
793+
pub(crate) fn is_interned(&self) -> bool {
794+
self.0.ref_count.is_leaked()
795+
}
786796
}
787797

788798
impl Borrow<PyObject> for PyObjectRef {

vm/src/vm/context.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::{
1313
class::{PyClassImpl, StaticType},
1414
exceptions,
1515
function::IntoPyNativeFunc,
16-
intern::{Internable, PyStrInterned, StringPool},
16+
intern::{Internable, MaybeInterned, PyStrInterned, StringPool},
1717
object::{PyObjectPayload, PyObjectRef, PyPayload, PyRef},
1818
types::{PyTypeFlags, PyTypeSlots, TypeZoo},
1919
};
@@ -119,6 +119,10 @@ impl Context {
119119
unsafe { self.string_pool.intern(s, self.types.str_type.clone()) }
120120
}
121121

122+
pub fn interned_str<S: MaybeInterned + ?Sized>(&self, s: &S) -> Option<&'static PyStrInterned> {
123+
self.string_pool.interned(s)
124+
}
125+
122126
#[inline(always)]
123127
pub fn none(&self) -> PyObjectRef {
124128
self.none.clone().into()

0 commit comments

Comments
 (0)