1
// SPDX-License-Identifier: GPL-3.0-or-later
2
3
-// NOT TO BE USED BY USERS YET
4
-#define DICTIONARY_FLAG_REFERENCE_COUNTERS (1 << 5) // maintain reference counter in walkthrough and foreach
3
+// NOT TO BE USED BY USERS
4
+#define DICTIONARY_FLAG_EXCLUSIVE_ACCESS (1 << 29) // there is only one thread accessing the dictionary
5
+#define DICTIONARY_FLAG_DESTROYED (1 << 30) // this dictionary has been destroyed
6
+#define DICTIONARY_FLAG_DEFER_ALL_DELETIONS (1 << 31) // defer all deletions of items in the dictionary
7
+
8
+// our reserved flags that cannot be set by users
9
+#define DICTIONARY_FLAGS_RESERVED (DICTIONARY_FLAG_EXCLUSIVE_ACCESS|DICTIONARY_FLAG_DESTROYED|DICTIONARY_FLAG_DEFER_ALL_DELETIONS)
10
11
typedef struct dictionary DICTIONARY;
12
#define DICTIONARY_INTERNALS
24
#include <Judy.h>
25
#endif
26
22
-/*
23
- * This version uses JudyHS arrays to index the dictionary
24
- *
25
- * The following output is from the unit test, at the end of this file:
26
- *
27
- * This is the JudyHS version:
28
- *
29
- * 1000000 x dictionary_set() (dictionary size 0 entries, 0 KB)...
30
- * 1000000 x dictionary_get(existing) (dictionary size 1000000 entries, 74001 KB)...
31
- * 1000000 x dictionary_get(non-existing) (dictionary size 1000000 entries, 74001 KB)...
32
- * Walking through the dictionary (dictionary size 1000000 entries, 74001 KB)...
33
- * 1000000 x dictionary_del(existing) (dictionary size 1000000 entries, 74001 KB)...
34
- * 1000000 x dictionary_set() (dictionary size 0 entries, 0 KB)...
35
- * Destroying dictionary (dictionary size 1000000 entries, 74001 KB)...
36
- *
37
- * TIMINGS:
38
- * adding 316027 usec, positive search 156740 usec, negative search 84524, walk through 15036 usec, deleting 361444, destroy 107394 usec
39
- *
40
- * This is from the JudySL version:
41
- *
42
- * Creating dictionary of 1000000 entries...
43
- * Checking index of 1000000 entries...
44
- * Walking 1000000 entries and checking name-value pairs...
45
- * Created and checked 1000000 entries, found 0 errors - used 58376 KB of memory
46
- * Destroying dictionary of 1000000 entries...
47
- * Deleted 1000000 entries
48
- * create 338975 usec, check 156080 usec, walk 80764 usec, destroy 444569 usec
49
- *
50
- * This is the AVL version:
51
- *
52
- * Creating dictionary of 1000000 entries...
53
- * Checking index of 1000000 entries...
54
- * Walking 1000000 entries and checking name-value pairs...
55
- * Created and checked 1000000 entries, found 0 errors - used 89626 KB of memory
56
- * Destroying dictionary of 1000000 entries...
57
- * create 413892 usec, check 220006 usec, walk 34247 usec, destroy 98062 usec
58
- *
59
- * So, the JudySL is a lot slower to WALK and DESTROY (DESTROY does a WALK)
60
- * It is slower, because for every item, JudySL copies the KEY/NAME to a
61
- * caller supplied buffer (Index). So, by just walking over 1 million items,
62
- * JudySL does 1 million strcpy() !!!
63
- *
64
- * It also seems that somehow JudySLDel() is unbelievably slow too!
65
- *
66
- */
67
-
27
+typedef enum name_value_flags {
28
+ NAME_VALUE_FLAG_NONE = 0,
29
+ NAME_VALUE_FLAG_DELETED = (1 << 0), // this item is deleted
30
+} NAME_VALUE_FLAGS;
31
32
/*
33
* Every item in the dictionary has the following structure.
34
*/
35
+
36
typedef struct name_value {
37
#ifdef DICTIONARY_WITH_AVL
38
avl_t avl_node;
47
void *value; // the value of the dictionary item
48
char *name; // the name of the dictionary item
49
50
+ int refcount; // the reference counter
51
+ NAME_VALUE_FLAGS flags; // the flags for this item
52
} NAME_VALUE;
53
88
-/*
89
- * When DICTIONARY_FLAG_REFERENCE_COUNTERS is set, we need to keep track of all the memory
90
- * we allocate and free. So, we need to keep track of the sizes of all names and values.
91
- * We do this by overloading NAME_VALUE with the following additional fields.
92
- */
93
-
94
-typedef enum name_value_flags {
95
- NAME_VALUE_FLAG_NONE = 0,
96
- NAME_VALUE_FLAG_DELETED = (1 << 0), // this item is deleted
97
-} NAME_VALUE_FLAGS;
98
-
99
-typedef struct name_value_with_reference_counters {
100
- NAME_VALUE name_value_data_here; // never used - just to put the lengths at the right position
101
-
102
- size_t refcount; // the reference counter
103
- NAME_VALUE_FLAGS flags; // the flags for this item
104
-} NAME_VALUE_WITH_REFERENCE_COUNTERS;
105
-
54
struct dictionary {
55
DICTIONARY_FLAGS flags; // the flags of the dictionary
56
77
void (*conflict_callback)(const char *name, void *old_value, void *new_value, void *data);
78
void *conflict_callback_data;
79
132
- size_t inserts;
133
- size_t deletes;
134
- size_t searches;
135
- size_t resets;
136
- size_t entries;
137
- size_t walkthroughs;
138
- size_t memory;
80
+ size_t inserts; // how many index insertions have been performed
81
+ size_t deletes; // how many index deletions have been performed
82
+ size_t searches; // how many index searches have been performed
83
+ size_t resets; // how many times items have reset their values
84
+ size_t walkthroughs; // how many walkthroughs have been done
85
+ long int memory; // how much memory the dictionary has currently allocated
86
+ long int entries; // how many items are currently in the index (the linked list may have more)
87
+ long int referenced_items; // how many items of the dictionary are currently being used by 3rd parties
88
+ long int pending_deletion_items; // how many items of the dictionary have been deleted, but have not been removed yet
89
+ int readers; // how many readers are currently using the dictionary
90
+ int writers; // how many writers are currently using the dictionary
91
};
92
93
+static inline void linkedlist_namevalue_unlink_unsafe(DICTIONARY *dict, NAME_VALUE *nv);
94
+static size_t namevalue_destroy_unsafe(DICTIONARY *dict, NAME_VALUE *nv);
95
+
96
+// ----------------------------------------------------------------------------
97
+// callbacks registration
98
+
99
void dictionary_register_insert_callback(DICTIONARY *dict, void (*ins_callback)(const char *name, void *value, void *data), void *data) {
100
dict->ins_callback = ins_callback;
101
dict->ins_callback_data = data;
114
// ----------------------------------------------------------------------------
115
// dictionary statistics maintenance
116
159
-size_t dictionary_stats_allocated_memory(DICTIONARY *dict) {
117
+long int dictionary_stats_allocated_memory(DICTIONARY *dict) {
118
return dict->memory;
119
}
162
-size_t dictionary_stats_entries(DICTIONARY *dict) {
120
+long int dictionary_stats_entries(DICTIONARY *dict) {
121
return dict->entries;
122
}
123
size_t dictionary_stats_searches(DICTIONARY *dict) {
137
}
138
139
static inline void DICTIONARY_STATS_SEARCHES_PLUS1(DICTIONARY *dict) {
182
- __atomic_fetch_add(&dict->searches, 1, __ATOMIC_SEQ_CST);
140
+ if(dict->flags & DICTIONARY_FLAG_EXCLUSIVE_ACCESS) {
141
+ dict->searches++;
142
+ }
143
+ else {
144
+ __atomic_fetch_add(&dict->searches, 1, __ATOMIC_RELAXED);
145
+ }
146
}
147
static inline void DICTIONARY_STATS_ENTRIES_PLUS1(DICTIONARY *dict, size_t size) {
185
- __atomic_fetch_add(&dict->inserts, 1, __ATOMIC_SEQ_CST);
186
- __atomic_fetch_add(&dict->entries, 1, __ATOMIC_SEQ_CST);
187
- __atomic_fetch_add(&dict->memory, size, __ATOMIC_SEQ_CST);
148
+ if(dict->flags & DICTIONARY_FLAG_EXCLUSIVE_ACCESS) {
149
+ dict->inserts++;
150
+ dict->entries++;
151
+ dict->memory += (long)size;
152
+ }
153
+ else {
154
+ __atomic_fetch_add(&dict->inserts, 1, __ATOMIC_RELAXED);
155
+ __atomic_fetch_add(&dict->entries, 1, __ATOMIC_RELAXED);
156
+ __atomic_fetch_add(&dict->memory, (long)size, __ATOMIC_RELAXED);
157
+ }
158
+}
159
+static inline void DICTIONARY_STATS_ENTRIES_MINUS1(DICTIONARY *dict) {
160
+ if(dict->flags & DICTIONARY_FLAG_EXCLUSIVE_ACCESS) {
161
+ dict->deletes++;
162
+ dict->entries--;
163
+ }
164
+ else {
165
+ __atomic_fetch_add(&dict->deletes, 1, __ATOMIC_RELAXED);
166
+ __atomic_fetch_sub(&dict->entries, 1, __ATOMIC_RELAXED);
167
+ }
168
}
189
-static inline void DICTIONARY_STATS_ENTRIES_MINUS1(DICTIONARY *dict, size_t size) {
190
- __atomic_fetch_add(&dict->deletes, 1, __ATOMIC_SEQ_CST);
191
- __atomic_fetch_sub(&dict->entries, 1, __ATOMIC_SEQ_CST);
192
- __atomic_fetch_sub(&dict->memory, size, __ATOMIC_SEQ_CST);
169
+static inline void DICTIONARY_STATS_ENTRIES_MINUS_MEMORY(DICTIONARY *dict, size_t size) {
170
+ if(dict->flags & DICTIONARY_FLAG_EXCLUSIVE_ACCESS) {
171
+ dict->memory -= (long)size;
172
+ }
173
+ else {
174
+ __atomic_fetch_sub(&dict->memory, (long)size, __ATOMIC_RELAXED);
175
+ }
176
}
177
static inline void DICTIONARY_STATS_VALUE_RESETS_PLUS1(DICTIONARY *dict, size_t oldsize, size_t newsize) {
195
- __atomic_fetch_add(&dict->resets, 1, __ATOMIC_SEQ_CST);
196
- __atomic_fetch_add(&dict->memory, newsize, __ATOMIC_SEQ_CST);
197
- __atomic_fetch_sub(&dict->memory, oldsize, __ATOMIC_SEQ_CST);
178
+ if(dict->flags & DICTIONARY_FLAG_EXCLUSIVE_ACCESS) {
179
+ dict->resets++;
180
+ dict->memory += (long)newsize;
181
+ dict->memory -= (long)oldsize;
182
+ }
183
+ else {
184
+ __atomic_fetch_add(&dict->resets, 1, __ATOMIC_RELAXED);
185
+ __atomic_fetch_add(&dict->memory, (long)newsize, __ATOMIC_RELAXED);
186
+ __atomic_fetch_sub(&dict->memory, (long)oldsize, __ATOMIC_RELAXED);
187
+ }
188
}
189
190
static inline void DICTIONARY_STATS_WALKTHROUGHS_PLUS1(DICTIONARY *dict) {
201
- __atomic_fetch_add(&dict->walkthroughs, 1, __ATOMIC_SEQ_CST);
191
+ if(dict->flags & DICTIONARY_FLAG_EXCLUSIVE_ACCESS) {
192
+ dict->walkthroughs++;
193
+ }
194
+ else {
195
+ __atomic_fetch_add(&dict->walkthroughs, 1, __ATOMIC_RELAXED);
196
+ }
197
+}
198
+
199
+static inline size_t DICTIONARY_STATS_REFERENCED_ITEMS_PLUS1(DICTIONARY *dict) {
200
+ return __atomic_add_fetch(&dict->referenced_items, 1, __ATOMIC_SEQ_CST);
201
+}
202
+
203
+static inline size_t DICTIONARY_STATS_REFERENCED_ITEMS_MINUS1(DICTIONARY *dict) {
204
+ return __atomic_sub_fetch(&dict->referenced_items, 1, __ATOMIC_SEQ_CST);
205
+}
206
+
207
+static inline size_t DICTIONARY_STATS_PENDING_DELETES_PLUS1(DICTIONARY *dict) {
208
+ return __atomic_add_fetch(&dict->pending_deletion_items, 1, __ATOMIC_SEQ_CST);
209
+}
210
+
211
+static inline size_t DICTIONARY_STATS_PENDING_DELETES_MINUS1(DICTIONARY *dict) {
212
+ return __atomic_sub_fetch(&dict->pending_deletion_items, 1, __ATOMIC_SEQ_CST);
213
+}
214
+
215
+static inline size_t DICTIONARY_STATS_PENDING_DELETES_GET(DICTIONARY *dict) {
216
+ return __atomic_load_n(&dict->pending_deletion_items, __ATOMIC_SEQ_CST);
217
+}
218
+
219
+static inline int DICTIONARY_NAME_VALUE_REFCOUNT_GET(NAME_VALUE *nv) {
220
+ return __atomic_load_n(&nv->refcount, __ATOMIC_SEQ_CST);
221
+}
222
+
223
+// ----------------------------------------------------------------------------
224
+// garbage collector
225
+// it is called every time someone gets a write lock to the dictionary
226
+
227
+static void garbage_collect_pending_deletes_unsafe(DICTIONARY *dict) {
228
+ if(!(dict->flags & DICTIONARY_FLAG_EXCLUSIVE_ACCESS)) return;
229
+
230
+ if(likely(!DICTIONARY_STATS_PENDING_DELETES_GET(dict))) return;
231
+
232
+ NAME_VALUE *nv = dict->first_item;
233
+ while(nv) {
234
+ if(nv->flags & NAME_VALUE_FLAG_DELETED && DICTIONARY_NAME_VALUE_REFCOUNT_GET(nv) == 0) {
235
+ NAME_VALUE *nv_next = nv->next;
236
+
237
+ linkedlist_namevalue_unlink_unsafe(dict, nv);
238
+ namevalue_destroy_unsafe(dict, nv);
239
+
240
+ size_t pending = DICTIONARY_STATS_PENDING_DELETES_MINUS1(dict);
241
+ if(!pending) break;
242
+
243
+ nv = nv_next;
244
+ }
245
+ else
246
+ nv = nv->next;
247
+ }
248
}
249
250
// ----------------------------------------------------------------------------
254
if(likely(!(dict->flags & DICTIONARY_FLAG_SINGLE_THREADED))) {
255
dict->rwlock = mallocz(sizeof(netdata_rwlock_t));
256
netdata_rwlock_init(dict->rwlock);
257
+
258
+ if(dict->flags & DICTIONARY_FLAG_EXCLUSIVE_ACCESS)
259
+ dict->flags &= ~DICTIONARY_FLAG_EXCLUSIVE_ACCESS;
260
+
261
return sizeof(netdata_rwlock_t);
262
}
263
+
264
+ // we are single threaded
265
+ dict->flags |= DICTIONARY_FLAG_EXCLUSIVE_ACCESS;
266
dict->rwlock = NULL;
267
return 0;
268
}
276
return 0;
277
}
278
226
-static inline void dictionary_lock_rlock(DICTIONARY *dict) {
227
- if(likely(!(dict->flags & DICTIONARY_FLAG_SINGLE_THREADED))) {
228
- // debug(D_DICTIONARY, "Dictionary READ lock");
279
+static void dictionary_lock(DICTIONARY *dict, char rw) {
280
+ if(rw == 'r' || rw == 'R') {
281
+ // read lock
282
+ __atomic_add_fetch(&dict->readers, 1, __ATOMIC_RELAXED);
283
+ }
284
+ else {
285
+ // write lock
286
+ __atomic_add_fetch(&dict->writers, 1, __ATOMIC_RELAXED);
287
+ }
288
+
289
+ if(likely(dict->flags & DICTIONARY_FLAG_SINGLE_THREADED))
290
+ return;
291
+
292
+ if(rw == 'r' || rw == 'R') {
293
+ // read lock
294
netdata_rwlock_rdlock(dict->rwlock);
295
+
296
+ if(dict->flags & DICTIONARY_FLAG_EXCLUSIVE_ACCESS) {
297
+ internal_error(true, "DICTIONARY: left-over exclusive access to dictionary found");
298
+ dict->flags &= ~DICTIONARY_FLAG_EXCLUSIVE_ACCESS;
299
+ }
300
+ }
301
+ else {
302
+ // write lock
303
+ netdata_rwlock_wrlock(dict->rwlock);
304
+
305
+ dict->flags |= DICTIONARY_FLAG_EXCLUSIVE_ACCESS;
306
}
307
}
308
233
-static inline void dictionary_lock_wrlock(DICTIONARY *dict) {
234
- if(likely(!(dict->flags & DICTIONARY_FLAG_SINGLE_THREADED))) {
235
- // debug(D_DICTIONARY, "Dictionary WRITE lock");
236
- netdata_rwlock_wrlock(dict->rwlock);
309
+static void dictionary_unlock(DICTIONARY *dict, char rw) {
310
+ if(rw == 'r' || rw == 'R') {
311
+ // read unlock
312
+ __atomic_sub_fetch(&dict->readers, 1, __ATOMIC_RELAXED);
313
+ }
314
+ else {
315
+ // write unlock
316
+ garbage_collect_pending_deletes_unsafe(dict);
317
+ __atomic_sub_fetch(&dict->writers, 1, __ATOMIC_RELAXED);
318
+ }
319
+
320
+ if(likely(dict->flags & DICTIONARY_FLAG_SINGLE_THREADED))
321
+ return;
322
+
323
+ if(dict->flags & DICTIONARY_FLAG_EXCLUSIVE_ACCESS)
324
+ dict->flags &= ~DICTIONARY_FLAG_EXCLUSIVE_ACCESS;
325
+
326
+ netdata_rwlock_unlock(dict->rwlock);
327
+}
328
+
329
+// ----------------------------------------------------------------------------
330
+// deferred deletions
331
+
332
+void dictionary_defer_all_deletions_unsafe(DICTIONARY *dict, char rw) {
333
+ if(rw == 'r' || rw == 'R') {
334
+ // read locked - no need to defer deletions
335
+ ;
336
+ }
337
+ else {
338
+ // write locked - defer deletions
339
+ dict->flags |= DICTIONARY_FLAG_DEFER_ALL_DELETIONS;
340
}
341
}
342
240
-static inline void dictionary_unlock(DICTIONARY *dict) {
241
- if(likely(!(dict->flags & DICTIONARY_FLAG_SINGLE_THREADED))) {
242
- // debug(D_DICTIONARY, "Dictionary UNLOCK lock");
243
- netdata_rwlock_unlock(dict->rwlock);
343
+void dictionary_restore_all_deletions_unsafe(DICTIONARY *dict, char rw) {
344
+ if(rw == 'r' || rw == 'R') {
345
+ // read locked - no need to defer deletions
346
+ internal_error(dict->flags & DICTIONARY_FLAG_DEFER_ALL_DELETIONS, "DICTIONARY: deletions are deferred on a read lock");
347
+ }
348
+ else {
349
+ // write locked - defer deletions
350
+ if(dict->flags & DICTIONARY_FLAG_DEFER_ALL_DELETIONS)
351
+ dict->flags &= ~DICTIONARY_FLAG_DEFER_ALL_DELETIONS;
352
}
353
}
354
371
return 0;
372
}
373
266
-static void reference_counter_acquire(DICTIONARY *dict, NAME_VALUE *nv) {
267
- if(unlikely(dict->flags & DICTIONARY_FLAG_REFERENCE_COUNTERS)) {
268
- NAME_VALUE_WITH_REFERENCE_COUNTERS *nvs = (NAME_VALUE_WITH_REFERENCE_COUNTERS *)nv;
269
- __atomic_fetch_add(&nvs->refcount, 1, __ATOMIC_SEQ_CST);
374
+static int reference_counter_acquire(DICTIONARY *dict, NAME_VALUE *nv) {
375
+ int refcount;
376
+ if(likely(dict->flags & DICTIONARY_FLAG_SINGLE_THREADED))
377
+ refcount = ++nv->refcount;
378
+ else
379
+ refcount = __atomic_add_fetch(&nv->refcount, 1, __ATOMIC_SEQ_CST);
380
+
381
+ if(refcount == 1) {
382
+ // referenced items counts number of unique items referenced
383
+ // so, we increase it only when refcount == 1
384
+ DICTIONARY_STATS_REFERENCED_ITEMS_PLUS1(dict);
385
+
386
+ // if this is a deleted item, but the counter increased to 1
387
+ // we need to remove it from the pending items to delete
388
+ if (nv->flags & NAME_VALUE_FLAG_DELETED)
389
+ DICTIONARY_STATS_PENDING_DELETES_MINUS1(dict);
390
}
391
+
392
+ return refcount;
393
}
394
273
-static void reference_counter_release(DICTIONARY *dict, NAME_VALUE *nv) {
274
- if(unlikely(dict->flags & DICTIONARY_FLAG_REFERENCE_COUNTERS)) {
275
- NAME_VALUE_WITH_REFERENCE_COUNTERS *nvs = (NAME_VALUE_WITH_REFERENCE_COUNTERS *)nv;
276
- __atomic_fetch_sub(&nvs->refcount, 1, __ATOMIC_SEQ_CST);
395
+static int reference_counter_release(DICTIONARY *dict, NAME_VALUE *nv, bool can_get_write_lock) {
396
+ // this function may be called without any lock on the dictionary
397
+ // or even when someone else has a write lock on the dictionary
398
+ // so, we cannot check for EXCLUSIVE ACCESS
399
+
400
+ int refcount;
401
+ if(likely(dict->flags & DICTIONARY_FLAG_SINGLE_THREADED))
402
+ refcount = --nv->refcount;
403
+ else
404
+ refcount = __atomic_sub_fetch(&nv->refcount, 1, __ATOMIC_SEQ_CST);
405
+
406
+ if(refcount == 0) {
407
+ if((nv->flags & NAME_VALUE_FLAG_DELETED))
408
+ DICTIONARY_STATS_PENDING_DELETES_PLUS1(dict);
409
+
410
+ // referenced items counts number of unique items referenced
411
+ // so, we decrease it only when refcount == 0
412
+ DICTIONARY_STATS_REFERENCED_ITEMS_MINUS1(dict);
413
}
278
-}
414
280
-static int reference_counter_mark_deleted(DICTIONARY *dict, NAME_VALUE *nv) {
281
- if(unlikely(dict->flags & DICTIONARY_FLAG_REFERENCE_COUNTERS)) {
282
- NAME_VALUE_WITH_REFERENCE_COUNTERS *nvs = (NAME_VALUE_WITH_REFERENCE_COUNTERS *)nv;
283
- nvs->flags |= NAME_VALUE_FLAG_DELETED;
284
- return 1;
415
+ if(can_get_write_lock && DICTIONARY_STATS_PENDING_DELETES_GET(dict)) {
416
+ // we can garbage collect now
417
+
418
+ dictionary_lock(dict, 'w');
419
+ garbage_collect_pending_deletes_unsafe(dict);
420
+ dictionary_unlock(dict, 'w');
421
}
286
- return 0;
422
+
423
+ return refcount;
424
}
425
426
// ----------------------------------------------------------------------------
503
}
504
505
static inline NAME_VALUE **hashtable_insert_unsafe(DICTIONARY *dict, const char *name, size_t name_len) {
506
+ internal_error(!(dict->flags & DICTIONARY_FLAG_EXCLUSIVE_ACCESS), "DICTIONARY: inserting to the index without exclusive access to the dictionary.");
507
+
508
JError_t J_Error;
509
Pvoid_t *Rc = JudyHSIns(&dict->JudyHSArray, (void *)name, name_len, &J_Error);
510
if (unlikely(Rc == PJERR)) {
523
}
524
525
static inline int hashtable_delete_unsafe(DICTIONARY *dict, const char *name, size_t name_len, NAME_VALUE *nv) {
387
- (void)nv;
526
+ internal_error(!(dict->flags & DICTIONARY_FLAG_EXCLUSIVE_ACCESS), "DICTIONARY: deleting from the index without exclusive access to the dictionary.");
527
528
+ (void)nv;
529
if(unlikely(!dict->JudyHSArray)) return 0;
530
531
JError_t J_Error;
580
// linked list management
581
582
static inline void linkedlist_namevalue_link_unsafe(DICTIONARY *dict, NAME_VALUE *nv) {
583
+ internal_error(!(dict->flags & DICTIONARY_FLAG_EXCLUSIVE_ACCESS), "DICTIONARY: adding item to the linked-list without exclusive access to the dictionary.");
584
+
585
if (unlikely(!dict->first_item)) {
586
// we are the only ones here
587
nv->next = NULL;
609
}
610
611
static inline void linkedlist_namevalue_unlink_unsafe(DICTIONARY *dict, NAME_VALUE *nv) {
612
+ internal_error(!(dict->flags & DICTIONARY_FLAG_EXCLUSIVE_ACCESS), "DICTIONARY: removing item from the linked-list without exclusive access to the dictionary.");
613
+
614
if(nv->next) nv->next->prev = nv->prev;
615
if(nv->prev) nv->prev->next = nv->next;
616
if(dict->first_item == nv) dict->first_item = nv->next;
620
// ----------------------------------------------------------------------------
621
// NAME_VALUE methods
622
479
-static inline size_t namevalue_alloc_size(DICTIONARY *dict) {
480
- return (dict->flags & DICTIONARY_FLAG_REFERENCE_COUNTERS) ? sizeof(NAME_VALUE_WITH_REFERENCE_COUNTERS) : sizeof(NAME_VALUE);
481
-}
482
-
623
static NAME_VALUE *namevalue_create_unsafe(DICTIONARY *dict, const char *name, size_t name_len, void *value, size_t value_len) {
624
debug(D_DICTIONARY, "Creating name value entry for name '%s'.", name);
625
486
- size_t size = namevalue_alloc_size(dict);
626
+ size_t size = sizeof(NAME_VALUE);
627
NAME_VALUE *nv = mallocz(size);
628
size_t allocated = size;
629
630
+ nv->refcount = 0;
631
+ nv->flags = NAME_VALUE_FLAG_NONE;
632
nv->name_len = name_len;
633
nv->value_len = value_len;
634
727
}
728
729
freez(nv);
588
- freed += namevalue_alloc_size(dict);
730
+ freed += sizeof(NAME_VALUE);
731
590
- DICTIONARY_STATS_ENTRIES_MINUS1(dict, freed);
732
+ DICTIONARY_STATS_ENTRIES_MINUS_MEMORY(dict, freed);
733
734
return freed;
735
}
736
737
+// if a dictionary item can be deleted, return true, otherwise return false
738
+static bool name_value_can_be_deleted(DICTIONARY *dict, NAME_VALUE *nv) {
739
+ if(unlikely(dict->flags & DICTIONARY_FLAG_DEFER_ALL_DELETIONS))
740
+ return false;
741
+
742
+ if(unlikely(DICTIONARY_NAME_VALUE_REFCOUNT_GET(nv) > 0))
743
+ return false;
744
+
745
+ return true;
746
+}
747
+
748
// ----------------------------------------------------------------------------
749
// API - dictionary management
750
751
DICTIONARY *dictionary_create(DICTIONARY_FLAGS flags) {
752
debug(D_DICTIONARY, "Creating dictionary.");
753
601
- if((flags & DICTIONARY_FLAG_REFERENCE_COUNTERS) && (flags & DICTIONARY_FLAG_SINGLE_THREADED)) {
602
- error("DICTIONARY: requested reference counters on single threaded dictionary. Not adding reference counters.");
603
- flags &= ~DICTIONARY_FLAG_REFERENCE_COUNTERS;
604
- }
754
+ if(unlikely(flags & DICTIONARY_FLAGS_RESERVED))
755
+ flags &= ~DICTIONARY_FLAGS_RESERVED;
756
757
DICTIONARY *dict = callocz(1, sizeof(DICTIONARY));
758
size_t allocated = sizeof(DICTIONARY);
762
763
allocated += dictionary_lock_init(dict);
764
allocated += reference_counter_init(dict);
614
- dict->memory = allocated;
765
+ dict->memory = (long)allocated;
766
767
hashtable_init_unsafe(dict);
768
return (DICTIONARY *)dict;
771
size_t dictionary_destroy(DICTIONARY *dict) {
772
if(!dict) return 0;
773
774
+ if(dict->referenced_items) {
775
+ dict->flags |= DICTIONARY_FLAG_DESTROYED;
776
+ return 0;
777
+ }
778
+
779
debug(D_DICTIONARY, "Destroying dictionary.");
780
625
- dictionary_lock_wrlock(dict);
781
+ dictionary_lock(dict, 'w');
782
783
size_t freed = 0;
784
NAME_VALUE *nv = dict->first_item;
798
// destroy the dictionary
799
freed += hashtable_destroy_unsafe(dict);
800
645
- dictionary_unlock(dict);
801
+ dictionary_unlock(dict, 'w');
802
freed += dictionary_lock_free(dict);
803
freed += reference_counter_free(dict);
804
813
814
void *dictionary_set_unsafe(DICTIONARY *dict, const char *name, void *value, size_t value_len) {
815
if(unlikely(!name || !*name)) {
660
- error("Attempted to dictionary_set() a dictionary item without a name");
816
+ internal_error(true, "DICTIONARY: attempted to dictionary_set() a dictionary item without a name");
817
return NULL;
818
}
819
820
+ if(unlikely(dict->flags & DICTIONARY_FLAG_DESTROYED)) {
821
+ internal_error(true, "DICTIONARY: attempted to dictionary_set() on a destroyed dictionary");
822
+ return NULL;
823
+ }
824
+
825
+ internal_error(!(dict->flags & DICTIONARY_FLAG_EXCLUSIVE_ACCESS), "DICTIONARY: inserting dictionary item '%s' without exclusive access to dictionary", name);
826
+
827
size_t name_len = strlen(name) + 1; // we need the terminating null too
828
829
debug(D_DICTIONARY, "SET dictionary entry with name '%s'.", name);
863
}
864
865
void *dictionary_set(DICTIONARY *dict, const char *name, void *value, size_t value_len) {
703
- dictionary_lock_wrlock(dict);
866
+ dictionary_lock(dict, 'w');
867
void *ret = dictionary_set_unsafe(dict, name, value, value_len);
705
- dictionary_unlock(dict);
868
+ dictionary_unlock(dict, 'w');
869
return ret;
870
}
871
709
-void *dictionary_get_unsafe(DICTIONARY *dict, const char *name) {
872
+static NAME_VALUE *dictionary_get_name_value_unsafe(DICTIONARY *dict, const char *name) {
873
if(unlikely(!name || !*name)) {
711
- error("Attempted to dictionary_get() without a name");
874
+ internal_error(true, "attempted to dictionary_get() without a name");
875
+ return NULL;
876
+ }
877
+
878
+ if(unlikely(dict->flags & DICTIONARY_FLAG_DESTROYED)) {
879
+ internal_error(true, "DICTIONARY: attempted to dictionary_get() on a destroyed dictionary");
880
return NULL;
881
}
882
891
}
892
893
debug(D_DICTIONARY, "Found dictionary entry with name '%s'.", name);
894
+ return nv;
895
+}
896
+
897
+void *dictionary_get_unsafe(DICTIONARY *dict, const char *name) {
898
+ NAME_VALUE *nv = dictionary_get_name_value_unsafe(dict, name);
899
+
900
+ if(unlikely(!nv))
901
+ return NULL;
902
+
903
return nv->value;
904
}
905
906
void *dictionary_get(DICTIONARY *dict, const char *name) {
730
- dictionary_lock_rlock(dict);
907
+ dictionary_lock(dict, 'r');
908
void *ret = dictionary_get_unsafe(dict, name);
732
- dictionary_unlock(dict);
909
+ dictionary_unlock(dict, 'r');
910
return ret;
911
}
912
913
+void *dictionary_acquire_item_unsafe(DICTIONARY *dict, const char *name) {
914
+ NAME_VALUE *nv = dictionary_get_name_value_unsafe(dict, name);
915
+
916
+ if(unlikely(!nv))
917
+ return NULL;
918
+
919
+ reference_counter_acquire(dict, nv);
920
+ return nv;
921
+}
922
+
923
+void *dictionary_acquire_item(DICTIONARY *dict, const char *name) {
924
+ dictionary_lock(dict, 'r');
925
+ void *ret = dictionary_acquire_item_unsafe(dict, name);
926
+ dictionary_unlock(dict, 'r');
927
+ return ret;
928
+}
929
+
930
+void *dictionary_acquired_item_value(DICTIONARY *dict __maybe_unused, void *item) {
931
+ if(unlikely(!item)) return NULL;
932
+ return ((NAME_VALUE *)item)->value;
933
+}
934
+
935
+void dictionary_acquired_item_release_unsafe(DICTIONARY *dict, void *item) {
936
+ if(unlikely(!item)) return;
937
+ reference_counter_release(dict, (NAME_VALUE *)item, false);
938
+}
939
+
940
+void dictionary_acquired_item_release(DICTIONARY *dict, void *item) {
941
+ if(unlikely(!item)) return;
942
+
943
+ // no need to get a lock here
944
+ // we pass the last parameter to reference_counter_release() as true
945
+ // so that the release may get a write-lock if required to clean up
946
+
947
+ reference_counter_release(dict, (NAME_VALUE *)item, true);
948
+
949
+ if(unlikely(dict->flags & DICTIONARY_FLAG_DESTROYED))
950
+ dictionary_destroy(dict);
951
+}
952
+
953
int dictionary_del_unsafe(DICTIONARY *dict, const char *name) {
954
+ if(unlikely(dict->flags & DICTIONARY_FLAG_DESTROYED)) {
955
+ internal_error(true, "DICTIONARY: attempted to dictionary_del() on a destroyed dictionary");
956
+ return -1;
957
+ }
958
+
959
if(unlikely(!name || !*name)) {
738
- error("Attempted to dictionary_det() without a name");
960
+ internal_error(true, "DICTIONARY: attempted to dictionary_del() without a name");
961
return -1;
962
}
963
964
+ internal_error(!(dict->flags & DICTIONARY_FLAG_EXCLUSIVE_ACCESS), "DICTIONARY: INTERNAL ERROR: deleting dictionary item '%s' without exclusive access to dictionary", name);
965
+
966
size_t name_len = strlen(name) + 1; // we need the terminating null too
967
968
debug(D_DICTIONARY, "DEL dictionary entry with name '%s'.", name);
983
if(hashtable_delete_unsafe(dict, name, name_len, nv) == 0)
984
error("DICTIONARY: INTERNAL ERROR: tried to delete item with name '%s' that is not in the index", name);
985
762
- if(!reference_counter_mark_deleted(dict, nv)) {
986
+ if(name_value_can_be_deleted(dict, nv)) {
987
linkedlist_namevalue_unlink_unsafe(dict, nv);
988
namevalue_destroy_unsafe(dict, nv);
989
}
990
+ else
991
+ nv->flags |= NAME_VALUE_FLAG_DELETED;
992
+
993
ret = 0;
994
+
995
+ DICTIONARY_STATS_ENTRIES_MINUS1(dict);
996
+
997
}
998
return ret;
999
}
1000
1001
int dictionary_del(DICTIONARY *dict, const char *name) {
772
- dictionary_lock_wrlock(dict);
1002
+ dictionary_lock(dict, 'w');
1003
int ret = dictionary_del_unsafe(dict, name);
774
- dictionary_unlock(dict);
1004
+ dictionary_unlock(dict, 'w');
1005
return ret;
1006
}
1007
1011
void *dictionary_foreach_start_rw(DICTFE *dfe, DICTIONARY *dict, char rw) {
1012
if(unlikely(!dfe || !dict)) return NULL;
1013
784
- DICTIONARY_STATS_WALKTHROUGHS_PLUS1(dict);
1014
+ if(unlikely(dict->flags & DICTIONARY_FLAG_DESTROYED)) {
1015
+ internal_error(true, "DICTIONARY: attempted to dictionary_foreach_start_rw() on a destroyed dictionary");
1016
+ dfe->last_item = NULL;
1017
+ dfe->name = NULL;
1018
+ dfe->value = NULL;
1019
+ return NULL;
1020
+ }
1021
1022
dfe->dict = dict;
1023
+ dfe->rw = rw;
1024
dfe->started_ut = now_realtime_usec();
1025
789
- if(rw == 'r' || rw == 'R')
790
- dictionary_lock_rlock(dict);
791
- else
792
- dictionary_lock_wrlock(dict);
1026
+ dictionary_lock(dict, dfe->rw);
1027
1028
+ DICTIONARY_STATS_WALKTHROUGHS_PLUS1(dict);
1029
+
1030
+ // get the first item from the list
1031
NAME_VALUE *nv = dict->first_item;
795
- dfe->last_position_index = (void *)nv;
1032
+
1033
+ // skip all the deleted items
1034
+ while(nv && (nv->flags & NAME_VALUE_FLAG_DELETED))
1035
+ nv = nv->next;
1036
1037
if(likely(nv)) {
798
- dfe->next_position_index = (void *)nv->next;
1038
+ dfe->last_item = nv;
1039
dfe->name = nv->name;
800
- dfe->value = (void *)nv->value;
1040
+ dfe->value = nv->value;
1041
reference_counter_acquire(dict, nv);
1042
}
1043
else {
804
- dfe->next_position_index = NULL;
1044
+ dfe->last_item = NULL;
1045
dfe->name = NULL;
1046
dfe->value = NULL;
1047
}
1052
void *dictionary_foreach_next(DICTFE *dfe) {
1053
if(unlikely(!dfe || !dfe->dict)) return NULL;
1054
815
- NAME_VALUE *nv = (NAME_VALUE *)dfe->last_position_index;
816
- if(likely(nv))
817
- reference_counter_release(dfe->dict, nv);
1055
+ if(unlikely(dfe->dict->flags & DICTIONARY_FLAG_DESTROYED)) {
1056
+ internal_error(true, "DICTIONARY: attempted to dictionary_foreach_next() on a destroyed dictionary");
1057
+ dfe->last_item = NULL;
1058
+ dfe->name = NULL;
1059
+ dfe->value = NULL;
1060
+ return NULL;
1061
+ }
1062
819
- nv = dfe->last_position_index = dfe->next_position_index;
1063
+ // the item we just did
1064
+ NAME_VALUE *nv = (NAME_VALUE *)dfe->last_item;
1065
821
- if(likely(nv)) {
822
- dfe->next_position_index = (void *)nv->next;
823
- dfe->name = nv->name;
824
- dfe->value = (void *)nv->value;
1066
+ // get the next item from the list
1067
+ NAME_VALUE *nv_next = (nv) ? nv->next : NULL;
1068
+
1069
+ // skip all the deleted items
1070
+ while(nv_next && (nv_next->flags & NAME_VALUE_FLAG_DELETED))
1071
+ nv_next = nv_next->next;
1072
+
1073
+ // release the old, so that it can possibly be deleted
1074
+ if(likely(nv))
1075
+ reference_counter_release(dfe->dict, nv, false);
1076
1077
+ if(likely(nv = nv_next)) {
1078
+ dfe->last_item = nv;
1079
+ dfe->name = nv->name;
1080
+ dfe->value = nv->value;
1081
reference_counter_acquire(dfe->dict, nv);
1082
}
1083
else {
829
- dfe->next_position_index = NULL;
1084
+ dfe->last_item = NULL;
1085
dfe->name = NULL;
1086
dfe->value = NULL;
1087
}
1092
usec_t dictionary_foreach_done(DICTFE *dfe) {
1093
if(unlikely(!dfe || !dfe->dict)) return 0;
1094
840
- NAME_VALUE *nv = (NAME_VALUE *)dfe->last_position_index;
841
- if(nv)
842
- reference_counter_release(dfe->dict, nv);
1095
+ if(unlikely(dfe->dict->flags & DICTIONARY_FLAG_DESTROYED)) {
1096
+ internal_error(true, "DICTIONARY: attempted to dictionary_foreach_next() on a destroyed dictionary");
1097
+ return 0;
1098
+ }
1099
+
1100
+ // the item we just did
1101
+ NAME_VALUE *nv = (NAME_VALUE *)dfe->last_item;
1102
+
1103
+ // release it, so that it can possibly be deleted
1104
+ if(likely(nv))
1105
+ reference_counter_release(dfe->dict, nv, false);
1106
844
- dictionary_unlock((DICTIONARY *)dfe->dict);
1107
+ dictionary_unlock(dfe->dict, dfe->rw);
1108
dfe->dict = NULL;
846
- dfe->last_position_index = NULL;
847
- dfe->next_position_index = NULL;
1109
+ dfe->last_item = NULL;
1110
dfe->name = NULL;
1111
dfe->value = NULL;
1112
1124
int dictionary_walkthrough_rw(DICTIONARY *dict, char rw, int (*callback)(const char *name, void *entry, void *data), void *data) {
1125
if(unlikely(!dict)) return 0;
1126
865
- DICTIONARY_STATS_WALKTHROUGHS_PLUS1(dict);
1127
+ if(unlikely(dict->flags & DICTIONARY_FLAG_DESTROYED)) {
1128
+ internal_error(true, "DICTIONARY: attempted to dictionary_walkthrough_rw() on a destroyed dictionary");
1129
+ return 0;
1130
+ }
1131
867
- if(rw == 'r' || rw == 'R')
868
- dictionary_lock_rlock(dict);
869
- else
870
- dictionary_lock_wrlock(dict);
1132
+ dictionary_lock(dict, rw);
1133
+
1134
+ DICTIONARY_STATS_WALKTHROUGHS_PLUS1(dict);
1135
1136
// written in such a way, that the callback can delete the active element
1137
1138
int ret = 0;
1139
NAME_VALUE *nv = dict->first_item, *nv_next;
1140
while(nv) {
877
- nv_next = nv->next;
1141
1142
+ // skip the deleted items
1143
+ if(unlikely(nv->flags & NAME_VALUE_FLAG_DELETED)) {
1144
+ nv = nv->next;
1145
+ continue;
1146
+ }
1147
+
1148
+ // get a reference counter, so that our item will not be deleted
1149
+ // while we are using it
1150
reference_counter_acquire(dict, nv);
1151
+
1152
int r = callback(nv->name, nv->value, data);
881
- reference_counter_release(dict, nv);
1153
+
1154
+ // since we have a reference counter, this item cannot be deleted
1155
+ // until we release the reference counter, so the pointers are there
1156
+ nv_next = nv->next;
1157
+ reference_counter_release(dict, nv, false);
1158
+
1159
if(unlikely(r < 0)) {
1160
ret = r;
1161
break;
1166
nv = nv_next;
1167
}
1168
892
- dictionary_unlock(dict);
1169
+ dictionary_unlock(dict, rw);
1170
1171
return ret;
1172
}
1173
1174
// ----------------------------------------------------------------------------
898
-// sort
1175
+// sorted walkthrough
1176
1177
static int dictionary_sort_compar(const void *nv1, const void *nv2) {
1178
return strcmp((*(NAME_VALUE **)nv1)->name, (*(NAME_VALUE **)nv2)->name);
1181
int dictionary_sorted_walkthrough_rw(DICTIONARY *dict, char rw, int (*callback)(const char *name, void *entry, void *data), void *data) {
1182
if(unlikely(!dict || !dict->entries)) return 0;
1183
907
- DICTIONARY_STATS_WALKTHROUGHS_PLUS1(dict);
1184
+ if(unlikely(dict->flags & DICTIONARY_FLAG_DESTROYED)) {
1185
+ internal_error(true, "DICTIONARY: attempted to dictionary_sorted_walkthrough_rw() on a destroyed dictionary");
1186
+ return 0;
1187
+ }
1188
909
- if(rw == 'r' || rw == 'R')
910
- dictionary_lock_rlock(dict);
911
- else
912
- dictionary_lock_wrlock(dict);
1189
+ dictionary_lock(dict, rw);
1190
+ dictionary_defer_all_deletions_unsafe(dict, rw);
1191
+
1192
+ DICTIONARY_STATS_WALKTHROUGHS_PLUS1(dict);
1193
1194
size_t count = dict->entries;
1195
NAME_VALUE **array = mallocz(sizeof(NAME_VALUE *) * count);
1196
1197
size_t i;
1198
NAME_VALUE *nv;
919
- for(nv = dict->first_item, i = 0; nv && i < count ;nv = nv->next, i++)
920
- array[i] = nv;
1199
+ for(nv = dict->first_item, i = 0; nv && i < count ;nv = nv->next) {
1200
+ if(likely(!(nv->flags & NAME_VALUE_FLAG_DELETED)))
1201
+ array[i++] = nv;
1202
+ }
1203
922
- if(unlikely(nv))
923
- error("DICTIONARY: during sorting expected to have %zu items in dictionary, but there are more. Sorted results may be incomplete. This is internal error - dictionaries fail to maintain an accurate number of the number of entries they have.", count);
1204
+ internal_error(nv != NULL, "DICTIONARY: during sorting expected to have %zu items in dictionary, but there are more. Sorted results may be incomplete. Dictionary fails to maintain an accurate number of the number of entries it has.", count);
1205
1206
if(unlikely(i != count)) {
926
- error("DICTIONARY: during sorting expected to have %zu items in dictionary, but there are %zu. Sorted results may be incomplete. This is internal error - dictionaries fail to maintain an accurate number of the number of entries they have.", count, i);
1207
+ internal_error(true, "DICTIONARY: during sorting expected to have %zu items in dictionary, but there are %zu. Sorted results may be incomplete. Dictionary fails to maintain an accurate number of the number of entries it has.", count, i);
1208
count = i;
1209
}
1210
1212
1213
int ret = 0;
1214
for(i = 0; i < count ;i++) {
934
- int r = callback((array[i])->name, (array[i])->value, data);
935
- if(r < 0) { ret = r; break; }
936
- ret += r;
1215
+ nv = array[i];
1216
+ if(likely(!(nv->flags & NAME_VALUE_FLAG_DELETED))) {
1217
+ reference_counter_acquire(dict, nv);
1218
+ int r = callback(nv->name, nv->value, data);
1219
+ reference_counter_release(dict, nv, false);
1220
+ if (r < 0) {
1221
+ ret = r;
1222
+ break;
1223
+ }
1224
+ ret += r;
1225
+ }
1226
}
1227
939
- dictionary_unlock(dict);
1228
+ dictionary_restore_all_deletions_unsafe(dict, rw);
1229
+ dictionary_unlock(dict, rw);
1230
freez(array);
1231
1232
return ret;
1276
static size_t dictionary_unittest_set_null(DICTIONARY *dict, char **names, char **values, size_t entries) {
1277
(void)values;
1278
size_t errors = 0;
989
- size_t i = 0;
990
- for(; i < entries ;i++) {
1279
+ long i = 0;
1280
+ for(; i < (long)entries ;i++) {
1281
void *val = dictionary_set(dict, names[i], NULL, 0);
1282
if(val != NULL) { fprintf(stderr, ">>> %s() returns a non NULL value\n", __FUNCTION__); errors++; }
1283
}
1488
}
1489
1490
static usec_t dictionary_unittest_run_and_measure_time(DICTIONARY *dict, char *message, char **names, char **values, size_t entries, size_t *errors, size_t (*callback)(DICTIONARY *dict, char **names, char **values, size_t entries)) {
1201
- fprintf(stderr, "%-40s... ", message);
1491
+ fprintf(stderr, "%40s ... ", message);
1492
1493
usec_t started = now_realtime_usec();
1494
size_t errs = callback(dict, names, values, entries);
1497
1498
if(callback == dictionary_unittest_destroy) dict = NULL;
1499
1210
- fprintf(stderr, " %zu errors, %zu items in dictionary, %llu usec \n", errs, dict? dictionary_stats_entries(dict):0, dt);
1500
+ fprintf(stderr, " %zu errors, %ld items in dictionary, %llu usec \n", errs, dict? dictionary_stats_entries(dict):0, dt);
1501
*errors += errs;
1502
return dt;
1503
}
1578
dictionary_unittest_run_and_measure_time(dict, "traverse foreach read loop", names, values, entries, errors, dictionary_unittest_foreach);
1579
}
1580
1581
+
1582
+static int check_dictionary_callback(const char *name, void *value, void *data) {
1583
+ (void)name;
1584
+ (void)value;
1585
+ (void)data;
1586
+ return 1;
1587
+}
1588
+
1589
+static size_t check_dictionary(DICTIONARY *dict, size_t entries, size_t linked_list_members) {
1590
+ size_t errors = 0;
1591
+
1592
+ fprintf(stderr, "dictionary entries %ld, expected %zu...\t\t\t\t\t", dictionary_stats_entries(dict), entries);
1593
+ if (dictionary_stats_entries(dict) != (long)entries) {
1594
+ fprintf(stderr, "FAILED\n");
1595
+ errors++;
1596
+ }
1597
+ else
1598
+ fprintf(stderr, "OK\n");
1599
+
1600
+ size_t ll = 0;
1601
+ void *t;
1602
+ dfe_start_read(dict, t)
1603
+ ll++;
1604
+ dfe_done(t);
1605
+
1606
+ fprintf(stderr, "dictionary foreach entries %zu, expected %zu...\t\t\t\t", ll, entries);
1607
+ if(ll != entries) {
1608
+ fprintf(stderr, "FAILED\n");
1609
+ errors++;
1610
+ }
1611
+ else
1612
+ fprintf(stderr, "OK\n");
1613
+
1614
+ ll = dictionary_walkthrough_read(dict, check_dictionary_callback, NULL);
1615
+ fprintf(stderr, "dictionary walkthrough entries %zu, expected %zu...\t\t\t\t", ll, entries);
1616
+ if(ll != entries) {
1617
+ fprintf(stderr, "FAILED\n");
1618
+ errors++;
1619
+ }
1620
+ else
1621
+ fprintf(stderr, "OK\n");
1622
+
1623
+ ll = dictionary_sorted_walkthrough_read(dict, check_dictionary_callback, NULL);
1624
+ fprintf(stderr, "dictionary sorted walkthrough entries %zu, expected %zu...\t\t\t", ll, entries);
1625
+ if(ll != entries) {
1626
+ fprintf(stderr, "FAILED\n");
1627
+ errors++;
1628
+ }
1629
+ else
1630
+ fprintf(stderr, "OK\n");
1631
+
1632
+ NAME_VALUE *nv;
1633
+ for(ll = 0, nv = dict->first_item; nv ;nv = nv->next)
1634
+ ll++;
1635
+
1636
+ fprintf(stderr, "dictionary linked list entries %zu, expected %zu...\t\t\t\t", ll, linked_list_members);
1637
+ if(ll != linked_list_members) {
1638
+ fprintf(stderr, "FAILED\n");
1639
+ errors++;
1640
+ }
1641
+ else
1642
+ fprintf(stderr, "OK\n");
1643
+
1644
+ return errors;
1645
+}
1646
+
1647
+static int check_name_value_callback(const char *name, void *value, void *data) {
1648
+ (void)name;
1649
+ return value == data;
1650
+}
1651
+
1652
+static size_t check_name_value(DICTIONARY *dict, NAME_VALUE *nv, const char *name, const char *value, int refcount, NAME_VALUE_FLAGS flags, bool searchable, bool browsable, bool linked) {
1653
+ size_t errors = 0;
1654
+
1655
+ fprintf(stderr, "NAME_VALUE name is '%s', expected '%s'...\t\t\t\t", nv->name, name);
1656
+ if(strcmp(nv->name, name) != 0) {
1657
+ fprintf(stderr, "FAILED\n");
1658
+ errors++;
1659
+ }
1660
+ else
1661
+ fprintf(stderr, "OK\n");
1662
+
1663
+ fprintf(stderr, "NAME_VALUE value is '%s', expected '%s'...\t\t\t", (const char *)nv->value, value);
1664
+ if(strcmp((const char *)nv->value, value) != 0) {
1665
+ fprintf(stderr, "FAILED\n");
1666
+ errors++;
1667
+ }
1668
+ else
1669
+ fprintf(stderr, "OK\n");
1670
+
1671
+ fprintf(stderr, "NAME_VALUE refcount is %d, expected %d...\t\t\t\t\t", nv->refcount, refcount);
1672
+ if (nv->refcount != refcount) {
1673
+ fprintf(stderr, "FAILED\n");
1674
+ errors++;
1675
+ }
1676
+ else
1677
+ fprintf(stderr, "OK\n");
1678
+
1679
+ fprintf(stderr, "NAME_VALUE flags is %u, expected %u...\t\t\t\t\t", nv->flags, flags);
1680
+ if (nv->flags != flags) {
1681
+ fprintf(stderr, "FAILED\n");
1682
+ errors++;
1683
+ }
1684
+ else
1685
+ fprintf(stderr, "OK\n");
1686
+
1687
+ void *v = dictionary_get(dict, name);
1688
+ bool found = v == nv->value;
1689
+ fprintf(stderr, "NAME_VALUE searchable %5s, expected %5s...\t\t\t\t", found?"true":"false", searchable?"true":"false");
1690
+ if(found != searchable) {
1691
+ fprintf(stderr, "FAILED\n");
1692
+ errors++;
1693
+ }
1694
+ else
1695
+ fprintf(stderr, "OK\n");
1696
+
1697
+ found = false;
1698
+ void *t;
1699
+ dfe_start_read(dict, t) {
1700
+ if(t == nv->value) found = true;
1701
+ }
1702
+ dfe_done(t);
1703
+
1704
+ fprintf(stderr, "NAME_VALUE dfe browsable %5s, expected %5s...\t\t\t", found?"true":"false", browsable?"true":"false");
1705
+ if(found != browsable) {
1706
+ fprintf(stderr, "FAILED\n");
1707
+ errors++;
1708
+ }
1709
+ else
1710
+ fprintf(stderr, "OK\n");
1711
+
1712
+ found = dictionary_walkthrough_read(dict, check_name_value_callback, nv->value);
1713
+ fprintf(stderr, "NAME_VALUE walkthrough browsable %5s, expected %5s...\t\t", found?"true":"false", browsable?"true":"false");
1714
+ if(found != browsable) {
1715
+ fprintf(stderr, "FAILED\n");
1716
+ errors++;
1717
+ }
1718
+ else
1719
+ fprintf(stderr, "OK\n");
1720
+
1721
+ found = dictionary_sorted_walkthrough_read(dict, check_name_value_callback, nv->value);
1722
+ fprintf(stderr, "NAME_VALUE sorted walkthrough browsable %5s, expected %5s...\t", found?"true":"false", browsable?"true":"false");
1723
+ if(found != browsable) {
1724
+ fprintf(stderr, "FAILED\n");
1725
+ errors++;
1726
+ }
1727
+ else
1728
+ fprintf(stderr, "OK\n");
1729
+
1730
+ found = false;
1731
+ NAME_VALUE *n;
1732
+ for(n = dict->first_item; n ;n = n->next)
1733
+ if(n == nv) found = true;
1734
+
1735
+ fprintf(stderr, "NAME_VALUE linked %5s, expected %5s...\t\t\t\t", found?"true":"false", linked?"true":"false");
1736
+ if(found != linked) {
1737
+ fprintf(stderr, "FAILED\n");
1738
+ errors++;
1739
+ }
1740
+ else
1741
+ fprintf(stderr, "OK\n");
1742
+
1743
+ return errors;
1744
+}
1745
+
1746
int dictionary_unittest(size_t entries) {
1747
if(entries < 10) entries = 10;
1748
1807
dictionary_unittest_null_dfe(dict, names, values, entries, &errors);
1808
dictionary_unittest_run_and_measure_time(dict, "destroying full dictionary", names, values, entries, &errors, dictionary_unittest_destroy);
1809
1810
+ // check reference counters
1811
+ {
1812
+ fprintf(stderr, "\nTesting reference counters:\n");
1813
+ dict = dictionary_create(DICTIONARY_FLAG_NONE);
1814
+ errors += check_dictionary(dict, 0, 0);
1815
+
1816
+ fprintf(stderr, "\nAdding test item to dictionary and acquiring it\n");
1817
+ dictionary_set(dict, "test", "ITEM1", 6);
1818
+ NAME_VALUE *nv = dictionary_acquire_item(dict, "test");
1819
+
1820
+ errors += check_dictionary(dict, 1, 1);
1821
+ errors += check_name_value(dict, nv, "test", "ITEM1", 1, NAME_VALUE_FLAG_NONE, true, true, true);
1822
+
1823
+ fprintf(stderr, "\nChecking that reference counters are increased:\n");
1824
+ void *t;
1825
+ dfe_start_read(dict, t) {
1826
+ errors += check_dictionary(dict, 1, 1);
1827
+ errors += check_name_value(dict, nv, "test", "ITEM1", 2, NAME_VALUE_FLAG_NONE, true, true, true);
1828
+ }
1829
+ dfe_done(t);
1830
+
1831
+ fprintf(stderr, "\nChecking that reference counters are decreased:\n");
1832
+ errors += check_dictionary(dict, 1, 1);
1833
+ errors += check_name_value(dict, nv, "test", "ITEM1", 1, NAME_VALUE_FLAG_NONE, true, true, true);
1834
+
1835
+ fprintf(stderr, "\nDeleting the item we have acquired:\n");
1836
+ dictionary_del(dict, "test");
1837
+
1838
+ errors += check_dictionary(dict, 0, 1);
1839
+ errors += check_name_value(dict, nv, "test", "ITEM1", 1, NAME_VALUE_FLAG_DELETED, false, false, true);
1840
+
1841
+ fprintf(stderr, "\nAdding another item with the same name of the item we deleted, while being acquired:\n");
1842
+ dictionary_set(dict, "test", "ITEM2", 6);
1843
+ errors += check_dictionary(dict, 1, 2);
1844
+
1845
+ fprintf(stderr, "\nAcquiring the second item:\n");
1846
+ NAME_VALUE *nv2 = dictionary_acquire_item(dict, "test");
1847
+ errors += check_name_value(dict, nv, "test", "ITEM1", 1, NAME_VALUE_FLAG_DELETED, false, false, true);
1848
+ errors += check_name_value(dict, nv2, "test", "ITEM2", 1, NAME_VALUE_FLAG_NONE, true, true, true);
1849
+
1850
+ fprintf(stderr, "\nReleasing the second item (the first is still acquired):\n");
1851
+ dictionary_acquired_item_release(dict, nv2);
1852
+ errors += check_dictionary(dict, 1, 2);
1853
+ errors += check_name_value(dict, nv, "test", "ITEM1", 1, NAME_VALUE_FLAG_DELETED, false, false, true);
1854
+ errors += check_name_value(dict, nv2, "test", "ITEM2", 0, NAME_VALUE_FLAG_NONE, true, true, true);
1855
+
1856
+ fprintf(stderr, "\nDeleting the second item (the first is still acquired):\n");
1857
+ dictionary_del(dict, "test");
1858
+ errors += check_dictionary(dict, 0, 1);
1859
+ errors += check_name_value(dict, nv, "test", "ITEM1", 1, NAME_VALUE_FLAG_DELETED, false, false, true);
1860
+
1861
+ fprintf(stderr, "\nReleasing the first item (which we have already deleted):\n");
1862
+ dictionary_acquired_item_release(dict, nv);
1863
+ errors += check_dictionary(dict, 0, 0);
1864
+
1865
+ fprintf(stderr, "\nAdding again the test item to dictionary and acquiring it\n");
1866
+ dictionary_set(dict, "test", "ITEM1", 6);
1867
+ nv = dictionary_acquire_item(dict, "test");
1868
+
1869
+ errors += check_dictionary(dict, 1, 1);
1870
+ errors += check_name_value(dict, nv, "test", "ITEM1", 1, NAME_VALUE_FLAG_NONE, true, true, true);
1871
+
1872
+ fprintf(stderr, "\nDestroying the dictionary while we have acquired an item\n");
1873
+ dictionary_destroy(dict);
1874
+
1875
+ fprintf(stderr, "Releasing the item (on a destroyed dictionary)\n");
1876
+ dictionary_acquired_item_release(dict, nv);
1877
+ nv = NULL;
1878
+ dict = NULL;
1879
+ }
1880
+
1881
dictionary_unittest_free_char_pp(names, entries);
1882
dictionary_unittest_free_char_pp(values, entries);
1883