The R Project SVN R-packages

Rev

Blame | Last modification | View Log | Download | RSS feed

#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