How do i run a future without awaiting it? (in rust) - rust

I have some async function
async fn get_player(name: String, i: Instant) -> Option<Player> {
// some code here that returns a player structs
}
in my main function i want to run the above function concurrently in a loop, this function takes about 1sec to complete and I need to run it at least 50 times, hence I would like to make it concurrently run this function 50 times. in my main function i have a lazy_static custom Client struct which should not be created more than once.
main function
#[tokio::main]
async fn main() {
client.init().await;
println!("start");
for i in 0..10 {
println!("{}", i);
let now = Instant::now();
tokio::spawn(async move {
client.get_player("jay".to_string(), now).await;
});
}
loop {}
}
the reason I am passing instant is because in my get_player function i have a println!() that just prints the execution time.
the above main method takes around 500ms for each function call, however the below code only takes 100ms.
#[tokio::main]
async fn maain(){
client.init().await;
for i in 0..10 {
let now = Instant::now();
client.get_player("jay".to_string(), now).await.expect("panic");
}
}
but this function is still synchronous code, how do I actually run the async function concurrently without the time cost?
To better understand what am after is an implemention similar to this (its in java btw),
CompleteableFuture.thenAccept(x -> x.SayHello(););
or in Js its something like .then after an async function.
is there any similar implementation in rust?

I assume that your get_player function takes one second because it waits for a network interaction, and not because some computation takes that long. If it's compute-bound instead, asynchronism is the wrong approach and you want to go with parallelism instead.
Further, I assume that the function signature of get_player is actually async fn get_player(&self, name: String, i: Instant) -> Option<Player> instead, because otherwise none of your main code samples would make any sense. Although I'm confused why it would be &self and not &mut self.
With those assumptions, I tried to reproduce your minimal reproducible example:
use std::time::{Duration, Instant};
#[derive(Debug)]
struct Player {
name: String,
}
struct Client {}
impl Client {
async fn init(&self) {}
async fn get_player(&self, name: String, _now: Instant) -> Option<Player> {
// Dummy code that simulates a delay of 1 second
tokio::time::sleep(Duration::from_millis(1000)).await;
Some(Player { name })
}
}
static client: Client = Client {};
#[tokio::main]
async fn main() {
let begin = Instant::now();
client.init().await;
for i in 0..10 {
let now = Instant::now();
let player = client
.get_player(format!("Player #{}", i), now)
.await
.expect("panic");
println!(
"[{} ms] Retreived player: {:?}",
begin.elapsed().as_millis(),
player.name
);
}
}
[1002 ms] Retreived player: "Player #0"
[2004 ms] Retreived player: "Player #1"
[3005 ms] Retreived player: "Player #2"
[4008 ms] Retreived player: "Player #3"
[5010 ms] Retreived player: "Player #4"
[6011 ms] Retreived player: "Player #5"
[7013 ms] Retreived player: "Player #6"
[8014 ms] Retreived player: "Player #7"
[9016 ms] Retreived player: "Player #8"
[10018 ms] Retreived player: "Player #9"
This is based on your last main example. As you can see, it takes 10 seconds to retrieve all players, because they all run in sequence.
Now let's run them all asynchronously. The problem here is joining them all simultaneously. Tokio sadly doesn't offer an easy way for that; you could tokio::spawn all of them, collect the JoinHandles and then join them one by one. The crate futures, however, offers exactly what you want:
use std::time::{Duration, Instant};
#[derive(Debug)]
struct Player {
name: String,
}
struct Client {}
impl Client {
async fn init(&self) {}
async fn get_player(&self, name: String, _now: Instant) -> Option<Player> {
// Dummy code her that simulates a delay of 1 second
tokio::time::sleep(Duration::from_millis(1000)).await;
Some(Player { name })
}
}
static client: Client = Client {};
#[tokio::main]
async fn main() {
let begin = Instant::now();
client.init().await;
let get_player_futures = (0..10).into_iter().map(|i| async move {
let now = Instant::now();
let player = client
.get_player(format!("Player #{}", i), now)
.await
.expect("panic");
println!(
"[{} ms] Retreived player: {:?}",
begin.elapsed().as_millis(),
player.name
);
});
futures::future::join_all(get_player_futures).await;
}
[1002 ms] Retreived player: "Player #0"
[1002 ms] Retreived player: "Player #1"
[1002 ms] Retreived player: "Player #2"
[1002 ms] Retreived player: "Player #3"
[1002 ms] Retreived player: "Player #4"
[1002 ms] Retreived player: "Player #5"
[1002 ms] Retreived player: "Player #6"
[1002 ms] Retreived player: "Player #7"
[1003 ms] Retreived player: "Player #8"
[1003 ms] Retreived player: "Player #9"
As you can see, the entire program only took one second, and all of them got retrieved simultaneously.
get_player_futures here is an iterator over all the futures that need to be awaited for in order to retrieve the players. futures::future::join_all then awaits all of them simultaneously. You can even use join_all's return value to retrieve the values of the futures, but we don't use that here.
I hope that helped somehow; it was hard to create an answer as parts of your question were incoherent.

Related

If "futures do nothing unless awaited", why does `tokio::spawn` work anyway?

I have read here that futures in Rust do nothing unless they are awaited. However, I tried a more complex example and it is a little unclear why I get a message printed by the 2nd print in this example because task::spawn gives me a JoinHanlde on which I do not do any .await.
Meanwhile, I tried the same example, but with an await above the 2nd print, and now I get printed only the message in the 1st print.
If I wait for all the futures at the end, I get printed both messages, which I understood. My question is why the behaviour in the previous 2 cases.
use futures::stream::{FuturesUnordered, StreamExt};
use futures::TryStreamExt;
use rand::prelude::*;
use std::collections::VecDeque;
use std::sync::Arc;
use tokio::sync::Semaphore;
use tokio::task::JoinHandle;
use tokio::{task, time};
fn candidates() -> Vec<i32> {
Vec::from([2, 2])
}
async fn produce_event(nanos: u64) -> i32 {
println!("waiting {}", nanos);
time::sleep(time::Duration::from_nanos(nanos)).await;
1
}
async fn f(seconds: i64, semaphore: &Arc<Semaphore>) {
let mut futures = vec![];
for (i, j) in (0..1).enumerate() {
for (i, event) in candidates().into_iter().enumerate() {
let permit = Arc::clone(semaphore).acquire_owned().await;
let secs = 500;
futures.push(task::spawn(async move {
let _permit = permit;
produce_event(500); // 2nd example has an .await here
println!("Event produced at {}", seconds);
}));
}
}
}
#[tokio::main()]
async fn main() {
let semaphore = Arc::new(Semaphore::new(45000));
for _ in 0..1 {
let mut futures: FuturesUnordered<_> = (0..2).map(|moment| f(moment, &semaphore)).collect();
while let Some(item) = futures.next().await {
let () = item;
}
}
}
However, I tried a more complex example and it is a little unclear why I get a message printed by the 2nd print in this example because task::spawn gives me a JoinHanlde on which I do not do any .await.
You're spawning tasks. A task is a separate thread of execution which can execute concurrently to the current task, and can be scheduled in parallel.
All the JoinHandle does there is wait for that task to end, it doesn't control the task running.
Meanwhile, I tried the same example, but with an await above the 2nd print, and now I get printed only the message in the 1st print.
You spawn a bunch of tasks and make them sleep. Since you don't wait for them to terminate (don't join them) nor is there any sort of sleep in their parent task, once all the tasks have been spawned the loops terminate, you reach the end of the main function and the program terminates.
At this point all the tasks are still sleeping.

How to connect bevy game to externel TCP server using tokios async TcpStream?

I want to send Events between the game client and server and I already got it working, but I do not know how to do it with bevy.
I am dependent to use tokios async TcpStream, because I have to be able to split the stream into a OwnedWriteHalf and OwnedReadhalf using stream.into_split().
My first idea was to just spawn a thread that handles the connection and then send the received events to a queue using mpsc::channel
Then I include this queue into a bevy resource using app.insert_resource(Queue) and pull events from it in the game loop.
the Queue:
use tokio::sync::mpsc;
pub enum Instruction {
Push(GameEvent),
Pull(mpsc::Sender<Option<GameEvent>>),
}
#[derive(Clone, Debug)]
pub struct Queue {
sender: mpsc::Sender<Instruction>,
}
impl Queue {
pub fn init() -> Self {
let (tx, rx) = mpsc::channel(1024);
init(rx);
Self{sender: tx}
}
pub async fn send(&self, event: GameEvent) {
self.sender.send(Instruction::Push(event)).await.unwrap();
}
pub async fn pull(&self) -> Option<GameEvent> {
println!("new pull");
let (tx, mut rx) = mpsc::channel(1);
self.sender.send(Instruction::Pull(tx)).await.unwrap();
rx.recv().await.unwrap()
}
}
fn init(mut rx: mpsc::Receiver<Instruction>) {
tokio::spawn(async move {
let mut queue: Vec<GameEvent> = Vec::new();
loop {
match rx.recv().await.unwrap() {
Instruction::Push(ev) => {
queue.push(ev);
}
Instruction::Pull(sender) => {
sender.send(queue.pop()).await.unwrap();
}
}
}
});
}
But because all this has to be async I have block the pull() function in the sync game loop.
I do this using the futures-lite crate:
fn event_pull(
communication: Res<Communication>
) {
let ev = future::block_on(communication.event_queue.pull());
println!("got event: {:?}", ev);
}
And this works fine, BUT after around 5 seconds the whole program just halts and does not receive any more events.
It seems like that future::block_on() does block indefinitely.
Having the main function, in which bevy::prelude::App gets built and run, to be the async tokio::main function might also be a problem here.
It would probably be best to wrap the async TcpStream initialisation and tokio::sync::mpsc::Sender and thus also Queue.pull into synchronous functions, but I do not know how to do this.
Can anyone help?
How to reproduce
The repo can be found here
Just compile both server and client and then run both in the same order.
I got it to work by just replacing every tokio::sync::mpsc with crossbeam::channel, which might be a problem, as it does block
and manually initializing the tokio runtime.
so the init code looks like this:
pub struct Communicator {
pub event_bridge: bridge::Bridge,
pub event_queue: event_queue::Queue,
_runtime: Runtime,
}
impl Communicator {
pub fn init(ip: &str) -> Self {
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_io()
.build()
.unwrap();
let (bridge, queue, game_rx) = rt.block_on(async move {
let socket = TcpStream::connect(ip).await.unwrap();
let (read, write) = socket.into_split();
let reader = TcpReader::new(read);
let writer = TcpWriter::new(write);
let (bridge, tcp_rx, game_rx) = bridge::Bridge::init();
reader::init(bridge.clone(), reader);
writer::init(tcp_rx, writer);
let event_queue = event_queue::Queue::init();
return (bridge, event_queue, game_rx);
});
// game of game_rx events to queue for game loop
let eq_clone = queue.clone();
rt.spawn(async move {
loop {
let event = game_rx.recv().unwrap();
eq_clone.send(event);
}
});
Self {
event_bridge: bridge,
event_queue: queue,
_runtime: rt,
}
}
}
And main.rs looks like this:
fn main() {
let communicator = communication::Communicator::init("0.0.0.0:8000");
communicator.event_bridge.push_tcp(TcpEvent::Register{name: String::from("luca")});
App::new()
.insert_resource(communicator)
.add_system(event_pull)
.add_plugins(DefaultPlugins)
.run();
}
fn event_pull(
communication: Res<communication::Communicator>
) {
let ev = communication.event_queue.pull();
if let Some(ev) = ev {
println!("ev");
}
}
Perhaps there might be a better solution.

Worker threads send many messages through a channel to main but only the first one is delivered

I've been trying to extend the thread pool example from the Multi-Threaded Web Server chapter in The Book. The original example works fine and dispatches messages to workers properly though the spsc channel (ingress), but now I want to return values (strings) from the worker threads through an mpsc channel (egress). Somehow the egress channel sends only one message instead of 10. egress_tx.send() seems to be executed 10 times but egress_rx.recv() gives me one message only and then the program finishes (i.e. no deadlocks etc). The worker threads are terminated properly in the Drop trait implementation (this code is not shown). I'd appreciate any suggestions about debugging such a problem: putting a breakpoint ar recv() and trying to find something meaningful in its internals hasn't helped much.
type Job = Box<dyn FnOnce(usize) -> String + Send + 'static>;
enum Message {
Run(Job),
Halt,
}
struct Worker {
id: usize,
thread: Option<thread::JoinHandle<()>>,
}
pub struct ThreadPool {
workers: Vec<Worker>,
ingress_tx: Sender<Message>,
pub egress_rx: Receiver<String>
}
impl Worker {
fn new(id: usize, rx: Arc<Mutex<mpsc::Receiver<Message>>>, tx: mpsc::Sender<String>) -> Worker {
let thread = thread::spawn(move ||
loop {
let msg = rx.lock().unwrap().recv().unwrap();
match msg {
Message::Run(job) => {
let s = job(id);
println!("Sending \"{}\"", s);
tx.send(s).unwrap();
},
Message::Halt => break,
}
}
);
Worker {id, thread: Some(thread)}
}
}
impl ThreadPool {
pub fn new(size: usize) -> Result<ThreadPool, ThreadPoolError> {
if size <= 0 {
return Err(ThreadPoolError::ZeroSizedPool)
}
let (ingress_tx, ingress_rx) = mpsc::channel();
let ingress_rx = Arc::new(Mutex::new(ingress_rx));
let (egress_tx, egress_rx) = mpsc::channel();
let mut workers = Vec::with_capacity(size);
for id in 0..size {
workers.push(Worker::new(id, ingress_rx.clone(), egress_tx.clone()));
}
Ok(ThreadPool {workers, ingress_tx, egress_rx})
}
pub fn execute<F>(&self, f: F)
where F: FnOnce(usize) -> String + Send + 'static
{
let j = Box::new(f);
self.ingress_tx.send(Message::Run(j)).unwrap();
}
}
fn run_me(id: usize, i: usize) -> String {
format!("Worker {} is processing tile {}...", id, i).to_string()
}
#[cfg(test)]
mod threadpool_tests {
use super::*;
#[test]
fn tp_test() {
let tpool = ThreadPool::new(4).expect("Cannot create threadpool");
for i in 0..10 {
let closure = move |worker_id| run_me(worker_id, i);
tpool.execute(closure);
}
for s in tpool.egress_rx.recv() {
println!("{}", s);
}
}
}
And the output is:
Sending "Worker 0 is processing tile 0..."
Sending "Worker 0 is processing tile 2..."
Sending "Worker 3 is processing tile 1..."
Sending "Worker 3 is processing tile 4..."
Sending "Worker 2 is processing tile 3..."
Sending "Worker 2 is processing tile 6..."
Sending "Worker 1 is processing tile 5..."
Sending "Worker 0 is processing tile 7..."
Sending "Worker 0 is processing tile 9..."
Sending "Worker 3 is processing tile 8..."
Receiving "Worker 0 is processing tile 0..."
Process finished with exit code 0
In your code, you have for s in tpool.egress_rx.recv(), which isn't doing quite what you want. Instead of iterating over the values received by the channel, you're receiving one element (wrapped in a Result) and then iterating over that, since Result implements IntoIterator to iterate over the success value (or nothing, if it contains an error).
Simply changing this to for s in tpool.egress_rx should fix it, since channels also implement IntoIterator.

Design help: threading within a struct

I'm new to Rust. As a learning exercise I'm trying to write a simple timer struct that I once wrote in C++. The interface and implementation looks something like this:
pub struct Timer {
handle: Option<std::thread::JoinHandle<()>>,
alive: bool,
}
impl Timer {
pub fn new() {
Timer {
handle: None,
alive: false,
}
}
pub fn start(&'static mut self) {
// Oops! How do I do this?
self.handle = Some(std::thread::spawn(move || {
self.alive = true;
self.loop()
}));
}
pub fn stop(&mut self) {
self.alive = false;
self.handle.unwrap().join()
}
pub fn loop(&self) {
// while alive
}
}
I understand why this is an error because of use of moved value: self within the start function, but I'm wondering how I'm supposed to design my struct so that something like this would work. In every scenario I can think of, I'll always have a double borrow situation.
I have a hunch that I need to learn more about interior mutability, but figured I would ask for design guidance before going down any more rabbit holes.
I think you are pretty close to getting it to work.
There are only two hurdles:
thread::spawn will not allow sharing references
alive and loop for you to share in this design
The solution is two-fold:
split up things between the controller (Timer) and the worker (the closure)
share state between the two using Arc since references are forbidden
Here is a minimal example for you to toy with:
use std::{sync, thread, time};
use std::sync::atomic::{AtomicBool, Ordering};
pub struct Timer {
handle: Option<thread::JoinHandle<()>>,
alive: sync::Arc<AtomicBool>,
}
impl Timer {
pub fn new() -> Timer {
Timer {
handle: None,
alive: sync::Arc::new(AtomicBool::new(false)),
}
}
pub fn start<F>(&mut self, fun: F)
where F: 'static + Send + FnMut() -> ()
{
self.alive.store(true, Ordering::SeqCst);
let alive = self.alive.clone();
self.handle = Some(thread::spawn(move || {
let mut fun = fun;
while alive.load(Ordering::SeqCst) {
fun();
thread::sleep(time::Duration::from_millis(10));
}
}));
}
pub fn stop(&mut self) {
self.alive.store(false, Ordering::SeqCst);
self.handle
.take().expect("Called stop on non-running thread")
.join().expect("Could not join spawned thread");
}
}
fn main() {
let mut timer = Timer::new();
timer.start(|| println!("Hello, World!") );
println!("Feeling sleepy...");
thread::sleep(time::Duration::from_millis(100));
println!("Time for dinner!");
timer.stop();
}
I invite you to poke holes at it one at a time (ie, change one thing that is different from your example, check the error message, and try to understand how the difference solved it).
On the playground, it printed for me:
Feeling sleepy...
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Time for dinner!
Though I would not rely on (1) the number of times "Hello, World!" appears and (2) "Feeling sleepy..." appearing first.
And damned, is Atomic verbose... I kinda wish there was a get/set with SeqCst (the stronger ordering) available.

Split/gather pattern for jobs

I have a set of jobs that I am trying to run in parallel. I want to run each task on its own thread and gather the responses on the calling thread.
Some jobs may take much longer than others, so I'd like to start using each result as it comes in, and not have to wait for all jobs to complete.
Here is an attempt:
struct Container<T> {
items : Vec<T>
}
#[derive(Debug)]
struct Item {
x: i32
}
impl Item {
fn foo (&mut self) {
self.x += 1; //consider an expensive mutating computation
}
}
fn main() {
use std;
use std::sync::{Mutex, Arc};
use std::collections::RingBuf;
//set up a container with 2 items
let mut item1 = Item { x: 0};
let mut item2 = Item { x: 1};
let container = Container { items: vec![item1, item2]};
//set a gather system for our results
let ringBuf = Arc::new(Mutex::new(RingBuf::<Item>::new()));
//farm out each job to its own thread...
for item in container.items {
std::thread::Thread::spawn(|| {
item.foo(); //job
ringBuf.lock().unwrap().push_back(item); //push item back to caller
});
}
loop {
let rb = ringBuf.lock().unwrap();
if rb.len() > 0 { //gather results as soon as they are available
println!("{:?}",rb[0]);
rb.pop_front();
}
}
}
For starters, this does not compile due to the impenetrable cannot infer an appropriate lifetime due to conflicting requirements error.
What am I doing wrong and how do I do it right?
You've got a couple compounding issues, but the first one is a misuse / misunderstanding of Arc. You need to give each thread it's own copy of the Arc. Arc itself will make sure that changes are synchronized. The main changes were the addition of .clone() and the move keyword:
for item in container.items {
let mrb = ringBuf.clone();
std::thread::Thread::spawn(move || {
item.foo(); //job
mrb.lock().unwrap().push_back(item); //push item back to caller
});
}
After changing this, you'll run into some simpler errors about forgotten mut qualifiers, and then you hit another problem - you are trying to send mutable references across threads. Your for loop will need to return &mut Item to call foo, but this doesn't match your Vec. Changing it, we can get to something that compiles:
for mut item in container.items.into_iter() {
let mrb = ringBuf.clone();
std::thread::Thread::spawn(move || {
item.foo(); //job
mrb.lock().unwrap().push_back(item); //push item back to caller
});
}
Here, we consume the input vector, moving each of the Items to the worker thread. Unfortunately, this hits the Playpen timeout, so there's probably some deeper issue.
All that being said, I'd highly recommend using channels:
#![feature(std_misc)]
use std::sync::mpsc::channel;
#[derive(Debug)]
struct Item {
x: i32
}
impl Item {
fn foo(&mut self) { self.x += 1; }
}
fn main() {
let items = vec![Item { x: 0 }, Item { x: 1 }];
let rx = {
let (tx, rx) = channel();
for item in items.into_iter() {
let my_tx = tx.clone();
std::thread::Thread::spawn(move || {
let mut item = item;
item.foo();
my_tx.send(item).unwrap();
});
}
rx
};
for item in rx.iter() {
println!("{:?}", item);
}
}
This also times-out in the playpen, but works fine when compiled and run locally.

Resources