diff --git a/window/src/os/x11/connection.rs b/window/src/os/x11/connection.rs index b8f02e9b4..009afa50a 100644 --- a/window/src/os/x11/connection.rs +++ b/window/src/os/x11/connection.rs @@ -264,12 +264,21 @@ impl ConnectionOps for Connection { // relied solely on that. self.process_queued_xcb()?; - let period = self - .timers - .borrow() - .time_until_due(Instant::now()) - .map(|duration| duration.min(period)) - .unwrap_or(period); + // Check the spawn queue before we try to sleep; there may + // be work pending and we don't guarantee that there is a + // 1:1 wakeup to queued function, so we need to be assertive + // in order to avoid missing wakeups + let period = if SPAWN_QUEUE.run() { + // if we processed one, we don't want to sleep because + // there may be others to deal with + Duration::new(0, 0) + } else { + self.timers + .borrow() + .time_until_due(Instant::now()) + .map(|duration| duration.min(period)) + .unwrap_or(period) + }; match poll.poll(&mut events, Some(period)) { Ok(_) => { @@ -282,7 +291,6 @@ impl ConnectionOps for Connection { } else { } } - // self.process_sigchld(); } Err(err) => { diff --git a/window/src/spawn.rs b/window/src/spawn.rs index 6b5ce8efc..40ec850b7 100644 --- a/window/src/spawn.rs +++ b/window/src/spawn.rs @@ -39,7 +39,7 @@ impl SpawnQueue { self.spawn_impl(f) } - pub fn run(&self) { + pub fn run(&self) -> bool { self.run_impl() } @@ -67,18 +67,36 @@ impl SpawnQueue { self.event_handle.set_event(); } - fn run_impl(&self) { + fn run_impl(&self) -> bool { self.event_handle.reset_event(); + let mut did_any = false; while let Some(func) = self.pop_func() { func(); + did_any = true; } + did_any } } #[cfg(all(unix, not(target_os = "macos")))] impl SpawnQueue { fn new_impl() -> Fallible { + // On linux we have a slightly sloppy wakeup mechanism; + // we have a non-blocking pipe that we can use to get + // woken up after some number of enqueues. We don't + // guarantee a 1:1 enqueue to wakeup with this mechanism + // but in practical terms it does guarantee a wakeup + // if the main thread is asleep and we enqueue some + // number of items. + // We can't affort to use a blocking pipe for the wakeup + // because the write needs to hold a mutex and that + // can block reads as well as other writers. let pipe = Pipe::new()?; + let on = 1; + unsafe { + libc::ioctl(pipe.write.as_raw_fd(), libc::FIONBIO, &on); + libc::ioctl(pipe.read.as_raw_fd(), libc::FIONBIO, &on); + } Ok(Self { spawned_funcs: Mutex::new(VecDeque::new()), write: Mutex::new(pipe.write), @@ -93,13 +111,19 @@ impl SpawnQueue { self.write.lock().unwrap().write(b"x").ok(); } - fn run_impl(&self) { + fn run_impl(&self) -> bool { + // On linux we only ever process one at at time, so that + // we can return to the main loop and process messages + // from the X server use std::io::Read; - while let Some(func) = self.pop_func() { + if let Some(func) = self.pop_func() { func(); let mut byte = [0u8]; self.read.lock().unwrap().read(&mut byte).ok(); + true + } else { + false } } } @@ -168,10 +192,13 @@ impl SpawnQueue { } } - fn run_impl(&self) { + fn run_impl(&self) -> bool { + let mut did_any = false; while let Some(func) = self.pop_func() { func(); + did_any = true; } + did_any } }