clean up mutex

This commit is contained in:
Sky Johnson 2025-09-06 21:43:05 -05:00
parent a2183bf2d0
commit c3f4cc0e42
36 changed files with 985 additions and 1123 deletions

View File

@ -7,7 +7,7 @@
#ifndef LWORLD_H #ifndef LWORLD_H
#define LWORLD_H #define LWORLD_H
#include "../common/Mutex.h" #include "../common/mutex.h"
#define ERROR_BADPASSWORD "Bad password" #define ERROR_BADPASSWORD "Bad password"
#define INVALID_ACCOUNT "Invalid Server Account." #define INVALID_ACCOUNT "Invalid Server Account."

View File

@ -21,7 +21,7 @@
#include "../common/types.h" #include "../common/types.h"
#include "../common/MiscFunctions.h" #include "../common/MiscFunctions.h"
#include "../common/servertalk.h" #include "../common/servertalk.h"
#include "../common/Mutex.h" #include "../common/mutex.h"
#include "PacketHeaders.h" #include "PacketHeaders.h"
#include "LoginAccount.h" #include "LoginAccount.h"
#include "LWorld.h" #include "LWorld.h"

View File

@ -22,7 +22,7 @@ along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
#define ACHIEVEMENTS_H_ #define ACHIEVEMENTS_H_
#include "../../common/types.h" #include "../../common/types.h"
#include "../../common/Mutex.h" #include "../../common/mutex.h"
#include "../Items/Items.h" #include "../Items/Items.h"
#include <map> #include <map>
#include <vector> #include <vector>

View File

@ -6,7 +6,7 @@
#include <vector> #include <vector>
#include "../../common/types.h" #include "../../common/types.h"
#include "../../common/EQPacket.h" #include "../../common/EQPacket.h"
#include "../../common/Mutex.h" #include "../../common/mutex.h"
#include "../client.h" #include "../client.h"
#include "ChatChannel.h" #include "ChatChannel.h"

View File

@ -2,7 +2,7 @@
#define COLLECTIONS_H_ #define COLLECTIONS_H_
#include "../../common/types.h" #include "../../common/types.h"
#include "../../common/Mutex.h" #include "../../common/mutex.h"
#include "../Items/Items.h" #include "../Items/Items.h"
#include <map> #include <map>
#include <vector> #include <vector>

View File

@ -20,7 +20,7 @@
#ifndef __EQ2_ENTITY__ #ifndef __EQ2_ENTITY__
#define __EQ2_ENTITY__ #define __EQ2_ENTITY__
#include "Spawn.h" #include "Spawn.h"
#include "../common/Mutex.h" #include "../common/mutex.h"
#include "Skills.h" #include "Skills.h"
#include "MutexList.h" #include "MutexList.h"
#include "MutexVector.h" #include "MutexVector.h"

View File

@ -4,7 +4,7 @@
#define EQ2_FACTIONS #define EQ2_FACTIONS
#include "../common/ConfigReader.h" #include "../common/ConfigReader.h"
#include "../common/Mutex.h" #include "../common/mutex.h"
struct Faction { struct Faction {
int32 id; int32 id;
@ -119,4 +119,3 @@ private:
map<int32, int8> faction_percent; map<int32, int8> faction_percent;
}; };
#endif #endif

View File

@ -5,7 +5,7 @@
#include "Spawn.h" #include "Spawn.h"
#include "client.h" #include "client.h"
#include "../common/Mutex.h" #include "../common/mutex.h"
class GroundSpawn : public Spawn { class GroundSpawn : public Spawn {
public: public:
@ -66,4 +66,3 @@ private:
bool randomize_heading; bool randomize_heading;
}; };
#endif #endif

View File

@ -7,7 +7,7 @@
#include <vector> #include <vector>
#include <deque> #include <deque>
#include <map> #include <map>
#include "../../common/Mutex.h" #include "../../common/mutex.h"
#include "../MutexMap.h" #include "../MutexMap.h"
using namespace std; using namespace std;

View File

@ -7,7 +7,7 @@
#include "../common/linked_list.h" #include "../common/linked_list.h"
#include "../common/timer.h" #include "../common/timer.h"
#include "../common/queue.h" #include "../common/queue.h"
#include "../common/Mutex.h" #include "../common/mutex.h"
#include "../common/TCPConnection.h" #include "../common/TCPConnection.h"
#include <deque> #include <deque>
#include "MutexMap.h" #include "MutexMap.h"

View File

@ -27,7 +27,7 @@
#include "Spawn.h" #include "Spawn.h"
#include "Spells.h" #include "Spells.h"
#include "../common/Mutex.h" #include "../common/mutex.h"
#include "Quests.h" #include "Quests.h"
#include "zoneserver.h" #include "zoneserver.h"
#include "client.h" #include "client.h"

View File

@ -4,7 +4,7 @@
#define MUTEXHELPER_H #define MUTEXHELPER_H
#include "../common/timer.h" #include "../common/timer.h"
#include "../common/Mutex.h" #include "../common/mutex.h"
#include <list> #include <list>
#include <map> #include <map>

View File

@ -21,7 +21,7 @@
#define RECIPE_H_ #define RECIPE_H_
#include "../../common/types.h" #include "../../common/types.h"
#include "../../common/Mutex.h" #include "../../common/mutex.h"
#include "../classes.h" #include "../classes.h"
#include <string.h> #include <string.h>

View File

@ -22,7 +22,7 @@
#include <string.h> #include <string.h>
#include <map> #include <map>
#include "../../common/Mutex.h" #include "../../common/mutex.h"
#include "../../common/types.h" #include "../../common/types.h"
using namespace std; using namespace std;

View File

@ -38,7 +38,7 @@
#include "Zone/map.h" #include "Zone/map.h"
#include "Zone/region_map.h" #include "Zone/region_map.h"
#include "Zone/region_map_v1.h" #include "Zone/region_map_v1.h"
#include "../common/Mutex.h" #include "../common/mutex.h"
#include "MutexList.h" #include "MutexList.h"
#include <deque> #include <deque>
#include <memory> // needed for LS to compile properly on linux #include <memory> // needed for LS to compile properly on linux
@ -1594,4 +1594,3 @@ private:
}; };
#endif #endif

View File

@ -11,7 +11,7 @@
#include "../common/MiscFunctions.h" #include "../common/MiscFunctions.h"
#include "client.h" #include "client.h"
#include "classes.h" #include "classes.h"
#include "../common/Mutex.h" #include "../common/mutex.h"
#include "AltAdvancement/AltAdvancement.h" #include "AltAdvancement/AltAdvancement.h"
#include <lua.hpp> #include <lua.hpp>
@ -435,4 +435,3 @@ private:
int32 max_spell_id; int32 max_spell_id;
}; };
#endif #endif

View File

@ -6,7 +6,7 @@
#include <string> #include <string>
#include <map> #include <map>
#include <vector> #include <vector>
#include "../common/Mutex.h" #include "../common/mutex.h"
#include "../common/types.h" #include "../common/types.h"
using namespace std; using namespace std;

View File

@ -4,7 +4,7 @@
#define __EQ2_TRADESKILLS__ #define __EQ2_TRADESKILLS__
#include "../../common/types.h" #include "../../common/types.h"
#include "../../common/Mutex.h" #include "../../common/mutex.h"
#include "../Items/Items.h" #include "../Items/Items.h"
#include <map> #include <map>
class Player; class Player;

View File

@ -23,7 +23,7 @@ along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
#include <vector> #include <vector>
#include <map> #include <map>
#include "../../common/Mutex.h" #include "../../common/mutex.h"
#include "../../common/types.h" #include "../../common/types.h"
#include "../../common/EQPacket.h" #include "../../common/EQPacket.h"

View File

@ -34,7 +34,7 @@
#include "../common/database.h" #include "../common/database.h"
#include "../common/types.h" #include "../common/types.h"
#include "../common/MiscFunctions.h" #include "../common/MiscFunctions.h"
#include "../common/Mutex.h" #include "../common/mutex.h"
#include "../common/DatabaseNew.h" #include "../common/DatabaseNew.h"
#include "client.h" #include "client.h"
#include "Object.h" #include "Object.h"
@ -686,4 +686,3 @@ private:
std::map<int32, int8> zone_instance_types; std::map<int32, int8> zone_instance_types;
}; };
#endif #endif

View File

@ -1,7 +1,7 @@
#include <string> #include <string>
#include <map> #include <map>
#include <list> #include <list>
#include "../../common/Mutex.h" #include "../../common/mutex.h"
#include "../../common/types.h" #include "../../common/types.h"
#pragma once #pragma once

View File

@ -24,7 +24,7 @@
#include "../../common/types.h" #include "../../common/types.h"
#include "../../common/MiscFunctions.h" #include "../../common/MiscFunctions.h"
#include "../../common/Mutex.h" #include "../../common/mutex.h"
#include "position.h" #include "position.h"
#include <stdio.h> #include <stdio.h>

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include "../Entity.h" #include "../Entity.h"
#include "../../common/Mutex.h" #include "../../common/mutex.h"
class Mob; class Mob;
class Client; class Client;

View File

@ -30,7 +30,7 @@
#include "../common/servertalk.h" #include "../common/servertalk.h"
#include "../common/TCPConnection.h" #include "../common/TCPConnection.h"
#include "WorldTCPConnection.h" #include "WorldTCPConnection.h"
#include "../common/Mutex.h" #include "../common/mutex.h"
#include "../common/DataBuffer.h" #include "../common/DataBuffer.h"
#include "net.h" #include "net.h"
#include "Player.h" #include "Player.h"

View File

@ -8,7 +8,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "xmlParser.h" #include "xmlParser.h"
#include "Mutex.h" #include "mutex.h"
using namespace std; using namespace std;
@ -32,4 +32,3 @@ private:
//vector<PacketStruct*> structs; //vector<PacketStruct*> structs;
}; };
#endif #endif

View File

@ -6,7 +6,7 @@
#endif #endif
#include "EQEMuError.h" #include "EQEMuError.h"
#include "linked_list.h" #include "linked_list.h"
#include "Mutex.h" #include "mutex.h"
#include "MiscFunctions.h" #include "MiscFunctions.h"
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@ -110,5 +110,3 @@ void CheckEQEMuErrorAndPause() {
getchar(); getchar();
} }
} }

View File

@ -27,7 +27,7 @@
#include "EQStream.h" #include "EQStream.h"
#include "EQStreamFactory.h" #include "EQStreamFactory.h"
#include "misc.h" #include "misc.h"
#include "Mutex.h" #include "mutex.h"
#include "op_codes.h" #include "op_codes.h"
#include "crypto/crc.h" #include "crypto/crc.h"
#include "packet_dump.h" #include "packet_dump.h"

View File

@ -18,7 +18,7 @@
// Project headers // Project headers
#include "EQPacket.h" #include "EQPacket.h"
#include "Mutex.h" #include "mutex.h"
#include "opcodemgr.h" #include "opcodemgr.h"
#include "misc.h" #include "misc.h"
#include "crypto/crypto.h" #include "crypto/crypto.h"

View File

@ -1,361 +0,0 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQ2Emulator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#include "../common/Log.h"
#include "../common/debug.h"
#include "../common/Mutex.h"
Mutex::Mutex() {
readers = 0;
mlocked = false;
writing = false;
name = "";
#ifdef DEBUG
stack.clear();
#endif
//CSLock is a pointer so we can use a different attribute type on create
CSLock = new CriticalSection(MUTEX_ATTRIBUTE_RECURSIVE);
}
Mutex::~Mutex() {
safe_delete(CSLock);
#ifdef DEBUG
stack.clear();
#endif
}
void Mutex::SetName(string in_name) {
#ifdef DEBUG
name = in_name;
#endif
}
void Mutex::lock() {
#ifdef DEBUG
int i = 0;
#endif
if (name.length() > 0) {
while (mlocked) {
#ifdef DEBUG
if (i > MUTEX_TIMEOUT_MILLISECONDS) {
LogWrite(MUTEX__ERROR, 0, "Mutex", "Possible deadlock attempt by '%s'!", name.c_str());
return;
}
i++;
#endif
Sleep(1);
}
}
mlocked = true;
CSLock->lock();
}
bool Mutex::trylock() {
return CSLock->trylock();
}
void Mutex::unlock() {
CSLock->unlock();
mlocked = false;
}
void Mutex::readlock(const char* function, int32 line) {
#ifdef DEBUG
int32 i = 0;
#endif
while (true) {
//Loop until there isn't a writer, then we can read!
CSRead.lock();
if (!writing) {
readers++;
CSRead.unlock();
#ifdef DEBUG
CSStack.lock();
if (function)
stack[(string)function]++;
CSStack.unlock();
#endif
return;
}
CSRead.unlock();
#ifdef DEBUG
if (i > MUTEX_TIMEOUT_MILLISECONDS) {
LogWrite(MUTEX__ERROR, 0, "Mutex", "The mutex %s called from %s at line %u timed out waiting for a readlock!", name.c_str(), function ? function : "name_not_provided", line);
LogWrite(MUTEX__ERROR, 0, "Mutex", "The following functions had locks:");
map<string, int32>::iterator itr;
CSStack.lock();
for (itr = stack.begin(); itr != stack.end(); itr++) {
if (itr->second > 0 && itr->first.length() > 0)
LogWrite(MUTEX__ERROR, 0, "Mutex", "%s, number of locks = %u", itr->first.c_str(), itr->second);
}
CSStack.unlock();
i = 0;
continue;
}
i++;
#endif
Sleep(1);
}
}
void Mutex::releasereadlock(const char* function, int32 line) {
//Wait for the readcount lock
CSRead.lock();
//Lower the readcount by one, when readcount is 0 writers may start writing
readers--;
CSRead.unlock();
#ifdef DEBUG
CSStack.lock();
if (function) {
map<string, int32>::iterator itr = stack.find((string)function);
if (itr != stack.end()) {
if (--(itr->second) == 0) {
stack.erase(itr);
}
}
}
CSStack.unlock();
#endif
}
bool Mutex::tryreadlock(const char* function) {
//This returns true if able to instantly obtain a readlock, false if not
CSRead.lock();
if (!writing) {
readers++;
CSRead.unlock();
}
else {
CSRead.unlock();
return false;
}
#ifdef DEBUG
CSStack.lock();
if (function)
stack[(string)function]++;
CSStack.unlock();
#endif
return true;
}
void Mutex::writelock(const char* function, int32 line) {
//Wait until the writer lock becomes available, then we can be the only writer!
#ifdef DEBUG
int32 i = 0;
#endif
while (!CSWrite.trylock()) {
#ifdef DEBUG
if (i > MUTEX_TIMEOUT_MILLISECONDS) {
LogWrite(MUTEX__ERROR, 0, "Mutex", "The mutex %s called from %s at line %u timed out waiting on another writelock!", name.c_str(), function ? function : "name_not_provided", line);
LogWrite(MUTEX__ERROR, 0, "Mutex", "The following functions had locks:");
map<string, int32>::iterator itr;
CSStack.lock();
for (itr = stack.begin(); itr != stack.end(); itr++) {
if (itr->second > 0 && itr->first.length() > 0)
LogWrite(MUTEX__ERROR, 0, "Mutex", "%s, number of locks = %u", itr->first.c_str(), itr->second);
}
CSStack.unlock();
i = 0;
continue;
}
i++;
#endif
Sleep(1);
}
waitReaders(function, line);
#ifdef DEBUG
CSStack.lock();
if (function)
stack[(string)function]++;
CSStack.unlock();
#endif
}
void Mutex::releasewritelock(const char* function, int32 line) {
//Wait for the readcount lock
CSRead.lock();
//Readers are aloud again
writing = false;
CSRead.unlock();
//Allow other writers to write
CSWrite.unlock();
#ifdef DEBUG
CSStack.lock();
if (function) {
map<string, int32>::iterator itr = stack.find((string)function);
if (itr != stack.end()) {
if (--(itr->second) == 0) {
stack.erase(itr);
}
}
}
CSStack.unlock();
#endif
}
bool Mutex::trywritelock(const char* function) {
//This returns true if able to instantly obtain a writelock, false if not
if (CSWrite.trylock()) {
CSRead.lock();
if (readers == 0)
writing = true;
CSRead.unlock();
if (!writing) {
CSWrite.unlock();
return false;
}
}
else
return false;
#ifdef DEBUG
CSStack.lock();
if (function)
stack[(string)function]++;
CSStack.unlock();
#endif
return true;
}
void Mutex::waitReaders(const char* function, int32 line)
{
//Wait for all current readers to stop, then we can write!
#ifdef DEBUG
int32 i = 0;
#endif
while (true)
{
CSRead.lock();
if (readers == 0)
{
writing = true;
CSRead.unlock();
break;
}
CSRead.unlock();
#ifdef DEBUG
if (i > MUTEX_TIMEOUT_MILLISECONDS) {
LogWrite(MUTEX__ERROR, 0, "Mutex", "The mutex %s called from %s at line %u timed out while waiting on readers!", name.c_str(), function ? function : "name_not_provided", line);
LogWrite(MUTEX__ERROR, 0, "Mutex", "The following functions had locks:");
map<string, int32>::iterator itr;
CSStack.lock();
for (itr = stack.begin(); itr != stack.end(); itr++) {
if (itr->second > 0 && itr->first.length() > 0)
LogWrite(MUTEX__ERROR, 0, "Mutex", "%s, number of locks = %u", itr->first.c_str(), itr->second);
}
CSStack.unlock();
i = 0;
continue;
}
i++;
#endif
Sleep(1);
}
}
LockMutex::LockMutex(Mutex* in_mut, bool iLock) {
mut = in_mut;
locked = iLock;
if (locked) {
mut->lock();
}
}
LockMutex::~LockMutex() {
if (locked) {
mut->unlock();
}
}
void LockMutex::unlock() {
if (locked)
mut->unlock();
locked = false;
}
void LockMutex::lock() {
if (!locked)
mut->lock();
locked = true;
}
CriticalSection::CriticalSection(int attribute) {
#ifdef WIN32
InitializeCriticalSection(&CSMutex);
#else
pthread_mutexattr_init(&type_attribute);
switch (attribute)
{
case MUTEX_ATTRIBUTE_FAST:
pthread_mutexattr_settype(&type_attribute, PTHREAD_MUTEX_FAST_NP);
break;
case MUTEX_ATTRIBUTE_RECURSIVE:
pthread_mutexattr_settype(&type_attribute, PTHREAD_MUTEX_RECURSIVE_NP);
break;
case MUTEX_ATTRIBUTE_ERRORCHK:
pthread_mutexattr_settype(&type_attribute, PTHREAD_MUTEX_ERRORCHECK_NP);
break;
default:
LogWrite(MUTEX__DEBUG, 0, "Critical Section", "Invalid mutex attribute type! Using PTHREAD_MUTEX_FAST_NP");
pthread_mutexattr_settype(&type_attribute, PTHREAD_MUTEX_FAST_NP);
break;
}
pthread_mutex_init(&CSMutex, &type_attribute);
#endif
}
CriticalSection::~CriticalSection() {
#ifdef WIN32
DeleteCriticalSection(&CSMutex);
#else
pthread_mutex_destroy(&CSMutex);
pthread_mutexattr_destroy(&type_attribute);
#endif
}
void CriticalSection::lock() {
//Waits for a lock on this critical section
#ifdef WIN32
EnterCriticalSection(&CSMutex);
#else
pthread_mutex_lock(&CSMutex);
#endif
}
void CriticalSection::unlock() {
//Gets rid of one of the current thread's locks on this critical section
#ifdef WIN32
LeaveCriticalSection(&CSMutex);
#else
pthread_mutex_unlock(&CSMutex);
#endif
}
bool CriticalSection::trylock() {
//Returns true if able to instantly get a lock on this critical section, false if not
#ifdef WIN32
return TryEnterCriticalSection(&CSMutex);
#else
return (pthread_mutex_trylock(&CSMutex) == 0);
#endif
}

View File

@ -1,86 +0,0 @@
// Copyright (C) 2007-2025 EQ2EMulator
// Licensed under GPL v3
#ifndef MYMUTEX_H
#define MYMUTEX_H
#ifdef WIN32
#include <WinSock2.h>
#include <windows.h>
#else
#include <pthread.h>
#include "../common/unix.h"
#endif
#include "../common/types.h"
#include <string>
#include <map>
#define MUTEX_ATTRIBUTE_FAST 1
#define MUTEX_ATTRIBUTE_RECURSIVE 2
#define MUTEX_ATTRIBUTE_ERRORCHK 3
#define MUTEX_TIMEOUT_MILLISECONDS 10000
class CriticalSection {
public:
CriticalSection(int attribute = MUTEX_ATTRIBUTE_FAST);
~CriticalSection();
void lock();
void unlock();
bool trylock();
private:
#ifdef WIN32
CRITICAL_SECTION CSMutex;
#else
pthread_mutex_t CSMutex;
pthread_mutexattr_t type_attribute;
#endif
};
class Mutex {
public:
Mutex();
~Mutex();
void lock();
void unlock();
bool trylock();
void readlock(const char* function = 0, int32 line = 0);
void releasereadlock(const char* function = 0, int32 line = 0);
bool tryreadlock(const char* function = 0);
void writelock(const char* function = 0, int32 line = 0);
void releasewritelock(const char* function = 0, int32 line = 0);
bool trywritelock(const char* function = 0);
void waitReaders(const char* function = 0, int32 line = 0);
void SetName(string in_name);
private:
CriticalSection CSRead;
CriticalSection CSWrite;
CriticalSection* CSLock;
#ifdef DEBUG //Used for debugging only
CriticalSection CSStack;
map<string, int32> stack;
#endif
int readers;
bool writing;
volatile bool mlocked;
string name;
};
class LockMutex {
public:
LockMutex(Mutex* in_mut, bool iLock = true);
~LockMutex();
void unlock();
void lock();
private:
bool locked;
Mutex* mut;
};
#endif

View File

@ -30,7 +30,7 @@
#endif #endif
#include "types.h" #include "types.h"
#include "Mutex.h" #include "mutex.h"
#include "linked_list.h" #include "linked_list.h"
#include "queue.h" #include "queue.h"
#include "servertalk.h" #include "servertalk.h"

View File

@ -14,7 +14,7 @@
#include "linked_list.h" #include "linked_list.h"
#include "EQStream.h" #include "EQStream.h"
#include "MiscFunctions.h" #include "MiscFunctions.h"
#include "Mutex.h" #include "mutex.h"
#include <string> #include <string>
#include <vector> #include <vector>
#include <map> #include <map>

View File

@ -10,7 +10,7 @@
#endif #endif
#include <mysql.h> #include <mysql.h>
#include "../common/types.h" #include "../common/types.h"
#include "../common/Mutex.h" #include "../common/mutex.h"
#include "../common/linked_list.h" #include "../common/linked_list.h"
#include "../common/queue.h" #include "../common/queue.h"
#include "../common/timer.h" #include "../common/timer.h"
@ -58,5 +58,3 @@ private:
bool pSSL; bool pSSL;
}; };
#endif #endif

238
source/common/mutex.cpp Normal file
View File

@ -0,0 +1,238 @@
// Copyright (C) 2007-2025 EQ2EMulator
// Licensed under GPL v3
#include "mutex.h"
// CriticalSection implementation
CriticalSection::CriticalSection(int attribute) : mutex_type(attribute)
{
if (mutex_type == MUTEX_ATTRIBUTE_RECURSIVE) {
recursive_mutex = std::make_unique<std::recursive_mutex>();
} else {
regular_mutex = std::make_unique<std::mutex>();
}
}
void CriticalSection::lock()
{
if (recursive_mutex) {
recursive_mutex->lock();
} else {
regular_mutex->lock();
}
}
void CriticalSection::unlock()
{
if (recursive_mutex) {
recursive_mutex->unlock();
} else {
regular_mutex->unlock();
}
}
bool CriticalSection::trylock()
{
if (recursive_mutex) {
return recursive_mutex->try_lock();
} else {
return regular_mutex->try_lock();
}
}
// Mutex implementation
Mutex::Mutex() : name("")
{
#ifdef DEBUG
stack.clear();
#endif
}
void Mutex::SetName(std::string in_name)
{
name = in_name;
}
void Mutex::lock()
{
#ifdef DEBUG
if (!name.empty()) {
auto start = std::chrono::steady_clock::now();
while (!basic_mutex.try_lock()) {
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - start).count();
if (elapsed > MUTEX_TIMEOUT_MILLISECONDS) {
LogWrite(MUTEX__ERROR, 0, "Mutex", "Possible deadlock attempt by '%s'!", name.c_str());
return;
}
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
} else {
basic_mutex.lock();
}
#else
basic_mutex.lock();
#endif
}
bool Mutex::trylock()
{
return basic_mutex.try_lock();
}
void Mutex::unlock()
{
basic_mutex.unlock();
}
void Mutex::readlock([[maybe_unused]] const char* function, [[maybe_unused]] std::int32_t line)
{
#ifdef DEBUG
auto start = std::chrono::steady_clock::now();
while (!rw_mutex.try_lock_shared()) {
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - start).count();
if (elapsed > MUTEX_TIMEOUT_MILLISECONDS) {
logDeadlock(function, line, "readlock");
start = std::chrono::steady_clock::now(); // Reset timer and continue
}
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
addDebugLock(function);
#else
rw_mutex.lock_shared();
#endif
}
void Mutex::releasereadlock([[maybe_unused]] const char* function, [[maybe_unused]] std::int32_t line)
{
rw_mutex.unlock_shared();
#ifdef DEBUG
removeDebugLock(function);
#endif
}
bool Mutex::tryreadlock([[maybe_unused]] const char* function)
{
bool result = rw_mutex.try_lock_shared();
#ifdef DEBUG
if (result && function) {
addDebugLock(function);
}
#endif
return result;
}
void Mutex::writelock([[maybe_unused]] const char* function, [[maybe_unused]] std::int32_t line)
{
#ifdef DEBUG
auto start = std::chrono::steady_clock::now();
while (!rw_mutex.try_lock()) {
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - start).count();
if (elapsed > MUTEX_TIMEOUT_MILLISECONDS) {
logDeadlock(function, line, "writelock");
start = std::chrono::steady_clock::now(); // Reset timer and continue
}
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
addDebugLock(function);
#else
rw_mutex.lock();
#endif
}
void Mutex::releasewritelock([[maybe_unused]] const char* function, [[maybe_unused]] std::int32_t line)
{
rw_mutex.unlock();
#ifdef DEBUG
removeDebugLock(function);
#endif
}
bool Mutex::trywritelock([[maybe_unused]] const char* function)
{
bool result = rw_mutex.try_lock();
#ifdef DEBUG
if (result && function) {
addDebugLock(function);
}
#endif
return result;
}
void Mutex::waitReaders(const char* function, std::int32_t line)
{
// Wait until we can get a write lock (which means no readers)
writelock(function, line);
releasewritelock(function, line);
}
#ifdef DEBUG
void Mutex::addDebugLock(const char* function)
{
if (function) {
std::lock_guard<std::mutex> guard(debug_mutex);
stack[std::string(function)]++;
}
}
void Mutex::removeDebugLock(const char* function)
{
if (function) {
std::lock_guard<std::mutex> guard(debug_mutex);
auto itr = stack.find(std::string(function));
if (itr != stack.end()) {
if (--(itr->second) == 0) {
stack.erase(itr);
}
}
}
}
void Mutex::logDeadlock(const char* function, std::int32_t line, const char* lock_type)
{
LogWrite(MUTEX__ERROR, 0, "Mutex", "The mutex %s called from %s at line %u timed out waiting for a %s!",
name.c_str(), function ? function : "name_not_provided", line, lock_type);
LogWrite(MUTEX__ERROR, 0, "Mutex", "The following functions had locks:");
std::lock_guard<std::mutex> guard(debug_mutex);
for (const auto& [func_name, count] : stack) {
if (count > 0 && !func_name.empty()) {
LogWrite(MUTEX__ERROR, 0, "Mutex", "%s, number of locks = %u", func_name.c_str(), count);
}
}
}
#endif
// LockMutex implementation
LockMutex::LockMutex(Mutex* in_mut, bool iLock)
: locked(false), mut(in_mut)
{
if (mut && iLock) {
lock();
}
}
LockMutex::~LockMutex()
{
if (locked) {
unlock();
}
}
void LockMutex::lock()
{
if (mut && !locked) {
mut->lock();
locked = true;
}
}
void LockMutex::unlock()
{
if (mut && locked) {
mut->unlock();
locked = false;
}
}

83
source/common/mutex.h Normal file
View File

@ -0,0 +1,83 @@
// Copyright (C) 2007-2025 EQ2EMulator
// Licensed under GPL v3
#pragma once
#include <shared_mutex>
#include <mutex>
#include <string>
#include <map>
#include <cstdint>
#include <memory>
#include <chrono>
#define MUTEX_ATTRIBUTE_FAST 1
#define MUTEX_ATTRIBUTE_RECURSIVE 2
#define MUTEX_ATTRIBUTE_ERRORCHK 3
#define MUTEX_TIMEOUT_MILLISECONDS 10000
class CriticalSection
{
public:
CriticalSection(int attribute = MUTEX_ATTRIBUTE_FAST);
~CriticalSection() = default;
void lock();
void unlock();
bool trylock();
private:
std::unique_ptr<std::mutex> regular_mutex;
std::unique_ptr<std::recursive_mutex> recursive_mutex;
int mutex_type;
};
class Mutex
{
public:
Mutex();
~Mutex() = default;
void lock();
void unlock();
bool trylock();
void readlock(const char* function = nullptr, std::int32_t line = 0);
void releasereadlock(const char* function = nullptr, std::int32_t line = 0);
bool tryreadlock(const char* function = nullptr);
void writelock(const char* function = nullptr, std::int32_t line = 0);
void releasewritelock(const char* function = nullptr, std::int32_t line = 0);
bool trywritelock(const char* function = nullptr);
void waitReaders(const char* function = nullptr, std::int32_t line = 0);
void SetName(std::string in_name);
private:
std::shared_mutex rw_mutex;
std::mutex basic_mutex;
std::string name;
#ifdef DEBUG
std::mutex debug_mutex;
std::map<std::string, std::int32_t> stack;
void addDebugLock(const char* function);
void removeDebugLock(const char* function);
void logDeadlock(const char* function, std::int32_t line, const char* lock_type);
#endif
};
class LockMutex
{
public:
LockMutex(Mutex* in_mut, bool iLock = true);
~LockMutex();
void unlock();
void lock();
private:
bool locked;
Mutex* mut;
};

View File

@ -4,7 +4,7 @@
#define OPCODE_MANAGER_H #define OPCODE_MANAGER_H
#include "types.h" #include "types.h"
#include "Mutex.h" #include "mutex.h"
#include "emu_opcodes.h" #include "emu_opcodes.h"
#include <map> #include <map>
@ -141,5 +141,3 @@ protected:
}; };
#endif #endif