fix: harden network and font resource lifetimes

- retain asynchronous HTTP operations until execution or queue disposal
  - make HTTP shutdown cancel and join queued and running shared-pool requests
  - avoid deadlocks when callbacks clear the HTTP pool
  - share atomic cancellation state across request copies
  - detect socket creation failures before configuring TCP or UDP sockets
  - release partially initialized mbedTLS state after failed handshakes
  - replace retained FreeType faces with bounded LRU probe-result caching
  - release non-trivial StaticLRU keys when clearing the cache
  - resolve the Texture Viewer close icon through the configured icon theme
  - add HTTP, TLS, UIWebView fan-out, font descriptor, and LRU regressions
  - update resource-refactor prerequisite and ownership planning documents
This commit is contained in:
Martín Lucas Golini
2026-07-14 02:16:01 -03:00
parent 8aa660104f
commit 7e66e77d89
19 changed files with 943 additions and 211 deletions
@@ -65,6 +65,17 @@ Regression coverage:
- Running and queued variants complete/cancel without UAF under ASAN.
- Callback re-entry does not deadlock.
Status: implemented. Shared-executor requests are registered before submission and retained by a
heap-backed operation. The operation destructor unregisters it both after execution and when the
external executor discards queued work. `Http::shutdown()` atomically rejects new work,
cancels registered requests, and waits without holding Pool or request-map locks. Callback-initiated
Pool clearing cannot wait for work queued behind that callback, so it requests cancellation and
lets shared operations defer destruction until the shared queue drains. `Request` cancellation is
shared and atomic across request copies. Focused ASAN coverage exercises queued memory, stream, and
file requests, running cancellation, concurrent Pool clearing, and Pool clearing from a callback
with another request queued behind it. All six focused HTTP tests pass under ASAN and unsuppressed
TSAN.
### A3. Engine stops HTTP/resource producers too late
Current behavior:
@@ -86,8 +97,9 @@ 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
Status: Engine now clears pool-owned HTTP clients before scene/Graphics teardown. Shared-executor
HTTP operations are covered by A2's explicit Pool barrier. The complete producer barrier remains
pending A4 because static UI
deliveries do not yet have complete close/reject semantics.
### A4. UISceneNode static delivery queue lacks shutdown semantics
@@ -264,6 +264,16 @@ Work-package exit criteria:
- No shared-pool lambda depends on an untracked raw Http lifetime.
- Http destruction provides a complete operation barrier without taking ownership of the executor.
Status: implemented. Shared-pool operations are heap-backed, registered before executor
submission, and unregister from their destructor even when queued work is discarded. Pool-managed
Http instances remain alive through callback completion, while stack/raw
instances use the destructor barrier. Pool clearing requests cancellation and waits outside the
Pool mutex. When clearing is initiated by a shared-pool callback, it defers the shared-operation
barrier because queued work may require the current executor thread; shared operations retain the
clients until that work drains. Cancellation state is atomic and shared by request copies. Focused
ASAN coverage for all three async output forms, running cancellation, and callback-initiated
clearing with queued work passes. All six focused HTTP tests also pass under unsuppressed TSAN.
## 6. Work package 4: deterministic Engine shutdown
Reorder shutdown only after HTTP and loader barriers are reliable.
@@ -318,8 +328,8 @@ first, makes the selected context current, destroys scenes, explicitly discards
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.
The complete asynchronous-producer exit criterion remains pending Work Package 5.
The complete ASAN unit suite passes: 749 tests passed and one opt-in visual test was skipped.
## 7. Work package 5: UISceneNode async delivery lifecycle
@@ -43,6 +43,11 @@ The architecture contracts can proceed with these refinements:
Members are destroyed in reverse declaration order, so the loader is destroyed last and can
run against already-destroyed state.
These are prerequisite bugs and should be fixed independently before the ownership refactor.
Status: the HTTP Pool lock-order defect and shared-ThreadPool operation lifetime defect are
fixed. Pool clearing now establishes an operation barrier without owning the executor, including
callback-initiated clearing and executor-discarded queued work. The TextureAtlasLoader lifetime
defect is also fixed and covered by sanitizer-backed regression tests.
6. `isStateful()` is unusable as a shareability test. Every current Drawable inherits mutable color
and position, and several classes reporting false mutate themselves or children during draw.
7. Stage 2 must migrate texture holders, loaders, ID-based construction, and queued batches in one