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