Sharing config in tokio code. Tbot framework

I am trying to share configuration between different threads and coroutines using tokio and tbot. I am struggling to share a simple string with a database URL. I've tried cloning, Arc<Mutex<String>> and nothing seems to work. Could someone show me the right direction to make the borrow checker work?

The problem is in the db_path: Arc<Mutex<String>> variable in the code below. Please see the comments for the FIRST, SECOND and THIRD usages of the variable.

#[tokio::main]
async fn main() {
    // skipping some setup code for brevity
    let settings = Settings::new(config).expect("Error while reading settings");
    let db_path = Arc::new(Mutex::new(settings.db_path.clone())); // this is the variable I am failing to share

    // this channel is used for interal message processing loop
    let (tok_tx, mut tok_rx) = tokio::sync::mpsc::unbounded_channel::<EspWeatherMessage>();

    tokio::spawn(async move {
            // FIRST usage of settings
            let notifications = connect_to_mqtt_server(&settings).await;
            // skipped some code for brevity
    });

    println!("Waiting for messages");
    let db = Arc::clone(&db_path); // I have added this clone to fix the "variable moved due to use in generator" which occured 4 lines below 
    tokio::spawn(async move {
        while let Some(msg) = tok_rx.recv().await {
            // here, a new thread is spawned for each `msg`, so I clone the Arc in each iteration
            let db = Arc::clone(&db); // SECOND usage

            tokio::task::spawn_blocking(move || {
                println!("Saving message to DB");
                let connection = establish_connection(&db.lock().unwrap()); // locking the mutex inside the blocking thread for the SECOND usage
                // skipped some code for brevity
            });
        }
    });

    let mut event_loop = (*bot).clone().event_loop(); // the origins of the bot variable are not important for the problem
    let dbc = Arc::clone(&db_path); // THIRD usage
    event_loop.command("subscribe", |context| async move {
        let chat_id = context.chat.id;
        let db = dbc.lock().unwrap(); // locking the mutex for the THIRD usage
        let connection = establish_connection(&db); 
        subscribe(chat_id.0, &connection);
    });

    event_loop.polling().start().await.unwrap();
}

The error I get is:

let dbc = Arc::clone(&db_path);
    |           --- captured outer variable
...
    | |         let db = dbc.lock().unwrap();
    | |                  ---
    | |                  |
    | |                  move occurs because `dbc` has type `std::sync::Arc<std::sync::Mutex<std::string::String>>`, which does not implement the `Copy` trait
    | |                  move occurs due to use in generator

Try this.

let dbc = Arc::clone(&db_path); // THIRD usage
event_loop.command("subscribe", move |context| {
    let dbc = Arc::clone(&db_path);
    async move {
        let chat_id = context.chat.id;
        let db = dbc.lock().unwrap(); // locking the mutex for the THIRD usage
        let connection = establish_connection(&db); 
        subscribe(chat_id.0, &connection);
    }
});

Note that the watch channel is often useful for sharing configs.

Thanks for the reply. This does not work:

158 |     event_loop.command("subscribe", move |context| {
    |                ^^^^^^^ the trait `std::future::Future` is not implemented for `()`

And thanks for the watch hint. I'll try to refactor the code to use it. However, I think that watch itself won't be sufficient since I use my config in spawn_blocking threads, which means that I still need some kind of synchronization.

Did you put a semicolon after the async block or something silly like that? As for spawn blocking, could you fetch a clone of it and move it into your spawn_blocking?

Yes, I missed the async move { } block inside the closure. Thanks a lot for the help. cargo check has passed successfully.

I think I need to read more about how async and move keywords work in this context. Do you have any suggestions for the reading material?

There's this post: Closures: Magic Functions that you might like. The canonical github pages url is 404'ing, so I'm linking the github markdown file instead. It explains how moving works with closures, and async blocks act in basically the exact same way.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.