-
Notifications
You must be signed in to change notification settings - Fork 567
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement timers for the X11 platform #1096
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, I think the timeouts in poll_with_timeout
aren't quite right. It looks good otherwise, though!
I did not yet have a look at the code, but I tried it with the
|
Signed-off-by: Uli Schlachter <[email protected]>
This makes the following command work correctly (as far as I can see): cargo run --example invalidate --no-default-features --features x11 Fixes-half-of: linebender#934 Signed-off-by: Uli Schlachter <[email protected]>
Signed-off-by: Uli Schlachter <[email protected]>
Signed-off-by: Uli Schlachter <[email protected]>
Signed-off-by: Uli Schlachter <[email protected]>
Thanks to @jneem for catching this. Signed-off-by: Uli Schlachter <[email protected]>
Yup. No idea yet what happens. I'll investigate. To make this a bit less subjective, I did: diff --git a/druid/examples/timer.rs b/druid/examples/timer.rs
index f31637a..5949fed 100644
--- a/druid/examples/timer.rs
+++ b/druid/examples/timer.rs
@@ -15,6 +15,7 @@
//! An example of a timer.
use std::time::Duration;
+use std::time::Instant;
use druid::widget::prelude::*;
use druid::widget::BackgroundBrush;
@@ -24,6 +25,7 @@ static TIMER_INTERVAL: Duration = Duration::from_millis(10);
struct TimerWidget {
timer_id: TimerToken,
+ timeout_requested: Instant,
simple_box: WidgetPod<u32, SimpleBox>,
pos: Point,
}
@@ -49,12 +51,20 @@ impl Widget<u32> for TimerWidget {
match event {
Event::WindowConnected => {
// Start the timer when the application launches
+ self.timeout_requested = Instant::now();
self.timer_id = ctx.request_timer(TIMER_INTERVAL);
}
Event::Timer(id) => {
if *id == self.timer_id {
+ let elapsed = self.timeout_requested.elapsed();
+ if elapsed < TIMER_INTERVAL {
+ println!("Timeout is {:?} early", TIMER_INTERVAL - elapsed);
+ } else {
+ println!("Timeout is {:?} late", elapsed - TIMER_INTERVAL);
+ }
self.adjust_box_pos(ctx.size());
ctx.request_layout();
+ self.timeout_requested = Instant::now();
self.timer_id = ctx.request_timer(TIMER_INTERVAL);
}
}
@@ -119,6 +129,7 @@ impl Widget<u32> for SimpleBox {
pub fn main() {
let window = WindowDesc::new(|| TimerWidget {
timer_id: TimerToken::INVALID,
+ timeout_requested: Instant::now(),
simple_box: WidgetPod::new(SimpleBox),
pos: Point::ZERO,
}) Output is:
X11:
|
Something is being slow and I do not know what. I also added diff --git a/druid-shell/src/platform/x11/application.rs b/druid-shell/src/platform/x11/application.rs
index 8d879eb..a1cc61f 100644
--- a/druid-shell/src/platform/x11/application.rs
+++ b/druid-shell/src/platform/x11/application.rs
@@ -569,6 +569,9 @@ fn poll_with_timeout(conn: &Rc<XCBConnection>, idle: RawFd, timer_timeout: Optio
.contains(PollFlags::POLLIN)
};
+ let before = Instant::now();
+ let p = poll(poll_fds, poll_timeout);
+ println!("Poll for {} returned {:?} in time {:?}", poll_timeout, p, before.elapsed());
match poll(poll_fds, poll_timeout) {
Ok(_) => {
if readable(poll_fds[0]) { Output is:
The No idea what to make of this. |
Found it. It's something like a rounding issue. When the Recomputing the timeout in each loop iteration "does the trick". |
Signed-off-by: Uli Schlachter <[email protected]>
if deadline < now { | ||
0 | ||
} else { | ||
c_int::try_from(deadline.duration_since(now).as_millis()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it would be best if this would round up instead of round down (in as_millis()
). Since that does not seem to be easily possible: How about adding a +1
to the result? That way, poll
should sleep until "something interesting" happens. Otherwise, it is possible that Instant::now()
is less than 1 ms before "something interesting" and the code here would busy loop with poll()
with a timeout of zero...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think adding 1 ms is a good approach.
if deadline < now { | ||
0 | ||
} else { | ||
c_int::try_from(deadline.duration_since(now).as_millis()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think adding 1 ms is a good approach.
// ...and convert the deadline into an argument for poll() | ||
let poll_timeout = if let Some(deadline) = deadline { | ||
if deadline < now { | ||
0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just break here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A combination of "historic reasons" and "I do not follow understand the idle drawing code". The previous code definitely did not return early and might have went into a short busy loop. I think. I am not sure.
I'll just change it to a break (and also to <=
instead of <
, but that shouldn't make much of a difference).
Duration::as_millis() seems to round down. This means that a timeout of e.g. 5.5 ms results in a call to poll() with a timeout of 5 ms. Thus, we wake up too early. This results in a short spike of CPU usage where poll() is called with a timeout of 0 ms until "it is time". Fix this by adding one to the timeout that is passed to poll(). Signed-off-by: Uli Schlachter <[email protected]>
Signed-off-by: Uli Schlachter <[email protected]>
This fixes half of #934, but does not deal with #934 (comment)
Timers are collected in a
BinaryHeap
per window, sorted by deadline. The main loop gets the first deadline of each window and uses the minimum of that for the timeout used in the call topoll
. I am least comfortable with my changes topoll_with_timeout()
. Hopefully, this function can be simplified a lot when someone (else) handles #934 (comment).This was tested with
cargo run --example invalidate --no-default-features --features x11
(and with nothing else). The result looks similar to the version without--features x11
, which should be GTK. Before this PR,--features x11
lead to an empty window instead.