Skip to content

Initial world output can starve the UI event loop #534

Description

@adkins17

Summary

Direct patch: https://github.com/potatomushclient/potato/pull/535.patch

Potato can become temporarily unresponsive while a newly connected world sends its initial output burst. During the stall, world switching and input are delayed or ignored, and the world toolbar may visibly redraw or flicker.

The receive path currently reads only 10 bytes for each readable socket event. Every completed line then calls skinStatus and synchronously executes update idletasks. A banner or login screen therefore creates many tiny socket callbacks and repeated full UI/status passes while the connection is populating.

Reproduction

  1. Keep several worlds open so toolbar/world-switch behavior is visible.
  2. Connect or reconnect a world that sends a substantial login banner and initial room output.
  3. Immediately try switching between the new connection and an existing connection using the world toolbar or Ctrl+<number>.
  4. Observe delayed/ignored actions and toolbar redraws until the initial output settles.

This was reproduced with Tcl/Tk 8.6.18 while connecting to Dev Asylum. An external PowerShell harness targeted Potato's HWND, sent alternating Ctrl+1 / Ctrl+6 commands during connection, and independently probed the Windows message loop with SendMessageTimeout(WM_NULL).

Measured behavior

Before the receive-loop change, a 25-second active trace recorded:

  • 2 Windows-message timeouts at a 100 ms deadline
  • 49 message-loop responses taking at least 50 ms
  • only 19 of 40 scheduled world-switch attempts completed during the exercise window
  • a multi-second interval where UI interaction could not be sampled normally

After the proposed change, the same reconnect and switch exercise recorded:

  • 0 Windows-message timeouts
  • 0 responses taking at least 50 ms
  • all 40 of 40 world-switch attempts completed
  • maximum message-loop response of 18.8 ms

CPU remained low during the original stalls, which is consistent with event-loop blocking/re-entrant UI work rather than CPU saturation.

Root cause

::potato::get_mushage reads only ten bytes per readable callback:

set disc [catch {::potato::ioRead $conn($c,id) 10} text]

::potato::get_mushageProcess then performs these operations for every completed line, regardless of whether connection/activity state changed:

skinStatus $c
update idletasks

skinStatus updates toolbar state and layout. update idletasks synchronously drains pending geometry and paint work from inside the receive callback. Together they amplify the callback flood and make input/window messages wait behind incoming data handling.

Proposed fix

  1. Read a bounded 4096-byte chunk per socket callback instead of 10 bytes.
  2. Remove unconditional per-line skinStatus and update idletasks calls.
  3. Preserve the required status notification exactly where get_mushageProcess directly changes conn($c,idle); the existing idle $c path already sends its own notification only on the first state transition.

This keeps socket reads bounded, allows Tk to return naturally to its event loop, and avoids rebuilding status UI when no state changed.

Validation

  • live /reload retained six connections and loaded only the affected procedures
  • reconnect plus 40-action interaction trace completed without a timeout or slow message-loop sample
  • local behavioral reliability suite: 24/24 passed
  • focused clean-upstream assertion passed
  • git diff --check passed
  • standalone patch applied cleanly and reproduced an exact diff against upstream commit 46d18387bd6b156b85176fa588a60991cbba6235

A focused pull request containing only potato.vfs/lib/potato.tcl will follow.

Activity

  1. adkins17 commented on Sep 12, 2026

    @adkins17
    Author

    Patch submitted as #535.

    Standalone patch details:

    • file: receive-burst-responsiveness.patch
    • bytes: 935
    • SHA-256: D54919D5DCE4505F7728BB77C391FAFA34301A3D613DDFDAF67783D2712FC0CB
    • commit: b22d099
    • baseline: 46d18387bd6b156b85176fa588a60991cbba6235
  2. adkins17 commented on Sep 12, 2026

    @adkins17
    Author

    Actual downloadable patch: https://github.com/potatomushclient/potato/pull/535.patch

    This is GitHub's generated patch for PR #535 and contains the complete focused change to potato.vfs/lib/potato.tcl.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions