Repository navigation
v0.1.9 spins at ~500k ppoll/s and saturates the Wayland compositor (fixed on main; release PR #11 still open) #12
Description
Summary
The released v0.1.9 binary keeps one tokio-runtime-worker thread spinning on
ppoll(), issuing roughly 250,000–500,000 syscalls per second indefinitely.
Because each wake-up round-trips to the Wayland compositor, the compositor itself
is dragged to ~40% CPU.
The rewritten runtime on main (4f4ea8a) fixes this completely, and the release
is already prepared: the changesets are in .changes/, and PR #11
(chore(release): bump versions) has been open since 10 August. So there is
nothing to implement — this issue is a request to merge #11 and publish, since
the AUR binary package still ships the February v0.1.9 build and every user of it
is running the spinning version.
Environment
| clip-bridge | v0.1.9, prebuilt release binary (AUR package) |
| Compositor | Hyprland 0.56.2, wlr-data-control |
| Platform | x86_64 Linux, Arch-based |
| Use case | synchronising plain text from an XWayland application to a native Wayland client |
Measurements
After about an hour of daemon uptime, the busy thread:
wchan: __x64_sys_ppoll.cold
voluntary_ctxt_switches: 1,111,426,794 -> ~252,000/s
A shorter sample taken right after startup measured ~500,000/s. Over that period
the daemon accumulated roughly 3x more CPU time than the wall-clock time it had
been running, i.e. several threads spinning at once.
Stopping and restarting the daemon isolates the effect on the compositor:
| daemon running | daemon stopped | |
|---|---|---|
| compositor CPU | 39.8% | 0.6% |
| clip-bridge CPU | ~50% | — |
| system load average, idle desktop | 4.61 | 0.11 |
The process also holds two anon_inode:sync_file descriptors open for its whole
lifetime. That may be relevant — a permanently-ready descriptor that is polled but
never consumed would produce exactly this pattern — but I did not confirm it, so
please treat it as an observation rather than a diagnosis.
Why this matters beyond CPU usage
Hyprland composites on a single thread. At high refresh rates the per-frame budget
is only a few milliseconds, so spending ~40% of it servicing clipboard round-trips
made compositing irregular: visible stutter and input latency on every composited
window. Fullscreen applications were unaffected, because direct scan-out bypasses
the compositor entirely.
That asymmetry is worth highlighting, because it makes the bug hard to attribute:
the desktop appears to stutter everywhere except in fullscreen, which points away
from a clipboard daemon. I spent a long time looking at graphics drivers and
launcher settings before measuring /proc and finding this.
Root cause is already documented upstream
docs/DESIGN.md on main describes exactly this behaviour in the 0.1.x code:
X11 uses fixed sleep polling, and polls global events again inside the selection
request function; this swallows unrelated events, blocks normal dispatch
Wayland repeatedly uses
roundtripto drive long-term event loops
Verification that main fixes it
I built main (4f4ea8a) and ran it in the same session, under the same workload:
| v0.1.9 | main @ 4f4ea8a |
|
|---|---|---|
| syscalls/s | ~500,000 | 20 |
| clip-bridge CPU | ~50% | 0.0% (TIME+ 00:00:00 after 30 min) |
| compositor CPU | 39.8% | 0.4% |
| idle load average | 4.61 | 0.83 |
Both event loops now block on poll() with a 100 ms timeout, which is the correct
shape.
Functionally verified on the rewritten build:
- XWayland → Wayland: a few KB of plain text copied out of an XWayland
application pasted correctly into a native Wayland browser. - Wayland → XWayland: works.
- A separate
v0.1.9bug also disappeared: it advertisedtext/plainthree
times (text/plain text/plain text/plain;charset=utf-8 TEXT STRING UTF8_STRING),
which froze Chromium-based browsers on paste-as-plain-text (Ctrl+Shift+V).
mainadvertises onlytext/plain;charset=utf-8andtext/plain, and the
freeze is gone.
Request
Merging PR #11 would publish the rewritten runtime and resolve this for every
user of the AUR binary package. Until then, building from git is the only way to
avoid the spin.
I appreciate there may be a reason the release is being held — if main is not
considered ready to ship, it would still help to know, so downstream packagers can
decide whether to carry a git build in the meantime.
Thanks for the tool — it is the only thing that makes XWayland → Wayland clipboard
work reliably here. Clipboard managers built on wl-clipboard (cliphist,
wl-clip-persist, clipman) cannot substitute for it, since they never observe
selections originating from XWayland.
Minor and unrelated: clip-bridge --help is not recognised — the argument is
ignored and the daemon starts instead, which hangs non-interactive shells. Happy to
file that separately if you prefer.
Activity