Skip part of loop while thread is sleeping - multithreading

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).

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.

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);
}));
}
}
}

Related

Polling a context inside another poll

I have a polling function that will forever poll and always do pending. Inside the polling function poll_event_loop I want to control timings when the context should be polled again and the function to be called again in x seconds depending on some conditions. I could do this using another thread which calls the waker.wake_by_ref function. But this feels like a cheat. How could I do this without other threads.
poll_fn(|cx| self.poll_event_loop(cx)).await
// function will never be Ready, always pending, polling sould be fast so not just sleeping x seconds inside
fn poll_event_loop(&mut self, cx: &mut Context) -> Poll<anyhow::Result<()>> {
while some_codeandfunc() { /*....*/ }
// guarantee another poll_fn in 1 sec
if condition {
context_callback(cx, 1000);
}
// guarantee another poll_fn in 2 sec
if condition {
context_callback(cx, 2000);
}
Poll::Pending
}
fn context_callback(context: &mut Context, millisec: u64) {
let mut future = Box::pin(tokio::time::sleep(Durationtk::from_millis(millisec)));
//let cb = future.as_mut().poll(context);
future.poll_unpin(context);
}
// ugly way to auto poll the function every x seconds
fn spawn_qeueu_thread(waker: &Waker, rx: &Receiver<String>) -> Option<JoinHandle<()>> {
debug!("doing spawning thread");
//self.thread_spawned = true;
let waker = waker.clone();
let rx2 = rx.clone();
let spawn = tokio::spawn(async move {
loop {
tokio::time::sleep(Durationtk::from_millis(WAKEUPINTERVAL)).await;
debug!("doing other thread wakebyref");
waker.wake_by_ref();
let try_result = rx2.try_recv();
match try_result {
Err(_) => {}
Ok(_msg) => break,
}
}
debug!("ending spawned thread");
});
return Some(spawn);
//self.threadhandle = Some(spawn);
}
After some experimenting, I found a working solution that lets you call the polling function on multiple desired times, working example:
use chrono::Utc;
use futures::FutureExt;
use futures::future::poll_fn;
use tokio::time::Sleep;
use std::pin::Pin;
use std::task::Context;
use std::task::Poll;
use tokio::time::Duration as Durationtk;
pub struct ControllerModule {
vec: Vec<Pin<Box<Sleep>>>,
i: i64,
}
impl ControllerModule {
fn new() -> Self {
let vec = vec![];
let i = 0;
Self { vec, i }
}
async fn start(&mut self) {
poll_fn(|cx| self.poll_event_loop(cx)).await;
print!("worked");
}
fn poll_event_loop(&mut self, context: &mut Context) -> Poll<anyhow::Result<()>> {
self.i += 1;
if self.i % 3 == 0 {
let mut sleep = Box::pin(tokio::time::sleep(Durationtk::from_millis(5000)));
sleep.poll_unpin(context);
self.vec.push(sleep);
} else if self.i % 3 == 1 {
let mut sleep = Box::pin(tokio::time::sleep(Durationtk::from_millis(4000)));
sleep.poll_unpin(context);
self.vec.push(sleep);
} else {
context.waker().wake_by_ref();
}
self.vec.retain(|e| !e.is_elapsed());
Poll::Pending
}
}
#[tokio::main]
async fn main() {
let mut i = ControllerModule::new();
i.start().await
}

egui interaction with background thread

My goal is to have a background thread counting up every second, which can be started and stopped by buttons on the UI. The gui should display the current time. I thought that the communication between the gui and the background thread could be done via channels for the commands, and via a mutex for the number being counted upwards. In this example, I have omitted the communication via channels, and just focus on the mutex.
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
use eframe::egui;
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let options = eframe::NativeOptions {
initial_window_size: Some(egui::vec2(320.0, 240.0)),
fullscreen: false,
default_theme: eframe::Theme::Light,
..Default::default()
};
eframe::run_native(
"My egui App",
options,
Box::new(|_cc| Box::new(Timer::default())),
);
}
struct Timer {
time: u32,
counter: Arc<Mutex<i32>>,
}
impl Default for Timer {
fn default() -> Self {
let counter = Arc::new(Mutex::new(0));
let counter_clone = Arc::clone(&counter);
//spawn looping thread
let _ = thread::spawn(move || {
loop {
let mut num = counter_clone.lock().unwrap();
*num += 1;
std::thread::sleep_ms(100);
}
});
Self {
time: 0,
counter: counter,
}
}
}
impl eframe::App for Timer {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
let mut num = self.counter.lock().unwrap();
ui.heading(format!("My egui Application {}", num));
});
}
}
This compiles and runs, but it is super laggy. Could you help me to understand what I can do better?
In your sleeping loop you hold the lock for the whole duration you're sleeping.
loop {
let mut num = counter_clone.lock().unwrap();
*num += 1;
std::thread::sleep_ms(100);
} // counter_clone only gets released here.
You either want to add a manual call to drop after you increase the number:
loop {
let mut num = counter_clone.lock().unwrap();
*num += 1;
drop(num); // counter_clone now gets released here before we wait.
std::thread::sleep_ms(100);
}
Or just lock it in place:
loop {
*counter_clone().lock.unwrap() += 1; // counter_clone gets unlocked at the end of this statement.
std::thread::sleep_ms(100);
}

tokio::select! but for a Vec of futures

I have a Vec of futures which I want to execute concurrently (but not necessarily in parallel). Basically, I'm looking for some kind of select function that is similar to tokio::select! but takes a collection of futures, or, conversely, a function that is similar to futures::join_all but returns once the first future is done.
An additional requirement is that once a future finished I might want to add a new future to the Vec.
With such a function, my code would roughly look like this:
use std::future::Future;
use std::time::Duration;
use tokio::time::sleep;
async fn wait(millis: u64) -> u64 {
sleep(Duration::from_millis(millis)).await;
millis
}
// This pseudo-implementation simply removes the last
// future and awaits it. I'm looking for something that
// instead polls all futures until one is finished, then
// removes that future from the Vec and returns it.
async fn select<F, O>(futures: &mut Vec<F>) -> O
where
F: Future<Output=O>
{
let future = futures.pop().unwrap();
future.await
}
#[tokio::main]
async fn main() {
let mut futures = vec![
wait(500),
wait(300),
wait(100),
wait(200),
];
while !futures.is_empty() {
let finished = select(&mut futures).await;
println!("Waited {}ms", finished);
if some_condition() {
futures.push(wait(200));
}
}
}
This is exactly what futures::stream::FuturesUnordered is for (which I've found by looking through the source of StreamExt::for_each_concurrent):
use futures::{stream::FuturesUnordered, StreamExt};
use std::time::Duration;
use tokio::time::{sleep, Instant};
async fn wait(millis: u64) -> u64 {
sleep(Duration::from_millis(millis)).await;
millis
}
#[tokio::main]
async fn main() {
let mut futures = FuturesUnordered::new();
futures.push(wait(500));
futures.push(wait(300));
futures.push(wait(100));
futures.push(wait(200));
let start_time = Instant::now();
let mut num_added = 0;
while let Some(wait_time) = futures.next().await {
println!("Waited {}ms", wait_time);
if num_added < 3 {
num_added += 1;
futures.push(wait(200));
}
}
println!("Completed all work in {}ms", start_time.elapsed().as_millis());
}
(playground)
Here's a working prototype based on streams and StreamExt::for_each_concurrent, as Martin Gallagher has suggested in a comment:
use std::time::Duration;
use tokio::sync::RwLock;
use tokio::time::sleep;
use futures::stream::{self, StreamExt};
use futures::{channel::mpsc, sink::SinkExt};
async fn wait(millis: u64) -> u64 {
sleep(Duration::from_millis(millis)).await;
millis
}
#[tokio::main]
async fn main() {
let (mut sink, futures_stream) = mpsc::unbounded();
let start_futures = vec![wait(500), wait(300), wait(100), wait(200)];
let num_futures = RwLock::new(start_futures.len());
sink.send_all(&mut stream::iter(start_futures.into_iter().map(Ok)))
.await
.unwrap();
let sink_lock = RwLock::new(sink);
futures_stream
.for_each_concurrent(None, |fut| async {
let wait_time = fut.await;
println!("Waited {}", wait_time);
if some_condition() {
println!("Adding new future");
let mut sink = sink_lock.write().await;
sink.send(wait(100)).await.unwrap();
} else {
let mut num_futures = num_futures.write().await;
*num_futures -= 1;
}
let num_futures = num_futures.read().await;
if *num_futures <= 0 {
// Close the sink to exit the for_each_concurrent
sink_lock.write().await.close().await.unwrap();
}
})
.await;
}
While this approach works it has the drawback that we need to maintain a separate counter of remaining futures so that we can close the sink -- there's no Vec of futures for which we can check whether it's empty. Closing the sink requires another lock.
Given that I'm fairly new to Rust I wouldn't be surprised if this approach could be made more elegant.

How to let struct hold a thread and destroy thread as soon as it go out of scope

struct ThreadHolder{
state: ???
thread: ???
}
impl ThreadHolder {
fn launch(&mut self) {
self.thread = ???
// in thread change self.state
}
}
#[test]
fn test() {
let mut th = ThreadHolder{...};
th.launch();
// thread will be destroy as soon as th go out of scope
}
I think there is something to deal with lifetime, but I don't know how to write it.
What you want is so simple that you don't even need it to be mutable in any way, and then it becomes trivial to share it across threads, unless you want to reset it. You said you need to leave a thread, for one reason or another, therefore I'll assume that you don't care about this.
You instead can poll it every tick (most games run in ticks so I don't think there will be any issue implementing that).
I will provide example that uses sleep, so it's not most accurate thing, it is painfully obvious on the last subsecond duration, but I am not trying to do your work for you anyway, there's enough resources on internet that can help you deal with it.
Here it goes:
use std::{
sync::Arc,
thread::{self, Result},
time::{Duration, Instant},
};
struct Timer {
end: Instant,
}
impl Timer {
fn new(duration: Duration) -> Self {
// this code is valid for now, but might break in the future
// future so distant, that you really don't need to care unless
// you let your players draw for eternity
let end = Instant::now().checked_add(duration).unwrap();
Timer { end }
}
fn left(&self) -> Duration {
self.end.saturating_duration_since(Instant::now())
}
// more usable than above with fractional value being accounted for
fn secs_left(&self) -> u64 {
let span = self.left();
span.as_secs() + if span.subsec_millis() > 0 { 1 } else { 0 }
}
}
fn main() -> Result<()> {
let timer = Timer::new(Duration::from_secs(10));
let timer_main = Arc::new(timer);
let timer = timer_main.clone();
let t = thread::spawn(move || loop {
let seconds_left = timer.secs_left();
println!("[Worker] Seconds left: {}", seconds_left);
if seconds_left == 0 {
break;
}
thread::sleep(Duration::from_secs(1));
});
loop {
let seconds_left = timer_main.secs_left();
println!("[Main] Seconds left: {}", seconds_left);
if seconds_left == 5 {
println!("[Main] 5 seconds left, waiting for worker thread to finish work.");
break;
}
thread::sleep(Duration::from_secs(1));
}
t.join()?;
println!("[Main] worker thread finished work, shutting down!");
Ok(())
}
By the way, this kind of implementation wouldn't be any different in any other language, so please don't blame Rust for it. It's not the easiest language, but it provides more than enough tools to build anything you want from scratch as long as you put effort into it.
Goodluck :)
I think I got it work
use std::sync::{Arc, Mutex};
use std::thread::{sleep, spawn, JoinHandle};
use std::time::Duration;
struct Timer {
pub(crate) time: Arc<Mutex<u32>>,
jh_ticker: Option<JoinHandle<()>>,
}
impl Timer {
fn new<T>(i: T, duration: Duration) -> Self
where
T: Iterator<Item = u32> + Send + 'static,
{
let time = Arc::new(Mutex::new(0));
let arc_time = time.clone();
let jh_ticker = Some(spawn(move || {
for item in i {
let mut mg = arc_time.lock().unwrap();
*mg = item;
drop(mg); // needed, otherwise this thread will always hold lock
sleep(duration);
}
}));
Timer { time, jh_ticker }
}
}
impl Drop for Timer {
fn drop(&mut self) {
self.jh_ticker.take().unwrap().join();
}
}
#[test]
fn test_timer() {
let t = Timer::new(0..=10, Duration::from_secs(1));
let a = t.time.clone();
for _ in 0..100 {
let b = *a.lock().unwrap();
println!("{}", b);
sleep(Duration::from_millis(100));
}
}

Implement a monitoring thread without lock?

I have a struct that sends messages to a channel as well as updating some of its own fields. How do I implement a monitoring thread that looks (read only) at its internal fields periodically?
I can write it using a Arc<Mutex<T>> wrapper, but I feel it is not that efficient as A::x could have been i32 which is stored and updated on the stack. Is there any better way to do it without the locks?
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{channel, Sender};
use std::{thread, time};
struct A {
x: Arc<Mutex<i32>>,
y: Sender<i32>,
}
impl A {
fn do_some_loop(&mut self) {
let sleep_time = time::Duration::from_millis(200);
// This is a long running thread.
for x in 1..1000000 {
*self.x.lock().unwrap() = x;
self.y.send(x);
thread::sleep(sleep_time);
}
}
}
fn test() {
let (sender, recever) = channel();
let x = Arc::new(Mutex::new(1));
let mut a = A { x: x.clone(), y: sender };
thread::spawn(move || {
// Monitor every 10 secs.
let sleep_time = time::Duration::from_millis(10000);
loop {
thread::sleep(sleep_time);
println!("{}", *x.lock().unwrap());
}
});
a.do_some_loop();
}

Resources