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