Skip to content

Commit 73d5cfd

Browse files
committed
use linked list
1 parent 6ed1207 commit 73d5cfd

File tree

2 files changed

+40
-22
lines changed

2 files changed

+40
-22
lines changed

src/atom.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ impl<Static> Drop for Atom<Static> {
236236

237237
// Out of line to guide inlining.
238238
fn drop_slow<Static>(this: &mut Atom<Static>) {
239-
DYNAMIC_SET.remove(this.unsafe_data.get() as *const Entry);
239+
DYNAMIC_SET.remove(this.unsafe_data.get() as *mut Entry);
240240
}
241241
}
242242
}

src/dynamic_set.rs

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,14 @@ const NB_BUCKETS: usize = 1 << 12; // 4096
1919
const BUCKET_MASK: u32 = (1 << 12) - 1;
2020

2121
pub(crate) struct Set {
22-
buckets: Box<[Mutex<Vec<Box<Entry>>>]>,
22+
buckets: Box<[Mutex<Option<Box<Entry>>>]>,
2323
}
2424

2525
pub(crate) struct Entry {
2626
pub(crate) string: Box<str>,
2727
pub(crate) hash: u32,
2828
pub(crate) ref_count: AtomicIsize,
29+
next_in_bucket: Option<Box<Entry>>,
2930
}
3031

3132
// Addresses are a multiples of this,
@@ -40,53 +41,70 @@ fn entry_alignment_is_sufficient() {
4041
pub(crate) static DYNAMIC_SET: Lazy<Set> = Lazy::new(|| {
4142
// NOTE: Using const initialization for buckets breaks the small-stack test.
4243
// ```
43-
// // buckets: [Mutex<Vec<Box<Entry>>>; NB_BUCKETS],
44-
// const MUTEX_VEC: Mutex<Vec<Box<Entry>>> = Mutex::new(vec![]);
45-
// let buckets = Box::new([MUTEX_VEC; NB_BUCKETS]);
44+
// // buckets: [Mutex<Option<Box<Entry>>>; NB_BUCKETS],
45+
// const MUTEX: Mutex<Option<Box<Entry>>> = Mutex::new(None);
46+
// let buckets = Box::new([MUTEX; NB_BUCKETS]);
4647
// ```
47-
let buckets = (0..NB_BUCKETS).map(|_| Mutex::new(vec![])).collect();
48+
let buckets = (0..NB_BUCKETS).map(|_| Mutex::new(None)).collect();
4849
Set { buckets }
4950
});
5051

5152
impl Set {
5253
pub(crate) fn insert(&self, string: Cow<str>, hash: u32) -> NonNull<Entry> {
5354
let bucket_index = (hash & BUCKET_MASK) as usize;
54-
let mut vec = self.buckets[bucket_index].lock();
55-
if let Some(entry) = vec
56-
.iter_mut()
57-
.find(|e| e.hash == hash && *e.string == *string)
55+
let mut linked_list = self.buckets[bucket_index].lock();
56+
5857
{
59-
if entry.ref_count.fetch_add(1, SeqCst) > 0 {
60-
return NonNull::from(&mut **entry);
58+
let mut ptr: Option<&mut Box<Entry>> = linked_list.as_mut();
59+
60+
while let Some(entry) = ptr.take() {
61+
if entry.hash == hash && *entry.string == *string {
62+
if entry.ref_count.fetch_add(1, SeqCst) > 0 {
63+
return NonNull::from(&mut **entry);
64+
}
65+
// Uh-oh. The pointer's reference count was zero, which means someone may try
66+
// to free it. (Naive attempts to defend against this, for example having the
67+
// destructor check to see whether the reference count is indeed zero, don't
68+
// work due to ABA.) Thus we need to temporarily add a duplicate string to the
69+
// list.
70+
entry.ref_count.fetch_sub(1, SeqCst);
71+
break;
72+
}
73+
ptr = entry.next_in_bucket.as_mut();
6174
}
62-
// Uh-oh. The pointer's reference count was zero, which means someone may try
63-
// to free it. (Naive attempts to defend against this, for example having the
64-
// destructor check to see whether the reference count is indeed zero, don't
65-
// work due to ABA.) Thus we need to temporarily add a duplicate string to the
66-
// list.
67-
entry.ref_count.fetch_sub(1, SeqCst);
6875
}
6976
debug_assert!(mem::align_of::<Entry>() >= ENTRY_ALIGNMENT);
7077
let string = string.into_owned();
7178
let mut entry = Box::new(Entry {
79+
next_in_bucket: linked_list.take(),
7280
hash,
7381
ref_count: AtomicIsize::new(1),
7482
string: string.into_boxed_str(),
7583
});
7684
let ptr = NonNull::from(&mut *entry);
77-
vec.push(entry);
85+
*linked_list = Some(entry);
7886
ptr
7987
}
8088

81-
pub(crate) fn remove(&self, ptr: *const Entry) {
89+
pub(crate) fn remove(&self, ptr: *mut Entry) {
8290
let bucket_index = {
8391
let value: &Entry = unsafe { &*ptr };
8492
debug_assert!(value.ref_count.load(SeqCst) == 0);
8593
(value.hash & BUCKET_MASK) as usize
8694
};
8795

88-
let mut vec = self.buckets[bucket_index].lock();
96+
let mut linked_list = self.buckets[bucket_index].lock();
97+
let mut current: &mut Option<Box<Entry>> = &mut linked_list;
8998

90-
vec.retain(|e| (&**e as *const Entry) != ptr);
99+
while let Some(entry_ptr) = current.as_mut() {
100+
let entry_ptr: *mut Entry = &mut **entry_ptr;
101+
if entry_ptr == ptr {
102+
mem::drop(mem::replace(current, unsafe {
103+
(*entry_ptr).next_in_bucket.take()
104+
}));
105+
break;
106+
}
107+
current = unsafe { &mut (*entry_ptr).next_in_bucket };
108+
}
91109
}
92110
}

0 commit comments

Comments
 (0)