]> arthur.barton.de Git - netdata.git/blob - src/dictionary.c
Merge remote-tracking branch 'fredericopissarra/changes' into registry
[netdata.git] / src / dictionary.c
1 #ifdef HAVE_CONFIG_H
2 #include <config.h>
3 #endif
4
5 #include <pthread.h>
6 #include <stdlib.h>
7 #include <string.h>
8
9 #include "avl.h"
10 #include "common.h"
11 #include "log.h"
12
13 #include "dictionary.h"
14
15 // ----------------------------------------------------------------------------
16 // dictionary locks
17
18 static inline void dictionary_read_lock(DICTIONARY *dict) {
19         if(likely(!(dict->flags & DICTIONARY_FLAG_SINGLE_THREADED))) {
20                 // debug(D_DICTIONARY, "Dictionary READ lock");
21                 pthread_rwlock_rdlock(&dict->rwlock);
22         }
23 }
24
25 static inline void dictionary_write_lock(DICTIONARY *dict) {
26         if(likely(!(dict->flags & DICTIONARY_FLAG_SINGLE_THREADED))) {
27                 // debug(D_DICTIONARY, "Dictionary WRITE lock");
28                 pthread_rwlock_wrlock(&dict->rwlock);
29         }
30 }
31
32 static inline void dictionary_unlock(DICTIONARY *dict) {
33         if(likely(!(dict->flags & DICTIONARY_FLAG_SINGLE_THREADED))) {
34                 // debug(D_DICTIONARY, "Dictionary UNLOCK lock");
35                 pthread_rwlock_unlock(&dict->rwlock);
36         }
37 }
38
39
40 // ----------------------------------------------------------------------------
41 // avl index
42
43 static int name_value_iterator(avl *a) { if(a) {}; return 0; }
44
45 static int name_value_compare(void* a, void* b) {
46         if(((NAME_VALUE *)a)->hash < ((NAME_VALUE *)b)->hash) return -1;
47         else if(((NAME_VALUE *)a)->hash > ((NAME_VALUE *)b)->hash) return 1;
48         else return strcmp(((NAME_VALUE *)a)->name, ((NAME_VALUE *)b)->name);
49 }
50
51 #define dictionary_name_value_index_add_nolock(dict, nv) do { (dict)->inserts++; avl_insert(&((dict)->values_index), (avl *)(nv)); } while(0)
52 #define dictionary_name_value_index_del_nolock(dict, nv) do { (dict)->deletes++; avl_remove(&(dict->values_index), (avl *)(nv)); } while(0)
53
54 static inline NAME_VALUE *dictionary_name_value_index_find_nolock(DICTIONARY *dict, const char *name, uint32_t hash) {
55         NAME_VALUE *result = NULL, tmp;
56         tmp.hash = (hash)?hash:simple_hash(name);
57         tmp.name = (char *)name;
58
59         dict->searches++;
60         avl_search(&(dict->values_index), (avl *) &tmp, name_value_iterator, (avl **) &result);
61
62         return result;
63 }
64
65 // ----------------------------------------------------------------------------
66 // internal methods
67
68 static NAME_VALUE *dictionary_name_value_create_nolock(DICTIONARY *dict, const char *name, void *value, size_t value_len, uint32_t hash) {
69         debug(D_DICTIONARY, "Creating name value entry for name '%s'.", name);
70
71         NAME_VALUE *nv = calloc(1, sizeof(NAME_VALUE));
72         if(unlikely(!nv)) fatal("Cannot allocate name_value of size %z", sizeof(NAME_VALUE));
73
74         if(dict->flags & DICTIONARY_FLAG_NAME_LINK_DONT_CLONE)
75                 nv->name = (char *)name;
76         else {
77                 nv->name = strdup(name);
78                 if (unlikely(!nv->name))
79                         fatal("Cannot allocate name_value.name of size %z", strlen(name));
80         }
81
82         nv->hash = (hash)?hash:simple_hash(nv->name);
83
84         if(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE)
85                 nv->value = value;
86         else {
87                 nv->value = malloc(value_len);
88                 if (unlikely(!nv->value))
89                         fatal("Cannot allocate name_value.value of size %z", value_len);
90
91                 memcpy(nv->value, value, value_len);
92         }
93
94         // index it
95         dictionary_name_value_index_add_nolock(dict, nv);
96         dict->entries++;
97
98         return nv;
99 }
100
101 static void dictionary_name_value_destroy_nolock(DICTIONARY *dict, NAME_VALUE *nv) {
102         debug(D_DICTIONARY, "Destroying name value entry for name '%s'.", nv->name);
103
104         dictionary_name_value_index_del_nolock(dict, nv);
105
106         dict->entries--;
107
108         if(!(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE)) {
109                 debug(D_REGISTRY, "Dictionary freeing value of '%s'", nv->name);
110                 free(nv->value);
111         }
112
113         if(!(dict->flags & DICTIONARY_FLAG_NAME_LINK_DONT_CLONE)) {
114                 debug(D_REGISTRY, "Dictionary freeing name '%s'", nv->name);
115                 free(nv->name);
116         }
117
118         free(nv);
119 }
120
121 // ----------------------------------------------------------------------------
122 // API - basic methods
123
124 DICTIONARY *dictionary_create(uint32_t flags) {
125         debug(D_DICTIONARY, "Creating dictionary.");
126
127         DICTIONARY *dict = calloc(1, sizeof(DICTIONARY));
128         if(unlikely(!dict)) fatal("Cannot allocate DICTIONARY");
129
130         avl_init(&dict->values_index, name_value_compare);
131         pthread_rwlock_init(&dict->rwlock, NULL);
132
133         dict->flags = flags;
134
135         return dict;
136 }
137
138 void dictionary_destroy(DICTIONARY *dict) {
139         debug(D_DICTIONARY, "Destroying dictionary.");
140
141         dictionary_write_lock(dict);
142
143         while(dict->values_index.root)
144                 dictionary_name_value_destroy_nolock(dict, (NAME_VALUE *)dict->values_index.root);
145
146         dictionary_unlock(dict);
147
148         free(dict);
149 }
150
151 // ----------------------------------------------------------------------------
152
153 void *dictionary_set(DICTIONARY *dict, const char *name, void *value, size_t value_len) {
154         debug(D_DICTIONARY, "SET dictionary entry with name '%s'.", name);
155
156         uint32_t hash = simple_hash(name);
157
158         dictionary_write_lock(dict);
159
160         NAME_VALUE *nv = dictionary_name_value_index_find_nolock(dict, name, hash);
161         if(unlikely(!nv)) {
162                 debug(D_DICTIONARY, "Dictionary entry with name '%s' not found. Creating a new one.", name);
163
164                 nv = dictionary_name_value_create_nolock(dict, name, value, value_len, hash);
165                 if(unlikely(!nv))
166                         fatal("Cannot create name_value.");
167         }
168         else {
169                 debug(D_DICTIONARY, "Dictionary entry with name '%s' found. Changing its value.", name);
170
171                 if(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE) {
172                         debug(D_REGISTRY, "Dictionary: linking value to '%s'", name);
173                         nv->value = value;
174                 }
175                 else {
176                         debug(D_REGISTRY, "Dictionary: cloning value to '%s'", name);
177
178                         void *value = malloc(value_len),
179                                         *old = nv->value;
180
181                         if(unlikely(!nv->value))
182                                 fatal("Cannot allocate value of size %z", value_len);
183
184                         memcpy(value, value, value_len);
185                         nv->value = value;
186
187                         debug(D_REGISTRY, "Dictionary: freeing old value of '%s'", name);
188                         free(old);
189                 }
190         }
191
192         dictionary_unlock(dict);
193
194         return nv->value;
195 }
196
197 void *dictionary_get(DICTIONARY *dict, const char *name) {
198         debug(D_DICTIONARY, "GET dictionary entry with name '%s'.", name);
199
200         dictionary_read_lock(dict);
201         NAME_VALUE *nv = dictionary_name_value_index_find_nolock(dict, name, 0);
202         dictionary_unlock(dict);
203
204         if(unlikely(!nv)) {
205                 debug(D_DICTIONARY, "Not found dictionary entry with name '%s'.", name);
206                 return NULL;
207         }
208
209         debug(D_DICTIONARY, "Found dictionary entry with name '%s'.", name);
210         return nv->value;
211 }
212
213 int dictionary_del(DICTIONARY *dict, const char *name) {
214         int ret;
215
216         debug(D_DICTIONARY, "DEL dictionary entry with name '%s'.", name);
217
218         dictionary_write_lock(dict);
219
220         NAME_VALUE *nv = dictionary_name_value_index_find_nolock(dict, name, 0);
221         if(unlikely(!nv)) {
222                 debug(D_DICTIONARY, "Not found dictionary entry with name '%s'.", name);
223                 ret = -1;
224         }
225         else {
226                 debug(D_DICTIONARY, "Found dictionary entry with name '%s'.", name);
227                 dictionary_name_value_destroy_nolock(dict, nv);
228                 ret = 0;
229         }
230
231         dictionary_unlock(dict);
232
233         return ret;
234 }
235
236
237 // ----------------------------------------------------------------------------
238 // API - walk through the dictionary
239 // the dictionary is locked for reading while this happens
240 // do not user other dictionary calls while walking the dictionary - deadlock!
241
242 static int dictionary_walker(avl *a, int (*callback)(void *entry, void *data), void *data) {
243         int total = 0, ret = 0;
244
245         if(a->right) {
246                 ret = dictionary_walker(a->right, callback, data);
247                 if(ret < 0) return ret;
248                 total += ret;
249         }
250
251         ret = callback(((NAME_VALUE *)a)->value, data);
252         if(ret < 0) return ret;
253         total += ret;
254
255         if(a->left) {
256                 dictionary_walker(a->left, callback, data);
257                 if (ret < 0) return ret;
258                 total += ret;
259         }
260
261         return total;
262 }
263
264 int dictionary_get_all(DICTIONARY *dict, int (*callback)(void *entry, void *data), void *data) {
265         int ret = 0;
266
267         dictionary_read_lock(dict);
268
269         if(likely(dict->values_index.root))
270                 ret = dictionary_walker(dict->values_index.root, callback, data);
271
272         dictionary_unlock(dict);
273
274         return ret;
275 }