#9279·sway

`output enable` with no outputs applies a vacuous modeset and emits an output event, subscriber feedback loop

Author: yesvusCreated Sep 12, 2026Updated Sep 12, 2026
  • Sway Version:

    • swaymsg -t get_version: human_readable 1.12, variant sway (arch package 1:1.12-4), wlroots 0.20.2, linux 7.2.4, intel i915, two outputs (HDMI-A-1, eDP-1).
  • Debug Log:

    • No sway -d capture: recording one needs a session restart plus deliberately re-running the storm that pinned this machine at 95C. Substituting the journal and strace evidence below; happy to record a debug log if maintainers want one and suggest a safe brief trigger.
    • At the VT switch the journal shows:
      • connector eDP-1: Failed to disable CRTC
      • connector HDMI-A-1: Failed to disable CRTC
      • IPC Client socket error, removing client
    • strace -c -p on sway during the storm: ~109k syscalls/10s dominated by accept, epoll_ctl, fcntl, recvfrom, write.
  • Configuration File:

    • Reproducible with the default configuration plus this minimal subscriber loop (the whole trigger, no special config needed):
      bash
      swaymsg -m -t subscribe '["output"]' | while read -r _event; do
          if ! swaymsg -t get_outputs -r | jq -e 'any(.[]; .name == "eDP-1" and .active == true)' >/dev/null; then
              swaymsg "output eDP-1 enable" >/dev/null
          fi
      done
      Then switch to another VT so the sway session is inactive and get_outputs returns [].
  • Stack Trace:

    • N/A, no crash.
  • Description:

    • Setup: a helper subscribed to output events as above and reconciled clamshell state on each event. With the lid open and the internal panel not active it issued swaymsg "output eDP-1 enable".
    • Trigger: switch to another VT, so the sway session is inactive and get_outputs returns [].
    • Result: self sustaining loop at ~450 IPC connects/sec and ~1200 forks/sec. Sway TIME grew seconds per minute, package temp sat at 95C with fans pinned. Killing the helper returned sway to zero syscalls/10s and temps fell to the 40s within minutes.
    • Analysis: output_cmd_enable only sets enabled, then cmd_output calls store_output_config plus request_modeset unconditionally. store_output_config merges with no change detection. With zero outputs the apply trivially succeeds and update_output_manager_config calls ipc_event_output unconditionally (change is always "unspecified"), so every enable manufactures the exact event the subscriber waits on. Each iteration stores an identical config and re arms the 10ms modeset timer. Open question for maintainers: confirm the zero output commit path emits the event; inferred from the event stream, not traced into wlroots.
    • Proposed fix: make store_output_config report whether anything changed and only request_modeset on change. The second identical enable would then stay silent and this loop class dies after one iteration. A PR is attached (#9281).
    • The client script was at fault too and is fixed locally (single daemon, debounce, refusal to act on empty output lists). Filing because the vacuous event makes any subscribe driven reconciler a fork bomb.