Drag-resize causes application lock-up
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 30.6k
- Forks
- 2.1k
- Avg merge
- 1d 9h
- Merged PRs (30d)
- 72
Description
Describe the bug
Using egui 0.35.0, eframe 0.35.0, winit 0.30.13, calloop 0.13.0 on a Wayland session, drag-resizing (edge/corner) the window causes 100% CPU load on the main thread, and the application locks up. The issue persists after ending the drag-resize interaction. Single-event resizing (for instance, hitting the maximize window button) does not cause this issue.
To Reproduce
Steps to reproduce the behavior:
- Any eframe (wgpu) app on a Wayland session. A non-trivial per-frame paint makes it more reliable (a paint that takes a few ms widens the window in which the compositor's frame callback is outstanding).
- Grab a window edge or corner and drag to resize
- Observe the process's main thread CPU load
Expected behavior
CPU load tracks the resize work and returns to idle when drag stops.
Actual behavior
The main thread remains at 100%. eframe::App::ui stops being invoked. The window does not repaint.
Suspected root cause
1. winit defers RedrawRequested while a frame callback is outstanding
winit-0.30.13/src/platform_impl/linux/wayland/event_loop/mod.rs ~L486:
While the surface is waiting for the compositor's wl_surface.frame callback, winit withholds RedrawRequest. An interactive resize (a storm of configures) leaves that callback outstanding.
2. eframe leaves ControlFlow::Poll when the redraw is deferred
eframe-0.35.0/src/native/run.rs ~L188-L243:
When a repaint is due, eframe sets ControlFlow::Poll, calls request_redraw() and removes the entry from windows_next_repaint_times. It relies on winit emitting RedrawRequested --> run_ui_and_paint() --> the app scheduling the next repaint --> the WaitUntil reschedule at L241.
If winit defers the redraw, run_ui_and_paint never runs, nothing re-populates windows_next_repaint_times, so next_repaint_time is None and ControlFlow is never moved off Poll.
3. The interaction causes a zero-timeout issue
winit-0.30.13/src/platform_impl/linux/wayland/event_loop/mod.rs ~L266:
ControlFlow::Poll --> timeout 0 --> loop_dispatch(0) returns immediately with no new Wayland events --> single_iteration runs (no RedrawRequested emitted) --> back to the top. The loop never blocks --> 100% CPU load.
In contrast a maximize window is a single-configure event whose one frame callback resolves promptly, so the deferral clears in one shot.
A resize drag keeps the callback outstanding, so eframe stays on Poll.
Evidence
egui::Context::repaint_causes() logged from the top of the application's ui closure: Fires on startup, then never again after starting drag-resize.
What does NOT fix it
- NativeOptions { run_and_return: false, .. } -- No effect. winit's EventLoop:run on the Wayland backend funnels through the same run_on_demand --> pump_events --> poll_events_with_timeout code
- Application-side mitigation -- Impossible from the application code, ui never runs during drag-resize, so no application code executes to break it, and egui schedules the minimum requested repaint delay, so a background-thread request_repaint_after cannot push the already-pending immediate repaint into the future to forace a WaitUntil.
- Not the swap chain -- The wgpu surface-error path is not involved (no surface recreation/Outdated churn).
Proposed resolution
Either layer resolves it; the eframe-side change is the smaller and more targeted one:
- eframe: In check_redraw_requests when a repaint was requested but no future repaint remains scheduled, fall back to a bounded ControlFlow::WaitUntil(now + small) instead of leaving ControlFlow::Poll. A deferred RedrawRequested then blocks on the Wayland fd (waking on the frame callback) instead of busy-spinning, while a normally-serviced redraw is unaffected (the RedrawRequested arrives on the same iteration, well before the small timeout). This turns a 100% CPU lock-up into an at-worst bounded wait.
- winit: Don't wait indefinitely on a stale frame callback. Reset or time-bound FrameCallbackState::Requested across an interactive drag-resize, so a lost callback can't inhibit RedrawRequested forever.
Related eframe busy-loop guards already exist for the invisible/minimized-window case (INVISIBLE_WINDOW_REPAINT_INTERVALL; see egui issues #7776, #5229). This is the same failure mode for the frame-callback-deferred case during resize.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Reproduce the lock-up in a Rust eframe app on Wayland, then read eframe-0.35.0/src/native/run.rs around check_redraw_requests and winit-0.30.13/src/platform_impl/linux/wayland/event_loop/mod.rs around the redraw and timeout handling. Compare the proposed eframe and winit fixes; done means drag-resizing no longer leaves the main thread at 100% CPU and repainting resumes.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- desktop, operating-systems
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100