/*
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 .
*/
#ifndef MUTEXLIST_H
#define MUTEXLIST_H
#include
#include "MutexHelper.h"
#define MUTEXLIST_PENDING_ADD 1
#define MUTEXLIST_PENDING_REMOVE 2
#define MUTEXLIST_PENDING_DELETE 3
template
class MutexList{
public:
MutexList(){
pending_changing = false;
has_pending_data = false;
pending_clear = false;
changing = false;
access_count = 0;
access_pending = 0;
}
MutexList(const MutexList& list){
pending_changing = false;
has_pending_data = false;
pending_clear = false;
changing = false;
access_count = 0;
access_pending = 0;
/*if(list.has_pending_data)
pending_data = list.pending_data;
current_data = list.current_data; */
}
~MutexList(){
while(!update(true)){
Sleep(1);
}
}
class iterator {
private:
typename std::list::iterator itr; // Current element
MutexList* list;
bool first_itr;
public:
iterator(){
}
iterator(MutexList* list){
if(list){
this->list = list;
list->update();
this->list->AddAccess();
first_itr = true;
itr = list->current_data.begin();
if(itr != list->current_data.end())
value = *itr;
}
else
this->list = 0;
}
~iterator(){
if(list)
list->RemoveAccess();
}
bool HasNext(){
return itr != list->current_data.end();
}
bool Next(){
if(list->pending_clear)
return false;
if(first_itr)
first_itr = false;
else
itr++;
if(itr != list->current_data.end()){
value = *itr;
if(list->PendingContains(value)) //pending delete
return Next();
return true;
}
return false;
}
iterator* operator->() {
return this;
}
T value;
};
void SetChanging(){
ChangingLock.lock();
changing = true;
ChangingLock.unlock();
}
void SetNotChanging(){
ChangingLock.lock();
changing = false;
ChangingLock.unlock();
}
void AddAccess(){
AccessLock.lock();
++access_count;
AccessLock.unlock();
}
void RemoveAccess(){
AccessLock.lock();
--access_count;
AccessLock.unlock();
}
unsigned int size(bool include_pending = false){
if(include_pending){
update();
return current_data.size() + pending_data.size();
}
return current_data.size();
}
iterator begin(){
return iterator(this);
}
void clear(bool erase_all = false){
pending_clear = true;
if(erase_all){
AddAccess();
PendingLock.lock();
typename std::list::iterator itr;
for(itr = current_data.begin(); itr != current_data.end(); itr++){
RemoveData(*itr);
}
PendingLock.unlock();
RemoveAccess();
}
update();
}
bool PendingContains(T key){
if(!has_pending_data)
return false;
bool ret = false;
PendingLock.lock();
ret = (pending_data.count(key) > 0 && pending_data[key] == false);
PendingLock.unlock();
return ret;
}
unsigned int count(T key){
unsigned int ret = 0;
while(changing){
Sleep(1);
}
AddAccess();
bool retry = false;
if(!changing){
typename std::list::iterator iter;
for(iter = current_data.begin(); iter != current_data.end(); iter++){
if(*iter == key)
ret++;
}
}
else
retry = true;
RemoveAccess();
if(retry)
return count(key); //only occurs whenever we change to changing state at the same time as a reading state
return ret;
}
void RemoveData(T key, int32 erase_time = 0){
handle_deletes.AddPendingDelete(key, Timer::GetCurrentTime2() + erase_time);
}
void Remove(T key, bool erase = false, int32 erase_time = 0){
while(changing){
Sleep(1);
}
AddAccess();
PendingLock.lock();
pending_data[key] = false;
PendingLock.unlock();
if(erase)
RemoveData(key, erase_time);
has_pending_data = true;
RemoveAccess();
update();
}
void Add(T key){
if(count(key) > 0)
return;
while(changing){
Sleep(1);
}
AddAccess();
PendingLock.lock();
pending_data[key] = true;
PendingLock.unlock();
has_pending_data = true;
RemoveAccess();
update();
}
private:
bool update(bool force = false){
//if(access_count > 5)
// cout << "Possible error.\n";
while(changing){
Sleep(1);
}
if(pending_clear && access_count == 0){
SetChanging();
while(access_count > 0){
Sleep(1);
}
AddAccess();
PendingLock.lock();
current_data.clear();
has_pending_data = (pending_data.size() > 0);
PendingLock.unlock();
pending_clear = false;
RemoveAccess();
SetNotChanging();
}
if(!pending_clear && has_pending_data && access_count == 0){
SetChanging();
while(access_count > 0){
Sleep(1);
}
AddAccess();
PendingLock.lock();
typename std::map::iterator pending_itr;
for(pending_itr = pending_data.begin(); pending_itr != pending_data.end(); pending_itr++){
if(pending_itr->second)
current_data.push_back(pending_itr->first);
else
current_data.remove(pending_itr->first);
}
pending_data.clear();
PendingLock.unlock();
has_pending_data = false;
RemoveAccess();
SetNotChanging();
}
handle_deletes.CheckDeletes(force);
return !pending_clear && !has_pending_data;
}
Locker PendingLock;
Locker AccessLock;
Locker ChangingLock;
volatile int access_count;
std::list current_data;
std::map pending_data;
HandleDeletes handle_deletes;
volatile int access_pending;
volatile bool pending_changing;
volatile bool changing;
volatile bool has_pending_data;
volatile bool pending_clear;
};
#endif