thread safety

This commit is contained in:
Benedikt Galbavy 2023-11-15 18:06:42 +01:00
parent 97fc3605ce
commit 90d338f0a0
7 changed files with 47 additions and 16 deletions

View File

@ -1,17 +1,20 @@
#include "mail.h" #include "mail.h"
#include <mutex>
mail::mail(std::string filename, std::string subject) : mail::mail(std::string filename, std::string subject) :
filename(filename), filename(filename),
timestamp(std::time(NULL)), timestamp(std::time(NULL)),
subject(subject), subject(subject),
deleted(false) deleted(false),
m_file()
{} {}
mail::mail(std::string filename, int64_t timestamp, std::string subject) : mail::mail(std::string filename, int64_t timestamp, std::string subject) :
filename(filename), filename(filename),
timestamp(timestamp), timestamp(timestamp),
subject(subject), subject(subject),
deleted(false) deleted(false),
m_file()
{} {}
/* /*

View File

@ -37,4 +37,6 @@ struct mail {
json mailToJson(); json mailToJson();
void remove(); void remove();
std::mutex m_file;
}; };

View File

@ -14,6 +14,7 @@
#include <cctype> #include <cctype>
#include <algorithm> #include <algorithm>
#include <future> #include <future>
#include <mutex>
#include <pthread.h> #include <pthread.h>
#include <string_view> #include <string_view>
#include <sstream> #include <sstream>
@ -43,10 +44,12 @@ enum commands {
namespace fs = std::filesystem; namespace fs = std::filesystem;
int abortRequested = 0; bool abortRequested = false;
int create_socket = -1; int create_socket = -1;
int new_socket = -1; int new_socket = -1;
std::vector<std::thread> threads;
void printUsage(); void printUsage();
inline bool isInteger(const std::string & s); inline bool isInteger(const std::string & s);
bool ichar_equals(char a, char b); bool ichar_equals(char a, char b);
@ -166,8 +169,7 @@ int main (int argc, char* argv[])
inet_ntoa(cliaddress.sin_addr), inet_ntoa(cliaddress.sin_addr),
ntohs(cliaddress.sin_port)); ntohs(cliaddress.sin_port));
// clientCommunication(&new_socket); // returnValue can be ignored // clientCommunication(&new_socket); // returnValue can be ignored
std::thread th(clientCommunication, new_socket); threads.emplace_back(clientCommunication, new_socket);
th.detach();
new_socket = -1; new_socket = -1;
} }
@ -305,7 +307,7 @@ void signalHandler(int sig)
{ {
if (sig == SIGINT) { if (sig == SIGINT) {
printf("abort Requested... "); // ignore error printf("abort Requested... "); // ignore error
abortRequested = 1; abortRequested = true;
if (new_socket != -1) if (new_socket != -1)
{ {
if (shutdown(new_socket, SHUT_RDWR) == -1) if (shutdown(new_socket, SHUT_RDWR) == -1)
@ -363,6 +365,10 @@ std::string getSha1(const std::string& str)
inline void exiting() inline void exiting()
{ {
for (auto& thread : threads) {
thread.join();
}
user_handler::getInstance().saveAll(); user_handler::getInstance().saveAll();
printf("Saving... \n"); printf("Saving... \n");
} }
@ -420,13 +426,14 @@ std::string cmdREAD(std::vector<std::string>& received)
mail->deleted) mail->deleted)
return "ERR\n"; return "ERR\n";
try { /*try */{
std::string path_str = user_handler::getInstance().getSpoolDir()/"messages"/mail->filename; std::string path_str = user_handler::getInstance().getSpoolDir()/"messages"/mail->filename;
std::lock_guard<std::mutex> guard(mail->m_file);
response.append(read_file(path_str)).append("\n"); response.append(read_file(path_str)).append("\n");
} }
catch (...) { // TODO: more specific error handling - then again, it will respond with ERR either way /*catch (...) { // TODO: more specific error handling - then again, it will respond with ERR either way
return "ERR\n"; return "ERR\n";
} }*/
return response; return response;
} }

View File

@ -4,6 +4,7 @@
#include <cstdio> #include <cstdio>
#include <fstream> #include <fstream>
#include <mutex>
#include <ostream> #include <ostream>
#include <string> #include <string>
#include <vector> #include <vector>
@ -11,7 +12,7 @@
using json = nlohmann::json; using json = nlohmann::json;
user::user(fs::path user_data_json) user::user(fs::path user_data_json) : m()
{ {
std::ifstream ifs(user_data_json); std::ifstream ifs(user_data_json);
json user_data = json::parse(ifs); json user_data = json::parse(ifs);
@ -50,7 +51,8 @@ user::user(fs::path user_data_json)
} }
user::user(std::string name, fs::path user_dir) user::user(std::string name, fs::path user_dir)
: name(name) : name(name),
m()
{ {
json user; json user;
user["mails"]["sent"] = json::object(); user["mails"]["sent"] = json::object();
@ -69,6 +71,8 @@ user::~user() {
void user::addMail(mail* mail) void user::addMail(mail* mail)
{ {
std::lock_guard<std::mutex> guard(this->m);
mail->id = this->inbox.size(); mail->id = this->inbox.size();
this->inbox.insert(mail); this->inbox.insert(mail);
@ -77,6 +81,8 @@ void user::addMail(mail* mail)
void user::sendMail(mail* mail, std::vector<std::string> recipients) void user::sendMail(mail* mail, std::vector<std::string> recipients)
{ {
std::lock_guard<std::mutex> guard(this->m);
std::vector<user*> users; std::vector<user*> users;
for ( auto& name : recipients) { for ( auto& name : recipients) {
// TODO: error handling for non existing user // TODO: error handling for non existing user
@ -104,6 +110,8 @@ mail* user::getMail(u_int id)
bool user::delMail(u_int id) bool user::delMail(u_int id)
{ {
std::lock_guard<std::mutex> guard(this->m);
maillist::iterator it = std::find_if(this->inbox.begin(), this->inbox.end(), [id](auto& i){ return (*i)(id); }); maillist::iterator it = std::find_if(this->inbox.begin(), this->inbox.end(), [id](auto& i){ return (*i)(id); });
bool success = true; bool success = true;

View File

@ -2,6 +2,7 @@
#include "mail.h" #include "mail.h"
#include <mutex>
#include <string> #include <string>
#include <set> #include <set>
#include <vector> #include <vector>
@ -37,4 +38,6 @@ private:
std::string name; std::string name;
maillist inbox; maillist inbox;
maillist sent; maillist sent;
std::mutex m;
}; };

View File

@ -4,7 +4,7 @@
#include <filesystem> #include <filesystem>
#include <string> #include <string>
user_handler::user_handler() user_handler::user_handler() : m_user()
{ {
for (const auto& entry : fs::directory_iterator()) { for (const auto& entry : fs::directory_iterator()) {
if (entry.path().extension() == ".json") { if (entry.path().extension() == ".json") {
@ -21,26 +21,31 @@ user_handler::~user_handler()
user* user_handler::getUser(std::string name) user* user_handler::getUser(std::string name)
{ {
if (this->users.find(name) == this->users.end()) { if (this->users.find(name) == this->users.end()) {
this->m_user.lock(); // avoid race condition of two threads creating the same user
if (!fs::exists(this->spool_dir/"users"/(name+".json"))) if (!fs::exists(this->spool_dir/"users"/(name+".json")))
return nullptr; return nullptr;
this->users[name] = new user(this->spool_dir/"users"/(name+".json")); this->users.insert({name, new user(this->spool_dir/"users"/(name+".json"))});
this->m_user.unlock();
} }
return this->users[name]; return (*this->users.find(name)).second;
} }
user* user_handler::getOrCreateUser(std::string name) user* user_handler::getOrCreateUser(std::string name)
{ {
if (this->users.find(name) == this->users.end()) { if (this->users.find(name) == this->users.end()) {
this->users[name] = fs::exists(this->spool_dir/"users"/(name+".json")) ? this->m_user.lock();
this->users.insert({name, fs::exists(this->spool_dir/"users"/(name+".json")) ?
new user(this->spool_dir/"users"/(name+".json")) : new user(this->spool_dir/"users"/(name+".json")) :
new user(name, this->spool_dir/"users"); new user(name, this->spool_dir/"users")});
this->m_user.unlock();
} }
return this->users[name]; return this->users[name];
} }
void user_handler::saveAll() void user_handler::saveAll()
{ {
// will only be called from main
for ( auto& user : this->users ) { for ( auto& user : this->users ) {
user.second->saveToFile(); user.second->saveToFile();
} }

View File

@ -2,6 +2,7 @@
#include <filesystem> #include <filesystem>
#include <map> #include <map>
#include <mutex>
#include <string> #include <string>
class user; class user;
@ -38,4 +39,6 @@ protected:
fs::path spool_dir; fs::path spool_dir;
std::map<std::string, user*> users; std::map<std::string, user*> users;
std::mutex m_user;
}; };