How to fire async callback in rust - rust

I'm trying to implement a StateMachine in Rust, but I encountered some problems while trying to fire the callback of StateMachine in a spawn thread.
Here is my StateMachine struct. The state is a generic T because I want to use it in many different scenerios, and I use a Vec to store all the callbacks those registered into this StateMachine.
At the very begining, I didn't use the lifetime 'a, but it will run into some lifetime problems, so I add the lifetime 'a by this suggestion: Idiomatic callbacks in Rust
pub struct StateMachine<'a, T> where T:Clone+Eq+'a {
state: RwLock<T>,
listeners2: Vec<Arc<Mutex<ListenerCallback<'a, T>>>>,
}
pub type ListenerCallback<'a, T> = dyn FnMut(T) -> Result<()> + Send + Sync + 'a ;
When the state is changed, the StateMachine will fire all the callbacks, as follows.
pub async fn try_set(&mut self, new_state:T) -> Result<()> {
if (block_on(self.state.read()).deref().eq(&new_state)) {
return Ok(())
}
// todo change the state
// fire every listener in spawn
let mut fire_results = vec![];
for listener in &mut self.listeners2 {
let state = new_state.clone();
let fire_listener = listener.clone();
fire_results.push(tokio::spawn(async move {
let mut guard = fire_listener.lock().unwrap();
guard.deref_mut()(state);
}));
}
// if fire result return Err, return it
for fire_result in fire_results {
fire_result.await?;
}
Ok(())
}
But it will cause a compilation error.
error[E0521]: borrowed data escapes outside of associated function
--> src/taf/taf-core/src/execution/state_machine.rs:54:33
|
15 | impl<'a,T> StateMachine<'a,T> where T:Clone+Eq+Send {
| -- lifetime `'a` defined here
...
34 | pub async fn try_set(&mut self, new_state:T) -> Result<()> {
| --------- `self` is a reference that is only valid in the associated function body
...
54 | let fire_listener = listener.clone();
| ^^^^^^^^^^^^^^^^
| |
| `self` escapes the associated function body here
| argument requires that `'a` must outlive `'static`
##########################################################
The full code is coupled with a lot of business logic, so I rewrite 2 demos as follows, the problems is the same. The first demo fire callback synchronously and it works, the second demo try to fire callback asynchronously, it encounter the same problem: self escapes the associated function body here.
First demo(it works):
use std::alloc::alloc;
use std::ops::DerefMut;
use std::sync::{Arc, Mutex, RwLock};
use anyhow::Result;
use dashmap::DashMap;
struct StateMachine<'a,T> where T:Clone+Eq+'a {
state: T,
listeners: Vec<Box<Callback<'a, T>>>,
}
type Callback<'a, T> = dyn FnMut(T) -> Result<()> + Send + Sync + 'a;
impl<'a, T> StateMachine<'a,T> where T:Clone+Eq+'a {
pub fn new(init_state: T) -> Self {
StateMachine {
state: init_state,
listeners: vec![]
}
}
pub fn add_listener(&mut self, listener: Box<Callback<'a, T>>) -> Result<()> {
self.listeners.push(listener);
Ok(())
}
pub fn set(&mut self, new_state: T) -> Result<()> {
self.state = new_state.clone();
for listener in &mut self.listeners {
listener(new_state.clone());
}
Ok(())
}
}
#[derive(Clone, Eq, PartialEq, Hash)]
enum ExeState {
Waiting,
Running,
Finished,
Failed,
}
struct Execution<'a> {
exec_id: String,
pub state_machine: StateMachine<'a, ExeState>,
}
struct ExecManager<'a> {
all_jobs: Arc<RwLock<DashMap<String, Execution<'a>>>>,
finished_jobs: Arc<RwLock<Vec<String>>>,
}
impl<'a> ExecManager<'a> {
pub fn new() -> Self {
ExecManager {
all_jobs: Arc::new(RwLock::new(DashMap::new())),
finished_jobs: Arc::new(RwLock::new(vec![]))
}
}
fn add_job(&mut self, job_id: String) {
let mut execution = Execution {
exec_id: job_id.clone(),
state_machine: StateMachine::new(ExeState::Waiting)
};
// add listener
let callback_finished_jobs = self.finished_jobs.clone();
let callback_job_id = job_id.clone();
execution.state_machine.add_listener( Box::new(move |new_state| {
println!("listener fired!, job_id {}", callback_job_id.clone());
if new_state == ExeState::Finished || new_state == ExeState::Failed {
let mut guard = callback_finished_jobs.write().unwrap();
guard.deref_mut().push(callback_job_id.clone());
}
Ok(())
}));
let mut guard = self.all_jobs.write().unwrap();
guard.deref_mut().insert(job_id, execution);
}
fn mock_exec(&mut self, job_id: String) {
let mut guard = self.all_jobs.write().unwrap();
let mut exec = guard.deref_mut().get_mut(&job_id).unwrap();
exec.state_machine.set(ExeState::Finished);
}
}
#[test]
fn test() {
let mut manager = ExecManager::new();
manager.add_job(String::from("job_id1"));
manager.add_job(String::from("job_id2"));
manager.mock_exec(String::from("job_id1"));
manager.mock_exec(String::from("job_id2"));
}
Second demo:
use std::alloc::alloc;
use std::ops::DerefMut;
use std::sync::{Arc, Mutex, RwLock};
use anyhow::Result;
use dashmap::DashMap;
use petgraph::algo::astar;
struct StateMachine<'a,T> where T:Clone+Eq+Send+'a {
state: T,
listeners: Vec<Arc<Mutex<Box<Callback<'a, T>>>>>,
}
type Callback<'a, T> = dyn FnMut(T) -> Result<()> + Send + Sync + 'a;
impl<'a, T> StateMachine<'a,T> where T:Clone+Eq+Send+'a {
pub fn new(init_state: T) -> Self {
StateMachine {
state: init_state,
listeners: vec![]
}
}
pub fn add_listener(&mut self, listener: Box<Callback<'a, T>>) -> Result<()> {
self.listeners.push(Arc::new(Mutex::new(listener)));
Ok(())
}
pub fn set(&mut self, new_state: T) -> Result<()> {
self.state = new_state.clone();
for listener in &mut self.listeners {
let spawn_listener = listener.clone();
tokio::spawn(async move {
let mut guard = spawn_listener.lock().unwrap();
guard.deref_mut()(new_state.clone());
});
}
Ok(())
}
}
#[derive(Clone, Eq, PartialEq, Hash)]
enum ExeState {
Waiting,
Running,
Finished,
Failed,
}
struct Execution<'a> {
exec_id: String,
pub state_machine: StateMachine<'a, ExeState>,
}
struct ExecManager<'a> {
all_jobs: Arc<RwLock<DashMap<String, Execution<'a>>>>,
finished_jobs: Arc<RwLock<Vec<String>>>,
}
impl<'a> ExecManager<'a> {
pub fn new() -> Self {
ExecManager {
all_jobs: Arc::new(RwLock::new(DashMap::new())),
finished_jobs: Arc::new(RwLock::new(vec![]))
}
}
fn add_job(&mut self, job_id: String) {
let mut execution = Execution {
exec_id: job_id.clone(),
state_machine: StateMachine::new(ExeState::Waiting)
};
// add listener
let callback_finished_jobs = self.finished_jobs.clone();
let callback_job_id = job_id.clone();
execution.state_machine.add_listener( Box::new(move |new_state| {
println!("listener fired!, job_id {}", callback_job_id.clone());
if new_state == ExeState::Finished || new_state == ExeState::Failed {
let mut guard = callback_finished_jobs.write().unwrap();
guard.deref_mut().push(callback_job_id.clone());
}
Ok(())
}));
let mut guard = self.all_jobs.write().unwrap();
guard.deref_mut().insert(job_id, execution);
}
fn mock_exec(&mut self, job_id: String) {
let mut guard = self.all_jobs.write().unwrap();
let mut exec = guard.deref_mut().get_mut(&job_id).unwrap();
exec.state_machine.set(ExeState::Finished);
}
}
#[test]
fn test() {
let mut manager = ExecManager::new();
manager.add_job(String::from("job_id1"));
manager.add_job(String::from("job_id2"));
manager.mock_exec(String::from("job_id1"));
manager.mock_exec(String::from("job_id2"));
}
Compile error of second demo:
error[E0521]: borrowed data escapes outside of associated function
--> generic/src/callback2.rs:34:34
|
15 | impl<'a, T> StateMachine<'a,T> where T:Clone+Eq+Send+'a {
| -- lifetime `'a` defined here
...
29 | pub fn set(&mut self, new_state: T) -> Result<()> {
| --------- `self` is a reference that is only valid in the associated function body
...
34 | let spawn_listener = listener.clone();
| ^^^^^^^^^^^^^^^^
| |
| `self` escapes the associated function body here
| argument requires that `'a` must outlive `'static`
|
= note: requirement occurs because of the type `std::sync::Mutex<Box<dyn FnMut(T) -> Result<(), anyhow::Error> + Send + Sync>>`, which makes the generic argument `Box<dyn FnMut(T) -> Result<(), anyhow::Error> + Send + Sync>` invariant
= note: the struct `std::sync::Mutex<T>` is invariant over the parameter `T`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance

Tasks spawned with tokio::spawn() cannot use borrowed data (here, the data with lifetime 'a, whatever it may be). This is because there is not currently (and likely will never be) any way to guarantee that the borrowed data reliably outlives the spawned task.
You have two choices:
Fire the notifications without spawning. You can put the notification futures into a FuturesUnordered to run them all concurrently, but they will still all have to finish before try_set() does.
Remove the lifetime parameter; stop allowing callbacks that borrow data. Put 'static on your dyn types where necessary. Change the users of the StateMachine so they do not try to use borrowed data but use Arc instead, if necessary.
pub struct StateMachine<T> where T: Clone + Eq + 'static {
state: RwLock<T>,
listeners2: Vec<Arc<Mutex<ListenerCallback<T>>>>,
}
pub type ListenerCallback<T> = dyn FnMut(T) -> Result<()> + Send + Sync + 'static;

Related

Cannot relax lifetime of Trait Object

I have the following code:
use tokio; // 1.7.1
use futures::future::Future;
use std::pin::Pin;
use futures::FutureExt;
use std::marker::PhantomData;
use std::marker::Send;
use std::sync::Arc;
use async_trait::async_trait;
struct Orchestrator {
worker: Box<dyn WorkerT<Box<dyn Fn(i32) -> Pin<Box<dyn Future<Output = i32> + Send>> + Send + Sync>> + Send + Sync>
}
impl Orchestrator {
async fn other_things(&self, num: i32) -> i32{
// Do some async stuff in here
num+1
}
async fn main_stuff(self: Arc<Self>) {
let func = |num: i32| {
let slf = self.clone();
async move {
slf.other_things(num).await
}.boxed()
};
self.worker.do_work(Box::new(func)).await;
}
}
#[async_trait]
trait WorkerT<F: Fn(i32) -> Pin<Box<dyn Future<Output = i32> + Send>> + Send + Sync> {
async fn do_work(& self, func: F);
}
struct Worker<F: Fn(i32) -> Pin<Box<dyn Future<Output = i32> + Send>> + Send + Sync> {
phantom: std::marker::PhantomData<F>
}
#[async_trait]
impl<F: Fn(i32) -> Pin<Box<dyn Future<Output = i32> + Send>> + Send + Sync> WorkerT<F> for Worker<F> {
async fn do_work(& self, func: F) {
for i in 0..5 {
func(i).await;
}
}
}
#[tokio::main]
async fn main() {
let orchestrator = Arc::new(Orchestrator { worker: Box::new(Worker { phantom: PhantomData }) });
orchestrator.main_stuff().await;
}
With the following error:
error[E0597]: `self` does not live long enough
--> src/main.rs:22:23
|
21 | let func = |num: i32| {
| ---------- value captured here
22 | let slf = self.clone();
| ^^^^ borrowed value does not live long enough
...
28 | self.worker.do_work(Box::new(func)).await;
| -------------- cast requires that `self` is borrowed for `'static`
29 | }
| - `self` dropped here while still borrowed
Currently, because the default lifetime of dyn WorkerT... is 'static, it requires that the borrow of self in main_stuff be 'static. I need to relax the lifetime of the worker field but I don't know how. If I change worker to be of type 'Workerinstead ofdyn WorkerT...` this works, but I would much prefer keep it as the trait.
I have tried adding a lifetime to the Orchestrator struct and then giving that lifetime to the worker field, but then it says that my created lifetime needs to outlive 'static.
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=7f34fa394d706409942659f1d8ce36c0
You need to clone the Arc before creating the closure so that you can give an Arc to the closure. What you have in your code tries to borrow self, which won't outlive the closure. Cloning the Arc solves this issue as the closure can now own its own Arc pointing at the same value.
For example:
async fn main_stuff(self: Arc<Self>) {
let self2 = self.clone();
let func = move |num: i32| {
let self3 = self2.clone();
async move {
self3.other_things(num).await
}.boxed()
};
self.worker.do_work(Box::new(func)).await;
}
The inner clone is still required so that the type of func implements Fn (otherwise it will implement FnOnce due to moving a captured variable into the future).
Solution
async fn main_stuff(self: Arc<Self>) {
let slf = self.clone();
let func = move |num: i32| {
let slf = slf.clone();
async move { slf.other_things(num).await }.boxed()
};
self.worker.do_work(Box::new(func)).await;
}
Explanation
You are passing a closure that in turns creates async routine. First, the closure it self is static, so it has to take ownership of self. We clone self to slf and add move to the closure, thus closure moved it. Then we have to clone the slf each time and let the async routine own it when it exits the closure.

hyper : implementation of `FnOnce` is not general enough

I'm trying to refactor code by moving the hyper server out of main and allow the struct function to accept a function as below:
// main.rs
#[tokio::main]
async fn main() {
let listener = Listener::new(String::from("127.0.0.1"), String::from("3000"));
listener.start(request_handler);
}
async fn request_handler(req: Request<Body>) -> ConfigResult<Response<Body>> {
match req.method() {
&Method::GET => get(req.body()),
_ => Ok(Response::builder()
.status(StatusCode::NOT_FOUND)
.body("NOT FOUND".into())
.unwrap())
}
}
// lib.rs
pub type ConfigError = Box<dyn Error + Send + Sync>;
pub type ConfigResult<T> = Result<T, Box<dyn Error + Send + Sync>>;
#[derive(Debug, Clone)]
pub struct Listener(pub SocketAddr);
impl<'a> Listener {
pub fn new(addr: String, port:String) -> Self {
let ip = IpAddr::from_str(addr.as_str()).unwrap();
let address = SocketAddr::new(ip, port.parse::<u16>().unwrap());
Listener(address)
}
pub async fn start<F>(&'a self, request_handler: F ) -> ConfigResult<()>
where F: Fn(Request<Body>) -> ConfigResult<Response<Body>> + Sync + 'a
{
let mut service_closure = move |req| {
async {
request_handler(req)
}
};
let mut make_service_closure = move |_| {
async {
Ok::<_, ConfigError>(service_fn(&service_closure))
}
};
let service = make_service_fn(make_service_closure);
let server = Server::bind(&self.0).serve(service);
server.await?;
Ok(())
}
}
Im getting the error:
error: implementation of `FnOnce` is not general enough
--> src/config/server.rs:36:15
|
36 | server.await?;
| ^^^^^^ implementation of `FnOnce` is not general enough
|
= note: closure with signature `fn(&'2 AddrStream) -> impl Future<Output = Result<hyper::service::util::ServiceFn<&[closure#src/config/serva.rs:23:35: 27:10], Body>, Box<dyn StdError + Send + Sync>>>` must implement `FnOnce<(&'1 AddrStream,)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&'2 AddrStream,)>`, for some specific lifetime `'2`
Looking at the error above, my conclusion is its a lifetime error, where let make_service_closure = move |_| {...} requires lifetime of '1 but when i try let make_service_closure = move | a: &'a AddrStream| {...} i get lifetime mismatch error
expected type `for<'r> FnMut<(&'r AddrStream,)>`
found type `FnMut<(&'a AddrStream,)>`
plus the error implementation of FnOnce is not general enough. Please help understand which lifetime should i use for the hyper service function and why is FnOnce is not general enough.

How do I call a Box<dyn FnOnce> stored inside a struct inside an enum? [duplicate]

This question already has answers here:
FnOnce inside Enum: cannot move out of borrowed content
(1 answer)
Why is calling a FnOnce closure a move?
(1 answer)
Closed 10 months ago.
I store a FnOnce closure inside a struct inside an enum (Rust Playground):
pub struct ClosureContainer {
closure: Box<dyn FnOnce(i32) -> i32>,
}
pub enum MathOperation {
DoNothing,
RunOperation(ClosureContainer),
}
impl MathOperation {
pub fn doMath(&self, input: i32) -> i32 {
match self {
MathOperation::DoNothing => input,
MathOperation::RunOperation(closureContainer) => (closureContainer.closure)(input),
}
}
}
fn main() {
let f = Box::new(move |input: i32| 4 * input);
let closureContainer = ClosureContainer { closure: f };
let operation = MathOperation::RunOperation(closureContainer);
println!("{}", operation.doMath(5));
}
When I try to build, I get this error:
error[E0507]: cannot move out of `closureContainer.closure` which is behind a shared reference
--> src/main.rs:14:62
|
14 | MathOperation::RunOperation(closureContainer) => (closureContainer.closure)(input),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ move occurs because `closureContainer.closure` has type `Box<dyn FnOnce(i32) -> i32>`, which does not implement the `Copy` trait
How do I set things up so that I can call this function?
FnOnce can only be called once, so it needs to be consumed. If it's consumed, then the ClosureContainer must also be consumed (otherwise it's just an invalid struct). The fix, then, is to use self instead of &self in doMath:
pub struct ClosureContainer {
closure: Box<dyn FnOnce(i32) -> i32>,
}
pub enum MathOperation {
DoNothing,
RunOperation(ClosureContainer),
}
impl MathOperation {
pub fn doMath(self, input: i32) -> i32 {
match self {
MathOperation::DoNothing => input,
MathOperation::RunOperation(closureContainer) => (closureContainer.closure)(input),
}
}
}
fn main() {
let f = Box::new(move |input: i32| 4 * input);
let closureContainer = ClosureContainer { closure: f };
let operation = MathOperation::RunOperation(closureContainer);
println!("{}", operation.doMath(5));
}

How to downcast mutable structs not as references

I have this trait and implementation:
#[async_trait]
pub trait AsyncKeyProvider {
async fn get_key_async(&mut self, key_id: &str) -> Result<Option<Jwk>, ()>;
fn as_any(&self) -> &dyn Any;
}
#[async_trait]
impl AsyncKeyProvider for GoogleKeyProvider {
async fn get_key_async(&mut self, key_id: &str) -> Result<Option<Jwk>, ()> {
{...}
}
fn as_any(&self) -> &dyn Any {
self
}
}
In order to pass it into my handler in actix-web, I'm passing through a GoogleKeyProvider like this:
let key_provider = web::Data::from(Arc::new(GoogleKeyProvider::default()));
let server = HttpServer::new(move || {
App::new()
.app_data(key_provider.clone())
.route("/validate", web::post().to(validate))
})
With the handler doing this:
pub async fn validate(jwt_body: web::Json<JwtBody>, provider: web::Data<Box<dyn AsyncKeyProvider>>) -> impl Responder {
let provider_object: &GoogleKeyProvider = provider.as_any().downcast_ref::<GoogleKeyProvider>().expect("Wasn't a GoogleKeyProvider");
match validate_jwt(&jwt_body.jwt, provider_object).await {
{...}
}
}
validate_jwt then tries to call a method on the provider struct like this:
async fn validate_jwt(jwt: &String, provider: &GoogleKeyProvider) -> Result<bool, Box<dyn std::error::Error>> {
let key_to_use = provider.get_key_async(<thing>).await.unwrap();
}
Which presents me with this error:
error[E0596]: cannot borrow `*provider` as mutable, as it is behind a `&` reference
--> src\routes\validate.rs:48:22
|
48 | let key_to_use = provider.get_key_async(<thing>).await.unwrap();
| ^^^^^^^^ `provider` is a `&` reference, so the data it refers to cannot be borrowed as mutable
As far as I can understand, this is happening because the result of my downcasting is a reference (due to downcast_ref), but I think I'd be wanting the plain GoogleKeyProvider type instead - I'm not sure on that though. I believe the provider needs to be mutable as the values inside it (see below) can change during the lifetime of the provider (it's intended to provide a temporary cache for some keys, and automatically update them if they're out of date)
#[derive(Clone)]
pub struct GoogleKeyProvider {
cached: Option<JwkSet>,
expiration_time: Instant,
}
I'm not sure how to get this working with downcasting, though. Is anyone able to help me see where I've gone wrong?
You have to choice if get_key_async update somme thing at the struct.
The simple code below show you the error
trait Atrait {
fn afn(&mut self) -> i32;
}
struct Astruct {}
impl Atrait for Astruct {
fn afn(&mut self) -> i32 {
2
}
}
fn main()
{
// test should be mutable
let test = Astruct{};
let value = test.afn();
println!("Value {}", value);
}
This work because afn(self) is not declared mutable afn(&mut self)
trait Atrait {
fn afn(&self) -> i32;
}
struct Astruct {}
impl Atrait for Astruct {
fn afn(&self) -> i32 {
2
}
}
fn main()
{
let test = Astruct{};
let value = test.afn();
println!("Value {}", value);
}

How to send generic T to another thread?

How to send generic T?
I try to send a generic T to another thread but I'm getting:
error[E0308]: mismatched types
--> src/main.rs:23:22
|
23 | t1.merge(Element(vec![3]));
| ^^^^^^^^^^^^^^^^ expected associated type, found struct `Element`
|
= note: expected associated type `<T as Join>::Item`
found struct `Element`
= help: consider constraining the associated type `<T as Join>::Item` to `Element`
Full code:
trait Join {
type Item;
fn merge(&mut self, other: Self::Item);
}
#[derive(Debug, Default)]
struct Element(Vec<u8>);
impl Join for Element {
type Item = Element;
fn merge(&mut self, mut other: Self::Item) {
self.0.append(&mut other.0);
}
}
fn work<T>()
where
T: Default + Join + Send + Sync + 'static,
{
let (sender, receiver) = std::sync::mpsc::channel::<(T)>();
std::thread::spawn(move || {
while let (mut t1) = receiver.recv().unwrap() {
t1.merge(Element(vec![3]));
}
});
loop {
let mut t1 = T::default();
sender.send(t1);
std::thread::sleep(std::time::Duration::from_secs(5));
}
}
fn main() {
// works!
let mut e = Element(vec![1]);
e.merge(Element(vec![2]));
// bad!
work::<Element>();
}
Playground link
When you use generics you let the caller decide which types must be used by your generic function.
This line in your example t1.merge(Element(vec![3])); is invalid because it assumes T = Element but the caller can chose from infinitely many possible types of T where T != Element which is why the compiler is complaining.
To make your function fully generic you have to do something like add a Default bound to <T as Join>::Item in the function signature and then change the offending line to t1.merge(<T as Join>::Item::default());.
Updated working commented example:
use std::fmt::Debug;
trait Join {
type Item;
fn merge(&mut self, other: Self::Item);
}
#[derive(Debug)]
struct Element(Vec<u8>);
// updated Default impl so we can observe merges
impl Default for Element {
fn default() -> Self {
Element(vec![1])
}
}
impl Join for Element {
type Item = Element;
fn merge(&mut self, mut other: Self::Item) {
self.0.append(&mut other.0);
}
}
fn work<T>() -> Result<(), Box<dyn std::error::Error>>
where
T: Default + Join + Send + Sync + Debug + 'static,
<T as Join>::Item: Default, // added Default bound here
{
let (sender, receiver) = std::sync::mpsc::channel::<T>();
std::thread::spawn(move || {
while let Ok(mut t1) = receiver.recv() {
// changed this to use Default impl
t1.merge(<T as Join>::Item::default());
// prints "Element([1, 1])" three times
println!("{:?}", t1);
}
});
let mut iterations = 3;
loop {
let t1 = T::default();
sender.send(t1)?;
std::thread::sleep(std::time::Duration::from_millis(100));
iterations -= 1;
if iterations == 0 {
break;
}
}
Ok(())
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
// works!
let mut e = Element(vec![1]);
e.merge(Element(vec![2]));
// now also works!
work::<Element>()?;
Ok(())
}
playground

Resources