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