dictionary.h 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #ifndef NETDATA_DICTIONARY_H
  3. #define NETDATA_DICTIONARY_H 1
  4. #include "../libnetdata.h"
  5. /*
  6. * Netdata DICTIONARY features:
  7. *
  8. * CLONE or LINK
  9. * Names and Values in the dictionary can be cloned or linked.
  10. * In clone mode, the dictionary does all the memory management.
  11. * The default is clone for both names and values.
  12. * Set DICT_OPTION_NAME_LINK_DONT_CLONE to link names.
  13. * Set DICT_OPTION_VALUE_LINK_DONT_CLONE to link names.
  14. *
  15. * ORDERED
  16. * Items are ordered in the order they are added (new items are appended at the end).
  17. * You may reverse the order by setting the flag DICT_OPTION_ADD_IN_FRONT.
  18. *
  19. * LOOKUP
  20. * The dictionary uses JudyHS to maintain a very fast randomly accessible hash table.
  21. *
  22. * MULTI-THREADED and SINGLE-THREADED
  23. * Each dictionary may be single threaded (no locks), or multi-threaded (multiple readers or one writer).
  24. * The default is multi-threaded. Add the flag DICT_OPTION_SINGLE_THREADED for single-threaded.
  25. *
  26. * WALK-THROUGH and FOREACH traversal
  27. * The dictionary can be traversed on read or write mode, either with a callback (walkthrough) or with
  28. * a loop (foreach).
  29. *
  30. * In write mode traversal, the caller may delete only the current item, but may add as many items as needed.
  31. *
  32. */
  33. #ifdef NETDATA_INTERNAL_CHECKS
  34. #define DICT_WITH_STATS 1
  35. #endif
  36. #ifdef DICTIONARY_INTERNALS
  37. #define DICTFE_CONST
  38. #define DICT_ITEM_CONST
  39. #else
  40. #define DICTFE_CONST const
  41. #define DICT_ITEM_CONST const
  42. #endif
  43. typedef struct dictionary DICTIONARY;
  44. typedef struct dictionary_item DICTIONARY_ITEM;
  45. typedef enum __attribute__((packed)) dictionary_options {
  46. DICT_OPTION_NONE = 0, // the default is the opposite of all below
  47. DICT_OPTION_SINGLE_THREADED = (1 << 0), // don't use any locks (default: use locks)
  48. DICT_OPTION_VALUE_LINK_DONT_CLONE = (1 << 1), // don't copy the value, just point to the one provided (default: copy)
  49. DICT_OPTION_NAME_LINK_DONT_CLONE = (1 << 2), // don't copy the name, just point to the one provided (default: copy)
  50. DICT_OPTION_DONT_OVERWRITE_VALUE = (1 << 3), // don't overwrite values of dictionary items (default: overwrite)
  51. DICT_OPTION_ADD_IN_FRONT = (1 << 4), // add dictionary items at the front of the linked list (default: at the end)
  52. DICT_OPTION_FIXED_SIZE = (1 << 5), // the items of the dictionary have a fixed size
  53. } DICT_OPTIONS;
  54. struct dictionary_stats {
  55. const char *name; // the name of the category
  56. struct {
  57. size_t active; // the number of active dictionaries
  58. size_t deleted; // the number of dictionaries queued for destruction
  59. } dictionaries;
  60. struct {
  61. long entries; // active items in the dictionary
  62. long pending_deletion; // pending deletion items in the dictionary
  63. long referenced; // referenced items in the dictionary
  64. } items;
  65. struct {
  66. size_t creations; // dictionary creations
  67. size_t destructions; // dictionary destructions
  68. size_t flushes; // dictionary flushes
  69. size_t traversals; // dictionary foreach
  70. size_t walkthroughs; // dictionary walkthrough
  71. size_t garbage_collections; // dictionary garbage collections
  72. size_t searches; // item searches
  73. size_t inserts; // item inserts
  74. size_t resets; // item resets
  75. size_t deletes; // item deletes
  76. } ops;
  77. struct {
  78. size_t inserts; // number of times the insert callback is called
  79. size_t conflicts; // number of times the conflict callback is called
  80. size_t reacts; // number of times the react callback is called
  81. size_t deletes; // number of times the delete callback is called
  82. } callbacks;
  83. // memory
  84. struct {
  85. long index; // bytes of keys indexed (indication of the index size)
  86. long values; // bytes of caller structures
  87. long dict; // bytes of the structures dictionary needs
  88. } memory;
  89. // spin locks
  90. struct {
  91. size_t use_spins; // number of times a reference to item had to spin to acquire it or ignore it
  92. size_t search_spins; // number of times a successful search result had to be thrown away
  93. size_t insert_spins; // number of times an insertion to the hash table had to be repeated
  94. size_t delete_spins; // number of times a deletion had to spin to get a decision
  95. } spin_locks;
  96. };
  97. // Create a dictionary
  98. #ifdef NETDATA_INTERNAL_CHECKS
  99. #define dictionary_create(options) dictionary_create_advanced_with_trace(options, NULL, 0, __FUNCTION__, __LINE__, __FILE__)
  100. #define dictionary_create_advanced(options, stats, fixed_size) dictionary_create_advanced_with_trace(options, stats, fixed_size, __FUNCTION__, __LINE__, __FILE__)
  101. DICTIONARY *dictionary_create_advanced_with_trace(DICT_OPTIONS options, struct dictionary_stats *stats, size_t fixed_size, const char *function, size_t line, const char *file);
  102. #else
  103. #define dictionary_create(options) dictionary_create_advanced(options, NULL, 0)
  104. DICTIONARY *dictionary_create_advanced(DICT_OPTIONS options, struct dictionary_stats *stats, size_t fixed_size);
  105. #endif
  106. // Create a view on a dictionary
  107. #ifdef NETDATA_INTERNAL_CHECKS
  108. #define dictionary_create_view(master) dictionary_create_view_with_trace(master, __FUNCTION__, __LINE__, __FILE__)
  109. DICTIONARY *dictionary_create_view_with_trace(DICTIONARY *master, const char *function, size_t line, const char *file);
  110. #else
  111. DICTIONARY *dictionary_create_view(DICTIONARY *master);
  112. #endif
  113. // an insert callback to be called just after an item is added to the dictionary
  114. // this callback is called while the dictionary is write locked!
  115. void dictionary_register_insert_callback(DICTIONARY *dict, void (*ins_callback)(const DICTIONARY_ITEM *item, void *value, void *data), void *data);
  116. // a delete callback to be called just before an item is deleted forever
  117. // this callback is called while the dictionary is write locked!
  118. void dictionary_register_delete_callback(DICTIONARY *dict, void (*del_callback)(const DICTIONARY_ITEM *item, void *value, void *data), void *data);
  119. // a merge callback to be called when DICT_OPTION_DONT_OVERWRITE_VALUE
  120. // and an item is already found in the dictionary - the dictionary does nothing else in this case
  121. // the old_value will remain in the dictionary - the new_value is ignored
  122. // The callback should return true if the value has been updated (it increases the dictionary version).
  123. void dictionary_register_conflict_callback(DICTIONARY *dict, bool (*conflict_callback)(const DICTIONARY_ITEM *item, void *old_value, void *new_value, void *data), void *data);
  124. // a reaction callback to be called after every item insertion or conflict
  125. // after the constructors have finished and the items are fully available for use
  126. // and the dictionary is not write locked anymore
  127. void dictionary_register_react_callback(DICTIONARY *dict, void (*react_callback)(const DICTIONARY_ITEM *item, void *value, void *data), void *data);
  128. // Destroy a dictionary
  129. // Returns the number of bytes freed
  130. // The returned value will not include name/key sizes
  131. // Registered delete callbacks will be run for each item in the dictionary.
  132. size_t dictionary_destroy(DICTIONARY *dict);
  133. // Empties a dictionary
  134. // Referenced items will survive, but are not offered anymore.
  135. // Registered delete callbacks will be run for each item in the dictionary.
  136. void dictionary_flush(DICTIONARY *dict);
  137. void dictionary_version_increment(DICTIONARY *dict);
  138. void dictionary_garbage_collect(DICTIONARY *dict);
  139. void cleanup_destroyed_dictionaries(void);
  140. // ----------------------------------------------------------------------------
  141. // Set an item in the dictionary
  142. //
  143. // - if an item with the same name does not exist, create one
  144. // - if an item with the same name exists, then:
  145. // a) if DICT_OPTION_DONT_OVERWRITE_VALUE is set, just return the existing value (ignore the new value)
  146. // else b) reset the value to the new value passed at the call
  147. //
  148. // When DICT_OPTION_VALUE_LINK_DONT_CLONE is set, the value is linked, otherwise it is copied
  149. // When DICT_OPTION_NAME_LINK_DONT_CLONE is set, the name is linked, otherwise it is copied
  150. //
  151. // When neither DICT_OPTION_VALUE_LINK_DONT_CLONE nor DICT_OPTION_NAME_LINK_DONT_CLONE are set, all the
  152. // memory management for names and values is done by the dictionary.
  153. //
  154. // Passing NULL as value, the dictionary will callocz() the newly allocated value, otherwise it will copy it.
  155. // Passing 0 as value_len, the dictionary will set the value to NULL (no allocations for value will be made).
  156. #define dictionary_set(dict, name, value, value_len) dictionary_set_advanced(dict, name, -1, value, value_len, NULL)
  157. void *dictionary_set_advanced(DICTIONARY *dict, const char *name, ssize_t name_len, void *value, size_t value_len, void *constructor_data);
  158. #define dictionary_set_and_acquire_item(dict, name, value, value_len) dictionary_set_and_acquire_item_advanced(dict, name, -1, value, value_len, NULL)
  159. DICT_ITEM_CONST DICTIONARY_ITEM *dictionary_set_and_acquire_item_advanced(DICTIONARY *dict, const char *name, ssize_t name_len, void *value, size_t value_len, void *constructor_data);
  160. // set an item in a dictionary view
  161. #define dictionary_view_set_and_acquire_item(dict, name, master_item) dictionary_view_set_and_acquire_item_advanced(dict, name, -1, master_item)
  162. DICT_ITEM_CONST DICTIONARY_ITEM *dictionary_view_set_and_acquire_item_advanced(DICTIONARY *dict, const char *name, ssize_t name_len, DICTIONARY_ITEM *master_item);
  163. #define dictionary_view_set(dict, name, master_item) dictionary_view_set_advanced(dict, name, -1, master_item)
  164. void *dictionary_view_set_advanced(DICTIONARY *dict, const char *name, ssize_t name_len, DICT_ITEM_CONST DICTIONARY_ITEM *master_item);
  165. // ----------------------------------------------------------------------------
  166. // Get an item from the dictionary
  167. // If it returns NULL, the item is not found
  168. #define dictionary_get(dict, name) dictionary_get_advanced(dict, name, -1)
  169. void *dictionary_get_advanced(DICTIONARY *dict, const char *name, ssize_t name_len);
  170. #define dictionary_get_and_acquire_item(dict, name) dictionary_get_and_acquire_item_advanced(dict, name, -1)
  171. DICT_ITEM_CONST DICTIONARY_ITEM *dictionary_get_and_acquire_item_advanced(DICTIONARY *dict, const char *name, ssize_t name_len);
  172. // ----------------------------------------------------------------------------
  173. // Delete an item from the dictionary
  174. // returns true if the item was found and has been deleted
  175. // returns false if the item was not found in the index
  176. #define dictionary_del(dict, name) dictionary_del_advanced(dict, name, -1)
  177. bool dictionary_del_advanced(DICTIONARY *dict, const char *name, ssize_t name_len);
  178. // ----------------------------------------------------------------------------
  179. // reference counters management
  180. void dictionary_acquired_item_release(DICTIONARY *dict, DICT_ITEM_CONST DICTIONARY_ITEM *item);
  181. DICT_ITEM_CONST DICTIONARY_ITEM *dictionary_acquired_item_dup(DICTIONARY *dict, DICT_ITEM_CONST DICTIONARY_ITEM *item);
  182. const char *dictionary_acquired_item_name(DICT_ITEM_CONST DICTIONARY_ITEM *item);
  183. void *dictionary_acquired_item_value(DICT_ITEM_CONST DICTIONARY_ITEM *item);
  184. size_t dictionary_acquired_item_references(DICT_ITEM_CONST DICTIONARY_ITEM *item);
  185. // ----------------------------------------------------------------------------
  186. // Traverse (walk through) the items of the dictionary.
  187. // The order of traversal is currently the order of insertion.
  188. //
  189. // The callback function may return a negative number to stop the traversal,
  190. // in which case that negative value is returned to the caller.
  191. //
  192. // If all callback calls return zero or positive numbers, the sum of all of
  193. // them is returned to the caller.
  194. //
  195. // You cannot alter the dictionary from inside a dictionary_walkthrough_read() - deadlock!
  196. // You can only delete the current item from inside a dictionary_walkthrough_write() - you can add as many as you want.
  197. //
  198. #define dictionary_walkthrough_read(dict, callback, data) dictionary_walkthrough_rw(dict, 'r', callback, data)
  199. #define dictionary_walkthrough_write(dict, callback, data) dictionary_walkthrough_rw(dict, 'w', callback, data)
  200. int dictionary_walkthrough_rw(DICTIONARY *dict, char rw, int (*callback)(const DICTIONARY_ITEM *item, void *value, void *data), void *data);
  201. typedef int (*dictionary_sorted_compar)(const DICTIONARY_ITEM **item1, const DICTIONARY_ITEM **item2);
  202. #define dictionary_sorted_walkthrough_read(dict, callback, data) dictionary_sorted_walkthrough_rw(dict, 'r', callback, data, NULL)
  203. #define dictionary_sorted_walkthrough_write(dict, callback, data) dictionary_sorted_walkthrough_rw(dict, 'w', callback, data, NULL)
  204. int dictionary_sorted_walkthrough_rw(DICTIONARY *dict, char rw, int (*callback)(const DICTIONARY_ITEM *item, void *entry, void *data), void *data, dictionary_sorted_compar compar);
  205. // ----------------------------------------------------------------------------
  206. // Traverse with foreach
  207. //
  208. // Use like this:
  209. //
  210. // DICTFE dfe = {};
  211. // for(MY_ITEM *item = dfe_start_read(&dfe, dict); item ; item = dfe_next(&dfe)) {
  212. // // do things with the item and its dfe.name
  213. // }
  214. // dfe_done(&dfe);
  215. //
  216. // You cannot alter the dictionary from within a dfe_read_start() - deadlock!
  217. // You can only delete the current item from inside a dfe_start_write() - you can add as many as you want.
  218. //
  219. #define DICTIONARY_LOCK_READ 'r'
  220. #define DICTIONARY_LOCK_WRITE 'w'
  221. #define DICTIONARY_LOCK_REENTRANT 'z'
  222. void dictionary_write_lock(DICTIONARY *dict);
  223. void dictionary_write_unlock(DICTIONARY *dict);
  224. typedef DICTFE_CONST struct dictionary_foreach {
  225. DICTIONARY *dict; // the dictionary upon we work
  226. DICTIONARY_ITEM *item; // the item we work on, to remember the position we are at
  227. // this can be used with dictionary_acquired_item_dup() to
  228. // acquire the currently working item.
  229. const char *name; // the dictionary name of the last item used
  230. void *value; // the dictionary value of the last item used
  231. // same as the return value of dictfe_start() and dictfe_next()
  232. size_t counter; // counts the number of iterations made, starting from zero
  233. char rw; // the lock mode 'r' or 'w'
  234. bool locked; // true when the dictionary is locked
  235. } DICTFE;
  236. #define dfe_start_read(dict, value) dfe_start_rw(dict, value, DICTIONARY_LOCK_READ)
  237. #define dfe_start_write(dict, value) dfe_start_rw(dict, value, DICTIONARY_LOCK_WRITE)
  238. #define dfe_start_reentrant(dict, value) dfe_start_rw(dict, value, DICTIONARY_LOCK_REENTRANT)
  239. #define dfe_start_rw(dict, value, mode) \
  240. do { \
  241. DICTFE value ## _dfe = {}; \
  242. (void)(value); /* needed to avoid warning when looping without using this */ \
  243. for((value) = dictionary_foreach_start_rw(&value ## _dfe, (dict), (mode)); \
  244. (value ## _dfe.item) ; \
  245. (value) = dictionary_foreach_next(&value ## _dfe)) \
  246. {
  247. #define dfe_done(value) \
  248. } \
  249. dictionary_foreach_done(&value ## _dfe); \
  250. } while(0)
  251. #define dfe_unlock(value) dictionary_foreach_unlock(&value ## _dfe);
  252. void *dictionary_foreach_start_rw(DICTFE *dfe, DICTIONARY *dict, char rw);
  253. void *dictionary_foreach_next(DICTFE *dfe);
  254. void dictionary_foreach_done(DICTFE *dfe);
  255. void dictionary_foreach_unlock(DICTFE *dfe);
  256. // ----------------------------------------------------------------------------
  257. // Get statistics about the dictionary
  258. size_t dictionary_version(DICTIONARY *dict);
  259. size_t dictionary_entries(DICTIONARY *dict);
  260. size_t dictionary_referenced_items(DICTIONARY *dict);
  261. // for all cases that the caller does not provide a stats structure, this is where they are accumulated.
  262. extern struct dictionary_stats dictionary_stats_category_other;
  263. int dictionary_unittest(size_t entries);
  264. // ----------------------------------------------------------------------------
  265. // THREAD CACHE
  266. void *thread_cache_entry_get_or_set(void *key,
  267. ssize_t key_length,
  268. void *value,
  269. void *(*transform_the_value_before_insert)(void *key, size_t key_length, void *value));
  270. void thread_cache_destroy(void);
  271. #endif /* NETDATA_DICTIONARY_H */