diff options
Diffstat (limited to '')
-rw-r--r-- | src/dictionary.c | 245 |
1 files changed, 178 insertions, 67 deletions
diff --git a/src/dictionary.c b/src/dictionary.c index 31f4d52e..1543f4d0 100644 --- a/src/dictionary.c +++ b/src/dictionary.c @@ -1,6 +1,7 @@ #ifdef HAVE_CONFIG_H #include <config.h> #endif + #include <pthread.h> #include <stdlib.h> #include <string.h> @@ -12,9 +13,32 @@ #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; @@ -22,93 +46,100 @@ static int name_value_compare(void* a, void* b) { else return strcmp(((NAME_VALUE *)a)->name, ((NAME_VALUE *)b)->name); } -#define name_value_index_add(dict, cv) avl_insert(&((dict)->values_index), (avl *)(cv)) -#define name_value_index_del(dict, cv) avl_remove(&((dict)->values_index), (avl *)(cv)) +#define dictionary_name_value_index_add_nolock(dict, nv) do { (dict)->inserts++; avl_insert(&((dict)->values_index), (avl *)(nv)); } while(0) +#define dictionary_name_value_index_del_nolock(dict, nv) do { (dict)->deletes++; avl_remove(&(dict->values_index), (avl *)(nv)); } while(0) -static NAME_VALUE *dictionary_name_value_index_find(DICTIONARY *dict, const char *name, uint32_t hash) { - NAME_VALUE *result = NULL, tmp; +static inline NAME_VALUE *dictionary_name_value_index_find_nolock(DICTIONARY *dict, const char *name, uint32_t hash) { + NAME_VALUE tmp; tmp.hash = (hash)?hash:simple_hash(name); tmp.name = (char *)name; - avl_search(&(dict->values_index), (avl *)&tmp, name_value_iterator, (avl **)&result); - return result; + dict->searches++; + return (NAME_VALUE *)avl_search(&(dict->values_index), (avl *) &tmp); } // ---------------------------------------------------------------------------- +// internal methods -static NAME_VALUE *dictionary_name_value_create(DICTIONARY *dict, const char *name, void *value, size_t value_len) { - debug(D_DICTIONARY, "Creating name value entry for name '%s', value '%s'.", name, value); +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'.", name); NAME_VALUE *nv = calloc(1, sizeof(NAME_VALUE)); - if(!nv) { - fatal("Cannot allocate name_value of size %z", sizeof(NAME_VALUE)); - exit(1); + if(unlikely(!nv)) fatal("Cannot allocate name_value of size %z", sizeof(NAME_VALUE)); + + if(dict->flags & DICTIONARY_FLAG_NAME_LINK_DONT_CLONE) + nv->name = (char *)name; + else { + nv->name = strdup(name); + if (unlikely(!nv->name)) + fatal("Cannot allocate name_value.name of size %z", strlen(name)); } - nv->name = strdup(name); - if(!nv->name) fatal("Cannot allocate name_value.name of size %z", strlen(name)); - nv->hash = simple_hash(nv->name); + nv->hash = (hash)?hash:simple_hash(nv->name); - nv->value = malloc(value_len); - if(!nv->value) fatal("Cannot allocate name_value.value of size %z", value_len); - memcpy(nv->value, value, value_len); + if(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE) + nv->value = value; + else { + nv->value = malloc(value_len); + if (unlikely(!nv->value)) + fatal("Cannot allocate name_value.value of size %z", value_len); - // link it - pthread_rwlock_wrlock(&dict->rwlock); - nv->next = dict->values; - dict->values = nv; - pthread_rwlock_unlock(&dict->rwlock); + memcpy(nv->value, value, value_len); + } // index it - name_value_index_add(dict, nv); + dictionary_name_value_index_add_nolock(dict, nv); + dict->entries++; return nv; } -static void dictionary_name_value_destroy(DICTIONARY *dict, NAME_VALUE *nv) { +static void dictionary_name_value_destroy_nolock(DICTIONARY *dict, NAME_VALUE *nv) { debug(D_DICTIONARY, "Destroying name value entry for name '%s'.", nv->name); - pthread_rwlock_wrlock(&dict->rwlock); - if(dict->values == nv) dict->values = nv->next; - else { - NAME_VALUE *n = dict->values; - while(n && n->next && n->next != nv) nv = nv->next; - if(!n || n->next != nv) { - fatal("Cannot find name_value with name '%s' in dictionary.", nv->name); - exit(1); - } - n->next = nv->next; - nv->next = NULL; + dictionary_name_value_index_del_nolock(dict, nv); + + dict->entries--; + + 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); } - pthread_rwlock_unlock(&dict->rwlock); - free(nv->value); free(nv); } // ---------------------------------------------------------------------------- +// API - basic methods -DICTIONARY *dictionary_create(void) { +DICTIONARY *dictionary_create(uint32_t flags) { debug(D_DICTIONARY, "Creating dictionary."); DICTIONARY *dict = calloc(1, sizeof(DICTIONARY)); - if(!dict) { - fatal("Cannot allocate DICTIONARY"); - exit(1); - } + if(unlikely(!dict)) fatal("Cannot allocate DICTIONARY"); avl_init(&dict->values_index, name_value_compare); pthread_rwlock_init(&dict->rwlock, NULL); + dict->flags = flags; + return dict; } void dictionary_destroy(DICTIONARY *dict) { debug(D_DICTIONARY, "Destroying dictionary."); - pthread_rwlock_wrlock(&dict->rwlock); - while(dict->values) dictionary_name_value_destroy(dict, dict->values); - pthread_rwlock_unlock(&dict->rwlock); + dictionary_write_lock(dict); + + while(dict->values_index.root) + dictionary_name_value_destroy_nolock(dict, (NAME_VALUE *)dict->values_index.root); + + dictionary_unlock(dict); free(dict); } @@ -118,39 +149,55 @@ void dictionary_destroy(DICTIONARY *dict) { void *dictionary_set(DICTIONARY *dict, const char *name, void *value, size_t value_len) { debug(D_DICTIONARY, "SET dictionary entry with name '%s'.", name); - pthread_rwlock_rdlock(&dict->rwlock); - NAME_VALUE *nv = dictionary_name_value_index_find(dict, name, 0); - pthread_rwlock_unlock(&dict->rwlock); - if(!nv) { + uint32_t hash = simple_hash(name); + + 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); - nv = dictionary_name_value_create(dict, name, value, value_len); - if(!nv) { + + nv = dictionary_name_value_create_nolock(dict, name, value, value_len, hash); + if(unlikely(!nv)) fatal("Cannot create name_value."); - exit(1); - } - return nv->value; } else { debug(D_DICTIONARY, "Dictionary entry with name '%s' found. Changing its value.", name); - pthread_rwlock_wrlock(&dict->rwlock); - void *old = nv->value; - nv->value = malloc(value_len); - if(!nv->value) fatal("Cannot allocate value of size %z", value_len); - memcpy(nv->value, value, value_len); - pthread_rwlock_unlock(&dict->rwlock); - free(old); + + 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; + + if(unlikely(!nv->value)) + fatal("Cannot allocate value of size %z", value_len); + + 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; } void *dictionary_get(DICTIONARY *dict, const char *name) { debug(D_DICTIONARY, "GET dictionary entry with name '%s'.", name); - pthread_rwlock_rdlock(&dict->rwlock); - NAME_VALUE *nv = dictionary_name_value_index_find(dict, name, 0); - pthread_rwlock_unlock(&dict->rwlock); - if(!nv) { + dictionary_read_lock(dict); + NAME_VALUE *nv = dictionary_name_value_index_find_nolock(dict, name, 0); + dictionary_unlock(dict); + + if(unlikely(!nv)) { debug(D_DICTIONARY, "Not found dictionary entry with name '%s'.", name); return NULL; } @@ -158,3 +205,67 @@ void *dictionary_get(DICTIONARY *dict, const char *name) { debug(D_DICTIONARY, "Found dictionary entry with name '%s'.", name); return nv->value; } + +int dictionary_del(DICTIONARY *dict, const char *name) { + int ret; + + debug(D_DICTIONARY, "DEL dictionary entry with name '%s'.", name); + + 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); + ret = -1; + } + else { + debug(D_DICTIONARY, "Found dictionary entry with name '%s'.", name); + dictionary_name_value_destroy_nolock(dict, nv); + ret = 0; + } + + 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; +} |