rust futures-cpupool: inconsistent behavior explanations - concurrency

I'm experiencing weird behavior with cpu pools:
#[macro_use]
extern crate lazy_static;
extern crate tokio_core;
extern crate futures;
extern crate futures_cpupool;
use std::time::Duration;
use futures_cpupool::{CpuPool, Builder, CpuFuture};
use futures::Stream;
use futures::{Future, future, lazy};
use futures::sync::mpsc;
use futures::Sink;
lazy_static! {
static ref CPU_POOL: CpuPool = {
Builder::new()
.pool_size(10)
.after_start(|| {
println!("Pool started one thread");
})
.before_stop(|| {
println!("Pool stopped one thread");
})
.create()
};
}
struct Producer {}
impl Producer {
fn search_names(&self) -> Box<Stream<Item = String, Error = String> + Send> {
let (mut tx, rx) = mpsc::channel::<Result<String, String>>(1);
println!("Creating producer thread...");
let producer_cpu: CpuFuture<(), ()> = CPU_POOL.spawn(lazy(move || {
println!(" -- Begin to produce names");
for i in 0..10 {
match tx.send(Ok("name".to_string())).wait() {
Ok(t) => {
println!(" -- sent the name");
tx = t
}
Err(err) => {
println!(" -- Error occured sending name! {:?}", err);
break;
}
}
std::thread::sleep(Duration::from_secs(1));
}
future::ok::<(), ()>(())
})
.then(|result| {
match result {
Ok(data) => println!("Producer finished with data: {:?}", data),
Err(err) => println!("Producer finished with error: {:?}", err),
}
future::ok::<(), ()>(())
}));
rx.then(|r| r.unwrap()).boxed()
}
}
fn main() {
let producer = Producer {};
let names = CPU_POOL.spawn(producer.search_names()
.map(|name| {
println!("name = {:?}", name);
name
})
.collect()
.then(|result| {
match result {
Ok(data) => println!("Finished to read producer {:?}", data),
Err(err) => println!("Error reading stream of producer! {:?}", err),
}
future::ok::<(), ()>(())
}));
names.wait();
}
Here is the corresponding Cargo.toml
[package]
name = "example"
version = "0.1.0"
[dependencies]
lazy_static = "^0.1.*"
tokio-core = "^0.1"
futures = "^0.1"
futures-cpupool = "^0.1"
I'm running on Rust nightly (1.16.0-nightly (df8debf6d 2017-01-25))
I would expect this program to generate the 10 Strings, output it via println and exit. However, most of the time, the program doesn't generate the Strings and exits normally, other times the Strings are correctly generated.
Here is the output of the first case:
Creating producer thread...
Pool started one thread
Finished to read producer []
Pool started one thread
Pool started one thread
Pool started one thread
Pool started one thread
And the output when Strings get generated
Pool started one thread
Pool started one thread
Pool started one thread
Pool started one thread
Creating producer thread...
-- Begin to produce names
-- sent the name
name = "name"
Pool started one thread
-- sent the name
name = "name"
Producer finished with data: ()
Finished to read producer ["name", "name"]
I got the feeling that, for the first case, the producer thread doesn't get scheduled on the thread pool for whatever reason. I must be missing something but I don't know what.

The cause of the problem is the early drop of the producer future.
On the method search_names, the CpuFuture that produces value is dropped when search_names returns. When dropped, a CpuFuture is cancelled, thus skipping production of the values.
The difference in behavior certainly comes from a race between the drop of the future and it's execution.
A solution is to reference the producer future all along the application like this:
#[macro_use]
extern crate lazy_static;
extern crate tokio_core;
extern crate futures;
extern crate futures_cpupool;
use std::time::Duration;
use futures_cpupool::{CpuPool, Builder, CpuFuture};
use futures::Stream;
use futures::{Future, future, lazy};
use futures::sync::mpsc;
use futures::Sink;
lazy_static! {
static ref CPU_POOL: CpuPool = {
Builder::new()
.pool_size(5)
.after_start(|| {
println!("Pool started one thread");
})
.before_stop(|| {
println!("Pool stopped one thread");
})
.create()
};
}
struct Producer {}
impl Producer {
fn search_names(&self) -> (CpuFuture<(), ()>, Box<Stream<Item = String, Error = String> + Send>) {
let (mut tx, rx) = mpsc::channel::<Result<String, String>>(1);
println!("Creating producer thread...");
let producer_cpu: CpuFuture<(), ()> = CPU_POOL.spawn(
lazy(move || {
println!(" -- Begin to produce names");
for i in 0..2 {
match tx.send(Ok("name".to_string())).wait() {
Ok(t) => {
println!(" -- sent the name");
tx = t
},
Err(err) => {
println!(" -- Error occured sending name! {:?}", err);
break
},
}
std::thread::sleep(Duration::from_secs(1));
}
future::ok::<(), ()>(())
}).then(|result| {
match result {
Ok(data) => println!("Producer finished with data: {:?}", data),
Err(err) => println!("Producer finished with error: {:?}", err),
}
future::ok::<(), ()>(())
})
);
(
producer_cpu,
rx.then(|r| r.unwrap()).boxed()
)
}
}
fn main() {
let producer = Producer {};
let (future, stream) = producer.search_names();
let names = CPU_POOL.spawn(
stream
.map(|name| {
println!("name = {:?}", name);
name
})
.collect()
.then(|result| {
match result {
Ok(data) => println!("Finished to read producer {:?}", data),
Err(err) => println!("Error reading stream of producer! {:?}", err)
}
future::ok::<(), ()>(())
})
);
names.wait();
}

Related

Unhandled exception: Concurrent modification during iteration: Instance(length:1) of '_GrowableList'

I have global variable jobModelList declared as:
late List<JobModel> jobModelList = <JobModel>[];
I have http handler that get data and fill jobModelList with data.
main() async {
scanParserProcesses();
app.get('/get-jobs-status', (req, res) async {
print("get-jobs-status");
var result = jobModelList.map((e) => e.toJson()).toList();
res.json({'data': result});
});
app.post('/update-jobs-status', (req, res) async {
print("update-jobs-status");
JobModel job = JobModel.fromJson(req.body);
jobModelList.removeWhere((element) => element.jobPid == job.jobPid);
jobModelList.removeWhere((element) => element.jobNumbers.toString() == job.jobNumbers.toString());
jobModelList.add(job);
res.json({'data': 'Parser status updated'});
});
I need to scan model periodicly and remove hanging process.
scanParserProcesses() {
Timer.periodic(Duration(seconds:5), (Timer t) async {
for(var job in jobModelList) {
if(job.lastPingDate!.isBefore(DateTime.now().subtract(Duration(minutes: 5))) ) {
job.parserStatus = ParserStatus.idle;
if(job.isMiddlewareAlive ?? false ) {
if(job.lastPingDate!.isBefore(DateTime.now().subtract(Duration(minutes: 30))) ) {
print("Next PID: ${job.jobPid} is hangs and will be restarted");
job.parserStatus = ParserStatus.hanging;
}
}
}
}
});
removeDeadInstances();
}
removeDeadInstances() {
jobModelList.removeWhere((element) => element.parserStatus == ParserStatus.hanging );
}
But periodically I am getting error:
Unhandled exception: Concurrent modification during iteration: Instance(length:1) of '_GrowableList'.
And I can't understand how to fix it.

Is there a solution to asynchronously wait for C++ data on Flutter's invokeMethod?

Currently, this is how I read from C++ using Flutter:
final Uint8List result = await platform.invokeMethod(Common.MESSAGE_METHOD, {"message": buffer});
It is handled by Kotlin like this:
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
if (call.method == MESSAGE_METHOD) {
val message: ByteArray? = call.argument<ByteArray>("message")
//... //response = Read something FROM C++
result.success(response)
Since this happens in the main thread, if I take too much time to answer, I make Flutter's UI slow.
Is there a solution to get C++ data in an async way?
I know that Flutter has support for event channels to send data back from C++ to Flutter. But what about just requesting the data on the Flutter side and waiting for it to arrive in a Future, so I can have lots of widgets inside a FutureBuilder that resolves to something when ready?
If reading something from C++ is a heavy process, You can use AsysncTask to perform it in the background for android.
internal class HeavyMsgReader(var result: MethodChannel.Result) : AsyncTask<ByteArray?, Void?, String?>() {
override fun doInBackground(vararg message: ByteArray?): String {
//... //response = Read something FROM C++
return "response"
}
override fun onPostExecute(response: String?) {
result.success(response)
}
}
Calling async task:
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
if (call.method == MESSAGE_METHOD) {
val message: ByteArray? = call.argument<ByteArray>("message")
HeavyMsgReader(result).execute(message);
Hopefully this will work
import 'dart:async';
Future<Uint8List> fetchData(buffer) async {
final Uint8List result = await platform.invokeMethod(Common.MESSAGE_METHOD, {"message": buffer});
return result;
}
And just call it, like this
fetchData(buffer).then((result) => {
print(result)
}).catchError(print);
Proof that its working:
import 'dart:async';
Future<String> fetchUserOrder() async {
await Future.delayed(Duration(seconds: 5));
return 'Callback!';
}
Future<void> main() async {
fetchUserOrder().then((result) => {
print(result)
}).catchError(print);
while(true){
print('main_thread_running');
await Future.delayed(Duration(seconds: 1));
}
}
output:
main_thread_running
main_thread_running
main_thread_running
main_thread_running
main_thread_running
Callback!
main_thread_running
main_thread_running
...

Rust cache async traits

I'm running into an issue when I attempt to cache a value for as long as it's valid and update it when it becomes invalid. I believe the issue is due to my attempt to share state across async executions. Further, this component lives in a multi-threaded / concurrent environment.
The error I'm seeing that I don't know how to fix is
future is not `Send` as this value is used across an await
Following is a minimum example that I could come up with (it also features some ownership issues) that generally captures my use-case and the issue I'm seeing. Here is a playground of the code.
use async_trait::async_trait;
use chrono::{DateTime, Utc};
use std::sync::{Arc, Mutex};
struct Creds {
expires_at: DateTime<Utc>,
}
impl Creds {
fn is_expired(&self) -> bool {
self.expires_at.le(&Utc::now())
}
}
#[async_trait]
trait CredsProvider {
async fn get_creds(&self) -> Creds;
}
struct MyCredsProvider {
cached_creds: Arc<Mutex<Option<Creds>>>,
}
impl MyCredsProvider {
fn new() -> Self {
MyCredsProvider {
cached_creds: Arc::new(Mutex::new(None)),
}
}
async fn inner_get_creds(&self) -> Creds {
todo!()
}
}
#[async_trait]
impl CredsProvider for MyCredsProvider {
async fn get_creds(&self) -> Creds {
let mg = self
.cached_creds
.lock()
.expect("Unable to get lock on creds mutex");
if mg.is_some() && !mg.as_ref().unwrap().is_expired() {
return mg.unwrap();
}
let new_creds = self.inner_get_creds().await;
*mg = Some(new_creds);
return new_creds;
}
}
#[tokio::main]
async fn main() {
MyCredsProvider::new();
// Some multi-threaded / concurrent logic to periodically refresh creds
todo!()
}
I wasn't sure how to include this in the example but in main imagine multiple worker threads running concurrently / parallel that each call CredsProvider.get_creds and then use these creds to perform some work (if you can add that to a complete working example, that'd be much appreciated for my edification). Assume MyCredsProvider.inner_get_creds is expensive and should only be called when the cached creds expire.
How do I solve this? I thought that the Arc<Mutex<>> would be enough but it seems not. At one point, I tried making Creds and trait so that I could have Arc<Mutex<Option<Box<dyn Creds + Send + Sync>>>> but that felt like the wrong path and didn't work.
Thanks.
You may would like to switch to tokio::sync::Mutex (playground).
It solves
future is not `Send` as this value is used across an await
Code:
use async_trait::async_trait;
use chrono::{DateTime, Utc};
use std::sync::Arc;
use tokio::sync::Mutex;
#[derive(Clone)]
struct Creds {
expires_at: DateTime<Utc>,
}
impl Creds {
fn is_expired(&self) -> bool {
self.expires_at.le(&Utc::now())
}
}
#[async_trait]
trait CredsProvider {
async fn get_creds(&self) -> Creds;
}
struct MyCredsProvider {
cached_creds: Arc<Mutex<Option<Creds>>>,
}
impl MyCredsProvider {
fn new() -> Self {
MyCredsProvider {
cached_creds: Arc::new(Mutex::new(None)),
}
}
async fn inner_get_creds(&self) -> Creds {
todo!()
}
}
#[async_trait]
impl CredsProvider for MyCredsProvider {
async fn get_creds(&self) -> Creds {
let mut mg = self
.cached_creds
.lock()
.await;
if mg.is_some() && !mg.as_ref().unwrap().is_expired() {
return mg.clone().unwrap();
} else {
let new_creds = self.inner_get_creds().await;
*mg = Some(new_creds.clone());
return new_creds;
}
}
}
#[tokio::main]
async fn main() {
MyCredsProvider::new();
// Some multi-threaded / concurrent logic to periodically refresh creds
todo!()
}

How do I suppress output from multiple threads when running cargo test?

If I execute the below testcases with cargo test, the output of one_thread_test will be suppressed as stated in the documentation.
However the output from multi_thread_test will appear on stdout. Is it possible to match the behavior of single- and multi-threaded testcases?
#[test]
fn one_thread_test() {
println!("A");
println!("B");
}
#[test]
fn multi_thread_test() {
use std::thread;
let mut threads = vec![];
for _ in 0..100 {
let t = thread::spawn(move || {
println!("from thread");
});
threads.push(t);
}
for thread in threads {
thread.join().unwrap();
}
}
Here is a quick-and-dirty workaround.
It works by sending messages to a receiver owned by a struct in the main thread. The receiver prints all of the accumulated messages when it is dropped - this is important so that panics caused by failed assertions don't prevent the printing.
use std::sync::mpsc::{channel, Sender, Receiver};
struct TestPrinter {
receiver: Receiver<String>,
sender: Sender<String>,
}
impl TestPrinter {
fn new() -> TestPrinter {
let (sender, receiver) = channel();
TestPrinter { receiver, sender }
}
fn sender(&self) -> Sender<String> {
self.sender.clone()
}
}
impl Drop for TestPrinter {
fn drop(&mut self) {
while let Some(v) = self.receiver.try_recv().ok() {
println!("later: {}", v);
}
}
}
And a convenience macro so it feels mostly like calling println!:
macro_rules! myprint {
($send: expr, $($arg:tt)*) => {
(*&$send).send(format!($($arg)*));
};
}
In order to send messages for printing, you have get a sender for each thread:
#[test]
fn multi_thread_test() {
use std::thread;
let mut threads = vec![];
let printer = TestPrinter::new();
for _ in 0..100 {
let sender = printer.sender();
let t = thread::spawn(move || {
myprint!(sender, "from thread");
});
threads.push(t);
}
for thread in threads {
thread.join().unwrap();
}
}
The actual printing happens when printer goes out of scope. It's in the main thread so it won't print during successful tests unless --nocapture is specified.

My async call is returning before list is populated in forEach loop

I have a routine which gets a list of filenames from the device, then reads the file(s) to build a list. However, the calling routine always returns with zero items. I print the filenames, so I know they exist, however, it appears that the async is returning before I read the files. I used similar code when making an HTTP call. But, something here is causing the routine to return the list even though it hasn't completed. Perhaps, it is possible that I am calling it at the wrong time? I am calling retrieveItems here:
#override
void initState() {
super.initState();
retrieveItems();
}
Eventually I will have a refresh button, but for now I'd simply like the list to populate with the data from the files...
--------------------
Callee
Future<List<String>> readHeaderData() async {
List<String> l = new List();
List<String> files = await readHeaders(); // Gets filenames
files.forEach((filename) async {
final file = await File(filename);
String contents = await file.readAsString();
User usr = User.fromJson(json.decode(contents));
String name = usr.NameLast + ", " + usr.NameFirst;
print(name);
l.add(name);
}
return l;
Caller
void retrieveItems() async {
LocalStorage storage = new LocalStorage();
await storage.readHeaderData().then((item) {
try {
if ((item != null ) &&(item.length >= 1)) {
setState(() {
users.clear();
_users.addAll(item);
});
} else {
setState(() {
_users.clear();
final snackbar = new SnackBar(
content: new Text('No users found.'),
);
scaffoldKey.currentState.showSnackBar(snackbar);
});
}
} on FileNotFoundException catch (e) {
print(e.toString()); //For debug only
setState(() {
_users.clear();
});
});
}
});
This code
Future<List<String>> readHeaderData() async {
List<String> l = new List();
List<String> files = await readHeaders(); // Gets filenames
files.forEach((filename) async {
final file = await File(filename);
String contents = await file.readAsString();
User user = User.fromJson(json.decode(contents));
String name = user.NameLast + ", " + user.NameFirst;
print(name);
l.add(name);
}
return l;
}
returns the list l and then processes the asyc forEach(...) callbacks
If you change it to
Future<List<String>> readHeaderData() async {
List<String> l = new List();
List<String> files = await readHeaders(); // Gets filenames
for(var filename in files) { /// <<<<==== changed line
final file = await File(filename);
String contents = await file.readAsString();
User user = User.fromJson(json.decode(contents));
String name = user.NameLast + ", " + user.NameFirst;
print(name);
l.add(name);
}
return l;
}
the function will not return before all filenames are processed.
files.forEach((filename) async {
means that you can use await inside the callback, but forEach doesn't care about what (filename) async {...} returns.
Also possible
await Future.forEach(yourList, (T elem) async { ...async staff });
To expand on Günter's comment regarding using list.map(f), here's an example of converting a forEach call so that it works correctly.
Broken example
Incorrectly assumes forEach will wait on futures:
Future<void> brokenExample(List<String> someInput) async {
List<String> results;
someInput.forEach((input) async {
String result = await doSomethingAsync(input);
results.add(result);
});
return results;
}
Corrected example
Waits on the async functions to complete, using Future.wait and .map():
Future<void> correctedExample(List<String> someInput) async {
List<String> results;
await Future.wait(someInput.map((input) async {
String result = await doSomethingAsync(input);
results.add(result);
}));
return results;
}
I encountered the similar issue. The problem is that dart will NOT wait for "forEach" contrary to public believe. There are two solutions:
1) Convert forEach to for loop as indicated by others. Another is use Future:
2) await Future.forEach(list, (item) async {
// your code
final result = await getMyResult();
});
Another option
Future.wait(someList.map((item) => something_returns_future(item)));