wordreference/libjson/_internal/Source/JSONNode_Mutex.cpp

203 lines
6.4 KiB
C++

#include "JSONNode.h"
#include "JSONGlobals.h"
#ifdef JSON_MUTEX_CALLBACKS
json_mutex_callback_t json_lock_callback = 0;
json_mutex_callback_t json_unlock_callback = 0;
void * global_mutex = 0;
void * manager_mutex = 0;
struct AutoLock {
public:
LIBJSON_OBJECT(AutoLock);
AutoLock(void) json_nothrow {
LIBJSON_CTOR;
json_lock_callback(manager_mutex);
}
~AutoLock(void) json_nothrow {
LIBJSON_DTOR;
json_unlock_callback(manager_mutex);
}
private:
AutoLock(const AutoLock &);
AutoLock & operator = (const AutoLock &);
};
#ifdef JSON_MUTEX_MANAGE
json_mutex_callback_t json_destroy = 0;
//make sure that the global mutex is taken care of too
struct auto_global {
public:
LIBJSON_OBJECT(auto_global;)
auto_global(void) json_nothrow { LIBJSON_CTOR; }
~auto_global(void) json_nothrow {
LIBJSON_DTOR;
if (global_mutex){
JSON_ASSERT_SAFE(json_destroy != 0, JSON_TEXT("No json_destroy in mutex managed mode"), return;);
json_destroy(global_mutex);
}
}
private:
auto_global(const auto_global &);
auto_global & operator = (const auto_global &);
};
auto_global cleanupGlobal;
#endif
void JSONNode::register_mutex_callbacks(json_mutex_callback_t lock, json_mutex_callback_t unlock, void * manager_lock) json_nothrow {
json_lock_callback = lock;
json_unlock_callback = unlock;
manager_mutex = manager_lock;
}
void JSONNode::set_global_mutex(void * mutex) json_nothrow {
global_mutex = mutex;
}
void JSONNode::set_mutex(void * mutex) json_nothrow {
makeUniqueInternal();
internal -> _set_mutex(mutex);
}
void * JSONNode::getThisLock(JSONNode * pthis) json_nothrow {
if (pthis -> internal -> mylock != 0){
return pthis -> internal -> mylock;
}
JSON_ASSERT(global_mutex != 0, JSON_TEXT("No global_mutex")); //this is safe, because it's just goingi to return 0 anyway
return global_mutex;
}
void JSONNode::lock(int thread) json_nothrow {
JSON_ASSERT_SAFE(json_lock_callback != 0, JSON_TEXT("No locking callback"), return;);
AutoLock lockControl;
//first, figure out what needs to be locked
void * thislock = getThisLock(this);
#ifdef JSON_SAFE
if (json_unlikely(thislock == 0)) return;
#endif
//make sure that the same thread isn't locking it more than once (possible due to complex ref counting)
JSON_MAP(int, JSON_MAP(void *, unsigned int) )::iterator it = json_global(THREAD_LOCKS).find(thread);
if (it == json_global(THREAD_LOCKS).end()){
JSON_MAP(void *, unsigned int) newthread;
newthread[thislock] = 1;
json_global(THREAD_LOCKS).insert(std::pair<int, JSON_MAP(void *, unsigned int) >(thread, newthread));
} else { //this thread already has some things locked, check if the current mutex is
JSON_MAP(void *, unsigned int) & newthread = it -> second;
JSON_MAP(void *, unsigned int)::iterator locker(newthread.find(thislock));
if (locker == newthread.end()){ //current mutex is not locked, set it to locked
newthread.insert(std::pair<void *, unsigned int>(thislock, 1));
} else { //it's already locked, don't relock it
++(locker -> second);
return; //don't try to relock, it will deadlock the program
}
}
//if I need to, lock it
json_lock_callback(thislock);
}
void JSONNode::unlock(int thread) json_nothrow{
JSON_ASSERT_SAFE(json_unlock_callback != 0, JSON_TEXT("No unlocking callback"), return;);
AutoLock lockControl;
//first, figure out what needs to be locked
void * thislock = getThisLock(this);
#ifdef JSON_SAFE
if (thislock == 0) return;
#endif
//get it out of the map
JSON_MAP(int, JSON_MAP(void *, unsigned int) )::iterator it = json_global(THREAD_LOCKS).find(thread);
JSON_ASSERT_SAFE(it != json_global(THREAD_LOCKS).end(), JSON_TEXT("thread unlocking something it didn't lock"), return;);
//get the mutex out of the thread
JSON_MAP(void *, unsigned int) & newthread = it -> second;
JSON_MAP(void *, unsigned int)::iterator locker = newthread.find(thislock);
JSON_ASSERT_SAFE(locker != newthread.end(), JSON_TEXT("thread unlocking mutex it didn't lock"), return;);
//unlock it
if (--(locker -> second)) return; //other nodes is this same thread still have a lock on it
//if I need to, unlock it
newthread.erase(locker);
json_unlock_callback(thislock);
}
#ifdef JSON_MUTEX_MANAGE
void JSONNode::register_mutex_destructor(json_mutex_callback_t destroy) json_nothrow {
json_destroy = destroy;
}
#endif
void internalJSONNode::_set_mutex(void * mutex, bool unset) json_nothrow {
if (unset) _unset_mutex(); //for reference counting
mylock = mutex;
if (mutex != 0){
#ifdef JSON_MUTEX_MANAGE
JSON_MAP(void *, unsigned int)::iterator it = json_global(MUTEX_MANAGER).find(mutex);
if (it == json_global(MUTEX_MANAGER).end()){
json_global(MUTEX_MANAGER).insert(std::pair<void *, unsigned int>(mutex, 1));
} else {
++it -> second;
}
#endif
if (isContainer()){
json_foreach(CHILDREN, myrunner){
(*myrunner) -> set_mutex(mutex);
}
}
}
}
void internalJSONNode::_unset_mutex(void) json_nothrow {
#ifdef JSON_MUTEX_MANAGE
if (mylock != 0){
JSON_MAP(void *, unsigned int)::iterator it = json_global(MUTEX_MANAGER).find(mylock);
JSON_ASSERT_SAFE(it != json_global(MUTEX_MANAGER).end(), JSON_TEXT("Mutex not managed"), return;);
--it -> second;
if (it -> second == 0){
JSON_ASSERT_SAFE(json_destroy, JSON_TEXT("You didn't register a destructor for mutexes"), return;);
json_global(MUTEX_MANAGER).erase(it);
}
}
#endif
}
#ifdef JSON_DEBUG
#ifndef JSON_LIBRARY
JSONNode internalJSONNode::DumpMutex(void) const json_nothrow {
JSONNode mut(JSON_NODE);
mut.set_name(JSON_TEXT("mylock"));
#ifdef JSON_MUTEX_MANAGE
if (mylock != 0){
mut.push_back(JSON_NEW(JSONNode(JSON_TEXT("this"), (long)mylock)));
JSON_MAP(void *, unsigned int)::iterator it = json_global(MUTEX_MANAGER).find(mylock);
if (it == json_global(MUTEX_MANAGER).end()){
mut.push_back(JSON_NEW(JSONNode(JSON_TEXT("references"), JSON_TEXT("error"))));
} else {
mut.push_back(JSON_NEW(JSONNode(JSON_TEXT("references"), it -> second)));
}
} else {
mut = (long)mylock;
}
#else
mut = (long)mylock;
#endif
return mut;
}
#endif
#endif
#else
#ifdef JSON_MUTEX_MANAGE
#error You can not have JSON_MUTEX_MANAGE on without JSON_MUTEX_CALLBACKS
#endif
#endif