Fix server destroy lifecycle race - #712
Open
ViktorJannicke wants to merge 2 commits into
Open
Conversation
ViktorJannicke
marked this pull request as draft
August 8, 2026 15:13
ViktorJannicke
force-pushed
the
codex/server-destroy-lifecycle
branch
from
August 8, 2026 18:39
e3c5327 to
92c918d
Compare
ViktorJannicke
force-pushed
the
codex/server-destroy-lifecycle
branch
from
August 8, 2026 18:53
92c918d to
bcf6d1f
Compare
ViktorJannicke
marked this pull request as ready for review
August 8, 2026 18:53
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.
Summary
Fix crashes, use-after-free races, and shutdown deadlocks when a WebUI window is destroyed while its server, callbacks, WebSocket work, WebView work, or show operations are still active.
This introduces coordinated two-phase destruction: new work is rejected immediately, while final memory reclamation waits until every owner of the window and its CivetWeb connections has retired.
Motivation
We recently started using WebUI in a project and, while integrating it more deeply, began exercising window and server lifecycle paths beyond the basic examples. This exposed reproducible crashes when destroying a window from a callback and during repeated server teardown.
Investigating those crashes required following ownership across WebUI’s window state, server threads, callbacks, and the embedded CivetWeb connection storage. The resulting diff is larger than the visible fix might suggest because the affected lifetime rules were spread across several teardown paths. This change consolidates those rules so destruction consistently stops new work, waits for active work to retire, and only then releases the underlying state.
Problem
webui_destroy()could free window state while it was still referenced by:webui_show*()orwebui_start_server()callsDestroying a window from one of its own callbacks was especially unsafe. Waiting synchronously would deadlock because CivetWeb was waiting for that callback to return, while freeing immediately caused use-after-free crashes.
CivetWeb also needed a way to stop accepting new work and interrupt blocked connection I/O without immediately joining workers and freeing connection storage still referenced by WebUI tasks.
Changes
Window lifecycle
webui_destroy()calls wait on the global window registry instead of touching potentially reclaimed per-window synchronization objects.WEBUI_MAX_IDS.Show and global cleanup coordination
webui_show*()andwebui_start_server().webui_clean()wait for both server threads and show operations.webui_clean()is called from a WebUI callback, request application exit and defer process-wide reclamation until the callback has returned.webui_start_server()result lifetimeWorker and monitor shutdown
Vendored CivetWeb carry patch
Add a WebUI-private quiesce operation that:
INVALID_SOCKET.The extension is private to WebUI and is not added to CivetWeb’s public API.
Compatibility
webui_start_server()now provides safer returned-string storage.Verification
Verified on Linux with:
ALTERNATIVE_QUEUECivetWeb configurationswebui_start_server()URL lifetime across destruction and thread exit