diff options
| author | littleblack111 <littleblack11111@gmail.com> | 2025-05-26 14:07:35 +0800 |
|---|---|---|
| committer | littleblack111 <littleblack11111@gmail.com> | 2025-06-07 17:30:26 +0800 |
| commit | 31b2280f295a9afb86fc53ee534bbf48ecca8903 (patch) | |
| tree | 0c23459ab32fa3b305eb82c86a949ec07e9b300f | |
| parent | e30f6eb6872d57f7da764690589c2f0c9ffdb53b (diff) | |
v1, sigabrt on client disconnect
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | gdb.txt | 86 | ||||
| -rw-r--r-- | src/IOManager.cpp | 7 | ||||
| -rw-r--r-- | src/IOManager.hpp | 4 | ||||
| -rw-r--r-- | src/chatManager.cpp | 2 | ||||
| -rw-r--r-- | src/chatManager.hpp | 4 | ||||
| -rw-r--r-- | src/commandHandler.cpp | 8 | ||||
| -rw-r--r-- | src/commandHandler.hpp | 3 | ||||
| -rw-r--r-- | src/renderManager.cpp | 1 | ||||
| -rw-r--r-- | src/renderManager.hpp | 2 | ||||
| -rw-r--r-- | src/server.cpp | 33 | ||||
| -rw-r--r-- | src/server.hpp | 1 | ||||
| -rw-r--r-- | src/session.cpp | 50 | ||||
| -rw-r--r-- | src/session.hpp | 14 | ||||
| -rw-r--r-- | src/sessionManager.cpp | 58 | ||||
| -rw-r--r-- | src/sessionManager.hpp | 3 |
16 files changed, 233 insertions, 45 deletions
@@ -53,7 +53,7 @@ install: $(all) clean: @echo "Cleaning up" - -@rm $(OBJS) + -@rm -rf $(OBJS) clean-all: clean @echo "Cleaning up all" @@ -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; |
