Przeglądaj źródła

darkirc: Use RwLock instead of Mutex where applicable

parazyd 2 lat temu
rodzic
commit
597beddba4

+ 22 - 20
bin/darkirc/src/irc/client.rs

@@ -34,7 +34,7 @@ use futures::FutureExt;
 use log::{debug, error, warn};
 use smol::{
     io::{self, AsyncBufReadExt, AsyncWriteExt, BufReader},
-    lock::Mutex,
+    lock::RwLock,
     net::SocketAddr,
     prelude::{AsyncRead, AsyncWrite},
 };
@@ -64,9 +64,9 @@ pub struct Client {
     /// Client socket addr
     pub addr: SocketAddr,
     /// ID of the last sent event
-    pub last_sent: Mutex<blake3::Hash>,
+    pub last_sent: RwLock<blake3::Hash>,
     /// Active (joined) channels for this client
-    pub channels: Mutex<HashSet<String>>,
+    pub channels: RwLock<HashSet<String>>,
     /// Penalty counter, when limit is reached, disconnect client
     pub penalty: AtomicUsize,
     /// Registration marker
@@ -74,13 +74,13 @@ pub struct Client {
     /// Registration pause marker
     pub reg_paused: AtomicBool,
     /// Client username
-    pub username: Mutex<String>,
+    pub username: RwLock<String>,
     /// Client nickname
-    pub nickname: Mutex<String>,
+    pub nickname: RwLock<String>,
     /// Client realname
-    pub realname: Mutex<String>,
+    pub realname: RwLock<String>,
     /// Client caps
-    pub caps: Mutex<HashMap<String, bool>>,
+    pub caps: RwLock<HashMap<String, bool>>,
 }
 
 impl Client {
@@ -96,15 +96,15 @@ impl Client {
             server,
             incoming,
             addr,
-            last_sent: Mutex::new(NULL_ID),
-            channels: Mutex::new(HashSet::new()),
+            last_sent: RwLock::new(NULL_ID),
+            channels: RwLock::new(HashSet::new()),
             penalty: AtomicUsize::new(0),
             registered: AtomicBool::new(false),
             reg_paused: AtomicBool::new(false),
-            username: Mutex::new(String::from("*")),
-            nickname: Mutex::new(String::from("*")),
-            realname: Mutex::new(String::from("*")),
-            caps: Mutex::new(caps),
+            username: RwLock::new(String::from("*")),
+            nickname: RwLock::new(String::from("*")),
+            realname: RwLock::new(String::from("*")),
+            caps: RwLock::new(caps),
         })
     }
 
@@ -147,7 +147,8 @@ impl Client {
                         // handle the rest of the propagation.
                         Ok(Some(event)) => {
                             // Update the last sent event.
-                            *self.last_sent.lock().await = event.id();
+                            let event_id = event.id();
+                            *self.last_sent.write().await = event_id;
 
                             // If it fails for some reason, for now, we just note it
                             // and pass.
@@ -177,7 +178,8 @@ impl Client {
                 // Process message from the network. These should only be PRIVMSG.
                 r = self.incoming.receive().fuse() => {
                     // We will skip this if it's our own message.
-                    if *self.last_sent.lock().await == r.id() {
+                    let event_id = r.id();
+                    if *self.last_sent.read().await == event_id {
                         continue
                     }
 
@@ -195,12 +197,12 @@ impl Client {
 
                     // If we have this channel, or it's a DM to our nickname, forward
                     // it to the client.
-                    let have_channel = self.channels.lock().await.contains(&privmsg.channel);
-                    let msg_for_self = *self.nickname.lock().await == privmsg.channel;
+                    let have_channel = self.channels.read().await.contains(&privmsg.channel);
+                    let msg_for_self = *self.nickname.read().await == privmsg.channel;
 
                     if have_channel || msg_for_self {
                         // Add the nickname to the list of nicks on the channel
-                        (*self.server.channels.lock().await).get_mut(&privmsg.channel)
+                        (*self.server.channels.write().await).get_mut(&privmsg.channel)
                             .unwrap().nicks.insert(privmsg.nick.clone());
 
                         // Format the message
@@ -265,7 +267,7 @@ impl Client {
         // Commands can begin with :garbage, but we will reject clients
         // doing that for now to keep the protocol simple and focused.
         let cmd = tokens.next().ok_or(Error::ParseFailed("Invalid command line"))?;
-        let args = line.replacen(cmd, "", 1);
+        let args = line.replacen(cmd, "", 1).trim().to_string();
         let cmd = cmd.to_uppercase();
 
         debug!("[{}] --> {}{}", self.addr, cmd, args);
@@ -314,7 +316,7 @@ impl Client {
             let (_, msg) = args.split_at(msg_offset);
             let mut privmsg = Privmsg {
                 channel,
-                nick: self.nickname.lock().await.to_string(),
+                nick: self.nickname.read().await.to_string(),
                 msg: msg.to_string(),
             };
 

+ 68 - 70
bin/darkirc/src/irc/command.rs

@@ -50,7 +50,10 @@
 //! Some of the above commands could actually be implemented and could
 //! work in respect to the P2P network.
 
-use std::{collections::HashSet, sync::atomic::Ordering::SeqCst};
+use std::{
+    collections::HashSet,
+    sync::{atomic::Ordering::SeqCst, Arc},
+};
 
 use darkfi::Result;
 use log::{error, info};
@@ -74,7 +77,7 @@ impl Client {
             ))])
         }
 
-        let nick = self.nickname.lock().await.to_string();
+        let nick = self.nickname.read().await.to_string();
 
         let replies = vec![
             ReplyType::Server((
@@ -97,12 +100,12 @@ impl Client {
             self.penalty.fetch_add(1, SeqCst);
             return Ok(vec![ReplyType::Server((
                 ERR_NEEDMOREPARAMS,
-                format!("{} CAP :{}", self.nickname.lock().await, INVALID_SYNTAX),
+                format!("{} CAP :{}", self.nickname.read().await, INVALID_SYNTAX),
             ))])
         };
 
-        let caps_keys: Vec<String> = self.caps.lock().await.keys().cloned().collect();
-        let nick = self.nickname.lock().await.to_string();
+        let caps_keys: Vec<String> = self.caps.read().await.keys().cloned().collect();
+        let nick = self.nickname.read().await.to_string();
 
         match subcommand.to_uppercase().as_str() {
             "LS" => {
@@ -110,7 +113,7 @@ impl Client {
                 let Some(_version) = tokens.next() else {
                     return Ok(vec![ReplyType::Server((
                         ERR_NEEDMOREPARAMS,
-                        format!("{} CAP :{}", self.nickname.lock().await, INVALID_SYNTAX),
+                        format!("{} CAP :{}", self.nickname.read().await, INVALID_SYNTAX),
                     ))])
                 };
                 */
@@ -139,7 +142,7 @@ impl Client {
                 let mut ack_list = vec![];
                 let mut nak_list = vec![];
 
-                let mut available_caps = self.caps.lock().await;
+                let mut available_caps = self.caps.write().await;
                 for cap in cap_reqs {
                     if available_caps.contains_key(cap) {
                         available_caps.insert(cap.to_string(), true);
@@ -173,7 +176,7 @@ impl Client {
             "LIST" => {
                 let enabled_caps: Vec<String> = self
                     .caps
-                    .lock()
+                    .read()
                     .await
                     .clone()
                     .into_iter()
@@ -204,7 +207,7 @@ impl Client {
         self.penalty.fetch_add(1, SeqCst);
         Ok(vec![ReplyType::Server((
             ERR_NEEDMOREPARAMS,
-            format!("{} CAP :{}", self.nickname.lock().await, INVALID_SYNTAX),
+            format!("{} CAP :{}", nick, INVALID_SYNTAX),
         ))])
     }
 
@@ -223,7 +226,7 @@ impl Client {
             ))])
         }
 
-        let nick = self.nickname.lock().await.clone();
+        let nick = self.nickname.read().await.clone();
         let replies = vec![
             ReplyType::Server((
                 RPL_INFO,
@@ -250,19 +253,20 @@ impl Client {
         }
 
         // Client's (already) active channels
-        let mut active_channels = self.channels.lock().await;
+        let mut active_channels = self.channels.write().await;
         // Here we'll hold valid channel names.
         let mut channels = HashSet::new();
 
         // Let's scan through our channels. For now we'll only support
         // channel names starting with a single '#' character.
+        let nick = self.nickname.read().await.to_string();
         let tokens = args.split_ascii_whitespace();
         for channel in tokens {
             if !channel.starts_with('#') {
                 self.penalty.fetch_add(1, SeqCst);
                 return Ok(vec![ReplyType::Server((
                     ERR_NEEDMOREPARAMS,
-                    format!("{} JOIN :{}", self.nickname.lock().await, INVALID_SYNTAX),
+                    format!("{} JOIN :{}", nick, INVALID_SYNTAX),
                 ))])
             }
 
@@ -276,14 +280,13 @@ impl Client {
             self.penalty.fetch_add(1, SeqCst);
             return Ok(vec![ReplyType::Server((
                 ERR_NEEDMOREPARAMS,
-                format!("{} JOIN :{}", self.nickname.lock().await, INVALID_SYNTAX),
+                format!("{} JOIN :{}", nick, INVALID_SYNTAX),
             ))])
         }
 
         // Create new channels for this client and construct replies.
-        let mut server_channels = self.server.channels.lock().await;
+        let mut server_channels = self.server.channels.write().await;
         let mut replies = vec![];
-        let nick = self.nickname.lock().await.to_string();
         for channel in channels {
             // Insert the channel name into the set of client's active channels
             active_channels.insert(channel.clone());
@@ -337,10 +340,10 @@ impl Client {
             ))])
         }
 
-        let nick = self.nickname.lock().await.to_string();
+        let nick = self.nickname.read().await.to_string();
 
         let mut list = vec![];
-        for (name, channel) in self.server.channels.lock().await.iter() {
+        for (name, channel) in self.server.channels.read().await.iter() {
             list.push(format!("{} {} {} :{}", nick, name, channel.nicks.len(), channel.topic));
         }
 
@@ -368,18 +371,18 @@ impl Client {
             ))])
         }
 
+        let nick = self.nickname.read().await.to_string();
+
         let mut tokens = args.split_ascii_whitespace();
 
         let Some(target) = tokens.next() else {
             self.penalty.fetch_add(1, SeqCst);
             return Ok(vec![ReplyType::Server((
                 ERR_NEEDMOREPARAMS,
-                format!("{} MODE :{}", self.nickname.lock().await, INVALID_SYNTAX),
+                format!("{} MODE :{}", nick, INVALID_SYNTAX),
             ))])
         };
 
-        let nick = self.nickname.lock().await.to_string();
-
         if target == nick {
             return Ok(vec![ReplyType::Server((RPL_UMODEIS, format!("{} +", nick)))])
         }
@@ -391,7 +394,7 @@ impl Client {
             ))])
         }
 
-        if !self.server.channels.lock().await.contains_key(target) {
+        if !self.server.channels.read().await.contains_key(target) {
             return Ok(vec![ReplyType::Server((
                 ERR_NOSUCHNICK,
                 format!("{} {} :No such nick or channel name", nick, target),
@@ -433,14 +436,14 @@ impl Client {
             ))])
         }
 
-        let nick = self.nickname.lock().await.to_string();
+        let nick = self.nickname.read().await.to_string();
         let mut tokens = args.split_ascii_whitespace();
         let mut replies = vec![];
 
         // If a channel was requested, reply only with that one.
         // Otherwise, return info for all known channels.
         if let Some(req_chan) = tokens.next() {
-            if let Some(chan) = self.server.channels.lock().await.get(req_chan) {
+            if let Some(chan) = self.server.channels.read().await.get(req_chan) {
                 let nicks: Vec<String> = chan.nicks.iter().cloned().collect();
 
                 replies.push(ReplyType::Server((
@@ -456,7 +459,7 @@ impl Client {
 
             Ok(replies)
         } else {
-            for (name, chan) in self.server.channels.lock().await.iter() {
+            for (name, chan) in self.server.channels.read().await.iter() {
                 let nicks: Vec<String> = chan.nicks.iter().cloned().collect();
 
                 replies.push(ReplyType::Server((
@@ -482,7 +485,7 @@ impl Client {
         let mut tokens = args.split_ascii_whitespace();
 
         // Reference the current nickname
-        let old_nick = self.nickname.lock().await.to_string();
+        let old_nick = self.nickname.read().await.to_string();
 
         let Some(nickname) = tokens.next() else {
             self.penalty.fetch_add(1, SeqCst);
@@ -503,10 +506,10 @@ impl Client {
         }
 
         // Set the new nickname
-        *self.nickname.lock().await = nickname.to_string();
+        *self.nickname.write().await = nickname.to_string();
 
         // If the username is set, we can complete the registration
-        if *self.username.lock().await != "*" && !self.registered.load(SeqCst) {
+        if *self.username.read().await != "*" && !self.registered.load(SeqCst) {
             self.registered.store(true, SeqCst);
             if self.reg_paused.load(SeqCst) {
                 return Ok(vec![])
@@ -517,7 +520,7 @@ impl Client {
 
         // If we were registered, we send a client reply about it.
         if self.registered.load(SeqCst) {
-            Ok(vec![ReplyType::Client((old_nick, format!("NICK :{}", self.nickname.lock().await)))])
+            Ok(vec![ReplyType::Client((old_nick, format!("NICK :{}", nickname)))])
         } else {
             // Otherwise, we don't reply.
             Ok(vec![])
@@ -536,13 +539,14 @@ impl Client {
             ))])
         }
 
+        let nick = self.nickname.read().await.to_string();
         let mut tokens = args.split_ascii_whitespace();
 
         let Some(channel) = tokens.next() else {
             self.penalty.fetch_add(1, SeqCst);
             return Ok(vec![ReplyType::Server((
                 ERR_NEEDMOREPARAMS,
-                format!("{} PART :{}", self.nickname.lock().await, INVALID_SYNTAX),
+                format!("{} PART :{}", nick, INVALID_SYNTAX),
             ))])
         };
 
@@ -550,25 +554,22 @@ impl Client {
             self.penalty.fetch_add(1, SeqCst);
             return Ok(vec![ReplyType::Server((
                 ERR_NEEDMOREPARAMS,
-                format!("{} PART :{}", self.nickname.lock().await, INVALID_SYNTAX),
+                format!("{} PART :{}", nick, INVALID_SYNTAX),
             ))])
         }
 
-        let mut active_channels = self.channels.lock().await;
+        let mut active_channels = self.channels.write().await;
         if !active_channels.contains(channel) {
             return Ok(vec![ReplyType::Server((
                 ERR_NOSUCHCHANNEL,
-                format!("{} {} :No such channel", self.nickname.lock().await, channel),
+                format!("{} {} :No such channel", nick, channel),
             ))])
         }
 
         // Remove the channel from the client's channel list
         active_channels.remove(channel);
 
-        let replies = vec![ReplyType::Client((
-            self.nickname.lock().await.to_string(),
-            format!("PART {} :Bye", channel),
-        ))];
+        let replies = vec![ReplyType::Client((nick, format!("PART {} :Bye", channel)))];
 
         Ok(replies)
     }
@@ -591,7 +592,7 @@ impl Client {
             self.penalty.fetch_add(1, SeqCst);
             return Ok(vec![ReplyType::Server((
                 ERR_NOORIGIN,
-                format!("{} :No origin specified", self.nickname.lock().await),
+                format!("{} :No origin specified", self.nickname.read().await),
             ))])
         };
 
@@ -611,33 +612,34 @@ impl Client {
             ))])
         }
 
+        let nick = self.nickname.read().await.to_string();
         let mut tokens = args.split_ascii_whitespace();
 
         let Some(target) = tokens.next() else {
             return Ok(vec![ReplyType::Server((
                 ERR_NORECIPIENT,
-                format!("{} :No recipient given (PRIVMSG)", self.nickname.lock().await),
+                format!("{} :No recipient given (PRIVMSG)", nick),
             ))])
         };
 
         let Some(message) = tokens.next() else {
             return Ok(vec![ReplyType::Server((
                 ERR_NOTEXTTOSEND,
-                format!("{} :No text to send", self.nickname.lock().await),
+                format!("{} :No text to send", nick),
             ))])
         };
 
         if !message.starts_with(':') {
             return Ok(vec![ReplyType::Server((
                 ERR_NOTEXTTOSEND,
-                format!("{} :No text to send", self.nickname.lock().await),
+                format!("{} :No text to send", nick),
             ))])
         }
 
         // We only send a client reply if the message is for ourself.
         // Anything else is rendered by the IRC client and not supposed
         // to be echoed by the IRC serer.
-        if target == *self.nickname.lock().await {
+        if target == nick {
             return Ok(vec![ReplyType::Client((
                 target.to_string(),
                 format!("PRIVMSG {} {}", target, message),
@@ -646,11 +648,8 @@ impl Client {
 
         // If it's a DM and we don't have an encryption key, we will
         // refuse to send it. Send ERR_NORECIPIENT to the client.
-        if !target.starts_with('#') && !self.server.contacts.lock().await.contains_key(target) {
-            return Ok(vec![ReplyType::Server((
-                ERR_NOSUCHNICK,
-                format!("{} :{}", self.nickname.lock().await, target),
-            ))])
+        if !target.starts_with('#') && !self.server.contacts.read().await.contains_key(target) {
+            return Ok(vec![ReplyType::Server((ERR_NOSUCHNICK, format!("{} :{}", nick, target)))])
         }
 
         Ok(vec![])
@@ -681,48 +680,46 @@ impl Client {
             ))])
         }
 
+        let nick = self.nickname.read().await.to_string();
         let mut tokens = args.split_ascii_whitespace();
 
         let Some(channel) = tokens.next() else {
             self.penalty.fetch_add(1, SeqCst);
             return Ok(vec![ReplyType::Server((
                 ERR_NEEDMOREPARAMS,
-                format!("{} TOPIC :{}", self.nickname.lock().await, INVALID_SYNTAX),
+                format!("{} TOPIC :{}", nick, INVALID_SYNTAX),
             ))])
         };
 
-        if !self.server.channels.lock().await.contains_key(channel) {
+        if !self.server.channels.read().await.contains_key(channel) {
             return Ok(vec![ReplyType::Server((
                 ERR_NOSUCHCHANNEL,
-                format!("{} {} :No such channel", self.nickname.lock().await, channel),
+                format!("{} {} :No such channel", nick, channel),
             ))])
         }
 
         // If there's a topic, we'll set it, otherwise return the set topic.
         let Some(topic) = tokens.next() else {
-            let topic = self.server.channels.lock().await.get(channel).unwrap().topic.clone();
+            let topic = self.server.channels.read().await.get(channel).unwrap().topic.clone();
             if topic.is_empty() {
                 return Ok(vec![ReplyType::Server((
                     RPL_NOTOPIC,
-                    format!("{} {} :No topic is set", self.nickname.lock().await, channel),
+                    format!("{} {} :No topic is set", nick, channel),
                 ))])
             } else {
                 return Ok(vec![ReplyType::Server((
                     RPL_TOPIC,
-                    format!("{} {} :{}", self.nickname.lock().await, channel, topic),
+                    format!("{} {} :{}", nick, channel, topic),
                 ))])
             }
         };
 
         // Set the new topic
-        self.server.channels.lock().await.get_mut(channel).unwrap().topic =
+        self.server.channels.write().await.get_mut(channel).unwrap().topic =
             topic.strip_prefix(':').unwrap().to_string();
 
         // Send reply
-        let replies = vec![ReplyType::Client((
-            self.nickname.lock().await.to_string(),
-            format!("TOPIC {} {}", channel, topic),
-        ))];
+        let replies = vec![ReplyType::Client((nick, format!("TOPIC {} {}", channel, topic)))];
 
         Ok(replies)
     }
@@ -738,18 +735,19 @@ impl Client {
             self.penalty.fetch_add(1, SeqCst);
             return Ok(vec![ReplyType::Server((
                 ERR_ALREADYREGISTERED,
-                format!("{} :{}", self.nickname.lock().await, ALREADY_REGISTERED),
+                format!("{} :{}", self.nickname.read().await, ALREADY_REGISTERED),
             ))])
         }
 
         // Parse the line
+        let nick = self.nickname.read().await.to_string();
         let mut tokens = args.split_ascii_whitespace();
 
         let Some(username) = tokens.next() else {
             self.penalty.fetch_add(1, SeqCst);
             return Ok(vec![ReplyType::Server((
                 ERR_NEEDMOREPARAMS,
-                format!("{} USER :{}", self.nickname.lock().await, INVALID_SYNTAX),
+                format!("{} USER :{}", nick, INVALID_SYNTAX),
             ))])
         };
 
@@ -758,7 +756,7 @@ impl Client {
             self.penalty.fetch_add(1, SeqCst);
             return Ok(vec![ReplyType::Server((
                 ERR_NEEDMOREPARAMS,
-                format!("{} USER :{}", self.nickname.lock().await, INVALID_SYNTAX),
+                format!("{} USER :{}", nick, INVALID_SYNTAX),
             ))])
         };
 
@@ -767,7 +765,7 @@ impl Client {
             self.penalty.fetch_add(1, SeqCst);
             return Ok(vec![ReplyType::Server((
                 ERR_NEEDMOREPARAMS,
-                format!("{} USER :{}", self.nickname.lock().await, INVALID_SYNTAX),
+                format!("{} USER :{}", nick, INVALID_SYNTAX),
             ))])
         };
 
@@ -776,7 +774,7 @@ impl Client {
             self.penalty.fetch_add(1, SeqCst);
             return Ok(vec![ReplyType::Server((
                 ERR_NEEDMOREPARAMS,
-                format!("{} USER :{}", self.nickname.lock().await, INVALID_SYNTAX),
+                format!("{} USER :{}", nick, INVALID_SYNTAX),
             ))])
         };
 
@@ -784,15 +782,15 @@ impl Client {
             self.penalty.fetch_add(1, SeqCst);
             return Ok(vec![ReplyType::Server((
                 ERR_NEEDMOREPARAMS,
-                format!("{} USER :{}", self.nickname.lock().await, INVALID_SYNTAX),
+                format!("{} USER :{}", nick, INVALID_SYNTAX),
             ))])
         }
 
-        *self.username.lock().await = username.to_string();
-        *self.realname.lock().await = realname.to_string();
+        *self.username.write().await = username.to_string();
+        *self.realname.write().await = realname.to_string();
 
         // If the nickname is set, we can complete the registration
-        if *self.nickname.lock().await != "*" {
+        if nick != "*" {
             self.registered.store(true, SeqCst);
             if self.reg_paused.load(SeqCst) {
                 return Ok(vec![])
@@ -821,7 +819,7 @@ impl Client {
             RPL_VERSION,
             format!(
                 "{} {} {} :Let there be dark!",
-                self.nickname.lock().await,
+                self.nickname.read().await,
                 env!("CARGO_PKG_VERSION"),
                 SERVER_NAME
             ),
@@ -832,7 +830,7 @@ impl Client {
 
     /// Internal function that constructs the welcome message.
     async fn welcome(&self) -> Vec<ReplyType> {
-        let nick = self.nickname.lock().await.to_string();
+        let nick = self.nickname.read().await.to_string();
 
         let mut replies = vec![
             ReplyType::Server((RPL_WELCOME, format!("{} :{}", nick, WELCOME))),
@@ -851,8 +849,8 @@ impl Client {
 
         // If we have any configured autojoin channels, let's join the user
         // and set their topics, if any.
-        let mut config_chans = self.server.channels.lock().await;
-        for channel in self.server.autojoin.lock().await.iter() {
+        let mut config_chans = self.server.channels.write().await;
+        for channel in self.server.autojoin.read().await.iter() {
             replies.push(ReplyType::Client((nick.clone(), format!("JOIN :{}", channel))));
             replies.push(ReplyType::Server((
                 RPL_NAMREPLY,

+ 15 - 15
bin/darkirc/src/irc/server.rs

@@ -28,7 +28,7 @@ use darkfi::{
 use log::{debug, error, info};
 use smol::{
     fs,
-    lock::Mutex,
+    lock::{Mutex, RwLock},
     net::{SocketAddr, TcpListener},
     prelude::{AsyncRead, AsyncWrite},
     Executor,
@@ -53,11 +53,11 @@ pub struct IrcServer {
     /// TLS acceptor
     acceptor: Option<TlsAcceptor>,
     /// Configured autojoin channels
-    pub autojoin: Mutex<Vec<String>>,
+    pub autojoin: RwLock<Vec<String>>,
     /// Configured IRC channels
-    pub channels: Mutex<HashMap<String, IrcChannel>>,
+    pub channels: RwLock<HashMap<String, IrcChannel>>,
     /// Configured IRC contacts
-    pub contacts: Mutex<HashMap<String, IrcContact>>,
+    pub contacts: RwLock<HashMap<String, IrcContact>>,
     /// Active client connections
     clients: Mutex<HashMap<u16, StoppableTaskPtr>>,
 }
@@ -118,9 +118,9 @@ impl IrcServer {
             config_path,
             listener,
             acceptor,
-            autojoin: Mutex::new(Vec::new()),
-            channels: Mutex::new(HashMap::new()),
-            contacts: Mutex::new(HashMap::new()),
+            autojoin: RwLock::new(Vec::new()),
+            channels: RwLock::new(HashMap::new()),
+            contacts: RwLock::new(HashMap::new()),
             clients: Mutex::new(HashMap::new()),
         });
 
@@ -152,9 +152,9 @@ impl IrcServer {
 
         // FIXME: This will remove clients' joined channels. They need to stay.
         // Only if everything is fine, replace.
-        *self.autojoin.lock().await = autojoin;
-        *self.channels.lock().await = channels;
-        *self.contacts.lock().await = contacts;
+        *self.autojoin.write().await = autojoin;
+        *self.channels.write().await = channels;
+        *self.contacts.write().await = contacts;
 
         Ok(())
     }
@@ -253,7 +253,7 @@ impl IrcServer {
 
     /// Try encrypting a given `Privmsg` if there is such a channel/contact.
     pub async fn try_encrypt(&self, privmsg: &mut Privmsg) {
-        if let Some((name, channel)) = self.channels.lock().await.get_key_value(&privmsg.channel) {
+        if let Some((name, channel)) = self.channels.read().await.get_key_value(&privmsg.channel) {
             if let Some(saltbox) = &channel.saltbox {
                 privmsg.channel = saltbox::encrypt(saltbox, privmsg.channel.as_bytes());
                 privmsg.nick = saltbox::encrypt(saltbox, privmsg.nick.as_bytes());
@@ -263,7 +263,7 @@ impl IrcServer {
             }
         };
 
-        if let Some((name, contact)) = self.contacts.lock().await.get_key_value(&privmsg.channel) {
+        if let Some((name, contact)) = self.contacts.read().await.get_key_value(&privmsg.channel) {
             if let Some(saltbox) = &contact.saltbox {
                 privmsg.channel = saltbox::encrypt(saltbox, privmsg.channel.as_bytes());
                 privmsg.nick = saltbox::encrypt(saltbox, privmsg.nick.as_bytes());
@@ -292,9 +292,9 @@ impl IrcServer {
         };
 
         // Now go through all 3 ciphertexts. We'll use intermediate buffers
-        // for decryption, and only if all passes, we will return a modified
+        // for decryption, iff all passes, we will return a modified
         // (i.e. decrypted) privmsg, otherwise we return the original.
-        for (name, channel) in self.channels.lock().await.iter() {
+        for (name, channel) in self.channels.read().await.iter() {
             if let Some(saltbox) = &channel.saltbox {
                 let Some(channel_dec) = saltbox::try_decrypt(saltbox, &channel_ciphertext) else {
                     continue
@@ -316,7 +316,7 @@ impl IrcServer {
             }
         }
 
-        for (name, contact) in self.contacts.lock().await.iter() {
+        for (name, contact) in self.contacts.read().await.iter() {
             if let Some(saltbox) = &contact.saltbox {
                 let Some(channel_dec) = saltbox::try_decrypt(saltbox, &channel_ciphertext) else {
                     continue