Skip to content

Instantly share code, notes, and snippets.

@ba11b0y
Created May 16, 2025 23:33
Show Gist options
  • Save ba11b0y/c36815e8dbc624a2876d190d7017a416 to your computer and use it in GitHub Desktop.
Save ba11b0y/c36815e8dbc624a2876d190d7017a416 to your computer and use it in GitHub Desktop.
Deadlocks with Dashmap!

Deadlocks with Dashmap!

While working on a fix, I ran into a deadlock with Dashmap and when I tried to look up the reasons online, I found a bunch of people facing the same issue!

image

Here's a little deeper dive into the problem and the fix.

The problematic code:

if clients.contains_key(client_name) {
  let client = clients.get(client_name).unwrap();
  // if the env vars haven't changed, return the cached client
  if !client.has_env_vars_changed(ctx.env_vars()) {
      return Ok(client.provider.clone());
  }else{
      // if the env vars have changed, remove the client from the cache, and create a new one.
      println!("removing client {}", client_name);
      clients.remove(client_name); // <-------- Deadlock!!
      println!("removed client {}", client_name);
  }
}

So if you see Dashmap's get method, it returns a Ref to the map value which means we're holding a reference into the map and the author makes it explicitly clear in the docs that it may deadlock if called when holding a mutable reference into the map. But that's not the case here because a deadlock happens when we try to remove the client from the map since remove tries to use the same mutable reference that we're holding.

/// Get an immutable reference to an entry in the map
///
/// **Locking behaviour:** May deadlock if called when holding a mutable reference into the map.
///
/// # Examples
///
/// ```
/// use dashmap::DashMap;
///
/// let youtubers = DashMap::new();
/// youtubers.insert("Bosnian Bill", 457000);
/// assert_eq!(*youtubers.get("Bosnian Bill").unwrap(), 457000);
/// ```
pub fn get<Q>(&'a self, key: &Q) -> Option<Ref<'a, K, V, S>>
where
    K: Borrow<Q>,
    Q: Hash + Eq + ?Sized,
{
    self._get(key)
}
/// Removes an entry from the map, returning the key and value if they existed in the map.
///
/// **Locking behaviour:** May deadlock if called when holding any sort of reference into the map.
///
/// # Examples
///
/// ```
/// use dashmap::DashMap;
///
/// let soccer_team = DashMap::new();
/// soccer_team.insert("Jack", "Goalie");
/// assert_eq!(soccer_team.remove("Jack").unwrap().1, "Goalie");
/// ```
pub fn remove<Q>(&self, key: &Q) -> Option<(K, V)>
where
    K: Borrow<Q>,
    Q: Hash + Eq + ?Sized,
{
    self._remove(key)
}

The fix? Try to get a clone of the client when doing a get, which is probably not the best solution but it works!

if clients.contains_key(client_name) {
    let client = clients.get(client_name).map(|c| c.clone()).unwrap();
    // if the env vars haven't changed, return the cached client
    if !client.has_env_vars_changed(ctx.env_vars()) {
        return Ok(client.provider.clone());
    }else{
        // if the env vars have changed, remove the client from the cache, and create a new one.
        println!("removing client {}", client_name);
        clients.remove(client_name); // Works!
        println!("removed client {}", client_name);
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment