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