#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(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(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(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