rust futures::select modifying local variable in loop - asynchronous

I have a struct G like this
struct G { /* some member */ }
impl G {
async fn ref_foo(&self) { /* some code uses G's member */ }
async fn mut_foo(&mut self) { /* some code modifies G's member */ }
}
which is responsible to handle requests from a mpsc::Receiver, like this -- which won't compile:
async fn run_loop(mut rx: impl Stream<Item = Task> + FusedStream + Unpin) {
let mut g = G {};
let mut QS = FuturesUnordered::new();
loop {
select! {
t = rx.select_next_some() => match t {
TR {..} => QS.push(g.ref_foo()),
TM {..} => QS.push(g.mut_foo()),
},
_ = QS.select_next_some() => {},
}
}
}
the above code won't compile due to multiple mutable reference to g.
Target:
What I want is that the loop runs in parallel for any number of ref_foo tasks, and when it needs to run a mut_foo task, it waits until every ref_foo task finish, and then run the mut_foo task, then it can run other tasks as usual.
/ g.ref_foo() \ / ...
| g.ref_foo() | | ...
g.mut_foo() => < g.ref_foo() > => g.mut_foo() => g.mut_foo() => < ...
| g.ref_foo() | | ...
\ g.ref_foo() / \ ...
Additional Infomation:
I used to move implementation of mut_foo to the select loop, and remove async on g.mut_foo() so that no mutable reference would be used in stream QS.
But this implementation is really cubersome and undoubtedly broke G's design.
Just now, I come up with another implementation by make a wrapper:
async fn run_task(mut g: G, t: Task) -> G {
match t {
TR {..} => g.ref_foo().await,
TM {..} => g.mut_foo().await,
};
g
}
while in the select loop:
async fn run_loop(mut rx: impl Stream<Item = Task> + FusedStream + Unpin) {
let g0 = G {};
let mut QS = FuturesUnordered::new();
let mut getter = FuturesUnordered::new();
getter.push(ready(g0));
loop {
select! {
t = rx.select_next_some() => {
let mut g = getter.select_next_some().await;
QS.push(run_task(g, t));
},
mut g = QS.select_next_some() => getter.push(ready(g)),
}
}
}
this one compiles, but it's not so "async" as it can possibly be. In this implementation, ref_foo tasks are also running sequentially.
Question:
Are there more material I should learn to solve this problem? The technics I'm using comes from rust-async-book
Do I HAVE TO use RefCell to solve this problem? IMHO, this should be a trivial problem that can be solved without breaking rust's borrowing rules (by using RefCell).
Can I change my wrap run_task and the select loop so that ref_foo runs in parallel? I have problem in the implementation because G is flowing getter => QS => getter => ..., there's no long-term G instance, and I cannot figure out where I can store it.
Append some of my thoughts:
Since mut_foo can not be run in parallel, I am trying to solve this problem by removing async keyword on mut_foo -- with little progress. The core problem is that, immutable ref to G is needed for parallel running of ref_foo, but I have to get rid of all these immutable ref G when it's time for mut_foo. The fact do not change whether mut_foo is async or not ( or "whether mut_foo returns ref G or not").

I've solved Question 3 with a lot of if statements. I hope there are some more elegant implementations. And, I really appreciate any learning material as stated in Question 1.
here's the full code that compiles (simplified):
use tokio::runtime;
use std::thread;
use std::time::Duration;
use futures::{
select, StreamExt, SinkExt,
future::{ready},
stream::{FusedStream, FuturesUnordered, Stream},
};
struct G;
impl G {
async fn ref_foo(&self) { println!("ref_foo +++"); tokio::time::sleep(Duration::from_millis(500)).await; println!("ref_foo ---"); }
async fn mut_foo(&mut self) { println!("mut_foo +++"); tokio::time::sleep(Duration::from_millis(500)).await; println!("mut_foo ---"); }
}
#[derive(Clone)]
enum Task {
TR,
TM,
}
// wrappers
async fn run_ref_task(g: &G, task: Task) {
match task {
Task::TR => g.ref_foo().await,
_ => {},
};
}
async fn run_mut_task(mut g: G, task: Task) -> G {
match task {
Task::TM => g.mut_foo().await,
_ => {},
};
g
}
async fn run_loop(mut rx: impl Stream<Item = Task> + FusedStream + Unpin) {
let g0 = G;
let mut getter = FuturesUnordered::new();
getter.push(ready(g0));
// the following streams stores only `ready(task)`
let mut mut_tasks = FuturesUnordered::new(); // for tasks that's scheduled in this loop
let mut ref_tasks = FuturesUnordered::new();
let mut mut_delay = FuturesUnordered::new(); // for tasks that's scheduled in next loop
let mut ref_delay = FuturesUnordered::new();
loop {
println!("============ avoid idle loops ============");
let g = getter.select_next_some().await;
{
let mut queries = FuturesUnordered::new(); // where we schedule ref_foo tasks
loop {
println!("------------ avoid idle ref_task loops ------------");
select! {
task = rx.select_next_some() => {
match &task {
Task::TR => ref_delay.push(ready(task)),
Task::TM => mut_tasks.push(ready(task)),
};
if mut_delay.is_empty() && ref_tasks.is_empty() && queries.is_empty() { break; }
},
task = mut_delay.select_next_some() => {
mut_tasks.push(ready(task));
if mut_delay.is_empty() && ref_tasks.is_empty() && queries.is_empty() { break; }
}
task = ref_tasks.select_next_some() => {
queries.push(run_ref_task(&g, task));
}
_ = queries.select_next_some() => {
if mut_delay.is_empty() && ref_tasks.is_empty() && queries.is_empty() { break; }
},
}
}
}
getter.push(ready(g));
{
let mut queries = FuturesUnordered::new(); // where we schedule mut_foo tasks
loop {
println!("------------ avoid idle mut_task loops ------------");
select! {
task = rx.select_next_some() => {
match &task {
Task::TR => ref_tasks.push(ready(task)),
Task::TM => mut_delay.push(ready(task)),
};
if ref_delay.is_empty() && mut_tasks.is_empty() && queries.is_empty() { break; }
},
task = ref_delay.select_next_some() => {
ref_tasks.push(ready(task));
if ref_delay.is_empty() && mut_tasks.is_empty() && queries.is_empty() { break; }
}
g = getter.select_next_some() => {
if let Some(task) = mut_tasks.next().await {
queries.push(run_mut_task(g, task));
} else {
getter.push(ready(g));
if ref_delay.is_empty() && queries.is_empty() { break; }
}
}
g = queries.select_next_some() => {
getter.push(ready(g));
if ref_delay.is_empty() && mut_tasks.is_empty() && queries.is_empty() { break; }
}
}
}
}
}
}
fn main() {
let (mut tx, rx) = futures::channel::mpsc::channel(10000);
let th = thread::spawn(move || thread_main(rx));
let tasks = vec![Task::TR, Task::TR, Task::TM, Task::TM, Task::TR, Task::TR, Task::TR, Task::TM, Task::TM];
let rt = runtime::Builder::new_multi_thread().enable_time().build().unwrap();
rt.block_on(async {
loop {
for task in tasks.clone() {
tx.send(task).await.expect("");
}
tokio::time::sleep(Duration::from_secs(10)).await;
}
});
th.join().expect("");
}
fn thread_main(rx: futures::channel::mpsc::Receiver<Task>) {
let rt = runtime::Builder::new_multi_thread().enable_time().build().unwrap();
rt.block_on(async {
run_loop(rx).await;
});
}

Related

Is there a better way to infinitely error check other than recursion?

I'm trying to make a Tic-Tac-Toe game with a custom board size. I want this to be very hard to break, so I use recursion to get the board measurements if the input is invalid or an error occurs. However, this doesn't seem very clean to me, and I was wondering if there's a better/more rusty way of achieving the same thing.
Code in main function
let board_size_str = get_board_size_string();
let (x_pos, width, height) = get_board_measurement(&board_size_str);
Functions
fn get_board_size_string() -> String {
println!("Select the size of the board in the following format: 5x5 or 7x7");
println!("The size can be from 3x3 to 30x30");
print!("Size: ");
std::io::stdout().flush().expect("Failed to flush stdout!");
let mut board_size_str = String::new();
std::io::stdin().read_line(&mut board_size_str).expect("Failed to read board size!");
println!();
board_size_str
}
fn get_board_measurement(board_size_str: &str) -> (usize, i64, i64) {
let x_pos = get_x_pos(board_size_str);
let width = get_board_width(board_size_str, x_pos);
let height = get_board_height(board_size_str, x_pos);
(x_pos, width, height)
}
fn get_x_pos(board_size_str: &str) -> usize {
let x_pos_option = board_size_str.chars().position(|c| c == 'x');
match x_pos_option {
Some(x_pos) => x_pos,
None => {
println!("Board size must contain an x!");
let board_size_str = get_board_size_string();
get_x_pos(&board_size_str)
}
}
}
fn get_board_width(board_size_str: &str, x_pos: usize) -> i64 {
let width_result = board_size_str[..x_pos].parse::<i64>();
match width_result {
Ok(width) => width,
Err(_) => {
println!("Invalid board width!");
let board_size_str = get_board_size_string();
get_board_width(&board_size_str, get_x_pos(&board_size_str))
}
}
}
fn get_board_height(board_size_str: &str, x_pos: usize) -> i64 {
let height_result = board_size_str[x_pos + 1..].trim().parse::<i64>();
match height_result {
Ok(height) => height,
Err(_) => {
println!("Invalid board height!");
let board_size_str = get_board_size_string();
get_board_height(&board_size_str, get_x_pos(&board_size_str))
}
}
}
Just use an iterative loop?
fn get_x_pos(board_size_str: &str) -> usize {
loop {
let board_size_str = get_board_size_string();
let x_pos_option = board_size_str.chars().position(|c| c == 'x');
if let Some(x_pos) = x_pos_option {
break x_pos
}
}
}
Though the structure is strange because a correct board size is a correct pattern ( 'x' ) so it's not like splitting that into three unrelated routines makes any sense, even if two of them do delegate the localisation of the x separator.
With your method you can input something like 52xkf, get an error, input 24x36, and I think you'll get a 52x36 board rather than the 24x36 you might expect, which is just odd. Would be a lot easier to just do the entire thing in a single pseudo-step:
fn parse_board_size() -> (usize, usize) {
loop {
let s = get_board_size_string();
let Some((w_s, h_s)) = s.split_once('x') else {
// complain about a missing `x` here
continue;
};
match (w_s.parse(), h_s.parse()) {
(Ok(w), Ok(s)) => {
// can add more validation here,
// or as pattern guards
return (w, s);
}
(Ok(_), Err(h_error)) => {
// h was incorrect
}
(Err(w_error), Ok(_)) => {
// w was incorrect
}
(Err(w_error), Err(h_error)) => {
// both were incorrect
}
}
}
}
Alternatively for the parsing if you don't care about custom-reporting each error case individually you can lean on Option e.g.
fn parse_board_size() -> (usize, usize) {
loop {
let s = get_board_size_string();
let Some((w_s, h_s)) = s.split_once('x') else {
// complain about a missing `x` here
continue;
};
if let Some(r) = w_s.parse().ok().zip(h_s.parse().ok()) {
break r;
}
// report generic parsing error
}
}

Cannot infer an appropriate lifetime for self "required to be static"

I am prototyping channel based system and when converting code to use async tasks, I came across the error that &mut self needs to have an appropriate lifetime.
I have tried setting the &mut self to &'static self but that does not work. I have tried to wrap the entire code in an async block returning Future<Output=()> + 'static which also did not work.
Here is the code:
struct RequestManager {
connection_state: ConnectionState,
backoff: u64,
exponent: u32,
maximum: u64,
}
impl RequestManager {
async fn run(&mut self, mut feed_queue: Receiver<FeedItem>) {
let (mut fetch_result_sender, mut fetch_result_receiver) = channel(5);
let (mut fetch_request_sender, mut fetch_request_receiver) = channel::<FeedItem>(5);
let request_sender = mpsc::Sender::clone(&fetch_result_sender);
tokio::spawn(async move {
loop {
match self.connection_state {
ConnectionState::Closed => {
while let Some(feed) = fetch_request_receiver.recv().await {
let mut request_sender = mpsc::Sender::clone(&request_sender);
tokio::spawn(async move {
let response = make_request(&feed.feed_address).await;
if let Err(_) = response {
self.connection_state =
ConnectionState::HalfOpen(feed.feed_address);
} else {
let response = read_response_body(response.unwrap()).await;
let result = FetchResult { body: response };
if let Err(e) = request_sender.send(result).await {
eprintln!("could not send fetch result: {}", e);
}
}
});
}
}
ConnectionState::HalfOpen(url) => {
let response = make_request(&url).await;
if let Err(_) = response {
self.connection_state = ConnectionState::Open(url);
} else {
let response = read_response_body(response.unwrap()).await;
let result = FetchResult { body: response };
// // TODO: sends to task/feedService
connection_state = ConnectionState::Closed;
if let Err(e) = fetch_result_sender.send(result).await {
eprintln!("could not send fetch result: {}", e);
}
}
}
ConnectionState::Open(url) => {
let new_backoff = calculate_backoff();
delay_for(Duration::from_secs(new_backoff));
self.connection_state = ConnectionState::HalfOpen(url)
}
}
}
});
}
}

Searching a Vec for a match

My first Rust program compiles and runs:
use structopt::StructOpt;
use pcap::{Device,Capture};
use std::process::exit;
#[derive(StructOpt)]
struct Cli {
/// the capture device
device: String,
}
fn main() {
let devices = Device::list();
let args = Cli::from_args();
let mut optdev :Option<Device> = None;
for d in devices.unwrap() {
//println!("device: {:?}", d);
if d.name == args.device {
optdev = Some(d);
}
}
let dev = match optdev {
None => {
println!("Device {} not found.", args.device);
exit(1);
},
Some(dev) => dev,
};
let mut cap = Capture::from_device(dev).unwrap()
.promisc(true)
.snaplen(100)
.open().unwrap();
while let Ok(packet) = cap.next() {
println!("received packet! {:?}", packet);
}
}
I have some complex code which iterates through the Vec of devices, testing each one's .name property against args.device.
I'm guessing that there is a method of 'looking-up' an entry in a Vec, such that I can replace all the optdev lines with something like:
let dev = match devices.unwrap().look_up(.name == args.device) {
None => {
println!("Device {} not found.", args.device);
exit(1);
},
Some(dev) => dev,
};
What is the syntax for such a look_up()?
Or is there a more idiomatic way of doing this?
What is the syntax for such a look_up()?
Iterator::find. Since the operation is not specific to vectors (or slices), it doesn't live there, and is applicable to any iterator instead.
It'd look something like this:
let dev = match devices.unwrap().into_iter().find(|d| d.name == args.device) {
None => {
println!("Device {} not found.", args.device);
exit(1);
},
Some(dev) => dev,
};
or
let dev = if let Some(dev) = devices.unwrap().into_iter().find(|d| d.name == args.device) {
dev
} else {
println!("Device {} not found.", args.device);
exit(1);
};
(side-note: you may also want to use eprintln for, well, error reporting).
Though a somewhat cleaner error handling could be along the lines of (note: not tested so there might be semantic or syntactic mistakes):
use std::fmt;
use std:errors::Error;
#[derive(Debug)]
struct NoDevice(String);
impl fmt::Display for NoDevice {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Device {} not found", self.0)
}
}
impl Error for NoDevice {}
fn main() -> Result<(), Box<dyn Error>> {
let devices = Device::list()?;
let args = Cli::from_args();
let dev = devices.into_iter()
.find(|d| d.name == args.device)
.ok_or_else(|| NoDevice(args.device))?
let mut cap = Capture::from_device(dev)?
.promisc(true)
.snaplen(100)
.open()?;
while let Ok(packet) = cap.next() {
println!("received packet! {:?}", packet);
}
}

How to recursively call a closure that is stored in an Arc<Mutex<_>>?

I’m trying to transpile a dynamic language into Rust and closures are the most difficult part to implement.
I've tried using a Arc<Mutex<dyn FnMut>>, but it doesn't support recursion.
use std::sync::{Arc, Mutex};
type Data = Arc<DataUnpack>;
enum DataUnpack {
Number(f64),
Function(Box<Mutex<FnMut(Vec<Data>) -> Data>>),
}
fn call(f: Data, args: Vec<Data>) -> Data {
if let DataUnpack::Function(v) = &*f {
let f = &mut *v.lock().unwrap();
f(args)
} else {
panic!("TYPE ERR")
}
}
fn lambda(f: Box<FnMut(Vec<Data>) -> Data>) -> Data {
Arc::new(DataUnpack::Function(Box::new(Mutex::new(Box::leak(f)))))
}
fn main() {
let f: Arc<Mutex<Data>> = Arc::new(Mutex::new(Arc::new(DataUnpack::Number(0.0))));
*f.lock().unwrap() = {
let f = f.clone();
lambda(Box::new(move |xs| {
println!("Ha");
call(f.lock().unwrap().clone(), xs.clone())
}))
};
call(f.lock().unwrap().clone(), vec![]);
}
playground
It shows one Ha and then stops. Where am I wrong?

How to replace combinators with Future?

I have a function which returns Future. It accepts another function which accepts one argument and returns Future. Second function can be implemented as combinators chain passed into first function. It looks like this:
use bb8::{Pool, RunError};
use bb8_postgres::PostgresConnectionManager;
use tokio_postgres::{error::Error, Client, NoTls};
#[derive(Clone)]
pub struct DataManager(Pool<PostgresConnectionManager<NoTls>>);
impl DataManager {
pub fn new(pool: Pool<PostgresConnectionManager<NoTls>>) -> Self {
Self(pool)
}
pub fn create_user(
&self,
reg_req: UserRequest,
) -> impl Future<Item = User, Error = RunError<Error>> {
let sql = "long and awesome sql";
let query = move |mut conn: Client| { // function which accepts one argument and returns Future
conn.prepare(sql).then(move |r| match r {
Ok(select) => {
let f = conn
.query(&select, &[&reg_req.email, &reg_req.password])
.collect()
.map(|mut rows| {
let row = rows.remove(0);
row.into()
})
.then(move |r| match r {
Ok(v) => Ok((v, conn)),
Err(e) => Err((e, conn)),
});
Either::A(f)
}
Err(e) => Either::B(future::err((e, conn))),
})
};
self.0.run(query) // function which returns Future and accepts another function
}
}
But I want to write code of create_user as a struct implementing Future.
struct UserCreator(Pool<PostgresConnectionManager<NoTls>>, UserRequest);
impl UserCreator {
fn new(pool: Pool<PostgresConnectionManager<NoTls>>, reg_req: UserRequest) -> Self {
Self(pool, reg_req)
}
}
How to implement Future for this struct that works as first function? Please help me with an example.
Now I tried to make it like this, but nothing is computed and execution always blocks.
impl Future for UserCreator {
type Item = User;
type Error = RunError<Error>;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
// Code which which works like `DataManager.create_user`
let sql = "long and awesome sql";
let reg_req = &self.1;
let query = move |mut conn: Client| {
conn.prepare(sql).then(move |r| match r {
Ok(select) => {
let f = conn
.query(&select, &[&reg_req.email, &reg_req.password])
.collect()
.map(|mut rows| {
let row = rows.remove(0);
row.into()
})
.then(move |r| match r {
Ok(v) => Ok((v, conn)),
Err(e) => Err((e, conn)),
});
Either::A(f)
}
Err(e) => Either::B(future::err((e, conn))),
})
};
self.0.run(query).poll()
}
}

Resources