One more "Future cannot be sent between threads safely" thread (with serenity and rusqlite)

Hello everyone, you gonna hate me but i use ChatGPT as a tutor in understanding Rust. Its a study that i plan to write about. As a first program, with Chat's help im trying to make Discord bot for users to tip imaginary tokens to each other. Here is the tip function that cannot be sent between threads safely:

use serenity::{
    framework::standard::{ 
        Args, CommandResult,
        macros::{command, group},
    },
    model::{channel::Message,}
};
use serenity::prelude::Context;
use super::database::{insert_row, get_user, update_balance, get_balance, update_address};
use lazy_static::lazy_static;
use r2d2_sqlite::SqliteConnectionManager;
use std::future::*;
use std::sync::{Arc, Mutex};
use rusqlite;

// Set up the connection manager and pool
lazy_static! {
    static ref POOL: Arc<Mutex<r2d2::Pool<SqliteConnectionManager>>> = {
        let manager = SqliteConnectionManager::file("userstable.db");
        let pool = r2d2::Pool::builder()
            .build(manager)
            .unwrap_or_else(|_| panic!("Error creating pool"));
        Arc::new(Mutex::new(pool))
    };
}

#[group]
#[commands(tip, update, balance, register)]
pub struct General;

#[command]
pub async fn tip(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
    // Clone the connection pool and HTTP client
    let pool = POOL.clone();
    let http = ctx.http.clone();

    // Parse the user ID or username and amount from the command arguments
    let name = args.single::<String>()?;
    let amount = args.single::<i32>()?;

    // Look up the user ID of the recipient
    let recipient_id = match name.parse::<u64>() {
        Ok(id) => match http.get_user(id).await {
            Ok(user) => user.id,
            Err(_) => {
                msg.channel_id.say(&http, "User not found").await;
                return Err("User not found".into());
            }
        },
        Err(_) => {
            msg.channel_id.say(&http, "Invalid user ID or username").await;
            return Err("Invalid user ID or username".into());
        }
    };

    // Lock the connection pool and get a connection
    let mutex_guard = pool.lock().expect("Error acquiring mutex");
    let conn = mutex_guard.get()?;

    // Ensure that the sender and recipient exist in the database
    let sender_exists = get_user(&conn, *msg.author.id.as_u64()).await.is_ok();
    let recipient_exists = get_user(&conn, *recipient_id.as_u64()).await.is_ok();
    let sender_balance = get_balance(&conn, *msg.author.id.as_u64()).await;

    // Update the balances of the sender and recipient in the database
    update_balance(&conn, *msg.author.id.as_u64(), -amount).await?;
    update_balance(&conn, *recipient_id.as_u64(), amount).await?;
    
    // Send a message to the channel indicating that the tip was successful
    let sender_name = msg.author.id.as_u64();
    let recipient_name = recipient_id.as_u64();
    msg.channel_id.say(&http, format!("{} tipped {} {}", sender_name, recipient_name, amount)).await;
    
    Ok(())
}

and here is the error:

error: future cannot be sent between threads safely
   --> src\modules\commands.rs:38:1
    |
38  | #[command]
    | ^^^^^^^^^^ future created by async block is not `Send`
    |
    = help: within `impl futures::Future<Output = Result<(), Box<dyn std::error::Error + Sync + std::marker::Send>>>`, the trait `std::marker::Send` is not implemented for `std::sync::MutexGuard<'_, Pool<SqliteConnectionManager>>`
note: future is not `Send` as this value is used across an await
   --> src\modules\commands.rs:68:65
    |
38  | #[command]
    |          - `mutex_guard` is later dropped here
...
64  |     let mutex_guard = pool.lock().expect("Error acquiring mutex");
    |         ----------- has type `std::sync::MutexGuard<'_, Pool<SqliteConnectionManager>>` which is not `Send`
...
68  |     let sender_exists = get_user(&conn, *msg.author.id.as_u64()).await.is_ok();
    |                                                                 ^^^^^^ await occurs here, with `mutex_guard` maybe used later
note: required by a bound in `serenity::FutureExt::boxed`
   --> .cargo\registry\src\github.com-1ecc6299db9ec823\futures-util-0.3.25\src\future\future\mod.rs:520:23
    |
520 |         Self: Sized + Send + 'a,
    |                       ^^^^ required by this bound in `serenity::FutureExt::boxed`
    = note: this error originates in the attribute macro `command` (in Nightly builds, run with -Z macro-backtrace for more info)


I don't know what to do, I thought that creating Pool for sqlite and wrapping it in Arc and Mutex would help, but it didn't.

Sounds like you’re holding a mutex lock over an .await point somewhere. Ah right down there it is

    let mutex_guard = pool.lock().expect("Error acquiring mutex");
    let conn = mutex_guard.get()?;

Make sure not to put and keep the guard in a long-lived local variable (and thus the mutex locked for a long time, which is not only a compilation error due to the missing Send implementation, but likely also a logic bug in your program!). Do the thing in one step instead, so the guard can be dropped at the end of the statement; see if this fixed the issue:

    let conn = pool.lock().expect("Error acquiring mutex").get()?;
1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.