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