pengowen123
pengowen123

Reputation: 1017

Skip part of loop while thread is sleeping

I have two parts of code that I want to run in a loop. Sometimes I need to make the loop 'sleep', making each iteration skip the second part. The loop should stop sleeping after a set amount of time (for example using a thread with a call to thread::sleep). How do I accomplish this?

use std::thread;

let mut sleeping = false;
let mut handle = thread::spawn(|| {});

loop {
    part_1();

    if sleeping {
        continue;
    }

    part_2();

    if some_condition {
        sleeping = true;
        handle = thread::spawn(|| thread::sleep_ms(100));
    }
}

In this example, if the condition is met, the part_2 call would be skipped for some amount of iterations. My use case is continuing to run graphical updates in a game, while freezing the game's logic (such as counting down timers).

Upvotes: 1

Views: 201

Answers (2)

Shepmaster
Shepmaster

Reputation: 430524

There is no need for the overhead of threads or even the need to sleep. Simply track the time that you should delay executing code until:

use std::time::{Duration, Instant};

fn part_1() {}
fn part_2() {}
fn some_condition() -> bool {
    false
}

fn main() {
    let mut sleep_until = None;
    loop {
        part_1();

        if let Some(until) = sleep_until {
            if until > Instant::now() {
                continue;
            }
        }

        part_2();

        if some_condition() {
            let now = Instant::now();
            let until = now + Duration::from_millis(500);
            sleep_until = Some(until);
        }
    }
}

Although I'd probably avoid the use of continue here, and instead embed the logic within:

use std::time::{Duration, Instant};

fn perform_physics_calculation() {}
fn perform_graphics_render() {}

fn main() {
    let mut next_graphics_update = Instant::now();
    let graphics_delay = Duration::from_millis(500);

    loop {
        let now = Instant::now();
        perform_physics_calculation();

        if next_graphics_update <= now {
            perform_graphics_render();
            next_graphics_update = now + graphics_delay;
        }
    }
}

Note in one case I use an Option<Instant> and in the other I just use an Instant; both cases can make sense.

Upvotes: 2

Francis Gagn&#233;
Francis Gagn&#233;

Reputation: 65692

Turn your sleeping variable into a reference-counted atomic boolean so that you can reset it on the sleeping thread.

use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread;
use std::time::Duration;

fn part_1() {}
fn part_2() {}
fn some_condition() -> bool { false }

fn main() {
    let sleeping = Arc::new(AtomicBool::new(false));
    let mut handle = None;

    loop {
        part_1();

        if sleeping.load(Ordering::Acquire) {
            continue;
        }

        part_2();

        if some_condition() {
            sleeping.store(true, Ordering::Release);
            let sleeping_clone = sleeping.clone();
            handle = Some(thread::spawn(move || {
                thread::sleep(Duration::from_millis(100));
                sleeping_clone.store(false, Ordering::Release);
            }));
        }
    }
}

Upvotes: 1

Related Questions