Skip to main content

style/rule_tree/
core.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5#![allow(unsafe_code)]
6
7use crate::applicable_declarations::CascadePriority;
8use crate::shared_lock::StylesheetGuards;
9use crate::stylesheets::layer_rule::LayerOrder;
10use malloc_size_of::{MallocShallowSizeOf, MallocSizeOf, MallocSizeOfOps};
11use parking_lot::RwLock;
12use smallvec::SmallVec;
13use std::fmt;
14use std::hash;
15use std::io::Write;
16use std::mem;
17use std::ptr;
18use std::sync::atomic::{self, AtomicPtr, AtomicUsize, Ordering};
19
20use super::map::{Entry, Map};
21use super::unsafe_box::UnsafeBox;
22use super::{CascadeLevel, CascadeOrigin, RuleCascadeFlags, StyleSource};
23
24/// The rule tree, the structure servo uses to preserve the results of selector
25/// matching.
26///
27/// This is organized as a tree of rules. When a node matches a set of rules,
28/// they're inserted in order in the tree, starting with the less specific one.
29///
30/// When a rule is inserted in the tree, other elements may share the path up to
31/// a given rule. If that's the case, we don't duplicate child nodes, but share
32/// them.
33///
34/// When the rule node refcount drops to zero, it doesn't get freed. It gets
35/// instead put into a free list, and it is potentially GC'd after a while.
36///
37/// That way, a rule node that represents a likely-to-match-again rule (like a
38/// :hover rule) can be reused if we haven't GC'd it yet.
39#[derive(Debug)]
40pub struct RuleTree {
41    root: StrongRuleNode,
42}
43
44impl Drop for RuleTree {
45    fn drop(&mut self) {
46        unsafe { self.swap_free_list_and_gc(ptr::null_mut()) }
47    }
48}
49
50impl MallocSizeOf for RuleTree {
51    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
52        let mut n = 0;
53        let mut stack = SmallVec::<[_; 32]>::new();
54        stack.push(self.root.clone());
55
56        while let Some(node) = stack.pop() {
57            n += unsafe { ops.malloc_size_of(&*node.p) };
58            let children = node.p.children.read();
59            children.shallow_size_of(ops);
60            for c in &*children {
61                stack.push(unsafe { c.upgrade() });
62            }
63        }
64
65        n
66    }
67}
68
69#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
70struct ChildKey(CascadePriority, ptr::NonNull<()>);
71unsafe impl Send for ChildKey {}
72unsafe impl Sync for ChildKey {}
73
74impl Default for RuleTree {
75    fn default() -> Self {
76        Self::new()
77    }
78}
79
80impl RuleTree {
81    /// Construct a new rule tree.
82    pub fn new() -> Self {
83        RuleTree {
84            root: StrongRuleNode::new(Box::new(RuleNode::root())),
85        }
86    }
87
88    /// Get the root rule node.
89    pub fn root(&self) -> &StrongRuleNode {
90        &self.root
91    }
92
93    /// This can only be called when no other threads is accessing this tree.
94    pub fn gc(&self) {
95        unsafe { self.swap_free_list_and_gc(RuleNode::DANGLING_PTR) }
96    }
97
98    /// This can only be called when no other threads is accessing this tree.
99    pub fn maybe_gc(&self) {
100        #[cfg(debug_assertions)]
101        self.maybe_dump_stats();
102
103        if self.root.p.approximate_free_count.load(Ordering::Relaxed) > RULE_TREE_GC_INTERVAL {
104            self.gc();
105        }
106    }
107
108    #[cfg(debug_assertions)]
109    fn maybe_dump_stats(&self) {
110        use itertools::Itertools;
111        use std::cell::Cell;
112        use std::time::{Duration, Instant};
113
114        if !log_enabled!(log::Level::Trace) {
115            return;
116        }
117
118        const RULE_TREE_STATS_INTERVAL: Duration = Duration::from_secs(2);
119
120        thread_local! {
121            pub static LAST_STATS: Cell<Instant> = Cell::new(Instant::now());
122        };
123
124        let should_dump = LAST_STATS.with(|s| {
125            let now = Instant::now();
126            if now.duration_since(s.get()) < RULE_TREE_STATS_INTERVAL {
127                return false;
128            }
129            s.set(now);
130            true
131        });
132
133        if !should_dump {
134            return;
135        }
136
137        let mut children_count = rustc_hash::FxHashMap::default();
138
139        let mut stack = SmallVec::<[_; 32]>::new();
140        stack.push(self.root.clone());
141        while let Some(node) = stack.pop() {
142            let children = node.p.children.read();
143            *children_count.entry(children.len()).or_insert(0) += 1;
144            for c in &*children {
145                stack.push(unsafe { c.upgrade() });
146            }
147        }
148
149        trace!("Rule tree stats:");
150        let counts = children_count.keys().sorted();
151        for count in counts {
152            trace!(" {} - {}", count, children_count[count]);
153        }
154    }
155
156    /// Steals the free list and drops its contents.
157    unsafe fn swap_free_list_and_gc(&self, ptr: *mut RuleNode) {
158        let root = &self.root.p;
159
160        debug_assert!(!root.next_free.load(Ordering::Relaxed).is_null());
161
162        // Reset the approximate free count to zero, as we are going to steal
163        // the free list.
164        root.approximate_free_count.store(0, Ordering::Relaxed);
165
166        // Steal the free list head. Memory loads on nodes while iterating it
167        // must observe any prior changes that occured so this requires
168        // acquire ordering, but there are no writes that need to be kept
169        // before this swap so there is no need for release.
170        let mut head = root.next_free.swap(ptr, Ordering::Acquire);
171
172        while head != RuleNode::DANGLING_PTR {
173            debug_assert!(!head.is_null());
174
175            let mut node = unsafe { UnsafeBox::from_raw(head) };
176
177            // The root node cannot go on the free list.
178            debug_assert!(node.root.is_some());
179
180            // The refcount of nodes on the free list never goes below 1.
181            debug_assert!(node.refcount.load(Ordering::Relaxed) > 0);
182
183            // No one else is currently writing to that field. Get the address
184            // of the next node in the free list and replace it with null,
185            // other threads will now consider that this node is not on the
186            // free list.
187            head = node.next_free.swap(ptr::null_mut(), Ordering::Relaxed);
188
189            // This release write synchronises with the acquire fence in
190            // `WeakRuleNode::upgrade`, making sure that if `upgrade` observes
191            // decrements the refcount to 0, it will also observe the
192            // `node.next_free` swap to null above.
193            if node.refcount.fetch_sub(1, Ordering::Release) == 1 {
194                unsafe {
195                    // And given it observed the null swap above, it will need
196                    // `pretend_to_be_on_free_list` to finish its job, writing
197                    // `RuleNode::DANGLING_PTR` in `node.next_free`.
198                    RuleNode::pretend_to_be_on_free_list(&node);
199                    // Drop this node now that we just observed its refcount going down to zero.
200                    RuleNode::drop_without_free_list(&mut node);
201                }
202            }
203        }
204    }
205}
206
207/// The number of RuleNodes added to the free list before we will consider
208/// doing a GC when calling maybe_gc().  (The value is copied from Gecko,
209/// where it likely did not result from a rigorous performance analysis.)
210const RULE_TREE_GC_INTERVAL: usize = 300;
211
212/// A node in the rule tree.
213struct RuleNode {
214    /// The root node. Only the root has no root pointer, for obvious reasons.
215    root: Option<WeakRuleNode>,
216
217    /// The parent rule node. Only the root has no parent.
218    parent: Option<StrongRuleNode>,
219
220    /// The actual style source, either coming from a selector in a StyleRule,
221    /// or a raw property declaration block (like the style attribute).
222    ///
223    /// None for the root node.
224    source: Option<StyleSource>,
225
226    /// The cascade level + layer order this rule is positioned at.
227    cascade_priority: CascadePriority,
228
229    /// The refcount of this node.
230    ///
231    /// Starts at one. Incremented in `StrongRuleNode::clone` and
232    /// `WeakRuleNode::upgrade`. Decremented in `StrongRuleNode::drop`
233    /// and `RuleTree::swap_free_list_and_gc`.
234    ///
235    /// If a non-root node's refcount reaches zero, it is incremented back to at
236    /// least one in `RuleNode::pretend_to_be_on_free_list` until the caller who
237    /// observed it dropping to zero had a chance to try to remove it from its
238    /// parent's children list.
239    ///
240    /// The refcount should never be decremented to zero if the value in
241    /// `next_free` is not null.
242    refcount: AtomicUsize,
243
244    /// Only used for the root, stores the number of free rule nodes that are
245    /// around.
246    approximate_free_count: AtomicUsize,
247
248    /// The children of a given rule node. Children remove themselves from here
249    /// when they go away.
250    children: RwLock<Map<ChildKey, WeakRuleNode>>,
251
252    /// This field has two different meanings depending on whether this is the
253    /// root node or not.
254    ///
255    /// If it is the root, it represents the head of the free list. It may be
256    /// null, which means the free list is gone because the tree was dropped,
257    /// and it may be `RuleNode::DANGLING_PTR`, which means the free list is
258    /// empty.
259    ///
260    /// If it is not the root node, this field is either null if the node is
261    /// not on the free list, `RuleNode::DANGLING_PTR` if it is the last item
262    /// on the free list or the node is pretending to be on the free list, or
263    /// any valid non-null pointer representing the next item on the free list
264    /// after this one.
265    ///
266    /// See `RuleNode::push_on_free_list`, `swap_free_list_and_gc`, and
267    /// `WeakRuleNode::upgrade`.
268    ///
269    /// Two threads should never attempt to put the same node on the free list
270    /// both at the same time.
271    next_free: AtomicPtr<RuleNode>,
272}
273
274// On Gecko builds, hook into the leak checking machinery.
275#[cfg(feature = "gecko_refcount_logging")]
276mod gecko_leak_checking {
277    use super::RuleNode;
278    use std::mem::size_of;
279    use std::os::raw::{c_char, c_void};
280
281    unsafe extern "C" {
282        fn NS_LogCtor(aPtr: *mut c_void, aTypeName: *const c_char, aSize: u32);
283        fn NS_LogDtor(aPtr: *mut c_void, aTypeName: *const c_char, aSize: u32);
284    }
285    static NAME: &'static [u8] = b"RuleNode\0";
286
287    /// Logs the creation of a heap-allocated object to Gecko's leak-checking machinery.
288    pub(super) fn log_ctor(ptr: *const RuleNode) {
289        let s = NAME as *const [u8] as *const u8 as *const c_char;
290        unsafe {
291            NS_LogCtor(ptr as *mut c_void, s, size_of::<RuleNode>() as u32);
292        }
293    }
294
295    /// Logs the destruction of a heap-allocated object to Gecko's leak-checking machinery.
296    pub(super) fn log_dtor(ptr: *const RuleNode) {
297        let s = NAME as *const [u8] as *const u8 as *const c_char;
298        unsafe {
299            NS_LogDtor(ptr as *mut c_void, s, size_of::<RuleNode>() as u32);
300        }
301    }
302}
303
304#[inline(always)]
305fn log_new(_ptr: *const RuleNode) {
306    #[cfg(feature = "gecko_refcount_logging")]
307    gecko_leak_checking::log_ctor(_ptr);
308}
309
310#[inline(always)]
311fn log_drop(_ptr: *const RuleNode) {
312    #[cfg(feature = "gecko_refcount_logging")]
313    gecko_leak_checking::log_dtor(_ptr);
314}
315
316impl RuleNode {
317    const DANGLING_PTR: *mut Self = ptr::NonNull::dangling().as_ptr();
318
319    unsafe fn new(
320        root: WeakRuleNode,
321        parent: StrongRuleNode,
322        source: StyleSource,
323        cascade_priority: CascadePriority,
324    ) -> Self {
325        debug_assert!(root.p.parent.is_none());
326        source.mark_in_rule_tree();
327        RuleNode {
328            root: Some(root),
329            parent: Some(parent),
330            source: Some(source),
331            cascade_priority,
332            refcount: AtomicUsize::new(1),
333            children: Default::default(),
334            approximate_free_count: AtomicUsize::new(0),
335            next_free: AtomicPtr::new(ptr::null_mut()),
336        }
337    }
338
339    fn root() -> Self {
340        RuleNode {
341            root: None,
342            parent: None,
343            source: None,
344            cascade_priority: CascadePriority::new(
345                CascadeLevel::new(CascadeOrigin::UA),
346                LayerOrder::root(),
347                RuleCascadeFlags::empty(),
348            ),
349            refcount: AtomicUsize::new(1),
350            approximate_free_count: AtomicUsize::new(0),
351            children: Default::default(),
352            next_free: AtomicPtr::new(RuleNode::DANGLING_PTR),
353        }
354    }
355
356    fn key(&self) -> ChildKey {
357        ChildKey(
358            self.cascade_priority,
359            self.source
360                .as_ref()
361                .expect("Called key() on the root node")
362                .key(),
363        )
364    }
365
366    /// Drops a node without ever putting it on the free list.
367    ///
368    /// Note that the node may not be dropped if we observe that its refcount
369    /// isn't zero anymore when we write-lock its parent's children map to
370    /// remove it.
371    ///
372    /// This loops over parents of dropped nodes if their own refcount reaches
373    /// zero to avoid recursion when dropping deep hierarchies of nodes.
374    ///
375    /// For non-root nodes, this should always be preceded by a call of
376    /// `RuleNode::pretend_to_be_on_free_list`.
377    unsafe fn drop_without_free_list(this: &mut UnsafeBox<Self>) {
378        // We clone the box and shadow the original one to be able to loop
379        // over its ancestors if they also need to be dropped.
380        let mut this = unsafe { UnsafeBox::clone(this) };
381        loop {
382            // If the node has a parent, we need to remove it from its parent's
383            // children list.
384            if let Some(parent) = this.parent.as_ref() {
385                debug_assert!(!this.next_free.load(Ordering::Relaxed).is_null());
386
387                // We lock the parent's children list, which means no other
388                // thread will have any more opportunity to resurrect the node
389                // anymore.
390                let mut children = parent.p.children.write();
391
392                this.next_free.store(ptr::null_mut(), Ordering::Relaxed);
393
394                // We decrement the counter to remove the "pretend to be
395                // on the free list" reference.
396                let old_refcount = this.refcount.fetch_sub(1, Ordering::Release);
397                debug_assert!(old_refcount != 0);
398                if old_refcount != 1 {
399                    // Other threads resurrected this node and those references
400                    // are still alive, we have nothing to do anymore.
401                    return;
402                }
403
404                // We finally remove the node from its parent's children list,
405                // there are now no other references to it and it cannot
406                // be resurrected anymore even after we unlock the list.
407                debug!(
408                    "Remove from child list: {:?}, parent: {:?}",
409                    this.as_mut_ptr(),
410                    this.parent.as_ref().map(|p| p.p.as_mut_ptr())
411                );
412                let weak = children.remove(&this.key(), |node| node.p.key()).unwrap();
413                assert_eq!(weak.p.as_mut_ptr(), this.as_mut_ptr());
414            } else {
415                debug_assert_eq!(this.next_free.load(Ordering::Relaxed), ptr::null_mut());
416                debug_assert_eq!(this.refcount.load(Ordering::Relaxed), 0);
417            }
418
419            // We are going to drop this node for good this time, as per the
420            // usual refcounting protocol we need an acquire fence here before
421            // we run the destructor.
422            //
423            // See https://github.com/rust-lang/rust/pull/41714#issuecomment-298996916
424            // for why it doesn't matter whether this is a load or a fence.
425            atomic::fence(Ordering::Acquire);
426
427            // Remove the parent reference from the child to avoid
428            // recursively dropping it and putting it on the free list.
429            let parent = unsafe { UnsafeBox::deref_mut(&mut this).parent.take() };
430
431            // We now drop the actual box and its contents, no one should
432            // access the current value in `this` anymore.
433            log_drop(&*this);
434            unsafe { UnsafeBox::drop(&mut this) };
435
436            if let Some(parent) = parent {
437                // We will attempt to drop the node's parent without the free
438                // list, so we clone the inner unsafe box and forget the
439                // original parent to avoid running its `StrongRuleNode`
440                // destructor which would attempt to use the free list if it
441                // still exists.
442                this = unsafe { UnsafeBox::clone(&parent.p) };
443                mem::forget(parent);
444                if this.refcount.fetch_sub(1, Ordering::Release) == 1 {
445                    debug_assert_eq!(this.next_free.load(Ordering::Relaxed), ptr::null_mut());
446                    if this.root.is_some() {
447                        unsafe {
448                            RuleNode::pretend_to_be_on_free_list(&this);
449                        }
450                    }
451                    // Parent also reached refcount zero, we loop to drop it.
452                    continue;
453                }
454            }
455
456            return;
457        }
458    }
459
460    /// Pushes this node on the tree's free list. Returns false if the free list
461    /// is gone. Should only be called after we decremented a node's refcount
462    /// to zero and pretended to be on the free list.
463    unsafe fn push_on_free_list(this: &UnsafeBox<Self>) -> bool {
464        let root = &this.root.as_ref().unwrap().p;
465
466        debug_assert!(this.refcount.load(Ordering::Relaxed) > 0);
467        debug_assert_eq!(this.next_free.load(Ordering::Relaxed), Self::DANGLING_PTR);
468
469        // Increment the approximate free count by one.
470        root.approximate_free_count.fetch_add(1, Ordering::Relaxed);
471
472        // If the compare-exchange operation fails in the loop, we will retry
473        // with the new head value, so this can be a relaxed load.
474        let mut head = root.next_free.load(Ordering::Relaxed);
475
476        while !head.is_null() {
477            // Two threads can never attempt to push the same node on the free
478            // list both at the same time, so whoever else pushed a node on the
479            // free list cannot have done so with this node.
480            debug_assert_ne!(head, this.as_mut_ptr());
481
482            // Store the current head of the free list in this node.
483            this.next_free.store(head, Ordering::Relaxed);
484
485            // Any thread acquiring the free list must observe the previous
486            // next_free changes that occured, hence the release ordering
487            // on success.
488            match root.next_free.compare_exchange_weak(
489                head,
490                this.as_mut_ptr(),
491                Ordering::Release,
492                Ordering::Relaxed,
493            ) {
494                Ok(_) => {
495                    // This node is now on the free list, caller should not use
496                    // the node anymore.
497                    return true;
498                },
499                Err(new_head) => head = new_head,
500            }
501        }
502
503        // Tree was dropped and free list has been destroyed. We did not push
504        // this node on the free list but we still pretend to be on the free
505        // list to be ready to call `drop_without_free_list`.
506        false
507    }
508
509    /// Makes the node pretend to be on the free list. This will increment the
510    /// refcount by 1 and store `Self::DANGLING_PTR` in `next_free`. This
511    /// method should only be called after caller decremented the refcount to
512    /// zero, with the null pointer stored in `next_free`.
513    unsafe fn pretend_to_be_on_free_list(this: &UnsafeBox<Self>) {
514        debug_assert_eq!(this.next_free.load(Ordering::Relaxed), ptr::null_mut());
515        this.refcount.fetch_add(1, Ordering::Relaxed);
516        this.next_free.store(Self::DANGLING_PTR, Ordering::Release);
517    }
518
519    fn as_mut_ptr(&self) -> *mut RuleNode {
520        self as *const RuleNode as *mut RuleNode
521    }
522}
523
524pub(crate) struct WeakRuleNode {
525    p: UnsafeBox<RuleNode>,
526}
527
528/// A strong reference to a rule node.
529pub struct StrongRuleNode {
530    p: UnsafeBox<RuleNode>,
531}
532
533#[cfg(feature = "servo")]
534malloc_size_of::malloc_size_of_is_0!(StrongRuleNode);
535
536impl StrongRuleNode {
537    fn new(n: Box<RuleNode>) -> Self {
538        debug_assert_eq!(n.parent.is_none(), n.source.is_none());
539
540        log_new(&*n);
541
542        debug!("Creating rule node: {:p}", &*n);
543
544        Self {
545            p: UnsafeBox::from_box(n),
546        }
547    }
548
549    unsafe fn from_unsafe_box(p: UnsafeBox<RuleNode>) -> Self {
550        Self { p }
551    }
552
553    unsafe fn downgrade(&self) -> WeakRuleNode {
554        unsafe {
555            WeakRuleNode {
556                p: UnsafeBox::clone(&self.p),
557            }
558        }
559    }
560
561    /// Get the parent rule node of this rule node.
562    pub fn parent(&self) -> Option<&StrongRuleNode> {
563        self.p.parent.as_ref()
564    }
565
566    pub(super) fn ensure_child(
567        &self,
568        root: &StrongRuleNode,
569        source: StyleSource,
570        cascade_priority: CascadePriority,
571    ) -> StrongRuleNode {
572        debug_assert!(
573            self.p.cascade_priority <= cascade_priority,
574            "Should be ordered (instead {:?} > {:?}), from {:?} and {:?}",
575            self.p.cascade_priority,
576            cascade_priority,
577            self.p.source,
578            source,
579        );
580
581        let key = ChildKey(cascade_priority, source.key());
582        {
583            let children = self.p.children.read();
584            if let Some(child) = children.get(&key, |node| node.p.key()) {
585                // Sound to call because we read-locked the parent's children.
586                return unsafe { child.upgrade() };
587            }
588        }
589        let mut children = self.p.children.write();
590        match children.entry(key, |node| node.p.key()) {
591            Entry::Occupied(child) => {
592                // Sound to call because we write-locked the parent's children.
593                unsafe { child.upgrade() }
594            },
595            Entry::Vacant(entry) => unsafe {
596                let node = StrongRuleNode::new(Box::new(RuleNode::new(
597                    root.downgrade(),
598                    self.clone(),
599                    source,
600                    cascade_priority,
601                )));
602                // Sound to call because we still own a strong reference to
603                // this node, through the `node` variable itself that we are
604                // going to return to the caller.
605                entry.insert(node.downgrade());
606                node
607            },
608        }
609    }
610
611    /// Get the style source corresponding to this rule node. May return `None`
612    /// if it's the root node, which means that the node hasn't matched any
613    /// rules.
614    pub fn style_source(&self) -> Option<&StyleSource> {
615        self.p.source.as_ref()
616    }
617
618    /// The cascade priority.
619    #[inline]
620    pub fn cascade_priority(&self) -> CascadePriority {
621        self.p.cascade_priority
622    }
623
624    /// The cascade level.
625    #[inline]
626    pub fn cascade_level(&self) -> CascadeLevel {
627        self.cascade_priority().cascade_level()
628    }
629
630    /// The importance.
631    #[inline]
632    pub fn importance(&self) -> crate::properties::Importance {
633        self.cascade_level().importance()
634    }
635
636    /// Returns whether this node has any child, only intended for testing
637    /// purposes.
638    pub unsafe fn has_children_for_testing(&self) -> bool {
639        !self.p.children.read().is_empty()
640    }
641
642    pub(super) fn dump<W: Write>(&self, guards: &StylesheetGuards, writer: &mut W, indent: usize) {
643        const INDENT_INCREMENT: usize = 4;
644
645        for _ in 0..indent {
646            let _ = write!(writer, " ");
647        }
648
649        let _ = writeln!(
650            writer,
651            " - {:p} (ref: {:?}, parent: {:?})",
652            &*self.p,
653            self.p.refcount.load(Ordering::Relaxed),
654            self.parent().map(|p| &*p.p as *const RuleNode)
655        );
656
657        for _ in 0..indent {
658            let _ = write!(writer, " ");
659        }
660
661        if let Some(source) = self.style_source() {
662            source.dump(self.cascade_level().guard(guards), writer);
663        } else {
664            if indent != 0 {
665                warn!("How has this happened?");
666            }
667            let _ = write!(writer, "(root)");
668        }
669
670        let _ = writeln!(writer);
671        for child in &*self.p.children.read() {
672            unsafe {
673                child
674                    .upgrade()
675                    .dump(guards, writer, indent + INDENT_INCREMENT);
676            }
677        }
678    }
679}
680
681impl Clone for StrongRuleNode {
682    fn clone(&self) -> Self {
683        debug!(
684            "{:p}: {:?}+",
685            &*self.p,
686            self.p.refcount.load(Ordering::Relaxed)
687        );
688        debug_assert!(self.p.refcount.load(Ordering::Relaxed) > 0);
689        self.p.refcount.fetch_add(1, Ordering::Relaxed);
690        unsafe { StrongRuleNode::from_unsafe_box(UnsafeBox::clone(&self.p)) }
691    }
692}
693
694impl Drop for StrongRuleNode {
695    #[cfg_attr(feature = "servo", allow(unused_mut))]
696    fn drop(&mut self) {
697        let node = &*self.p;
698        debug!("{:p}: {:?}-", node, node.refcount.load(Ordering::Relaxed));
699        debug!(
700            "Dropping node: {:p}, root: {:?}, parent: {:?}",
701            node,
702            node.root.as_ref().map(|r| &*r.p as *const RuleNode),
703            node.parent.as_ref().map(|p| &*p.p as *const RuleNode)
704        );
705
706        let should_drop = {
707            debug_assert!(node.refcount.load(Ordering::Relaxed) > 0);
708            node.refcount.fetch_sub(1, Ordering::Release) == 1
709        };
710
711        if !should_drop {
712            // The refcount didn't even drop zero yet, there is nothing for us
713            // to do anymore.
714            return;
715        }
716
717        unsafe {
718            if node.root.is_some() {
719                // This is a non-root node and we just observed the refcount
720                // dropping to zero, we need to pretend to be on the free list
721                // to unstuck any thread who tried to resurrect this node first
722                // through `WeakRuleNode::upgrade`.
723                RuleNode::pretend_to_be_on_free_list(&self.p);
724
725                // Attempt to push the node on the free list. This may fail
726                // if the free list is gone.
727                if RuleNode::push_on_free_list(&self.p) {
728                    return;
729                }
730            }
731
732            // Either this was the last reference of the root node, or the
733            // tree rule is gone and there is no free list anymore. Drop the
734            // node.
735            RuleNode::drop_without_free_list(&mut self.p);
736        }
737    }
738}
739
740impl WeakRuleNode {
741    /// Upgrades this weak node reference, returning a strong one.
742    ///
743    /// Must be called with items stored in a node's children list. The children
744    /// list must at least be read-locked when this is called.
745    unsafe fn upgrade(&self) -> StrongRuleNode {
746        debug!("Upgrading weak node: {:p}", &*self.p);
747
748        if self.p.refcount.fetch_add(1, Ordering::Relaxed) == 0 {
749            // We observed a refcount of 0, we need to wait for this node to
750            // be put on the free list. Resetting the `next_free` pointer to
751            // null is only done in `RuleNode::drop_without_free_list`, just
752            // before a release refcount decrement, so this acquire fence here
753            // makes sure that we observed the write to null before we loop
754            // until there is a non-null value.
755            atomic::fence(Ordering::Acquire);
756            while self.p.next_free.load(Ordering::Relaxed).is_null() {}
757        }
758        unsafe { StrongRuleNode::from_unsafe_box(UnsafeBox::clone(&self.p)) }
759    }
760}
761
762impl fmt::Debug for StrongRuleNode {
763    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
764        (&*self.p as *const RuleNode).fmt(f)
765    }
766}
767
768impl Eq for StrongRuleNode {}
769impl PartialEq for StrongRuleNode {
770    fn eq(&self, other: &Self) -> bool {
771        &*self.p as *const RuleNode == &*other.p
772    }
773}
774
775impl hash::Hash for StrongRuleNode {
776    fn hash<H>(&self, state: &mut H)
777    where
778        H: hash::Hasher,
779    {
780        (&*self.p as *const RuleNode).hash(state)
781    }
782}
783
784// Large pages generate thousands of RuleNode objects.
785size_of_test!(RuleNode, 80);
786// StrongRuleNode should be pointer-sized even inside an option.
787size_of_test!(Option<StrongRuleNode>, 8);