An object pool

50 points aagmon

I have a struct that is expensive to init and it cannot be shared between threads (it's a third party lib so I can't change it).

However, I need to use it in a multi-threaded high-throughput environment - it has to be used by an Axum service serving multiple concurrent requests.

That leaves me with few choices:

  • Share it in Mutex - not a good possibility because the bottleneck might be too much
  • Initialize a copy for each request - also not good because its too expensive to create
  • keep a shared pool of these structs that I can use for each request.

I have tried to implement the latter and wonder if this makes sense. The general idea is to keep a vector of mutexes wrapping my struct, and then when a req arrives try to access a random mutex there, hoping that most of the time different mutexes will be accessed and therefore will be available to lock .

struct State {
    locked_objs: Vec<Mutex<Obj>>,
}
impl State {

    async fn get_model_instance(&self) -> MutexGuard<'_, Obj> {
        let objs_len = self.locked_models.len();
        let random_index = rand::random::<usize>() % objs_len;
        self.locked_objs[random_index].lock().await
    }
}

would be happy for feedback

​

UPDATE:after some great suggestions, it seems that using thread\_local is the most elegant, though using with once\_cell got me into troubles with lifetimes so implemented it like this

thread_local! {
    static LOCAL_MODEL: Arc<SequenceClassificationModel> = Arc::new(init_sequence_classifier());
}
fn get_model_instance() -> Arc<SequenceClassificationModel> {
    LOCAL_MODEL.with(|model| model.clone())
}

The other better solution is to iterate all mutexs first and try with try\_lock

loop {
 for model in &self.locked_models {
 match model.try_lock() {
    Ok(guard) => return guard,
    Err(_) => continue,
     }
  }

}

}