summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--gdb.txt86
-rw-r--r--src/IOManager.cpp7
-rw-r--r--src/IOManager.hpp4
-rw-r--r--src/chatManager.cpp2
-rw-r--r--src/chatManager.hpp4
-rw-r--r--src/commandHandler.cpp8
-rw-r--r--src/commandHandler.hpp3
-rw-r--r--src/renderManager.cpp1
-rw-r--r--src/renderManager.hpp2
-rw-r--r--src/server.cpp33
-rw-r--r--src/server.hpp1
-rw-r--r--src/session.cpp50
-rw-r--r--src/session.hpp14
-rw-r--r--src/sessionManager.cpp58
-rw-r--r--src/sessionManager.hpp3
16 files changed, 233 insertions, 45 deletions
diff --git a/Makefile b/Makefile
index 9ff8a96..061e463 100644
--- a/Makefile
+++ b/Makefile
@@ -53,7 +53,7 @@ install: $(all)
clean:
@echo "Cleaning up"
- -@rm $(OBJS)
+ -@rm -rf $(OBJS)
clean-all: clean
@echo "Cleaning up all"
diff --git a/gdb.txt b/gdb.txt
new file mode 100644
index 0000000..051880e
--- /dev/null
+++ b/gdb.txt
@@ -0,0 +1,86 @@
+Starting program: /mnt/DATA/personal/Code/projects/chat-server/build/chat-server 8080
+
+This GDB supports auto-downloading debuginfo from the following URLs:
+ <https://debuginfod.archlinux.org>
+Debuginfod has been enabled.
+To make this setting permanent, add 'set debuginfod enabled on' to .gdbinit.
+Function(s) ^std::(move|forward|as_const|(__)?addressof) will be skipped when stepping.
+Function(s) ^std::(shared|unique)_ptr<.*>::(get|operator) will be skipped when stepping.
+Function(s) ^std::(basic_string|vector|array|deque|(forward_)?list|(unordered_|flat_)?(multi)?(map|set)|span)<.*>::(c?r?(begin|end)|front|back|data|size|empty) will be skipped when stepping.
+Function(s) ^std::(basic_string|vector|array|deque|span)<.*>::operator.] will be skipped when stepping.
+[Thread debugging using libthread_db enabled]
+Using host libthread_db library "/usr/lib/libthread_db.so.1".
+[New Thread 0x7ffff37ff6c0 (LWP 193117)]
+[New Thread 0x7ffff2ffe6c0 (LWP 193118)]
+[New Thread 0x7ffff27fd6c0 (LWP 193119)]
+[New Thread 0x7ffff1ffc6c0 (LWP 193120)]
+[New Thread 0x7ffff13ff6c0 (LWP 193192)]
+[New Thread 0x7fffec7ff6c0 (LWP 193218)]
+[Thread 0x7ffff13ff6c0 (LWP 193192) exited]
+
+Thread 7 "chat-server" received signal SIGPIPE, Broken pipe.
+[Switching to Thread 0x7fffec7ff6c0 (LWP 193218)]
+__syscall_cancel_arch () at ../sysdeps/unix/sysv/linux/x86_64/syscall_cancel.S:56
+56 ret
+#0 __syscall_cancel_arch () at ../sysdeps/unix/sysv/linux/x86_64/syscall_cancel.S:56
+No locals.
+#1 0x00007ffff68a1fda in __internal_syscall_cancel (a1=<optimized out>, a2=<optimized out>, a3=<optimized out>, a4=<optimized out>, a5=a5@entry=0, a6=a6@entry=0, nr=44) at cancellation.c:49
+ result = <optimized out>
+ pd = <optimized out>
+ ch = <optimized out>
+#2 0x00007ffff68a2024 in __syscall_cancel (a1=<optimized out>, a2=<optimized out>, a3=<optimized out>, a4=<optimized out>, a5=a5@entry=0, a6=a6@entry=0, nr=44) at cancellation.c:75
+ r = <optimized out>
+#3 0x00007ffff692acaf in __libc_send (fd=<optimized out>, buf=<optimized out>, len=<optimized out>, flags=<optimized out>) at ../sysdeps/unix/sysv/linux/send.c:28
+No locals.
+#4 0x00007ffff6e80ce2 in ___interceptor_send (fd=4, buf=0x7208000050a0, len=23, flags=<optimized out>) at /usr/src/debug/gcc/gcc/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:6826
+ ctx = <synthetic pointer>
+ thr = <optimized out>
+ si = {thr_ = 0x7fffec7fef40, in_ignored_lib_ = false, in_blocking_func_ = false, ignoring_ = false}
+ pc = 140737335790634
+ _ctx = {thr = <optimized out>, pc = 140737335790634}
+ res = <optimized out>
+#5 0x00005555555b407c in CSession::write<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&> (this=this@entry=0x72480000c010, type=type@entry=NONE, fmt=...) at src/session.cpp:253
+ lock = {_M_device = @0x72480000c0a8}
+ msg = "dfsg has left the chat\n"
+#6 0x00005555555b255f in CSession::write<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&> (this=0x72480000c010, fmt=...) at src/session.cpp:233
+No locals.
+#7 CSession::write (this=0x72480000c010, msg="dfsg has left the chat") at src/session.cpp:265
+No locals.
+#8 0x00005555555b4649 in CSessionManager::broadcast (this=this@entry=0x7214000047a0, msg="dfsg has left the chat", self=std::optional = {...}) at src/sessionManager.cpp:66
+ thread = @0x721000002000: {_M_stop_source = {_M_state = {_M_ptr = 0x720800001000}}, _M_thread = {_M_id = {_M_thread = 140737240889024}}}
+ __for_range = std::vector of length 2, capacity 2 = {{first = {_M_stop_source = {_M_state = {_M_ptr = 0x720800001000}}, _M_thread = {_M_id = {_M_thread = 140737240889024}}}, second = std::shared_ptr<CSession> (use count 1, weak count 0) = {get() = 0x72480000c010}}, {first = {_M_stop_source = {_M_state = {_M_ptr = 0x7208000010c0}}, _M_thread = {_M_id = {_M_thread = 140737161197248}}}, second = std::shared_ptr<CSession> (use count 1, weak count 0) = {get() = 0x72480000c190}}}
+ __for_begin = <optimized out>
+ __for_end = <optimized out>
+ session = std::shared_ptr<CSession> (use count 1, weak count 0) = {get() = 0x72480000c010}
+ lock = {_M_device = 0x7214000047b8, _M_owns = false}
+#9 0x00005555555b4d23 in CSessionManager::kick (this=this@entry=0x7214000047a0, session=<optimized out>, kill=kill@entry=false, reason="") at /usr/include/c++/15.1.1/bits/allocator.h:189
+ lock = {_M_device = 0x7214000047b8, _M_owns = false}
+ it = {first = {_M_stop_source = {_M_state = {_M_ptr = 0x7208000010c0}}, _M_thread = {_M_id = {_M_thread = 140737161197248}}}, second = std::shared_ptr<CSession> (use count 1, weak count 0) = {get() = 0x72480000c190}}
+#10 0x00005555555b2ed3 in CSession::run (this=0x72480000c190) at /usr/include/c++/15.1.1/bits/basic_string.tcc:248
+No locals.
+#11 0x00005555555b748b in std::__invoke_impl<void, void (CSession::*)(), CSession*> (__f=<optimized out>, __t=<optimized out>) at /usr/include/c++/15.1.1/bits/invoke.h:76
+No locals.
+#12 std::__invoke<void (CSession::*)(), CSession*> (__fn=<optimized out>) at /usr/include/c++/15.1.1/bits/invoke.h:98
+No locals.
+#13 std::thread::_Invoker<std::tuple<void (CSession::*)(), CSession*> >::_M_invoke<0ul, 1ul> (this=<optimized out>) at /usr/include/c++/15.1.1/bits/std_thread.h:303
+No locals.
+#14 std::thread::_Invoker<std::tuple<void (CSession::*)(), CSession*> >::operator() (this=<optimized out>) at /usr/include/c++/15.1.1/bits/std_thread.h:310
+No locals.
+#15 std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (CSession::*)(), CSession*> > >::_M_run (this=<optimized out>) at /usr/include/c++/15.1.1/bits/std_thread.h:255
+No locals.
+#16 0x00007ffff6ae51a4 in std::execute_native_thread_routine (__p=0x7208000010e0) at /usr/src/debug/gcc/gcc/libstdc++-v3/src/c++11/thread.cc:104
+ __t = <optimized out>
+#17 0x00007ffff6e5433a in __tsan_thread_start_func (arg=0x7ffff2ffd470) at /usr/src/debug/gcc/gcc/libsanitizer/tsan/tsan_interceptors_posix.cpp:1031
+ p = 0x7ffff2ffd470
+ callback = 0x7ffff6ae5190 <std::execute_native_thread_routine(void*)>
+ param = 0x7208000010e0
+ res = <optimized out>
+ foo = 543449445
+#18 0x00007ffff68a57eb in start_thread (arg=<optimized out>) at pthread_create.c:448
+ ret = <optimized out>
+ pd = <optimized out>
+ out = <optimized out>
+ unwind_buf = {cancel_jmp_buf = {{jmp_buf = {140737161197248, -1622313507526851745, 140737161197248, 140737270239808, 0, 140737270240071, -1622313507501685921, -1622325426672942241}, mask_was_saved = 0}}, priv = {pad = {0x0, 0x0, 0x0, 0x0}, data = {prev = 0x0, cleanup = 0x0, canceltype = 0}}}
+ not_first_call = <optimized out>
+#19 0x00007ffff692918c in __GI___clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:78
+No locals.
diff --git a/src/IOManager.cpp b/src/IOManager.cpp
index eb648e5..75e130e 100644
--- a/src/IOManager.cpp
+++ b/src/IOManager.cpp
@@ -1,17 +1,22 @@
#include "IOManager.hpp"
#include "inputManager.hpp"
#include "log.hpp"
+#include <mutex>
CIOManager::CIOManager() {
log(LOG, "IOManager: initialized");
}
CIOManager::~CIOManager() {
- m_vIO.clear();
+ {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ m_vIO.clear();
+ }
log(SYS, "IOManager: bye");
}
const std::vector<CIOManager::SIO> &CIOManager::getIO() const {
+ std::lock_guard<std::mutex> lock(m_mutex);
return m_vIO;
}
diff --git a/src/IOManager.hpp b/src/IOManager.hpp
index 670e597..d2e4ca9 100644
--- a/src/IOManager.hpp
+++ b/src/IOManager.hpp
@@ -2,7 +2,9 @@
#include "chatManager.hpp"
#include "format.hpp"
+#include <mutex>
#include <optional>
+#include <vector>
class CIOManager {
public:
@@ -28,6 +30,8 @@ class CIOManager {
private:
std::vector<SIO> m_vIO;
+ mutable std::mutex m_mutex;
+
friend class CChatManager;
};
inline std::unique_ptr<CIOManager> g_pIOManager;
diff --git a/src/chatManager.cpp b/src/chatManager.cpp
index 6e8424b..ed2a2a4 100644
--- a/src/chatManager.cpp
+++ b/src/chatManager.cpp
@@ -27,6 +27,8 @@ std::string CChatManager::fmtBroadcastMessage(const SMessage &msg) {
}
std::vector<CChatManager::SMessage> CChatManager::getChat() const {
+ std::lock_guard<std::mutex> lock(m_mutex);
+
std::vector<SMessage> chat;
for (const auto &[msg, _, _] : g_pIOManager->getIO())
diff --git a/src/chatManager.hpp b/src/chatManager.hpp
index b48dcdb..e77027e 100644
--- a/src/chatManager.hpp
+++ b/src/chatManager.hpp
@@ -1,6 +1,7 @@
#pragma once
#include <memory>
+#include <mutex>
#include <optional>
#include <string>
#include <vector>
@@ -19,5 +20,8 @@ class CChatManager {
std::string fmtBroadcastMessage(const SMessage &msg);
std::vector<SMessage> getChat() const;
+
+ private:
+ mutable std::mutex m_mutex;
};
inline std::unique_ptr<CChatManager> g_pChatManager;
diff --git a/src/commandHandler.cpp b/src/commandHandler.cpp
index 731ea3f..dd6a88d 100644
--- a/src/commandHandler.cpp
+++ b/src/commandHandler.cpp
@@ -1,7 +1,6 @@
#include "commandHandler.hpp"
#include "log.hpp"
#include "session.hpp"
-#include "sessionManager.hpp"
#include <algorithm>
CCommandHandler::CCommandHandler() {
@@ -10,6 +9,8 @@ CCommandHandler::CCommandHandler() {
CCommandHandler::~CCommandHandler() {
log(SYS, "CommandHandler: bye");
+
+ std::lock_guard<std::mutex> lock(m_mutex);
m_vCommands.clear();
}
@@ -27,7 +28,10 @@ bool CCommandHandler::registerCommand(const SCommand &command) {
if (command.name.empty() || !command.exe || !command.parser)
return false;
- m_vCommands.push_back(command);
+ {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ m_vCommands.push_back(command);
+ }
log(TRACE, "CommandHandler: registered command: {}", command.name);
diff --git a/src/commandHandler.hpp b/src/commandHandler.hpp
index bce93d2..2c8fd30 100644
--- a/src/commandHandler.hpp
+++ b/src/commandHandler.hpp
@@ -4,6 +4,7 @@
#include <any>
#include <functional>
#include <memory>
+#include <mutex>
#include <netinet/in.h>
#include <string>
#include <vector>
@@ -67,5 +68,7 @@ class CCommandHandler {
SResult exeCommand(const SCommand &command, const std::string &args) const;
std::vector<SCommand> m_vCommands;
+
+ mutable std::mutex m_mutex;
};
inline std::unique_ptr<CCommandHandler> g_pCommandHandler;
diff --git a/src/renderManager.cpp b/src/renderManager.cpp
index 18b0664..22265fc 100644
--- a/src/renderManager.cpp
+++ b/src/renderManager.cpp
@@ -34,5 +34,6 @@ void CRenderManager::enterLoop() {
}
void CRenderManager::setRenderer(ftxui::Component component) {
+ std::lock_guard<std::mutex> lock(m_mutex);
renderer = std::move(component);
}
diff --git a/src/renderManager.hpp b/src/renderManager.hpp
index 7a14cc0..874e1f5 100644
--- a/src/renderManager.hpp
+++ b/src/renderManager.hpp
@@ -18,6 +18,8 @@ class CRenderManager {
ftxui::ScreenInteractive screen;
ftxui::Component renderer;
+ std::mutex m_mutex;
+
friend class CInputManager;
};
inline std::unique_ptr<CRenderManager> g_pRenderManager;
diff --git a/src/server.cpp b/src/server.cpp
index db92481..cd0c698 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -2,6 +2,7 @@
#include "log.hpp"
#include <cstring>
#include <memory>
+#include <mutex>
#include <sys/socket.h>
CServer::CServer(uint16_t port)
@@ -11,20 +12,24 @@ CServer::CServer(uint16_t port)
constexpr int opt = 1;
constexpr int size = sizeof(opt);
- if (setsockopt(m_sockfd->get(), SOL_SOCKET, SO_REUSEADDR, &opt, size) < 0)
- log(stderr, WARN, "setsockopt(SO_REUSEADDR) failed");
-
- if (setsockopt(m_sockfd->get(), SOL_SOCKET, SO_KEEPALIVE, &opt, size) < 0)
- log(stderr, WARN, "setsockopt(SO_KEEPALIVE) failed");
-
- memset(&m_addr, 0, sizeof(m_addr));
- m_addr.sin_family = AF_INET;
- m_addr.sin_port = htons(port);
- m_addr.sin_addr.s_addr = INADDR_ANY;
- bind(m_sockfd->get(), reinterpret_cast<sockaddr *>(&m_addr), sizeof(m_addr)) ||
- listen(m_sockfd->get(), 5)
- ? throw std::runtime_error("Failed to bind or listen")
- : log(LOG, "Server started on port: {}", port);
+ {
+ std::lock_guard<std::mutex> lock(m_mutex);
+
+ if (setsockopt(m_sockfd->get(), SOL_SOCKET, SO_REUSEADDR, &opt, size) < 0)
+ log(stderr, WARN, "setsockopt(SO_REUSEADDR) failed");
+
+ if (setsockopt(m_sockfd->get(), SOL_SOCKET, SO_KEEPALIVE, &opt, size) < 0)
+ log(stderr, WARN, "setsockopt(SO_KEEPALIVE) failed");
+
+ memset(&m_addr, 0, sizeof(m_addr));
+ m_addr.sin_family = AF_INET;
+ m_addr.sin_port = htons(port);
+ m_addr.sin_addr.s_addr = INADDR_ANY;
+ bind(m_sockfd->get(), reinterpret_cast<sockaddr *>(&m_addr), sizeof(m_addr)) ||
+ listen(m_sockfd->get(), 5)
+ ? throw std::runtime_error("Failed to bind or listen")
+ : log(LOG, "Server started on port: {}", port);
+ }
log(LOG, "Server: initialized");
};
diff --git a/src/server.hpp b/src/server.hpp
index ce375de..9ddb1ac 100644
--- a/src/server.hpp
+++ b/src/server.hpp
@@ -2,6 +2,7 @@
#include "FileDescriptor.hpp"
#include <memory>
+#include <mutex>
#include <netinet/in.h>
class CServer {
diff --git a/src/session.cpp b/src/session.cpp
index 116c9bc..afcb79b 100644
--- a/src/session.cpp
+++ b/src/session.cpp
@@ -10,6 +10,7 @@
#include <cerrno>
#include <cstring>
#include <memory>
+#include <mutex>
#include <sys/socket.h>
CSession::CSession() {
@@ -18,11 +19,14 @@ CSession::CSession() {
if (!m_sockfd->isValid())
throw std::runtime_error("session: Failed to create socket");
- m_ip.resize(INET_ADDRSTRLEN);
- inet_ntop(AF_INET, &m_addr.sin_addr, &m_ip[0], INET_ADDRSTRLEN);
- m_ip.resize(strlen(m_ip.c_str()));
- m_port = ntohs(m_addr.sin_port);
- m_isAdmin = std::ranges::any_of(m_adminIps, [this](const char *ip) { return m_ip == ip; });
+ {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ m_ip.resize(INET_ADDRSTRLEN);
+ inet_ntop(AF_INET, &m_addr.sin_addr, &m_ip[0], INET_ADDRSTRLEN);
+ m_ip.resize(strlen(m_ip.c_str()));
+ m_port = ntohs(m_addr.sin_port);
+ m_isAdmin = std::ranges::any_of(m_adminIps, [this](const char *ip) { return m_ip == ip; });
+ }
log(LOG, "Client {} connected on port {}", m_ip, m_port);
@@ -115,7 +119,12 @@ std::unique_ptr<CSession::SRecvData> CSession::read() {
auto recvData = std::make_unique<SRecvData>();
recvData->data.resize(recvData->size);
- ssize_t size = recv(m_sockfd->get(), &recvData->data[0], recvData->size, 0);
+
+ size_t size;
+ {
+ std::lock_guard<std::mutex> lock(m_readMutex);
+ size = recv(m_sockfd->get(), &recvData->data[0], recvData->size, 0);
+ }
if (size < 0) {
recvData->good = false;
@@ -130,7 +139,10 @@ std::unique_ptr<CSession::SRecvData> CSession::read() {
#ifdef DEBUG
onRecv(*recvData);
#endif
- m_szReading.reset();
+ {
+ std::lock_guard<std::mutex> lock(m_readingMutex);
+ m_szReading.reset();
+ }
return recvData;
}
@@ -210,7 +222,10 @@ bool CSession::isValid() {
int err = 0;
socklen_t size = sizeof(err);
- return getsockopt(m_sockfd->get(), SOL_SOCKET, SO_ERROR, &err, &size) >= 0 && err == 0;
+ {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ return getsockopt(m_sockfd->get(), SOL_SOCKET, SO_ERROR, &err, &size) >= 0 && err == 0;
+ }
}
template <typename... Args>
@@ -226,14 +241,21 @@ bool CSession::write(eFormatType type, std::format_string<Args...> fmt, Args &&.
return false;
std::string msg = NFormatter::fmt(type, fmt, std::forward<Args>(args)...);
- if (m_szReading) {
- msg.insert(0, "\r");
- msg.append(*m_szReading);
+ {
+ std::lock_guard<std::mutex> lock(m_readingMutex);
+
+ if (m_szReading) {
+ msg.insert(0, "\r");
+ msg.append(*m_szReading);
+ }
}
- if (send(m_sockfd->get(), msg.c_str(), msg.size(), 0) < 0) {
- onErrno(WRITE);
- return false;
+ {
+ std::lock_guard<std::mutex> lock(m_writeMutex);
+ if (send(m_sockfd->get(), msg.c_str(), msg.size(), MSG_NOSIGNAL) < 0) {
+ onErrno(WRITE);
+ return false;
+ }
}
#ifdef DEBUG
onSend(msg);
diff --git a/src/session.hpp b/src/session.hpp
index af5d9ee..9adcfad 100644
--- a/src/session.hpp
+++ b/src/session.hpp
@@ -3,7 +3,9 @@
#include "FileDescriptor.hpp"
#include "chatManager.hpp"
#include "format.hpp"
+#include <atomic>
#include <memory>
+#include <mutex>
#include <netinet/in.h>
#include <optional>
#include <string>
@@ -56,18 +58,19 @@ class CSession {
WRITE
};
+ // no need atomic since they don't change after first set
std::pair<std::jthread, std::shared_ptr<CSession>> *self;
std::shared_ptr<CFileDescriptor> m_sockfd;
sockaddr_in m_addr;
socklen_t m_addrLen = sizeof(m_addr);
- std::string m_name;
+ std::string m_name; // need thread safety if we impl rename later
std::string m_ip;
int m_port;
bool m_isAdmin = false;
std::optional<std::string> m_szReading = std::nullopt;
- bool m_bMuted = false;
- bool m_bDeaf = true; // init as true as we don't want anything during registerSession
+ std::atomic<bool> m_bMuted = false;
+ std::atomic<bool> m_bDeaf = true; // init as true as we don't want anything during registerSession
void recvLoop();
bool registerSession();
@@ -82,5 +85,10 @@ class CSession {
void onSend(const std::string &msg);
#endif
+ mutable std::mutex m_writeMutex;
+ mutable std::mutex m_readMutex;
+ mutable std::mutex m_readingMutex;
+ mutable std::mutex m_mutex;
+
friend class CSessionManager;
};
diff --git a/src/sessionManager.cpp b/src/sessionManager.cpp
index aae56f1..03ad9b0 100644
--- a/src/sessionManager.cpp
+++ b/src/sessionManager.cpp
@@ -5,16 +5,21 @@
#include "log.hpp"
#include "session.hpp"
#include <algorithm>
+#include <mutex>
#include <ranges>
#include <unistd.h>
#include <utility>
void CSessionManager::shutdownSessions() {
+ std::unique_lock<std::mutex> lock(m_mutex);
if (m_vSessions.empty())
return;
- for (auto &session : m_vSessions)
+ for (auto &session : m_vSessions) {
+ lock.unlock();
kick(&session, false, "Server shutting down");
+ lock.lock();
+ }
}
CSessionManager::CSessionManager() {
@@ -28,6 +33,8 @@ CSessionManager::CSessionManager() {
};
CSessionManager::~CSessionManager() {
+ std::lock_guard<std::mutex> lock(m_mutex);
+
for (auto &[thread, session] : m_vSessions) {
if (thread.joinable())
thread.detach();
@@ -38,8 +45,10 @@ CSessionManager::~CSessionManager() {
}
std::pair<std::jthread, std::shared_ptr<CSession>> *CSessionManager::newSession() {
- std::shared_ptr session = std::make_shared<CSession>();
- const auto instance = &m_vSessions.emplace_back(std::jthread(&CSession::run, session.get()), session);
+ std::shared_ptr<CSession> session = std::make_shared<CSession>();
+ std::unique_lock<std::mutex> lock(m_mutex);
+ const auto instance = &m_vSessions.emplace_back(std::jthread(&CSession::run, session.get()), session);
+ lock.unlock();
instance->second->setSelf(instance);
return instance;
}
@@ -50,26 +59,35 @@ void CSessionManager::run() {
}
void CSessionManager::broadcast(const std::string &msg, std::optional<const CSession *const> self) const {
- for (const auto &[thread, session] : m_vSessions)
+ std::unique_lock<std::mutex> lock(m_mutex);
+ for (const auto &[thread, session] : m_vSessions) {
+ lock.unlock();
if (!self || (self && session.get() != *self))
session->write(msg);
+ lock.lock();
+ }
g_pIOManager->addCustom({"", msg});
}
void CSessionManager::broadcast(const CChatManager::SMessage &msg) const {
+ std::unique_lock<std::mutex> lock(m_mutex);
for (const auto &[thread, session] : m_vSessions) {
+ lock.unlock();
if (!session)
continue;
session->writeChat(msg);
+ lock.lock();
}
g_pIOManager->addMessage(msg);
}
bool CSessionManager::nameExists(const std::string &name) {
- auto it = std::ranges::find_if(m_vSessions, [&name](const auto &s) { return s.second->getName() == name; });
+ std::unique_lock<std::mutex> lock(m_mutex);
+ auto it = std::ranges::find_if(m_vSessions, [&name](const auto &s) { return s.second->getName() == name; });
+ lock.unlock();
if (it != m_vSessions.end()) {
if (!it->second->isValid()) {
@@ -82,32 +100,47 @@ bool CSessionManager::nameExists(const std::string &name) {
}
CSession *CSessionManager::getByName(const std::string &name) const {
- auto it = std::ranges::find_if(m_vSessions, [&name](const auto &s) { return s.second->getName() == name; });
+ std::lock_guard<std::mutex> lock(m_mutex);
+
+ const auto it = std::ranges::find_if(m_vSessions, [&name](const auto &s) { return s.second->getName() == name; });
return it != m_vSessions.end() ? it->second.get() : nullptr;
}
CSession *CSessionManager::getByIp(const std::string &ip) const {
- auto it = std::ranges::find_if(m_vSessions, [&ip](const auto &s) { log(LOG, s.second->m_ip); return s.second->m_ip == ip; });
+ std::lock_guard<std::mutex> lock(m_mutex);
+
+ const auto it = std::ranges::find_if(m_vSessions, [&ip](const auto &s) { log(LOG, s.second->m_ip); return s.second->m_ip == ip; });
return it != m_vSessions.end() ? it->second.get() : nullptr;
}
CSession *CSessionManager::getByPtr(const uintptr_t &p) const {
- auto it = std::ranges::find_if(m_vSessions, [&p](const auto &s) { return (uintptr_t)s.second.get() == p; });
+ std::lock_guard<std::mutex> lock(m_mutex);
+
+ const auto it = std::ranges::find_if(m_vSessions, [&p](const auto &s) { return (uintptr_t)s.second.get() == p; });
return it != m_vSessions.end() ? it->second.get() : nullptr;
}
std::vector<std::shared_ptr<CSession>> CSessionManager::getSessions() const {
+ std::lock_guard<std::mutex> lock(m_mutex);
+
return m_vSessions | std::views::transform([](const auto &s) { return s.second; }) | std::ranges::to<std::vector>();
}
void CSessionManager::kick(CSession *session, const bool kill, const std::string &reason) {
- for (auto &_session : m_vSessions)
+ std::unique_lock<std::mutex> lock(m_mutex);
+
+ for (auto &_session : m_vSessions) {
+ lock.unlock();
if (_session.second.get() == session)
kick(&_session, kill, reason);
+ lock.lock();
+ }
}
void CSessionManager::kick(std::pair<std::jthread, std::shared_ptr<CSession>> *session, const bool kill, const std::string &reason) {
- auto it = std::ranges::find_if(m_vSessions, [session](const auto &s) { return s.second.get() == session->second.get(); });
+ std::unique_lock<std::mutex> lock(m_mutex);
+ auto it = std::ranges::find_if(m_vSessions, [session](const auto &s) { return s.second.get() == session->second.get(); });
+ lock.unlock();
if (session->second)
// this only exist when the session is registered
@@ -115,8 +148,11 @@ void CSessionManager::kick(std::pair<std::jthread, std::shared_ptr<CSession>> *s
// but we don't need to notify people if the session didn't even "join"/register anyways
g_pSessionManager->broadcast(NFormatter::fmt(NONEWLINE, "{} has left the chat", session->second->m_name), session->second.get());
+ lock.lock();
if (it != m_vSessions.end()) {
+ lock.unlock();
if (!reason.empty()) {
+ lock.unlock();
it->second->setDeaf(false);
it->second->write(reason);
}
@@ -124,7 +160,9 @@ void CSessionManager::kick(std::pair<std::jthread, std::shared_ptr<CSession>> *s
if (it->first.joinable())
it->first.detach(); // .detach the thread since it's removing itself
it->second.reset();
+ lock.lock();
m_vSessions.erase(it);
+ lock.unlock();
if (kill && native_handle != 0)
// cancel(terminal/kill) the thread if it doesn't exit on its own
// pthread_cancel cuz it's the safest https://stackoverflow.com/a/3438576
diff --git a/src/sessionManager.hpp b/src/sessionManager.hpp
index 3aa4b19..d9444b2 100644
--- a/src/sessionManager.hpp
+++ b/src/sessionManager.hpp
@@ -4,6 +4,7 @@
#include "format.hpp"
#include "session.hpp"
#include <memory>
+#include <mutex>
#include <thread>
#include <vector>
@@ -33,6 +34,8 @@ class CSessionManager {
std::vector<std::pair<std::jthread, std::shared_ptr<CSession>>> m_vSessions;
+ mutable std::mutex m_mutex;
+
friend class CChatManager;
};
inline std::shared_ptr<CSessionManager> g_pSessionManager;