Skip to content

Don't block the server thread when a keepalive kick moves to the player region - #83

Open
ConnexionDev wants to merge 1 commit into
MultiPaper:ver/26.2from
ConnexionDev:fix/keepalive-disconnect-deadlock
Open

ConnexionDev wants to merge 1 commit into
MultiPaper:ver/26.2from
ConnexionDev:fix/keepalive-disconnect-deadlock

Conversation

@ConnexionDev

@ConnexionDev ConnexionDev commented Oct 18, 2026 •

Copy link
Copy Markdown

Summary

On ver/26.2 (build 26.2-11 / 068d8c5) a keepalive timeout freezes the whole server.

MinecraftServer calls keepConnectionAlive() on the global server thread for every player. disconnect() then sees that this thread does not own the player, queues the kick onto the player region, and waits with managedBlock() / waitForTasks. The region cannot tick until that server tick returns, so the wait never finishes. The watchdog fires after 10 seconds. While the player is still connected, the same path logs was kicked due to keepalive timeout again on the next tick.

Seen on 2026-10-11, 10-12, 10-16 and 10-18, each time a different player. The stuck stack is only server code:

keepConnectionAlive
-> ServerCommonPacketListenerImpl.disconnect
-> managedBlock
-> MinecraftServer.waitForTasks

This is not covered by #64 or #70. Both fixes are already in 068d8c5, and ver/26.3 still has the same wait.

Change

In ServerCommonPacketListenerImpl:

  • From a tick thread that does not own the player, scheduleTask the kick onto that player's region and return. Do not call managedBlock or waitable.get().
  • A flag stops the next ticks from logging another timeout and queueing another kick until the region has run the first one.
  • Callers that are not a tick thread still wait, same as before.

Test plan

  • Build ver/26.2 with this patch applied.
  • With players online, let one connection hit the keepalive limit (or kick a player from the server thread). The server tick should continue and the player should leave on the next region tick.
  • The console should log the timeout once, not once per tick.
  • A kick from an async thread should still complete.

…er region.

keepConnectionAlive runs on the global tick. disconnect() then waited in managedBlock for that player's region, which cannot run until the tick returns. Queue the kick onto the region and return instead.
@ConnexionDev
ConnexionDev force-pushed the fix/keepalive-disconnect-deadlock branch from 71b719e to 56ac79c Compare October 18, 2026 18:50
@PureGero

Copy link
Copy Markdown
Contributor

Please send the thread dumps from these crashes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants