mirror of
https://github.com/servo/servo.git
synced 2025-08-04 05:00:08 +01:00
style: Add simple rule-tree benchmarks. Fix rule node drop race.
This commit is contained in:
parent
8412008525
commit
e67ea42c3f
6 changed files with 276 additions and 28 deletions
|
@ -438,27 +438,58 @@ impl StrongRuleNode {
|
|||
// That's... suspicious, but it's fine if it happens for the rule tree
|
||||
// case, so just don't crash in the case we're doing the final GC in
|
||||
// script.
|
||||
debug_assert!(!thread_state::get().is_worker() &&
|
||||
(thread_state::get().is_layout() ||
|
||||
thread_state::get().is_script()));
|
||||
let current = me.next_free.load(Ordering::SeqCst);
|
||||
if !cfg!(feature = "testing") {
|
||||
debug_assert!(!thread_state::get().is_worker() &&
|
||||
(thread_state::get().is_layout() ||
|
||||
thread_state::get().is_script()));
|
||||
}
|
||||
|
||||
let current = me.next_free.load(Ordering::SeqCst);
|
||||
if current == FREE_LIST_SENTINEL {
|
||||
return None;
|
||||
}
|
||||
|
||||
let current = WeakRuleNode { ptr: current };
|
||||
debug_assert!(!current.is_null(),
|
||||
"Multiple threads are operating on the free list at the \
|
||||
same time?");
|
||||
debug_assert!(current != self.ptr,
|
||||
"How did the root end up in the free list?");
|
||||
|
||||
let next = (*current).next_free.swap(ptr::null_mut(), Ordering::SeqCst);
|
||||
|
||||
debug_assert!(!next.is_null(),
|
||||
"How did a null pointer end up in the free list?");
|
||||
|
||||
let node = &*current.ptr();
|
||||
let next = node.next_free.swap(ptr::null_mut(), Ordering::SeqCst);
|
||||
me.next_free.store(next, Ordering::SeqCst);
|
||||
|
||||
debug!("Popping from free list: cur: {:?}, next: {:?}", current.ptr(), next);
|
||||
debug!("Popping from free list: cur: {:?}, next: {:?}", current, next);
|
||||
|
||||
Some(current)
|
||||
Some(WeakRuleNode { ptr: current })
|
||||
}
|
||||
|
||||
unsafe fn assert_free_list_has_no_duplicates_or_null(&self) {
|
||||
assert!(cfg!(debug_assertions));
|
||||
|
||||
let me = &*self.ptr;
|
||||
assert!(me.is_root());
|
||||
|
||||
let mut current = self.ptr;
|
||||
let mut seen = vec![];
|
||||
while current != FREE_LIST_SENTINEL {
|
||||
let next = (*current).next_free.load(Ordering::SeqCst);
|
||||
assert!(!next.is_null());
|
||||
assert!(!seen.contains(&next));
|
||||
seen.push(next);
|
||||
|
||||
current = next;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn gc(&self) {
|
||||
if cfg!(debug_assertions) {
|
||||
self.assert_free_list_has_no_duplicates_or_null();
|
||||
}
|
||||
|
||||
// NB: This can run from the root node destructor, so we can't use
|
||||
// `get()`, since it asserts the refcount is bigger than zero.
|
||||
let me = &*self.ptr;
|
||||
|
@ -552,29 +583,41 @@ impl Drop for StrongRuleNode {
|
|||
return;
|
||||
}
|
||||
|
||||
// The node is already in the free list, so do nothing.
|
||||
let root = unsafe { &*node.root.as_ref().unwrap().ptr() };
|
||||
let free_list = &root.next_free;
|
||||
|
||||
// We're sure we're already in the free list, don't spinloop.
|
||||
if node.next_free.load(Ordering::SeqCst) != ptr::null_mut() {
|
||||
return;
|
||||
}
|
||||
|
||||
let root = unsafe { &*node.root.as_ref().unwrap().ptr() };
|
||||
let free_list = &root.next_free;
|
||||
// Ensure we "lock" the free list head swapping it with a null pointer.
|
||||
let mut old_head = free_list.load(Ordering::SeqCst);
|
||||
loop {
|
||||
let next_free = free_list.load(Ordering::SeqCst);
|
||||
debug_assert!(!next_free.is_null());
|
||||
|
||||
node.next_free.store(next_free, Ordering::SeqCst);
|
||||
|
||||
let existing =
|
||||
free_list.compare_and_swap(next_free,
|
||||
self.ptr(),
|
||||
Ordering::SeqCst);
|
||||
if existing == next_free {
|
||||
// Successfully inserted, yay! Otherwise try again.
|
||||
root.free_count.fetch_add(1, Ordering::Relaxed);
|
||||
break;
|
||||
match free_list.compare_exchange_weak(old_head,
|
||||
ptr::null_mut(),
|
||||
Ordering::SeqCst,
|
||||
Ordering::Relaxed) {
|
||||
Ok(..) => {
|
||||
if old_head != ptr::null_mut() {
|
||||
break;
|
||||
}
|
||||
},
|
||||
Err(new) => old_head = new,
|
||||
}
|
||||
}
|
||||
|
||||
// If other thread has raced with use while using the same rule node,
|
||||
// just store the old head again, we're done.
|
||||
if node.next_free.load(Ordering::SeqCst) != ptr::null_mut() {
|
||||
free_list.store(old_head, Ordering::SeqCst);
|
||||
return;
|
||||
}
|
||||
|
||||
// Else store the old head as the next pointer, and store ourselves as
|
||||
// the new head of the free list.
|
||||
node.next_free.store(old_head, Ordering::SeqCst);
|
||||
free_list.store(self.ptr(), Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue