diff --git a/src/config.rs b/src/config.rs index 7bddaa8..339fec7 100644 --- a/src/config.rs +++ b/src/config.rs @@ -82,6 +82,28 @@ pub struct Config { /// Background color as RGBA (default: white). pub background: [u8; 4], + + /// Number of threads stylo uses for parallel style traversal. + /// + /// - `None` (default): use blitz's auto heuristic + /// (`num_cpus * 3/4`, capped at 6). + /// - `Some(1)`: disable parallel traversal. Style work runs serially + /// on the calling thread. + /// - `Some(n)` with `n >= 2`: use a pool of `n` worker threads. + /// + /// Set this to `Some(1)` when calling `render` concurrently from + /// multiple OS threads (e.g. a server rendering one document per + /// request via `tokio::task::spawn_blocking`). Stylo's + /// `STYLE_THREAD_POOL` is process-global; two parallel traversals + /// landing on the same rayon worker both try to mutably borrow the + /// worker's thread-local sharing cache and one panics. Disabling + /// parallelism keeps each render's stylo work on the caller's OS + /// thread, where the thread-local is uniquely owned. + /// + /// **One-shot:** stylo's `STYLE_THREAD_POOL` is initialised on first + /// access. The value set on the first `render(...)` call in the + /// process wins; later calls inherit that pool size regardless. + pub style_thread_count: Option, } impl Default for Config { @@ -94,6 +116,7 @@ impl Default for Config { color_scheme: ColorScheme::Light, auto_height: false, background: [255, 255, 255, 255], // White + style_thread_count: None, } } } @@ -248,6 +271,26 @@ impl Config { self.background([0, 0, 0, 0]) } + /// Configure stylo's parallel style-traversal thread count. + /// + /// Pass `1` to disable parallel traversal when calling `render` + /// concurrently from multiple OS threads. See the field-level + /// docs on [`Config::style_thread_count`] for the full rationale. + /// + /// # Example + /// + /// ```rust + /// use hyper_render::Config; + /// + /// // Server context: one render per `spawn_blocking` thread. + /// let config = Config::new().style_thread_count(1); + /// assert_eq!(config.style_thread_count, Some(1)); + /// ``` + pub fn style_thread_count(mut self, count: i32) -> Self { + self.style_thread_count = Some(count); + self + } + /// Minimum supported width/height in pixels. /// /// Very small dimensions can cause overflow issues in the underlying diff --git a/src/lib.rs b/src/lib.rs index 50f66a8..f39ecc4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -166,6 +166,7 @@ fn create_document(html: &str, config: &Config) -> Result { let doc_config = DocumentConfig { viewport: Some(viewport), + stylo_thread_count: config.style_thread_count, ..Default::default() };