From f929654f96a987de0f9b018d7616f338c19edf91 Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Fri, 16 Oct 2026 21:39:03 -0700 Subject: [PATCH] fix(web): the diff worker pool keeps one subscription and size without memoization Co-Authored-By: Claude Opus 5.5 --- .../src/components/DiffWorkerPoolProvider.tsx | 54 +++++++++---------- 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/apps/web/src/components/DiffWorkerPoolProvider.tsx b/apps/web/src/components/DiffWorkerPoolProvider.tsx index cf83d33d62dce..71c3997dfbb0e 100644 --- a/apps/web/src/components/DiffWorkerPoolProvider.tsx +++ b/apps/web/src/components/DiffWorkerPoolProvider.tsx @@ -2,14 +2,8 @@ import { WorkerPoolContext, useWorkerPool } from "@pierre/diffs/react"; import { WorkerPoolManager } from "@pierre/diffs/worker"; import DiffsWorker from "@pierre/diffs/worker/worker.js?worker"; import * as Schema from "effect/Schema"; -import { - useCallback, - useEffect, - useMemo, - useState, - useSyncExternalStore, - type ReactNode, -} from "react"; +import { useEffect, useState, useSyncExternalStore, type ReactNode } from "react"; +import { useStableCallback } from "../hooks/useStableCallback"; import { useTheme } from "../hooks/useTheme"; import { resolveDiffThemeName, type DiffThemeName } from "../lib/diffRendering"; import { PREFERRED_HIGHLIGHTER } from "../lib/syntaxHighlighting"; @@ -25,6 +19,11 @@ export class DiffWorkerError extends Schema.TaggedError()("Diff } const DIFF_WORKER_IDLE_TTL_MS = 30_000; +function resolveDiffWorkerPoolSize() { + const cores = + typeof navigator === "undefined" ? 4 : Math.max(1, navigator.hardwareConcurrency || 4); + return Math.max(2, Math.min(6, Math.floor(cores / 2))); +} let sharedWorkerPool: | { readonly pool: WorkerPoolManager; @@ -129,29 +128,24 @@ function DiffWorkerReady({ children }: { children?: ReactNode }) { export function DiffWorkerPoolProvider({ children }: { children?: ReactNode }) { const { resolvedTheme } = useTheme(); const diffThemeName = resolveDiffThemeName(resolvedTheme); - const workerPoolSize = useMemo(() => { - const cores = - typeof navigator === "undefined" ? 4 : Math.max(1, navigator.hardwareConcurrency || 4); - return Math.max(2, Math.min(6, Math.floor(cores / 2))); - }, []); + const [workerPoolSize] = useState(resolveDiffWorkerPoolSize); + // A new subscribe identity makes React release and reacquire the shared pool on every render. + const subscribe = useStableCallback((onStoreChange: () => void) => { + if (typeof window === "undefined") return () => {}; + const entry = acquireDiffWorkerPool(diffThemeName, workerPoolSize); + onStoreChange(); + return () => { + entry.consumers -= 1; + if (entry.consumers !== 0) return; + entry.idleTimer = setTimeout(() => { + entry.idleTimer = undefined; + entry.pool.terminate(); + if (sharedWorkerPool === entry) sharedWorkerPool = undefined; + }, DIFF_WORKER_IDLE_TTL_MS); + }; + }); const workerPool = useSyncExternalStore( - useCallback( - (onStoreChange) => { - if (typeof window === "undefined") return () => {}; - const entry = acquireDiffWorkerPool(diffThemeName, workerPoolSize); - onStoreChange(); - return () => { - entry.consumers -= 1; - if (entry.consumers !== 0) return; - entry.idleTimer = setTimeout(() => { - entry.idleTimer = undefined; - entry.pool.terminate(); - if (sharedWorkerPool === entry) sharedWorkerPool = undefined; - }, DIFF_WORKER_IDLE_TTL_MS); - }; - }, - [diffThemeName, workerPoolSize], - ), + subscribe, () => sharedWorkerPool?.pool, () => undefined, );