]> arthur.barton.de Git - netdata.git/blob - src/dictionary.c
replaced AVL by Daniel A. Nagy (GPLv2) with an adaptation of libavl by Ben Pfaff...
[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 statistics
17
18 static inline void NETDATA_DICTIONARY_STATS_INSERTS_PLUS1(DICTIONARY *dict) {
19         if(likely(dict->stats))
20                 dict->stats->inserts++;
21 }
22 static inline void NETDATA_DICTIONARY_STATS_DELETES_PLUS1(DICTIONARY *dict) {
23         if(likely(dict->stats))
24                 dict->stats->deletes++;
25 }
26 static inline void NETDATA_DICTIONARY_STATS_SEARCHES_PLUS1(DICTIONARY *dict) {
27         if(likely(dict->stats))
28                 dict->stats->searches++;
29 }
30 static inline void NETDATA_DICTIONARY_STATS_ENTRIES_PLUS1(DICTIONARY *dict) {
31         if(likely(dict->stats))
32                 dict->stats->entries++;
33 }
34 static inline void NETDATA_DICTIONARY_STATS_ENTRIES_MINUS1(DICTIONARY *dict) {
35         if(likely(dict->stats))
36                 dict->stats->entries--;
37 }
38
39
40 // ----------------------------------------------------------------------------
41 // dictionary locks
42
43 static inline void dictionary_read_lock(DICTIONARY *dict) {
44         if(likely(dict->rwlock)) {
45                 // debug(D_DICTIONARY, "Dictionary READ lock");
46                 pthread_rwlock_rdlock(dict->rwlock);
47         }
48 }
49
50 static inline void dictionary_write_lock(DICTIONARY *dict) {
51         if(likely(dict->rwlock)) {
52                 // debug(D_DICTIONARY, "Dictionary WRITE lock");
53                 pthread_rwlock_wrlock(dict->rwlock);
54         }
55 }
56
57 static inline void dictionary_unlock(DICTIONARY *dict) {
58         if(likely(dict->rwlock)) {
59                 // debug(D_DICTIONARY, "Dictionary UNLOCK lock");
60                 pthread_rwlock_unlock(dict->rwlock);
61         }
62 }
63
64
65 // ----------------------------------------------------------------------------
66 // avl index
67
68 static int name_value_compare(void* a, void* b) {
69         if(((NAME_VALUE *)a)->hash < ((NAME_VALUE *)b)->hash) return -1;
70         else if(((NAME_VALUE *)a)->hash > ((NAME_VALUE *)b)->hash) return 1;
71         else return strcmp(((NAME_VALUE *)a)->name, ((NAME_VALUE *)b)->name);
72 }
73
74 #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)
75 #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)
76
77 static inline NAME_VALUE *dictionary_name_value_index_find_nolock(DICTIONARY *dict, const char *name, uint32_t hash) {
78         NAME_VALUE tmp;
79         tmp.hash = (hash)?hash:simple_hash(name);
80         tmp.name = (char *)name;
81
82         NETDATA_DICTIONARY_STATS_SEARCHES_PLUS1(dict);
83         return (NAME_VALUE *)avl_search(&(dict->values_index), (avl *) &tmp);
84 }
85
86 // ----------------------------------------------------------------------------
87 // internal methods
88
89 static NAME_VALUE *dictionary_name_value_create_nolock(DICTIONARY *dict, const char *name, void *value, size_t value_len, uint32_t hash) {
90         debug(D_DICTIONARY, "Creating name value entry for name '%s'.", name);
91
92         NAME_VALUE *nv = calloc(1, sizeof(NAME_VALUE));
93         if(unlikely(!nv)) fatal("Cannot allocate name_value of size %zu", sizeof(NAME_VALUE));
94
95         if(dict->flags & DICTIONARY_FLAG_NAME_LINK_DONT_CLONE)
96                 nv->name = (char *)name;
97         else {
98                 nv->name = strdup(name);
99                 if (unlikely(!nv->name))
100                         fatal("Cannot allocate name_value.name of size %zu", strlen(name));
101         }
102
103         nv->hash = (hash)?hash:simple_hash(nv->name);
104
105         if(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE)
106                 nv->value = value;
107         else {
108                 nv->value = malloc(value_len);
109                 if (unlikely(!nv->value))
110                         fatal("Cannot allocate name_value.value of size %zu", value_len);
111
112                 memcpy(nv->value, value, value_len);
113         }
114
115         // index it
116         dictionary_name_value_index_add_nolock(dict, nv);
117         NETDATA_DICTIONARY_STATS_ENTRIES_PLUS1(dict);
118
119         return nv;
120 }
121
122 static void dictionary_name_value_destroy_nolock(DICTIONARY *dict, NAME_VALUE *nv) {
123         debug(D_DICTIONARY, "Destroying name value entry for name '%s'.", nv->name);
124
125         dictionary_name_value_index_del_nolock(dict, nv);
126
127         NETDATA_DICTIONARY_STATS_ENTRIES_MINUS1(dict);
128
129         if(!(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE)) {
130                 debug(D_REGISTRY, "Dictionary freeing value of '%s'", nv->name);
131                 free(nv->value);
132         }
133
134         if(!(dict->flags & DICTIONARY_FLAG_NAME_LINK_DONT_CLONE)) {
135                 debug(D_REGISTRY, "Dictionary freeing name '%s'", nv->name);
136                 free(nv->name);
137         }
138
139         free(nv);
140 }
141
142 // ----------------------------------------------------------------------------
143 // API - basic methods
144
145 DICTIONARY *dictionary_create(uint32_t flags) {
146         debug(D_DICTIONARY, "Creating dictionary.");
147
148         DICTIONARY *dict = calloc(1, sizeof(DICTIONARY));
149         if(unlikely(!dict)) fatal("Cannot allocate DICTIONARY");
150
151         if(flags & DICTIONARY_FLAG_WITH_STATISTICS) {
152                 dict->stats = calloc(1, sizeof(struct dictionary_stats));
153                 if(!dict->stats) fatal("Cannot allocate statistics for DICTIONARY");
154         }
155
156         if(!(flags & DICTIONARY_FLAG_SINGLE_THREADED)) {
157                 dict->rwlock = calloc(1, sizeof(pthread_rwlock_t));
158                 if(!dict->rwlock) fatal("Cannot allocate pthread_rwlock_t for DICTIONARY");
159                 pthread_rwlock_init(dict->rwlock, NULL);
160         }
161
162         avl_init(&dict->values_index, name_value_compare);
163         dict->flags = flags;
164
165         return dict;
166 }
167
168 void dictionary_destroy(DICTIONARY *dict) {
169         debug(D_DICTIONARY, "Destroying dictionary.");
170
171         dictionary_write_lock(dict);
172
173         while(dict->values_index.root)
174                 dictionary_name_value_destroy_nolock(dict, (NAME_VALUE *)dict->values_index.root);
175
176         dictionary_unlock(dict);
177
178         if(dict->stats)
179                 free(dict->stats);
180
181         if(dict->rwlock)
182                 free(dict->rwlock);
183
184         free(dict);
185 }
186
187 // ----------------------------------------------------------------------------
188
189 void *dictionary_set(DICTIONARY *dict, const char *name, void *value, size_t value_len) {
190         debug(D_DICTIONARY, "SET dictionary entry with name '%s'.", name);
191
192         uint32_t hash = simple_hash(name);
193
194         dictionary_write_lock(dict);
195
196         NAME_VALUE *nv = dictionary_name_value_index_find_nolock(dict, name, hash);
197         if(unlikely(!nv)) {
198                 debug(D_DICTIONARY, "Dictionary entry with name '%s' not found. Creating a new one.", name);
199
200                 nv = dictionary_name_value_create_nolock(dict, name, value, value_len, hash);
201                 if(unlikely(!nv))
202                         fatal("Cannot create name_value.");
203         }
204         else {
205                 debug(D_DICTIONARY, "Dictionary entry with name '%s' found. Changing its value.", name);
206
207                 if(dict->flags & DICTIONARY_FLAG_VALUE_LINK_DONT_CLONE) {
208                         debug(D_REGISTRY, "Dictionary: linking value to '%s'", name);
209                         nv->value = value;
210                 }
211                 else {
212                         debug(D_REGISTRY, "Dictionary: cloning value to '%s'", name);
213
214                         // copy the new value without breaking
215                         // any other thread accessing the same entry
216                         void *new = malloc(value_len),
217                                         *old = nv->value;
218
219                         if(unlikely(!new))
220                                 fatal("Cannot allocate value of size %zu", value_len);
221
222                         memcpy(new, value, value_len);
223                         nv->value = new;
224
225                         debug(D_REGISTRY, "Dictionary: freeing old value of '%s'", name);
226                         free(old);
227                 }
228         }
229
230         dictionary_unlock(dict);
231
232         return nv->value;
233 }
234
235 void *dictionary_get(DICTIONARY *dict, const char *name) {
236         debug(D_DICTIONARY, "GET dictionary entry with name '%s'.", name);
237
238         dictionary_read_lock(dict);
239         NAME_VALUE *nv = dictionary_name_value_index_find_nolock(dict, name, 0);
240         dictionary_unlock(dict);
241
242         if(unlikely(!nv)) {
243                 debug(D_DICTIONARY, "Not found dictionary entry with name '%s'.", name);
244                 return NULL;
245         }
246
247         debug(D_DICTIONARY, "Found dictionary entry with name '%s'.", name);
248         return nv->value;
249 }
250
251 int dictionary_del(DICTIONARY *dict, const char *name) {
252         int ret;
253
254         debug(D_DICTIONARY, "DEL dictionary entry with name '%s'.", name);
255
256         dictionary_write_lock(dict);
257
258         NAME_VALUE *nv = dictionary_name_value_index_find_nolock(dict, name, 0);
259         if(unlikely(!nv)) {
260                 debug(D_DICTIONARY, "Not found dictionary entry with name '%s'.", name);
261                 ret = -1;
262         }
263         else {
264                 debug(D_DICTIONARY, "Found dictionary entry with name '%s'.", name);
265                 dictionary_name_value_destroy_nolock(dict, nv);
266                 ret = 0;
267         }
268
269         dictionary_unlock(dict);
270
271         return ret;
272 }
273
274
275 // ----------------------------------------------------------------------------
276 // API - walk through the dictionary
277 // the dictionary is locked for reading while this happens
278 // do not user other dictionary calls while walking the dictionary - deadlock!
279
280 static int dictionary_walker(avl *a, int (*callback)(void *entry, void *data), void *data) {
281         int total = 0, ret = 0;
282
283         if(a->avl_link[0]) {
284                 ret = dictionary_walker(a->avl_link[0], callback, data);
285                 if(ret < 0) return ret;
286                 total += ret;
287         }
288
289         ret = callback(((NAME_VALUE *)a)->value, data);
290         if(ret < 0) return ret;
291         total += ret;
292
293         if(a->avl_link[1]) {
294                 ret = dictionary_walker(a->avl_link[1], callback, data);
295                 if (ret < 0) return ret;
296                 total += ret;
297         }
298
299         return total;
300 }
301
302 int dictionary_get_all(DICTIONARY *dict, int (*callback)(void *entry, void *data), void *data) {
303         int ret = 0;
304
305         dictionary_read_lock(dict);
306
307         if(likely(dict->values_index.root))
308                 ret = dictionary_walker(dict->values_index.root, callback, data);
309
310         dictionary_unlock(dict);
311
312         return ret;
313 }