3 // ----------------------------------------------------------------------------
4 // dictionary statistics
6 static inline void NETDATA_DICTIONARY_STATS_INSERTS_PLUS1(DICTIONARY *dict) {
7 if(likely(dict->stats))
8 dict->stats->inserts++;
10 static inline void NETDATA_DICTIONARY_STATS_DELETES_PLUS1(DICTIONARY *dict) {
11 if(likely(dict->stats))
12 dict->stats->deletes++;
14 static inline void NETDATA_DICTIONARY_STATS_SEARCHES_PLUS1(DICTIONARY *dict) {
15 if(likely(dict->stats))
16 dict->stats->searches++;
18 static inline void NETDATA_DICTIONARY_STATS_ENTRIES_PLUS1(DICTIONARY *dict) {
19 if(likely(dict->stats))
20 dict->stats->entries++;
22 static inline void NETDATA_DICTIONARY_STATS_ENTRIES_MINUS1(DICTIONARY *dict) {
23 if(likely(dict->stats))
24 dict->stats->entries--;
28 // ----------------------------------------------------------------------------
31 static inline void dictionary_read_lock(DICTIONARY *dict) {
32 if(likely(dict->rwlock)) {
33 // debug(D_DICTIONARY, "Dictionary READ lock");
34 pthread_rwlock_rdlock(dict->rwlock);
38 static inline void dictionary_write_lock(DICTIONARY *dict) {
39 if(likely(dict->rwlock)) {
40 // debug(D_DICTIONARY, "Dictionary WRITE lock");
41 pthread_rwlock_wrlock(dict->rwlock);
45 static inline void dictionary_unlock(DICTIONARY *dict) {
46 if(likely(dict->rwlock)) {
47 // debug(D_DICTIONARY, "Dictionary UNLOCK lock");
48 pthread_rwlock_unlock(dict->rwlock);
53 // ----------------------------------------------------------------------------
56 static int name_value_compare(void* a, void* b) {
57 if(((NAME_VALUE *)a)->hash < ((NAME_VALUE *)b)->hash) return -1;
58 else if(((NAME_VALUE *)a)->hash > ((NAME_VALUE *)b)->hash) return 1;
59 else return strcmp(((NAME_VALUE *)a)->name, ((NAME_VALUE *)b)->name);
62 #define dictionary_name_value_index_add_nolock(dict, nv) do { NETDATA_DICTIONARY_STATS_INSERTS_PLUS1(dict); avl_insert(&((dict)->values_index), (avl *)(nv)); } while(0)
63 #define dictionary_name_value_index_del_nolock(dict, nv) do { NETDATA_DICTIONARY_STATS_DELETES_PLUS1(dict); avl_remove(&(dict->values_index), (avl *)(nv)); } while(0)
65 static inline NAME_VALUE *dictionary_name_value_index_find_nolock(DICTIONARY *dict, const char *name, uint32_t hash) {
67 tmp.hash = (hash)?hash:simple_hash(name);
68 tmp.name = (char *)name;
70 NETDATA_DICTIONARY_STATS_SEARCHES_PLUS1(dict);
71 return (NAME_VALUE *)avl_search(&(dict->values_index), (avl *) &tmp);
74 // ----------------------------------------------------------------------------
77 static NAME_VALUE *dictionary_name_value_create_nolock(DICTIONARY *dict, const char *name, void *value, size_t value_len, uint32_t hash) {
78 debug(D_DICTIONARY, "Creating name value entry for name '%s'.", name);
80 NAME_VALUE *nv = calloc(1, sizeof(NAME_VALUE));
81 if(unlikely(!nv)) fatal("Cannot allocate name_value of size %zu", sizeof(NAME_VALUE));
83 if(dict->flags & DICTIONARY_FLAG_NAME_LINK_DONT_CLONE)
84 nv->name = (char *)name;
86 nv->name = strdup(name);
87 if (unlikely(!nv->name))
88 fatal("Cannot allocate name_value.name of size %zu", strlen(name));
91 nv->hash = (hash)?hash:simple_hash(nv->name);
93 if(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE)
96 nv->value = malloc(value_len);
97 if (unlikely(!nv->value))
98 fatal("Cannot allocate name_value.value of size %zu", value_len);
100 memcpy(nv->value, value, value_len);
104 dictionary_name_value_index_add_nolock(dict, nv);
105 NETDATA_DICTIONARY_STATS_ENTRIES_PLUS1(dict);
110 static void dictionary_name_value_destroy_nolock(DICTIONARY *dict, NAME_VALUE *nv) {
111 debug(D_DICTIONARY, "Destroying name value entry for name '%s'.", nv->name);
113 dictionary_name_value_index_del_nolock(dict, nv);
115 NETDATA_DICTIONARY_STATS_ENTRIES_MINUS1(dict);
117 if(!(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE)) {
118 debug(D_REGISTRY, "Dictionary freeing value of '%s'", nv->name);
122 if(!(dict->flags & DICTIONARY_FLAG_NAME_LINK_DONT_CLONE)) {
123 debug(D_REGISTRY, "Dictionary freeing name '%s'", nv->name);
130 // ----------------------------------------------------------------------------
131 // API - basic methods
133 DICTIONARY *dictionary_create(uint32_t flags) {
134 debug(D_DICTIONARY, "Creating dictionary.");
136 DICTIONARY *dict = calloc(1, sizeof(DICTIONARY));
137 if(unlikely(!dict)) fatal("Cannot allocate DICTIONARY");
139 if(flags & DICTIONARY_FLAG_WITH_STATISTICS) {
140 dict->stats = calloc(1, sizeof(struct dictionary_stats));
141 if(!dict->stats) fatal("Cannot allocate statistics for DICTIONARY");
144 if(!(flags & DICTIONARY_FLAG_SINGLE_THREADED)) {
145 dict->rwlock = calloc(1, sizeof(pthread_rwlock_t));
146 if(!dict->rwlock) fatal("Cannot allocate pthread_rwlock_t for DICTIONARY");
147 pthread_rwlock_init(dict->rwlock, NULL);
150 avl_init(&dict->values_index, name_value_compare);
156 void dictionary_destroy(DICTIONARY *dict) {
157 debug(D_DICTIONARY, "Destroying dictionary.");
159 dictionary_write_lock(dict);
161 while(dict->values_index.root)
162 dictionary_name_value_destroy_nolock(dict, (NAME_VALUE *)dict->values_index.root);
164 dictionary_unlock(dict);
175 // ----------------------------------------------------------------------------
177 void *dictionary_set(DICTIONARY *dict, const char *name, void *value, size_t value_len) {
178 debug(D_DICTIONARY, "SET dictionary entry with name '%s'.", name);
180 uint32_t hash = simple_hash(name);
182 dictionary_write_lock(dict);
184 NAME_VALUE *nv = dictionary_name_value_index_find_nolock(dict, name, hash);
186 debug(D_DICTIONARY, "Dictionary entry with name '%s' not found. Creating a new one.", name);
188 nv = dictionary_name_value_create_nolock(dict, name, value, value_len, hash);
190 fatal("Cannot create name_value.");
193 debug(D_DICTIONARY, "Dictionary entry with name '%s' found. Changing its value.", name);
195 if(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE) {
196 debug(D_REGISTRY, "Dictionary: linking value to '%s'", name);
200 debug(D_REGISTRY, "Dictionary: cloning value to '%s'", name);
202 // copy the new value without breaking
203 // any other thread accessing the same entry
204 void *new = malloc(value_len),
208 fatal("Cannot allocate value of size %zu", value_len);
210 memcpy(new, value, value_len);
213 debug(D_REGISTRY, "Dictionary: freeing old value of '%s'", name);
218 dictionary_unlock(dict);
223 void *dictionary_get(DICTIONARY *dict, const char *name) {
224 debug(D_DICTIONARY, "GET dictionary entry with name '%s'.", name);
226 dictionary_read_lock(dict);
227 NAME_VALUE *nv = dictionary_name_value_index_find_nolock(dict, name, 0);
228 dictionary_unlock(dict);
231 debug(D_DICTIONARY, "Not found dictionary entry with name '%s'.", name);
235 debug(D_DICTIONARY, "Found dictionary entry with name '%s'.", name);
239 int dictionary_del(DICTIONARY *dict, const char *name) {
242 debug(D_DICTIONARY, "DEL dictionary entry with name '%s'.", name);
244 dictionary_write_lock(dict);
246 NAME_VALUE *nv = dictionary_name_value_index_find_nolock(dict, name, 0);
248 debug(D_DICTIONARY, "Not found dictionary entry with name '%s'.", name);
252 debug(D_DICTIONARY, "Found dictionary entry with name '%s'.", name);
253 dictionary_name_value_destroy_nolock(dict, nv);
257 dictionary_unlock(dict);
263 // ----------------------------------------------------------------------------
264 // API - walk through the dictionary
265 // the dictionary is locked for reading while this happens
266 // do not user other dictionary calls while walking the dictionary - deadlock!
268 static int dictionary_walker(avl *a, int (*callback)(void *entry, void *data), void *data) {
269 int total = 0, ret = 0;
272 ret = dictionary_walker(a->avl_link[0], callback, data);
273 if(ret < 0) return ret;
277 ret = callback(((NAME_VALUE *)a)->value, data);
278 if(ret < 0) return ret;
282 ret = dictionary_walker(a->avl_link[1], callback, data);
283 if (ret < 0) return ret;
290 int dictionary_get_all(DICTIONARY *dict, int (*callback)(void *entry, void *data), void *data) {
293 dictionary_read_lock(dict);
295 if(likely(dict->values_index.root))
296 ret = dictionary_walker(dict->values_index.root, callback, data);
298 dictionary_unlock(dict);