203 lines
6.4 KiB
C++
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
|