Repository navigation
fix(session): free retired sessions at the start of a frame, not mid-frame - #73
Merged
notchyves merged 1 commit intoNov 6, 2026
Conversation
…frame Connect() retired the current session and then freed every retired one right away. When it ran in the middle of a frame (a Discord join from UpdateDiscord, a transfer, a menu button), the rest of that frame still held the old session as f.live: UpdateChat locked its freed chat mutex and the client aborted with 'mutex lock failed'. Retired sessions are now freed in UpdateSession, before the frame takes its live session, so one always outlives the frame that retired it.
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.
Problem
Joining a server from Discord while already connected crashes the client:
The macOS crash report (
EXC_CRASH/SIGABRT, render thread) stops in:The run log right before it shows
[discord] joining nethernet.playhive.com:19132 from Discord.Cause
In the release binary,
FrameStep + 18708ismutex::lock(live + 0xA940), right afterPollJavaPack(live). That is the inlinedUpdateChat→live->TakeChat(), whereliveis the frame'sf.livesession.FrameSteprunsUpdateSession(f)(which setsf.live), thenUpdateDiscord(), thenUpdateViewSettingsandUpdateChat. A Discord join goesUpdateDiscord→HandleDiscordJoin→JoinServer→Connect(), in the middle of the frame.Connect()moved the current session intoretiredSessionsand then immediately ran:That freed the session it had just retired, while
f.livestill pointed at it. The rest of the frame then locked its destroyed chat mutex. The replay setup (PerlinReplay.cpp) pruned the same way. Any other mid-frameConnect()(a transfer, a menu button) has the same hazard.Change
Connect()and the replay session setup no longer free retired sessions.UpdateSessionfrees them at the start of a frame, before the frame takes itslivesession, with the sameconnectsInFlight/Transferring()conditions as before.A retired session therefore always outlives the frame that retired it. The other raw session pointers are safe with this:
hurtSessionis only compared, never dereferenced, andmodLiveis reassigned every frame inPumpMods, after the prune.Testing