Surface: fix leaks on init error paths - #632
Draft
deblasis wants to merge 1 commit into
Draft
Conversation
Three allocations in Surface.init are not released when a later step fails. Surface creation is recoverable, so each failure retains memory for the life of the process. The font grid reference taken from the shared set has no matching deref. Any failure after it leaves the refcount above zero, so the SharedGrid and its atlases are never freed and the map entry is never removed, even once every real surface using that font config has closed. setFontSize already registers this errdefer around the same call. The derived configs passed to Renderer.init and Termio.init are built inline as arguments. Both callees store them only on success and neither unwinds them on its own failure paths, so a failure inside either leaks an arena. Each is now a local guarded by an errdefer scoped to the window before ownership transfers. Scoping matters for the renderer one: at function scope that errdefer would double free the config alongside renderer_impl.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three allocations in
Surface.initare not released when a later step fails. Surface creation is recoverable (the apprt reports the error and the app keeps running), so each failure retains memory for the life of the process.The font grid reference has no matching deref.
app.font_grid_set.ref(...)increments a refcount, and nothing ininitreleases it on the error paths. Any failure after that call leaves the count above zero, so theSharedGridand its atlases are never freed and the map entry is never removed, even after every real surface using that font config has closed.setFontSizealready registers exactly this errdefer around the same call.Two derived configs are built inline as call arguments.
Renderer.initandTermio.initboth receive.config = try .init(alloc, config). Each stores it only on success and neither unwinds it on its own failure paths, so a failure inside either leaks an arena.Renderer.initcan fail in graphics API, swapchain or shaper setup;Termio.initcan fail inTerminal.init.Each is now a local guarded by an errdefer scoped to the window before ownership transfers. The scoping matters for the renderer one: at function scope that errdefer would double free the config alongside
renderer_impl, so it is confined to a block that ends onceRenderer.initreturns. The termio one is already inside a block whose last statement isTermio.init.Reachability is ordinary rather than exotic: a bad
commandin the config, a failing GPU init, or pty spawn failure all take these paths.Testing
zig build teston macOS: 3349/3365 passed, 16 skipped, no failures. The one failing build step isxcodebuild testaborting onchildPID > 0, which fails identically on the parent commit; that machine has no codesigning certificate.Not included
Surface.inithas a related defect I have deliberately left out. Its errdefers stay live paststd.Thread.spawnfor the renderer and IO threads, so a failure after that point deinits state the running threads are still reading, with no stop-notify or join.Thread.deinitdocuments that the caller must join first. On this branch the only reachable trigger is the IO thread spawn failing while the renderer thread is live, sinceset_titlecannot fail on either apprt. The fix needs teardown-ordering judgment rather than an errdefer, so it seemed better raised separately.