Skip to content

Commit

Permalink
On X11, Fix for repeated event loop iteration when ControlFlow was …
Browse files Browse the repository at this point in the history
…`Wait` (#2155)

* On X11, Fix for repeated event loop iteration
when `ControlFlow` was `Wait`

* ControlFlow::Poll now runs continously as should
  • Loading branch information
ArturKovacs authored Feb 4, 2022
1 parent bc1dc1f commit 0e52672
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 44 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ And please only add new entries to the top of this list, right below the `# Unre

# Unreleased

- On X11, fix for repeated event loop iteration when `ControlFlow` was `Wait`
- On Wayland, report unaccelerated mouse deltas in `DeviceEvent::MouseMotion`.
- **Breaking:** Bump `ndk` version to 0.6, ndk-sys to `v0.3`, `ndk-glue` to `0.6`.
- Remove no longer needed `WINIT_LINK_COLORSYNC` environment variable.
Expand Down
169 changes: 125 additions & 44 deletions src/platform_impl/linux/x11/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use std::{
ptr,
rc::Rc,
slice,
sync::mpsc::{Receiver, Sender},
sync::mpsc::{Receiver, Sender, TryRecvError},
sync::{mpsc, Arc, Weak},
time::{Duration, Instant},
};
Expand Down Expand Up @@ -63,6 +63,39 @@ struct WakeSender<T> {
waker: Arc<Waker>,
}

struct PeekableReceiver<T> {
recv: Receiver<T>,
first: Option<T>,
}

impl<T> PeekableReceiver<T> {
pub fn from_recv(recv: Receiver<T>) -> Self {
Self { recv, first: None }
}
pub fn has_incoming(&mut self) -> bool {
if self.first.is_some() {
return true;
}
match self.recv.try_recv() {
Ok(v) => {
self.first = Some(v);
return true;
}
Err(TryRecvError::Empty) => return false,
Err(TryRecvError::Disconnected) => {
warn!("Channel was disconnected when checking incoming");
return false;
}
}
}
pub fn try_recv(&mut self) -> Result<T, TryRecvError> {
if let Some(first) = self.first.take() {
return Ok(first);
}
self.recv.try_recv()
}
}

pub struct EventLoopWindowTarget<T> {
xconn: Arc<XConnection>,
wm_delete_window: ffi::Atom,
Expand All @@ -79,8 +112,8 @@ pub struct EventLoop<T: 'static> {
poll: Poll,
waker: Arc<Waker>,
event_processor: EventProcessor<T>,
redraw_channel: Receiver<WindowId>,
user_channel: Receiver<T>, //waker.wake needs to be called whenever something gets sent
redraw_receiver: PeekableReceiver<WindowId>,
user_receiver: PeekableReceiver<T>, //waker.wake needs to be called whenever something gets sent
user_sender: Sender<T>,
target: Rc<RootELW<T>>,
}
Expand Down Expand Up @@ -240,8 +273,8 @@ impl<T: 'static> EventLoop<T> {
poll,
waker,
event_processor,
redraw_channel,
user_channel,
redraw_receiver: PeekableReceiver::from_recv(redraw_channel),
user_receiver: PeekableReceiver::from_recv(user_channel),
user_sender,
target,
}
Expand All @@ -262,120 +295,168 @@ impl<T: 'static> EventLoop<T> {
where
F: FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
{
let mut control_flow = ControlFlow::default();
let mut events = Events::with_capacity(8);
let mut cause = StartCause::Init;

let exit_code = loop {
struct IterationResult {
deadline: Option<Instant>,
timeout: Option<Duration>,
wait_start: Instant,
}
fn single_iteration<T, F>(
this: &mut EventLoop<T>,
control_flow: &mut ControlFlow,
cause: &mut StartCause,
callback: &mut F,
) -> IterationResult
where
F: FnMut(Event<'_, T>, &RootELW<T>, &mut ControlFlow),
{
sticky_exit_callback(
crate::event::Event::NewEvents(cause),
&self.target,
&mut control_flow,
&mut callback,
crate::event::Event::NewEvents(*cause),
&this.target,
control_flow,
callback,
);

// Process all pending events
self.drain_events(&mut callback, &mut control_flow);
this.drain_events(callback, control_flow);

// Empty the user event buffer
{
while let Ok(event) = self.user_channel.try_recv() {
while let Ok(event) = this.user_receiver.try_recv() {
sticky_exit_callback(
crate::event::Event::UserEvent(event),
&self.target,
&mut control_flow,
&mut callback,
&this.target,
control_flow,
callback,
);
}
}
// send MainEventsCleared
{
sticky_exit_callback(
crate::event::Event::MainEventsCleared,
&self.target,
&mut control_flow,
&mut callback,
&this.target,
control_flow,
callback,
);
}
// Empty the redraw requests
{
let mut windows = HashSet::new();

while let Ok(window_id) = self.redraw_channel.try_recv() {
while let Ok(window_id) = this.redraw_receiver.try_recv() {
windows.insert(window_id);
}

for window_id in windows {
let window_id = crate::window::WindowId(super::WindowId::X(window_id));
sticky_exit_callback(
Event::RedrawRequested(window_id),
&self.target,
&mut control_flow,
&mut callback,
&this.target,
control_flow,
callback,
);
}
}
// send RedrawEventsCleared
{
sticky_exit_callback(
crate::event::Event::RedrawEventsCleared,
&self.target,
&mut control_flow,
&mut callback,
&this.target,
control_flow,
callback,
);
}

let start = Instant::now();
let (deadline, timeout);

match control_flow {
ControlFlow::ExitWithCode(code) => break code,
ControlFlow::ExitWithCode(_) => {
return IterationResult {
wait_start: start,
deadline: None,
timeout: None,
};
}
ControlFlow::Poll => {
cause = StartCause::Poll;
*cause = StartCause::Poll;
deadline = None;
timeout = Some(Duration::from_millis(0));
}
ControlFlow::Wait => {
cause = StartCause::WaitCancelled {
*cause = StartCause::WaitCancelled {
start,
requested_resume: None,
};
deadline = None;
timeout = None;
}
ControlFlow::WaitUntil(wait_deadline) => {
cause = StartCause::ResumeTimeReached {
*cause = StartCause::ResumeTimeReached {
start,
requested_resume: wait_deadline,
requested_resume: *wait_deadline,
};
timeout = if wait_deadline > start {
Some(wait_deadline - start)
timeout = if *wait_deadline > start {
Some(*wait_deadline - start)
} else {
Some(Duration::from_millis(0))
};
deadline = Some(wait_deadline);
deadline = Some(*wait_deadline);
}
}
return IterationResult {
wait_start: start,
deadline,
timeout,
};
}

let mut control_flow = ControlFlow::default();
let mut events = Events::with_capacity(8);
let mut cause = StartCause::Init;

// If the XConnection already contains buffered events, we don't
// need to wait for data on the socket.
if !self.event_processor.poll() {
if let Err(e) = self.poll.poll(&mut events, timeout) {
// run the initial loop iteration
let mut iter_result = single_iteration(self, &mut control_flow, &mut cause, &mut callback);

let exit_code = loop {
if let ControlFlow::ExitWithCode(code) = control_flow {
break code;
}
let has_pending = self.event_processor.poll()
|| self.user_receiver.has_incoming()
|| self.redraw_receiver.has_incoming();
if !has_pending {
// Wait until
if let Err(e) = self.poll.poll(&mut events, iter_result.timeout) {
if e.raw_os_error() != Some(libc::EINTR) {
panic!("epoll returned an error: {:?}", e);
}
}
events.clear();

if control_flow == ControlFlow::Wait {
// We don't go straight into executing the event loop iteration, we instead go
// to the start of this loop and check again if there's any pending event. We
// must do this because during the execution of the iteration we sometimes wake
// the mio waker, and if the waker is already awaken before we call poll(),
// then poll doesn't block, but it returns immediately. This caused the event
// loop to run continously even if the control_flow was `Wait`
continue;
}
}

let wait_cancelled = deadline.map_or(false, |deadline| Instant::now() < deadline);
let wait_cancelled = iter_result
.deadline
.map_or(false, |deadline| Instant::now() < deadline);

if wait_cancelled {
cause = StartCause::WaitCancelled {
start,
requested_resume: deadline,
start: iter_result.wait_start,
requested_resume: iter_result.deadline,
};
}

iter_result = single_iteration(self, &mut control_flow, &mut cause, &mut callback);
};

callback(
Expand Down

0 comments on commit 0e52672

Please sign in to comment.