Implemented the deterministic Engine teardown ordering in Engine destructor:

- Pool-owned HTTP clients stop first.
  - The active GL context is made current.
  - Scenes are destroyed before batches and resource managers.
  - Pending batches explicitly discard vertices and borrowed textures.
  - TextLayout cache clears before fonts.
  - Framebuffer/vertex managers and textures are released while Renderer/context remain alive.
  - ShaderProgramManager is destroyed before Renderer.
  - Windows and contexts are destroyed last.

The lifecycle test uncovered and fixed another bug: StaticLRU::clear() retained non-trivial values such as cached shared_ptr<TextLayout> objects. It now releases active values in LRUCache.
This commit is contained in:
Martín Lucas Golini
2026-07-14 00:03:23 -03:00
parent 513ef0f12b
commit 8aa660104f
8 changed files with 155 additions and 21 deletions
@@ -1,6 +1,6 @@
# Resource-refactor prerequisite bug fixes
Status: active defect track, 2026-07-12.
Status: active defect track, updated 2026-07-13.
This document isolates correctness defects discovered during the shared-resource ownership audit.
They should be fixed before the public resource API refactor wherever practical. Fixes in this track
@@ -86,6 +86,10 @@ Regression coverage:
- Repeat Engine creation/destruction in the same test process.
- Assert no callback touches the destroyed scene/factory and no singleton is recreated.
Status: Engine now clears pool-owned HTTP clients before scene/Graphics teardown. The complete
producer barrier remains pending A2 and A4 because shared-executor operations and static UI
deliveries do not yet have complete close/reject semantics.
### A4. UISceneNode static delivery queue lacks shutdown semantics
Current behavior:
@@ -122,21 +126,29 @@ Regression coverage:
- Create/link programs, destroy Engine, and repeat under ASAN.
Status: fixed, 2026-07-13. ShaderProgramManager is destroyed before Renderer while the selected
window and context are still alive.
### B2. TextLayout cache destroyed after FontManager
Current behavior:
Cached shaped glyphs retain raw FontTrueType pointers. The global TextLayout cache is currently
cleared after FontManager destruction.
cleared after FontManager destruction. `StaticLRU::clear()` also resets only its indexes, leaving
non-trivial cached values such as shared TextLayout pointers alive in its backing array.
Fix:
- Clear TextLayout and related shaped-font caches before FontManager.
- Release every active StaticLRU value when clearing the cache.
Regression coverage:
- Populate shaped-layout cache, destroy Engine, and verify repeated Engine lifecycle under ASAN.
Status: fixed, 2026-07-13. TextLayout is cleared before FontManager, and StaticLRU now resets its
active values. A weak cached layout expires during each tested Engine teardown.
### B3. Scene/global resource manager order
Current behavior:
@@ -153,6 +165,10 @@ Regression coverage:
- Destroy an Engine with live scene widgets using nine-patches and a non-empty batch.
Status: fixed, 2026-07-13. Scenes are destroyed first, BatchRenderer destruction explicitly drops
queued vertices and borrowed texture state without GL work, and global drawable/resource managers
remain alive until scene destruction completes.
## 4. Priority C: loader and callback lifetime
### C0. MemoryManager first-use synchronization
@@ -1,6 +1,7 @@
# Resource-refactor prerequisite bug-fix execution plan
Status: active; work packages 1 and 2 completed, 2026-07-13.
Status: active; work packages 1 and 2 completed; Work Package 4 deterministic ordering implemented,
2026-07-13.
This plan defines the bounded correctness work to complete before Stage 1 of the shared-resource
ownership refactor. It turns the findings in `resource_refactor_prerequisite_bugfixes.md` into an
@@ -143,6 +144,23 @@ Status: implemented. The focused TSAN suite initially reproduced the race in
`MemoryManager::addPointer()`. After the fix, all six `ResourcePrerequisites` tests pass under
TSAN without suppressions. The ASAN build and focused tests also pass.
### 3.6 StaticLRU clear retains non-trivial values
Current defect found during Work Package 4 validation:
`StaticLRU::clear()` resets its hash/list metadata and active count but leaves values in its backing
array. For `TextLayout::Cache`, this means `TextLayout::clearLayoutCache()` does not release cached
layouts or their raw font references.
Fix:
- Reset only the active value slots before clearing StaticLRU metadata.
- Keep the operation proportional to the number of live entries rather than total capacity.
- Verify cache release through a weak TextLayout handle during repeated Engine teardown.
Status: implemented. The cached layout expires before FontManager destruction in both tested
Engine lifecycles.
Work-package exit criteria:
- Each fix has an isolated regression test.
@@ -295,6 +313,14 @@ Work-package exit criteria:
- Every GPU-owning manager is destroyed while its required Renderer/context services remain valid.
- Repeated Engine lifecycle tests pass.
Status: the deterministic dependency order is implemented. Engine clears pool-owned HTTP clients
first, makes the selected context current, destroys scenes, explicitly discards batch state, clears
TextLayout, releases high-level Graphics managers in dependency order, destroys shaders before
Renderer, and only then destroys windows/contexts. A focused test covers two Engine lifecycles with
a live UI scene, framebuffer, nine-patch, texture, font/layout cache, shaders, and pending batch.
The complete asynchronous-producer exit criterion remains pending Work Packages 3 and 5.
The complete ASAN unit suite passes: 746 tests passed and one opt-in visual test was skipped.
## 7. Work package 5: UISceneNode async delivery lifecycle
Current behavior:
@@ -342,6 +342,13 @@ TextLayout cache
SystemFontResolver
```
This list records the pre-prerequisite order found by the audit. On 2026-07-13 the deterministic
portion was corrected: pool-owned HTTP clients stop first; the selected context is made current;
scenes precede GlobalBatchRenderer and global resource managers; TextLayout precedes FontManager;
ShaderProgramManager precedes Renderer; and windows/contexts remain until all Graphics singleton
teardown is complete. Complete shared-executor and static UI-delivery barriers remain assigned to
their prerequisite work packages.
Concrete violations:
| Current edge/order | Violation |