I'm working on a program that updates the background of an x11 desktop at a specified interval. However, it eats large amounts of my CPU just sitting idle. I know this is due to the main loop running faster than I want it to. I've used a couple shameful hacks to quell usage down to about 15% but now I am searching for a long-term solution.

Here is the function that contains the main loop:

pub fn run_mapped(&mut self) {
    info!(self.logger, "Running in mapped mode!");
    let wait_d = Duration::new(0,500);
    let long_wait_d = Duration::new(0,750);
    loop {
        if self.img_dir.1.elapsed() > self.reload_time {
            self.img_dir.1 = Instant::now();
        let cd = self.x.get_current_desktop();
        // change background if timeout is reached
        if self.since_timeout.elapsed() > self.timeout {
            let ref mut current_bg = self.image_map[cd];
            self.since_timeout = Instant::now();
        match self.x.next_event() {
                Some(_) => {
                    let ref mut current_bg = self.image_map[cd];
                None => { sleep(wait_d); }

Not shown, but I have additionally set process priority lower to give more important process priority over this one.

I added the sleep calls to prevent >50% CPU usage but it still consumes ~20% which seems a little excessive for what I want this program to do. I'm looking at some of the CPU usage from other programs I'm running in my DE (candybar) and they're nowhere near as resource-hungry as my program.

All that being said, my two questions are:

  1. Why is this? Meaning, why is my program using so much CPU since it is basically 'sleeping' most of the time (in its current state)?

  2. How can I fix this? The only thing I can think of now is scrapping it and abusing cron jobs to the same effect.

The full code can be found on Github.

  • \$\begingroup\$ Does Rust have a Timer you can use that fires an event periodically? \$\endgroup\$
    – user34073
    Dec 30 '16 at 5:48
  • \$\begingroup\$ I don't believe so and the only external timing library's appear to be basic fps counters :/. But i don't think that would work because it needs to monitor "_NET_CURRENT_DESKTOP" which could change at anytime. But i could be wrong? \$\endgroup\$ Dec 30 '16 at 5:55

since it is basically 'sleeping' most of the time

That's not really the most accurate thing...

impl Duration {
    fn new(secs: u64, nanos: u32) -> Duration

Duration's second argument is nanoseconds, thus your "long wait" is 750 nanoseconds. That's 0.75 microseconds / 0.00075 milliseconds / 0.00000075 seconds. For reference, a cycle of a 3GHz computer is 0.3333 nanoseconds. If your code takes zero time, you are waking 1 million times per second!

This is the very definition of a busy wait loop.

So how do you fix it? You need to rearchitect. You state:

and abusing cron jobs to the same effect

Cron jobs have resolution of a minute (8×10^7 bigger!). If you can live with that, sleep for that long.

Another solution is to move to an event-driven system. You make some mention:

it needs to monitor _NET_CURRENT_DESKTOP which could change at anytime.

If that's the case, you'd have two sources of events: a timer and whatever provides the _NET_CURRENT_DESKTOP event. The current front-runners of this type of code in Rust are futures and mio. I don't know exactly how the X code works, but it's probably event driven (GUI code usually is), and there's probably some mio/futures periodic timer. You "simply" need to configure both of them and then respond whenever either triggers.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.