Multi-diff editor disposal hangs as rendering replenishes the template pool
- Dominant language
- TypeScript
- Stars
- 193k
- Forks
- 42.4k
- PR merge metrics
- PR metrics pending
Description
Does this issue occur when all extensions are disabled?: Yes — reproduced in the core Chromium browser-test harness, without an extension host.
- VS Code Version: source build at `a8f49160195d9e967d2d51e8544dc895207518e7` (1.139.0 development sources); not an installed-Insiders reproduction.
- OS Version: macOS arm64; headless Chromium.
Steps to Reproduce:
1. In a browser test, create a real `MultiDiffEditor` with workbench test services and `MockUserInteractionService(false)`. Supply 12 binary diff items: original/modified `fileN.png` URIs, no text models. Use an input whose `getViewModel()` returns the same still-live view model, matching the normal cached-input behavior. Keep the input, view model and services alive through editor teardown.
2. Create the editor in a detached container, lay out at 800×220, and `await setInput(input, { preserveFocus: true }, {}, CancellationToken.None)`. Wait until rows have templates, then lay out at 800×1400.
3. `await editor.clearInput()`, then set the **same input** again and call `editor.dispose()` directly. The synchronous disposal does not return.
4. Control: add another `await editor.clearInput()` immediately before disposal. This completes in approximately 340 ms. An extra clear is an isolation workaround, not a documented disposal prerequisite: [EditorPane's lifecycle permits disposal after setInput](https://github.com/microsoft/vscode/blob/a8f49160195d9e967d2d51e8544dc895207518e7/src/vs/workbench/browser/parts/editor/editorPane.ts#L28-L48).
**Expected:** disposal returns and releases the active templates without rendering replacements.
**Actual / cause:** externally paused renderer stacks show:
```text
VirtualizedTemplatePool.dispose
-> template.dispose
-> binding.onDidDispose / currentBinding.set(undefined)
-> scroll-layout autorun
-> ManagedVirtualizedItem.render / _render
-> acquire
-> createTemplate
```
[Pool disposal iterates the live `_used` Set](https://github.com/microsoft/vscode/blob/a8f49160195d9e967d2d51e8544dc895207518e7/src/vs/editor/browser/widget/multiDiffEditor/virtualizedItemManager.ts#L251-L294). Still-live rendering consumers acquire replacement templates into that same Set, extending the teardown iteration. [The widget registers its manager during scroll-view construction](https://github.com/microsoft/vscode/blob/a8f49160195d9e967d2d51e8544dc895207518e7/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidgetImpl.ts), so rendering consumers can remain active when pool disposal starts.
**Isolation:** the direct-dispose sequence stalled both with the original manager and with the proposed per-item release fix for #335720. Each run was paused after two seconds and closed under an external deadline; the explicit-clear control completed. This is distinct from disposing the *same binding twice* in #335720. No claim is made about first-open-only behavior or the original LanguageService warning in #206959.
**Fix direction:** stop rendering/acquisition before draining the pools and make whole-pool teardown safe against synchronous notifications. Merely snapshotting the Set risks leaving newly acquired templates alive. Add a bounded regression covering clear/rebind/direct-dispose with a live input, asserting disposal finishes and no replacement templates are created during teardown.
Assigning to @hediet, author of the pool/lifecycle code in `10de1b9343662a4043258b418630481e27c863b1`.
Contributor guide
Assessment
This issue has not been assessed yet.