@cryptotaxi247 / netdata-1 / commits / 68251d586

dictionary updated documentation and cosmetics (#13679)

* dictionary updated documentation and cosmetics * improved dictionaries views unit test

Costa Tsaousis committed Sep 20, 2022 at 10:33 UTC 68251d586f9c5547ba20c41942124e0420fd1547
4 files changed +119 -79
libnetdata/dictionary/README.md
+45 -50
@@ -18,26 +18,27 @@ Dictionaries provide an interface to:
18 - **Delete** an item from the dictionary (provided its `name`)
19 - **Traverse** the list of items in the dictionary
20
21 -Dictionaries are **ordered**, meaning that the order they have been added is preserved while traversing them. The caller may reverse this order by passing the flag `DICT_OPTION_ADD_IN_FRONT` when creating the dictionary.
21 +Dictionaries are **ordered**, meaning that the order they have been added, is preserved while traversing them. The caller may reverse this order by passing the flag `DICT_OPTION_ADD_IN_FRONT` when creating the dictionary.
22
23 -Dictionaries guarantee **uniqueness** of all items added to them, meaning that only one item with a given name can exist in the dictionary at any given time.
23 +Dictionaries guarantee **uniqueness** of all items added to them, meaning that only one item with a given `name` can exist in the dictionary at any given time.
24
25 -Dictionaries are extremely fast in all operations. They are indexing the keys with `JudyHS` (or `AVL` when `libJudy` is not available) and they utilize a double-linked-list for the traversal operations. Deletion is the most expensive operation, usually somewhat slower than insertion.
25 +Dictionaries are extremely fast in all operations. They are indexing the keys with `JudyHS` and they utilize a double-linked-list for the traversal operations. Deletion is the most expensive operation, usually somewhat slower than insertion.
26
27 ## Memory management
28
29 Dictionaries come with 2 memory management options:
30
31 -- **Clone** (copy) the name and/or the value to memory allocated by the dictionary.
32 -- **Link** the name and/or the value, without allocating any memory about them.
31 +- **Clone** (copy) the `name` and/or the `value` to memory allocated by the dictionary.
32 +- **Link** the `name` and/or the `value`, without allocating any memory about them.
33
34 -In **clone** mode, the dictionary guarantees that all operations on the dictionary items will automatically take care of the memory used by the name and/or the value. In case the value is an object that needs to have user allocated memory, the following callback functions can be registered:
34 +In **clone** mode, the dictionary guarantees that all operations on the dictionary items, will automatically take care of the memory used by the `name` and/or the `value`. In case the `value` is an object that needs to have user allocated memory, the following callback functions can be registered:
35
36 - 1.`dictionary_register_insert_callback()` that will be called just after the insertion of an item to the dictionary, or after the replacement of the value of a dictionary item (but while the dictionary is write-locked - if locking is enabled).
37 - 2. `dictionary_register_delete_callback()` that will be called just prior to the deletion of an item from the dictionary, or prior to the replacement of the value of a dictionary item (but while the dictionary is write-locked - if locking is enabled).
38 - 3. `dictionary_register_conflict_callback()` that will be called when `DICT_OPTION_DONT_OVERWRITE_VALUE` is set and another value is attempted to be inserted for the same key.
36 +1. `dictionary_register_insert_callback()` that can be called just after the insertion of an item to the dictionary, or after the replacement of the value of a dictionary item.
37 +2. `dictionary_register_delete_callback()` that will be called just prior to the deletion of an item from the dictionary, or prior to the replacement of the value of a dictionary item.
38 +3. `dictionary_register_conflict_callback()` that will be called when `DICT_OPTION_DONT_OVERWRITE_VALUE` is set, and another `value` is attempted to be inserted for the same key.
39 +4. `dictionary_register_react_callback()` that will be called after the the `insert` and the `conflict` callbacks. The `conflict` callback is called while the dictionary hash table is available for other threads.
40
40 -In **link** mode, the name and/or the value are just linked to the dictionary item, and it is the user's responsibility to free the memory they use after an item is deleted from the dictionary or when the dictionary is destroyed.
41 +In **link** mode, the `name` and/or the `value` are just linked to the dictionary item, and it is the user's responsibility to free the memory they use after an item is deleted from the dictionary or when the dictionary is destroyed.
42
43 By default, **clone** mode is used for both the name and the value.
44
@@ -51,19 +52,27 @@ The dictionary allows both **single-threaded** operation (no locks - faster) and
52
53 The default is **multi-threaded**. To enable **single-threaded** add `DICT_OPTION_SINGLE_THREADED` to the flags when creating the dictionary.
54
55 +When in **multi-threaded** mode, the dictionaries have 2 independent R/W locks. One for the linked list and one for the hash table (index). An insertion and a deletion will acquire both independently (one after another) for as long as they are needed, but a traversal may hold the the linked list for longer durations. The hash table (index) lock may be acquired while the linked list is acquired, but not the other way around (and the way the code is structured, it is not technically possible to hold and index lock and then lock the linked list one).
56 +
57 +These locks are R/W locks. They allow multiple readers, but only one writer.
58 +
59 +Unlike POSIX standards, the linked-list lock, allows one writer to lock it multiple times. This has been implemented in such a way, so that a traversal to the items of the dictionary in write-lock mode, allows the writing thread to call `dictionary_set()` or `dictionary_del()`, which alter the dictionary index and the linked list. Especially for the deletion of the currently working item, the dictionary support delayed removal, so it will remove it from the index immediately and mark it as deleted, so that it can be added to the dictionary again with a different value and the traversal will still proceed from the point it was.
60 +
61 ## Hash table operations
62
63 The dictionary supports the following operations supported by the hash table:
64
65 - `dictionary_set()` to add an item to the dictionary, or change its value.
59 -- `dictionary_get()` to get an item from the dictionary.
66 +- `dictionary_get()` and `dictionary_get_and_acquire_item()` to get an item from the dictionary.
67 - `dictionary_del()` to delete an item from the dictionary.
68
69 +For all the calls, there are also `*_advanced()` versions of them, that support more parameters. Check the header file for more information about them.
70 +
71 ## Creation and destruction
72
73 Use `dictionary_create()` to create a dictionary.
74
66 -Use `dictionary_destroy()` to destroy a dictionary. When destroyed, a dictionary frees all the memory it has allocated on its own. This can be complemented by the registration of a deletion callback function that can be called upon deletion of each item in the dictionary, which may free additional resources.
75 +Use `dictionary_destroy()` to destroy a dictionary. When destroyed, a dictionary frees all the memory it has allocated on its own. This can be complemented by the registration of a deletion callback function that can be called upon deletion of each item in the dictionary, which may free additional resources linked to it.
76
77 ### dictionary_set()
78
@@ -74,8 +83,6 @@ This call is used to:
83
84 If **resetting** is not desired, add `DICT_OPTION_DONT_OVERWRITE_VALUE` to the flags when creating the dictionary. In this case, `dictionary_set()` will return the value of the original item found in the dictionary instead of resetting it and the value passed to the call will be ignored. Optionally a conflict callback function can be registered, to manipulate (probably merge or extend) the original value, based on the new value attempted to be added to the dictionary.
85
77 -For **multi-threaded** operation, the `dictionary_set()` calls get an exclusive write lock on the dictionary.
78 -
86 The format is:
87
88 ```c
@@ -87,17 +94,15 @@ Where:
94 * `dict` is a pointer to the dictionary previously created.
95 * `name` is a pointer to a string to be used as the key of this item. The name must not be `NULL` and must not be an empty string `""`.
96 * `value` is a pointer to the value associated with this item. In **clone** mode, if `value` is `NULL`, a new memory allocation will be made of `value_len` size and will be initialized to zero.
90 -* `value_len` is the size of the `value` data. If `value_len` is zero, no allocation will be done and the dictionary item will permanently have the `NULL` value.
91 -
92 -> **IMPORTANT**<br/>There is also an **unsafe** version (without locks) of this call. This is to be used when traversing the dictionary in write mode. It should never be called without an active lock on the dictionary, which can only be acquired while traversing.
97 +* `value_len` is the size of the `value` data in bytes. If `value_len` is zero, no allocation will be done and the dictionary item will permanently have the `NULL` value.
98
99 ### dictionary_get()
100
96 -This call is used to get the value of an item, given its name. It utilizes the `JudyHS` hash table for making the lookup.
101 +This call is used to get the `value` of an item, given its `name`. It utilizes the hash table (index) for making the lookup.
102
98 -For **multi-threaded** operation, the `dictionary_get()` call gets a shared read lock on the dictionary.
103 +For **multi-threaded** operation, the `dictionary_get()` call gets a shared read lock on the index lock (multiple readers are allowed). The linked-list lock is not used.
104
100 -In clone mode, the value returned is not guaranteed to be valid, as any other thread may delete the item from the dictionary at any time. To ensure the value will be available, use `dictionary_get_and_acquire_item()`, which uses a reference counter to defer deletes until the item is released.
105 +In clone mode, the value returned is not guaranteed to be valid, as any other thread may delete the item from the dictionary at any time. To ensure the value will be available, use `dictionary_get_and_acquire_item()`, which uses a reference counter to defer deletes until the item is released with `dictionary_acquired_item_release()`.
106
107 The format is:
108
@@ -110,16 +115,12 @@ Where:
115 * `dict` is a pointer to the dictionary previously created.
116 * `name` is a pointer to a string to be used as the key of this item. The name must not be `NULL` and must not be an empty string `""`.
117
113 -> **IMPORTANT**<br/>There is also an **unsafe** version (without locks) of this call. This is to be used when traversing the dictionary. It should never be called without an active lock on the dictionary, which can only be acquired while traversing.
114 -
118 ### dictionary_del()
119
120 This call is used to delete an item from the dictionary, given its name.
121
122 If there is a deletion callback registered to the dictionary (`dictionary_register_delete_callback()`), it is called prior to the actual deletion of the item.
123
121 -For **multi-threaded** operation, the `dictionary_del()` calls get an exclusive write lock on the dictionary.
122 -
124 The format is:
125
126 ```c
@@ -131,11 +132,9 @@ Where:
132 * `dict` is a pointer to the dictionary previously created.
133 * `name` is a pointer to a string to be used as the key of this item. The name must not be `NULL` and must not be an empty string `""`.
134
134 -> **IMPORTANT**<br/>There is also an **unsafe** version (without locks) of this call. This is to be used when traversing the dictionary, to delete the current item. It should never be called without an active lock on the dictionary, which can only be acquired while traversing.
135 -
135 ### dictionary_get_and_acquire_item()
136
138 -This call can be used the search and get a dictionary item, while ensuring that it will be available for use, until `dictionary_acquired_item_release()` is called.
137 +This call can be used to search and acquire a dictionary item, while ensuring that it will be available for use, until `dictionary_acquired_item_release()` is called.
138
139 This call **does not return the value** of the dictionary item. It returns an internal pointer to a structure that maintains the reference counter used to protect the actual value. To get the value of the item (the same value as returned by `dictionary_get()`), the function `dictionary_acquired_item_value()` has to be called.
140
@@ -149,7 +148,7 @@ DICTIONARY *dict = dictionary_create(DICT_OPTION_NONE);
148 dictionary_set(dict, "name", "value", 6);
149
150 // find the item we added and acquire it
152 -void *item = dictionary_get_and_acquire_item(dict, "name");
151 +const DICTIONARY_ITEM *item = dictionary_get_and_acquire_item(dict, "name");
152
153 // extract its value
154 char *value = (char *)dictionary_acquired_item_value(dict, item);
@@ -164,7 +163,7 @@ dictionary_acquired_item_release(dict, item);
163 dictionary_destroy(dict);
164 ```
165
167 -When items are acquired, a reference counter is maintained to keep track of how many users exist for it. If an item with a non-zero number of users is deleted, it is removed from the index, it can be added again to the index (without conflict), and although it exists in the linked-list, it is not offered during traversal. Garbage collection to actually delete the item happens every time a write-locked dictionary is unlocked (just before the unlock) and items are deleted only if no users are using them.
166 +When items are acquired, a reference counter is maintained to keep track of how many users exist for it. If an item with a non-zero number of users is deleted, it is removed from the index, it can be added again to the index (without conflict), and although it exists in the linked-list, it is not offered during traversal. Garbage collection to actually delete the item happens every time another item is added or removed from the linked-list and items are deleted only if no users are using them.
167
168 If any item is still acquired when the dictionary is destroyed, the destruction of the dictionary is also deferred until all the acquired items are released. When the dictionary is destroyed like that, all operations on the dictionary fail (traversals do not traverse, insertions do not insert, deletions do not delete, searches do not find any items, etc). Once the last item in the dictionary is released, the dictionary is automatically destroyed too.
169
@@ -176,18 +175,14 @@ Dictionaries offer 3 ways to traverse the entire dictionary:
175 - **sorted walkthrough**, which first sorts the dictionary and then call a callback function for every item.
176 - **foreach**, a way to traverse the dictionary with a for-next loop.
177
179 -All these methods are available in **read** or **write** mode. In **read** mode only lookups are allowed to the dictionary. In **write** lookups but also insertions and deletions are allowed.
180 -
181 -While traversing the dictionary with any of these methods, all calls to the dictionary have to use the `_unsafe` versions of the function calls, otherwise deadlocks may arise.
182 -
183 -> **IMPORTANT**<br/>The dictionary itself does not check to ensure that a user is actually using the right lock mode (read or write) while traversing the dictionary for each of the unsafe calls.
178 +All these methods are available in **read**, **write**, or **reentrant** mode. In **read** mode only lookups are allowed to the dictionary. In **write** lookups but also insertions and deletions are allowed, and in **reentrant** mode the dictionary is unlocked outside dictionary code.
179
180 ### walkthrough (callback)
181
182 There are 4 calls:
183
189 -- `dictionary_walkthrough_read()` and `dictionary_sorted_walkthrough_read()` that acquire a shared read lock, and they call a callback function for every item of the dictionary. The callback function may use the unsafe versions of the `dictionary_get()` calls to lookup other items in the dictionary, but it should not attempt to add or remove items to/from the dictionary.
190 -- `dictionary_walkthrough_write()` and `dictionary_sorted_walkthrough_write()` that acquire an exclusive write lock, and they call a callback function for every item of the dictionary. This is to be used when items need to be added to or removed from the dictionary. The `write` versions can be used to delete any or all the items from the dictionary, including the currently working one. For the `sorted` version, all items in the dictionary maintain a reference counter, so all deletions are deferred until the sorted walkthrough finishes.**
184 +- `dictionary_walkthrough_read()` and `dictionary_sorted_walkthrough_read()` acquire a shared read lock on the linked-list, and they call a callback function for every item of the dictionary.
185 +- `dictionary_walkthrough_write()` and `dictionary_sorted_walkthrough_write()` acquire a write lock on the linked-list, and they call a callback function for every item of the dictionary. This is to be used when items need to be added to or removed from the dictionary. The `write` versions can be used to delete any or all the items from the dictionary, including the currently working one. For the `sorted` version, all items in the dictionary maintain a reference counter, so all deletions are deferred until the sorted walkthrough finishes.
186
187 The non sorted versions traverse the items in the same order they have been added to the dictionary (or the reverse order if the flag `DICT_OPTION_ADD_IN_FRONT` is set during dictionary creation). The sorted versions sort alphabetically the items based on their name, and then they traverse them in the sorted order.
188
@@ -198,40 +193,40 @@ The callback function returns an `int`. If this value is negative, traversal of
193 The following is a snippet of such a loop:
194
195 ```c
201 -MY_ITEM *item;
202 -dfe_start_read(dict, item) {
203 - printf("hey, I got an item named '%s' with value ptr %08X", item_name, item);
196 +MY_STRUCTURE *x;
197 +dfe_start_read(dict, x) {
198 + printf("hey, I got an item named '%s' with value ptr %08X", x_dfe.name, x);
199 }
205 -dfe_done(item);
200 +dfe_done(x);
201 ```
202
208 -The `item` parameter gives the name of the pointer to be used while iterating the items. Any name is accepted.
203 +The `x` parameter gives the name of the pointer to be used while iterating the items. Any name is accepted. `x` points to the `value` of the item in the dictionary.
204
210 -The `item_name` is a variable that is automatically created, by concatenating whatever is given as `item` and `_name`. So, if you call `dfe_start_read(dict, myvar)`, the name will be `myvar_name`.
205 +The `x_dfe.name` is a variable that is automatically created, by concatenating whatever is given as `x` and `_dfe`. It is an object and it has a few members, including `x_dfe.counter` that counts the iterations made so far, `x_dfe.item` that provides the acquired item from the dictionary and which can be used to pass it over for further processing, etc. Check the header file for more info. So, if you call `dfe_start_read(dict, myvar)`, the name will be `myvar_dfe`.
206
207 Both `dfe_start_read(dict, item)` and `dfe_done(item)` are together inside a `do { ... } while(0)` loop, so that the following will work:
208
209 ```c
210 MY_ITEM *item;
211
217 -if(x = 1)
212 +if(a = 1)
213 // do {
219 - dfe_start_read(dict, item)
220 - printf("hey, I got an item named '%s' with value ptr %08X", item_name, item);
221 - dfe_done(item);
214 + dfe_start_read(dict, x)
215 + printf("hey, I got an item named '%s' with value ptr %08X", x_dfe.name, x);
216 + dfe_done(x);
217 // } while(0);
218 else
219 something else;
220 ```
221
227 -In the above, the `if(x == 1)` condition will work as expected. It will do the foreach loop when x is 1, otherwise it will run `something else`.
222 +In the above, the `if(a == 1)` condition will work as expected. It will do the foreach loop when a is 1, otherwise it will run `something else`.
223
224 There are 2 versions of `dfe_start`:
225
231 -- `dfe_start_read()` that acquires a shared read lock to the dictionary.
232 -- `dfe_start_write()` that acquires an exclusive write lock to the dictionary.
226 +- `dfe_start_read()` that acquires a shared read linked-list lock to the dictionary.
227 +- `dfe_start_write()` that acquires an exclusive write linked-list lock to the dictionary.
228
234 -While in the loop, depending on the read or write versions of `dfe_start`, the caller may lookup or manipulate the dictionary using the unsafe functions. The rules are the same with the unsorted walkthrough callback functions.
229 +While in the loop, depending on the read or write versions of `dfe_start`, the caller may lookup or manipulate the dictionary. The rules are the same with the unsorted walkthrough callback functions.
230
231 PS: DFE is Dictionary For Each.
232
libnetdata/dictionary/dictionary.c
+69 -24
@@ -2951,25 +2951,38 @@ struct thread_view_unittest {
2951 static void *unittest_dict_master_thread(void *arg) {
2952 struct thread_view_unittest *tv = arg;
2953
2954 + DICTIONARY_ITEM *item = NULL;
2955 + int loops = 0;
2956 while(!__atomic_load_n(&tv->join, __ATOMIC_SEQ_CST)) {
2955 - if(__atomic_load_n(&tv->item_master, __ATOMIC_SEQ_CST) != NULL)
2957 +
2958 + if(!item)
2959 + item = dictionary_set_and_acquire_item(tv->master, "ITEM1", "123", strlen("123") + 1);
2960 +
2961 + if(__atomic_load_n(&tv->item_master, __ATOMIC_SEQ_CST) != NULL) {
2962 + dictionary_acquired_item_release(tv->master, item);
2963 + dictionary_del(tv->master, "ITEM1");
2964 + item = NULL;
2965 + loops++;
2966 continue;
2967 + }
2968
2958 - DICTIONARY_ITEM *item = dictionary_set_and_acquire_item(tv->master, "ITEM1", "123", strlen("123") + 1);
2959 - dictionary_acquired_item_dup(tv->master, item);
2969 + dictionary_acquired_item_dup(tv->master, item); // for the view thread
2970 + __atomic_store_n(&tv->item_master, item, __ATOMIC_SEQ_CST);
2971 dictionary_del(tv->master, "ITEM1");
2972
2962 - __atomic_store_n(&tv->item_master, item, __ATOMIC_SEQ_CST);
2973
2964 - for(int i = 0; i < tv->dups ; i++) {
2974 + for(int i = 0; i < tv->dups + loops ; i++) {
2975 dictionary_acquired_item_dup(tv->master, item);
2976 }
2977
2968 - for(int i = 0; i < tv->dups ; i++) {
2978 + for(int i = 0; i < tv->dups + loops ; i++) {
2979 dictionary_acquired_item_release(tv->master, item);
2980 }
2981
2982 dictionary_acquired_item_release(tv->master, item);
2983 +
2984 + item = NULL;
2985 + loops = 0;
2986 }
2987
2988 return arg;
@@ -2978,9 +2991,11 @@ static void *unittest_dict_master_thread(void *arg) {
2991 static void *unittest_dict_view_thread(void *arg) {
2992 struct thread_view_unittest *tv = arg;
2993
2994 + DICTIONARY_ITEM *m_item = NULL;
2995 +
2996 while(!__atomic_load_n(&tv->join, __ATOMIC_SEQ_CST)) {
2982 - DICTIONARY_ITEM *m_item = __atomic_load_n(&tv->item_master, __ATOMIC_SEQ_CST);
2983 - if(!m_item) continue;
2997 + if(!(m_item = __atomic_load_n(&tv->item_master, __ATOMIC_SEQ_CST)))
2998 + continue;
2999
3000 DICTIONARY_ITEM *v_item = dictionary_view_set_and_acquire_item(tv->view, "ITEM2", m_item);
3001 dictionary_acquired_item_release(tv->master, m_item);
@@ -2996,6 +3011,11 @@ static void *unittest_dict_view_thread(void *arg) {
3011
3012 dictionary_del(tv->view, "ITEM2");
3013
3014 + while(!__atomic_load_n(&tv->join, __ATOMIC_SEQ_CST) && !(m_item = __atomic_load_n(&tv->item_master, __ATOMIC_SEQ_CST))) {
3015 + dictionary_acquired_item_dup(tv->view, v_item);
3016 + dictionary_acquired_item_release(tv->view, v_item);
3017 + }
3018 +
3019 dictionary_acquired_item_release(tv->view, v_item);
3020 }
3021
@@ -3013,9 +3033,11 @@ static int dictionary_unittest_view_threads() {
3033 };
3034
3035 // threads testing of dictionary
3016 - struct dictionary_stats stats = {};
3017 - tv.master = dictionary_create_advanced(DICT_OPTION_NAME_LINK_DONT_CLONE | DICT_OPTION_DONT_OVERWRITE_VALUE, &stats);
3036 + struct dictionary_stats stats_master = {};
3037 + struct dictionary_stats stats_view = {};
3038 + tv.master = dictionary_create_advanced(DICT_OPTION_NAME_LINK_DONT_CLONE | DICT_OPTION_DONT_OVERWRITE_VALUE, &stats_master);
3039 tv.view = dictionary_create_view(tv.master);
3040 + tv.view->stats = &stats_view;
3041
3042 time_t seconds_to_run = 5;
3043 fprintf(
@@ -3048,27 +3070,50 @@ static int dictionary_unittest_view_threads() {
3070 netdata_thread_join(master_thread, &retval);
3071
3072 fprintf(stderr,
3051 - "inserts %zu"
3073 + "MASTER: inserts %zu"
3074 ", deletes %zu"
3075 ", searches %zu"
3076 ", resets %zu"
3055 - ", entries %ld (%ld on view)"
3056 - ", referenced_items %ld (%ld on view)"
3057 - ", pending deletions %ld (%ld on view)"
3077 + ", entries %ld"
3078 + ", referenced_items %ld"
3079 + ", pending deletions %ld"
3080 + ", check spins %zu"
3081 + ", insert spins %zu"
3082 + ", search ignores %zu"
3083 + "\n",
3084 + stats_master.ops.inserts,
3085 + stats_master.ops.deletes,
3086 + stats_master.ops.searches,
3087 + stats_master.ops.resets,
3088 + tv.master->entries,
3089 + tv.master->referenced_items,
3090 + tv.master->pending_deletion_items,
3091 + stats_master.spin_locks.use,
3092 + stats_master.spin_locks.insert,
3093 + stats_master.spin_locks.search
3094 + );
3095 + fprintf(stderr,
3096 + "VIEW : inserts %zu"
3097 + ", deletes %zu"
3098 + ", searches %zu"
3099 + ", resets %zu"
3100 + ", entries %ld"
3101 + ", referenced_items %ld"
3102 + ", pending deletions %ld"
3103 ", check spins %zu"
3104 ", insert spins %zu"
3105 ", search ignores %zu"
3106 "\n",
3062 - stats.ops.inserts,
3063 - stats.ops.deletes,
3064 - stats.ops.searches,
3065 - stats.ops.resets,
3066 - tv.master->entries, tv.view->entries,
3067 - tv.master->referenced_items, tv.view->referenced_items,
3068 - tv.master->pending_deletion_items, tv.view->pending_deletion_items,
3069 - stats.spin_locks.use,
3070 - stats.spin_locks.insert,
3071 - stats.spin_locks.search
3107 + stats_view.ops.inserts,
3108 + stats_view.ops.deletes,
3109 + stats_view.ops.searches,
3110 + stats_view.ops.resets,
3111 + tv.view->entries,
3112 + tv.view->referenced_items,
3113 + tv.view->pending_deletion_items,
3114 + stats_view.spin_locks.use,
3115 + stats_view.spin_locks.insert,
3116 + stats_view.spin_locks.search
3117 );
3118 dictionary_destroy(tv.master);
3119 dictionary_destroy(tv.view);
libnetdata/dictionary/dictionary.h
+3 -3
@@ -84,9 +84,9 @@ struct dictionary_stats {
84
85 struct {
86 size_t inserts; // number of times the insert callback is called
87 - size_t conflicts; // number of times the insert conflict is called
88 - size_t reacts; // number of times the insert react is called
89 - size_t deletes; // number of times the insert delete is called
87 + size_t conflicts; // number of times the conflict callback is called
88 + size_t reacts; // number of times the react callback is called
89 + size_t deletes; // number of times the delete callback is called
90 } callbacks;
91
92 // memory
libnetdata/string/string.c
+2 -2
@@ -12,8 +12,8 @@ struct netdata_string {
12 uint32_t length; // the string length including the terminating '\0'
13
14 REFCOUNT refcount; // how many times this string is used
15 - // We use a signed number to be able to detect duplicate frees of a string.
16 - // If at any point this goes below zero, we have a duplicate free.
15 + // We use a signed number to be able to detect duplicate frees of a string.
16 + // If at any point this goes below zero, we have a duplicate free.
17
18 const char str[]; // the string itself, is appended to this structure
19 };