]> arthur.barton.de Git - netdata.git/blobdiff - src/dictionary.c
40% faster AVL searching and DICTIONARY operations
[netdata.git] / src / dictionary.c
index c794d0865433625424c4a46bdff743d308f23131..1543f4d0e7334bce5ad63e2b5469e5b47b0dffb8 100644 (file)
 #include "dictionary.h"
 
 // ----------------------------------------------------------------------------
-// name_value index
+// dictionary locks
 
-static int name_value_iterator(avl *a) { if(a) {}; return 0; }
+static inline void dictionary_read_lock(DICTIONARY *dict) {
+       if(likely(!(dict->flags & DICTIONARY_FLAG_SINGLE_THREADED))) {
+               // debug(D_DICTIONARY, "Dictionary READ lock");
+               pthread_rwlock_rdlock(&dict->rwlock);
+       }
+}
+
+static inline void dictionary_write_lock(DICTIONARY *dict) {
+       if(likely(!(dict->flags & DICTIONARY_FLAG_SINGLE_THREADED))) {
+               // debug(D_DICTIONARY, "Dictionary WRITE lock");
+               pthread_rwlock_wrlock(&dict->rwlock);
+       }
+}
+
+static inline void dictionary_unlock(DICTIONARY *dict) {
+       if(likely(!(dict->flags & DICTIONARY_FLAG_SINGLE_THREADED))) {
+               // debug(D_DICTIONARY, "Dictionary UNLOCK lock");
+               pthread_rwlock_unlock(&dict->rwlock);
+       }
+}
+
+
+// ----------------------------------------------------------------------------
+// avl index
 
 static int name_value_compare(void* a, void* b) {
        if(((NAME_VALUE *)a)->hash < ((NAME_VALUE *)b)->hash) return -1;
@@ -27,34 +50,19 @@ static int name_value_compare(void* a, void* b) {
 #define dictionary_name_value_index_del_nolock(dict, nv) do { (dict)->deletes++; avl_remove(&(dict->values_index), (avl *)(nv)); } while(0)
 
 static inline NAME_VALUE *dictionary_name_value_index_find_nolock(DICTIONARY *dict, const char *name, uint32_t hash) {
-       NAME_VALUE *result = NULL, tmp;
+       NAME_VALUE tmp;
        tmp.hash = (hash)?hash:simple_hash(name);
        tmp.name = (char *)name;
 
        dict->searches++;
-       avl_search(&(dict->values_index), (avl *)&tmp, name_value_iterator, (avl **)&result);
-       return result;
-}
-
-static void dictionary_read_lock(DICTIONARY *dict) {
-       if(likely(!(dict->flags & DICTIONARY_FLAG_SINGLE_THREADED)))
-               pthread_rwlock_rdlock(&dict->rwlock);
-}
-
-static void dictionary_write_lock(DICTIONARY *dict) {
-       if(likely(!(dict->flags & DICTIONARY_FLAG_SINGLE_THREADED)))
-               pthread_rwlock_wrlock(&dict->rwlock);
-}
-
-static void dictionary_unlock(DICTIONARY *dict) {
-       if(likely(!(dict->flags & DICTIONARY_FLAG_SINGLE_THREADED)))
-               pthread_rwlock_unlock(&dict->rwlock);
+       return (NAME_VALUE *)avl_search(&(dict->values_index), (avl *) &tmp);
 }
 
 // ----------------------------------------------------------------------------
+// internal methods
 
 static NAME_VALUE *dictionary_name_value_create_nolock(DICTIONARY *dict, const char *name, void *value, size_t value_len, uint32_t hash) {
-       debug(D_DICTIONARY, "Creating name value entry for name '%s', value '%s'.", name, value);
+       debug(D_DICTIONARY, "Creating name value entry for name '%s'.", name);
 
        NAME_VALUE *nv = calloc(1, sizeof(NAME_VALUE));
        if(unlikely(!nv)) fatal("Cannot allocate name_value of size %z", sizeof(NAME_VALUE));
@@ -79,15 +87,10 @@ static NAME_VALUE *dictionary_name_value_create_nolock(DICTIONARY *dict, const c
                memcpy(nv->value, value, value_len);
        }
 
-       dictionary_write_lock(dict);
-
        // index it
        dictionary_name_value_index_add_nolock(dict, nv);
-
        dict->entries++;
 
-       dictionary_unlock(dict);
-
        return nv;
 }
 
@@ -98,15 +101,21 @@ static void dictionary_name_value_destroy_nolock(DICTIONARY *dict, NAME_VALUE *n
 
        dict->entries--;
 
-       free(nv->name);
-
-       if(!(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE))
+       if(!(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE)) {
+               debug(D_REGISTRY, "Dictionary freeing value of '%s'", nv->name);
                free(nv->value);
+       }
+
+       if(!(dict->flags & DICTIONARY_FLAG_NAME_LINK_DONT_CLONE)) {
+               debug(D_REGISTRY, "Dictionary freeing name '%s'", nv->name);
+               free(nv->name);
+       }
 
        free(nv);
 }
 
 // ----------------------------------------------------------------------------
+// API - basic methods
 
 DICTIONARY *dictionary_create(uint32_t flags) {
        debug(D_DICTIONARY, "Creating dictionary.");
@@ -142,27 +151,26 @@ void *dictionary_set(DICTIONARY *dict, const char *name, void *value, size_t val
 
        uint32_t hash = simple_hash(name);
 
-       dictionary_read_lock(dict);
-       NAME_VALUE *nv = dictionary_name_value_index_find_nolock(dict, name, hash);
-       dictionary_unlock(dict);
+       dictionary_write_lock(dict);
 
+       NAME_VALUE *nv = dictionary_name_value_index_find_nolock(dict, name, hash);
        if(unlikely(!nv)) {
                debug(D_DICTIONARY, "Dictionary entry with name '%s' not found. Creating a new one.", name);
 
-               pthread_rwlock_wrlock(&dict->rwlock);
                nv = dictionary_name_value_create_nolock(dict, name, value, value_len, hash);
-
                if(unlikely(!nv))
                        fatal("Cannot create name_value.");
-
-               dictionary_unlock(dict);
        }
        else {
                debug(D_DICTIONARY, "Dictionary entry with name '%s' found. Changing its value.", name);
 
-               if(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE)
+               if(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE) {
+                       debug(D_REGISTRY, "Dictionary: linking value to '%s'", name);
                        nv->value = value;
+               }
                else {
+                       debug(D_REGISTRY, "Dictionary: cloning value to '%s'", name);
+
                        void *value = malloc(value_len),
                                        *old = nv->value;
 
@@ -172,10 +180,13 @@ void *dictionary_set(DICTIONARY *dict, const char *name, void *value, size_t val
                        memcpy(value, value, value_len);
                        nv->value = value;
 
+                       debug(D_REGISTRY, "Dictionary: freeing old value of '%s'", name);
                        free(old);
                }
        }
 
+       dictionary_unlock(dict);
+
        return nv->value;
 }
 
@@ -195,20 +206,66 @@ void *dictionary_get(DICTIONARY *dict, const char *name) {
        return nv->value;
 }
 
-void dictionary_del(DICTIONARY *dict, const char *name) {
+int dictionary_del(DICTIONARY *dict, const char *name) {
+       int ret;
+
        debug(D_DICTIONARY, "DEL dictionary entry with name '%s'.", name);
 
-       dictionary_read_lock(dict);
-       NAME_VALUE *nv = dictionary_name_value_index_find_nolock(dict, name, 0);
-       dictionary_unlock(dict);
+       dictionary_write_lock(dict);
 
+       NAME_VALUE *nv = dictionary_name_value_index_find_nolock(dict, name, 0);
        if(unlikely(!nv)) {
                debug(D_DICTIONARY, "Not found dictionary entry with name '%s'.", name);
-               return;
+               ret = -1;
+       }
+       else {
+               debug(D_DICTIONARY, "Found dictionary entry with name '%s'.", name);
+               dictionary_name_value_destroy_nolock(dict, nv);
+               ret = 0;
        }
 
-       debug(D_DICTIONARY, "Found dictionary entry with name '%s'.", name);
-       pthread_rwlock_wrlock(&dict->rwlock);
-       dictionary_name_value_destroy_nolock(dict, nv);
        dictionary_unlock(dict);
+
+       return ret;
+}
+
+
+// ----------------------------------------------------------------------------
+// API - walk through the dictionary
+// the dictionary is locked for reading while this happens
+// do not user other dictionary calls while walking the dictionary - deadlock!
+
+static int dictionary_walker(avl *a, int (*callback)(void *entry, void *data), void *data) {
+       int total = 0, ret = 0;
+
+       if(a->right) {
+               ret = dictionary_walker(a->right, callback, data);
+               if(ret < 0) return ret;
+               total += ret;
+       }
+
+       ret = callback(((NAME_VALUE *)a)->value, data);
+       if(ret < 0) return ret;
+       total += ret;
+
+       if(a->left) {
+               dictionary_walker(a->left, callback, data);
+               if (ret < 0) return ret;
+               total += ret;
+       }
+
+       return total;
+}
+
+int dictionary_get_all(DICTIONARY *dict, int (*callback)(void *entry, void *data), void *data) {
+       int ret = 0;
+
+       dictionary_read_lock(dict);
+
+       if(likely(dict->values_index.root))
+               ret = dictionary_walker(dict->values_index.root, callback, data);
+
+       dictionary_unlock(dict);
+
+       return ret;
 }