123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361 |
- /*
- 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
- }
|