Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  driver.rs   Sprache: unbekannt

 
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

//! Implements traversal over the DOM tree. The traversal starts in sequential
//! mode, and optionally parallelizes as it discovers work.

#![deny(missing_docs)]

use crate::context::{PerThreadTraversalStatistics, StyleContext};
use crate::context::{ThreadLocalStyleContext, TraversalStatistics};
use crate::dom::{SendNode, TElement, TNode};
use crate::parallel;
use crate::scoped_tls::ScopedTLS;
use crate::traversal::{DomTraversal, PerLevelTraversalData, PreTraverseToken};
use std::collections::VecDeque;
use std::time::Instant;

#[cfg(feature = "servo")]
fn should_report_statistics() -> bool {
    false
}

#[cfg(feature = "gecko")]
fn should_report_statistics() -> bool {
    unsafe { crate::gecko_bindings::structs::ServoTraversalStatistics_sActive }
}

#[cfg(feature = "servo")]
fn report_statistics(_stats: &PerThreadTraversalStatistics) {
    unreachable!("Servo never report stats");
}

#[cfg(feature = "gecko")]
fn report_statistics(stats: &PerThreadTraversalStatistics) {
    // This should only be called in the main thread, or it may be racy
    // to update the statistics in a global variable.
    unsafe {
        debug_assert!(crate::gecko_bindings::bindings::Gecko_IsMainThread());
        let gecko_stats = std::ptr::addr_of_mut!(
            crate::gecko_bindings::structs::ServoTraversalStatistics_sSingleton
        );
        (*gecko_stats).mElementsTraversed += stats.elements_traversed;
        (*gecko_stats).mElementsStyled += stats.elements_styled;
        (*gecko_stats).mElementsMatched += stats.elements_matched;
        (*gecko_stats).mStylesShared += stats.styles_shared;
        (*gecko_stats).mStylesReused += stats.styles_reused;
    }
}

fn with_pool_in_place_scope<'scope>(
    work_unit_max: usize,
    pool: Option<&rayon::ThreadPool>,
    closure: impl FnOnce(Option<&rayon::ScopeFifo<'scope>>) + Send + 'scope,
) {
    if work_unit_max == 0 || pool.is_none() {
        closure(None);
    } else {
        let pool = pool.unwrap();
        pool.in_place_scope_fifo(|scope| {
            #[cfg(feature = "gecko")]
            debug_assert_eq!(
                pool.current_thread_index(),
                Some(0),
                "Main thread should be the first thread"
            );
            if cfg!(feature = "gecko") || pool.current_thread_index().is_some() {
                closure(Some(scope));
            } else {
                scope.spawn_fifo(|scope| closure(Some(scope)));
            }
        });
    }
}

/// See documentation of the pref for performance characteristics.
fn work_unit_max() -> usize {
    static_prefs::pref!("layout.css.stylo-work-unit-size") as usize
}

/// Do a DOM traversal for top-down and (optionally) bottom-up processing, generic over `D`.
///
/// We use an adaptive traversal strategy. We start out with simple sequential processing, until we
/// arrive at a wide enough level in the DOM that the parallel traversal would parallelize it.
/// If a thread pool is provided, we then transfer control over to the parallel traversal.
///
/// Returns true if the traversal was parallel, and also returns the statistics object containing
/// information on nodes traversed (on nightly only). Not all of its fields will be initialized
/// since we don't call finish().
pub fn traverse_dom<E, D>(
    traversal: &D,
    token: PreTraverseToken<E>,
    pool: Option<&rayon::ThreadPool>,
) -> E
where
    E: TElement,
    D: DomTraversal<E>,
{
    let root = token
        .traversal_root()
        .expect("Should've ensured we needed to traverse");

    let report_stats = should_report_statistics();
    let dump_stats = traversal.shared_context().options.dump_style_statistics;
    let start_time = if dump_stats {
        Some(Instant::now())
    } else {
        None
    };

    // Declare the main-thread context, as well as the worker-thread contexts,
    // which we may or may not instantiate. It's important to declare the worker-
    // thread contexts first, so that they get dropped second. This matters because:
    //   * ThreadLocalContexts borrow AtomicRefCells in TLS.
    //   * Dropping a ThreadLocalContext can run SequentialTasks.
    //   * Sequential tasks may call into functions like
    //     Servo_StyleSet_GetBaseComputedValuesForElement, which instantiate a
    //     ThreadLocalStyleContext on the main thread. If the main thread
    //     ThreadLocalStyleContext has not released its TLS borrow by that point,
    //     we'll panic on double-borrow.
    let mut scoped_tls = ScopedTLS::<ThreadLocalStyleContext<E>>::new(pool);
    // Process the nodes breadth-first. This helps keep similar traversal characteristics for the
    // style sharing cache.
    let work_unit_max = work_unit_max();

    let send_root = unsafe { SendNode::new(root.as_node()) };
    with_pool_in_place_scope(work_unit_max, pool, |maybe_scope| {
        let mut tlc = scoped_tls.ensure(parallel::create_thread_local_context);
        let mut context = StyleContext {
            shared: traversal.shared_context(),
            thread_local: &mut tlc,
        };

        let mut discovered = VecDeque::with_capacity(work_unit_max * 2);
        let current_dom_depth = send_root.depth();
        let opaque_root = send_root.opaque();
        discovered.push_back(send_root);
        parallel::style_trees(
            &mut context,
            discovered,
            opaque_root,
            work_unit_max,
            PerLevelTraversalData { current_dom_depth },
            maybe_scope,
            traversal,
            &scoped_tls,
        );
    });

    // Collect statistics from thread-locals if requested.
    if dump_stats || report_stats {
        let mut aggregate = PerThreadTraversalStatistics::default();
        for slot in scoped_tls.slots() {
            if let Some(cx) = slot.get_mut() {
                aggregate += cx.statistics.clone();
            }
        }

        if report_stats {
            report_statistics(&aggregate);
        }
        // dump statistics to stdout if requested
        if dump_stats {
            let parallel = pool.is_some();
            let stats =
                TraversalStatistics::new(aggregate, traversal, parallel, start_time.unwrap());
            if stats.is_large {
                println!("{}", stats);
            }
        }
    }

    root
}

[ Dauer der Verarbeitung: 0.22 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge