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