Reputation: 442
I'm new to Rust so please bare with me. I'm trying to do what I would consider to be quite simple - insert an item into a HashMap. When I try to insert an item, it just freezes and I can't seem to figure out why.
VSCode isn't picking up any errors, been googling around and can't seem to find anything that might indicate what might be the issue.
I feel like that it might be related the the Mutex but really not sure. Below is a snippet of the relevant bit of the code.
use lazy_static::lazy_static;
use std::collections::{HashMap, HashSet};
use std::net::TcpStream;
use std::sync::Mutex;
lazy_static! {
static ref SUBSCRIPTIONS: Mutex<HashMap<String, HashSet<String>>> = Mutex::new(HashMap::new());
}
pub struct Subscription {}
impl Subscription {
fn is_channel_registered(&self, channel: &String) -> bool {
SUBSCRIPTIONS.lock().unwrap().contains_key(channel)
}
pub fn add_subscription(&self, client: &TcpStream, channel: &String) {
let mut subscriptions = SUBSCRIPTIONS.lock().unwrap();
// Check if the a key for the channel already exists. If not create it.
if !self.is_channel_registered(&channel) {
subscriptions.insert(channel.to_string(), HashSet::new());
}
}
Now, I am trying to test this using a unittest. Below is that chunk of code:
#[cfg(test)]
mod subscription_tests {
use super::*;
use std::net::TcpStream;
#[test]
fn test_add_subscription() {
let client = TcpStream::connect("localhost:8080").unwrap();
let channel: "test_channel".to_string();
Subscription {}.add_subscription(&client, &channel);
}
}
I've commented out a bunch of code to try and find the issue by process of elimination, but it really looks like the issue is the actual code in the initial block.
Welp! I'm hoping this is enough to reproduce, but just incase here is the code.
Upvotes: 0
Views: 694
Reputation: 70860
is_channel_registered()
tries to lock the mutex while it's already locked. That results in a deadlock.
Either pass the unlocked value explicitly to it:
impl Subscription {
fn is_channel_registered(subscriptions: &HashMap<String, HashSet<String>>, channel: &String) -> bool {
subscriptions.contains_key(channel)
}
pub fn add_subscription(&self, client: &TcpStream, channel: &String) {
let mut subscriptions = SUBSCRIPTIONS.lock().unwrap();
// Check if the a key for the channel already exists. If not create it.
if !Self::is_channel_registered(&*subscriptions, &channel) {
subscriptions.insert(channel.to_string(), HashSet::new());
}
}
}
Or lock it inside add_subscription()
only after is_channel_registered()
is used:
impl Subscription {
fn is_channel_registered(&self, channel: &String) -> bool {
SUBSCRIPTIONS.lock().unwrap().contains_key(channel)
}
pub fn add_subscription(&self, client: &TcpStream, channel: &String) {
// Check if the a key for the channel already exists. If not create it.
if !self.is_channel_registered(&channel) {
SUBSCRIPTIONS.lock().unwrap().insert(channel.to_string(), HashSet::new());
}
}
Upvotes: 1