| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #ifndef NETDATA_DICTIONARY_INTERNALS_H |
| 4 | #define NETDATA_DICTIONARY_INTERNALS_H |
| 5 | |
| 6 | #define DICTIONARY_INTERNALS |
| 7 | #include "../libnetdata.h" |
| 8 | |
| 9 | // runtime flags of the dictionary - must be checked with atomics |
| 10 | typedef enum __attribute__ ((__packed__)) { |
| 11 | DICT_FLAG_NONE = 0, |
| 12 | DICT_FLAG_DESTROYED = (1 << 0), // this dictionary has been destroyed |
| 13 | DICT_FLAG_QUEUED_FOR_DESTRUCTION = (1 << 1), // this dictionary is queued for delayed destruction |
| 14 | } DICT_FLAGS; |
| 15 | |
| 16 | #define dict_flag_check(dict, flag) (__atomic_load_n(&((dict)->flags), __ATOMIC_RELAXED) & (flag)) |
| 17 | #define dict_flag_set(dict, flag) __atomic_or_fetch(&((dict)->flags), flag, __ATOMIC_RELAXED) |
| 18 | #define dict_flag_clear(dict, flag) __atomic_and_fetch(&((dict)->flags), ~(flag), __ATOMIC_RELAXED) |
| 19 | |
| 20 | // flags macros |
| 21 | #define is_dictionary_destroyed(dict) dict_flag_check(dict, DICT_FLAG_DESTROYED) |
| 22 | |
| 23 | // configuration options macros |
| 24 | #define is_dictionary_single_threaded(dict) ((dict)->options & DICT_OPTION_SINGLE_THREADED) |
| 25 | #define is_view_dictionary(dict) ((dict)->master) |
| 26 | #define is_master_dictionary(dict) (!is_view_dictionary(dict)) |
| 27 | |
| 28 | typedef enum __attribute__ ((__packed__)) item_options { |
| 29 | ITEM_OPTION_NONE = 0, |
| 30 | ITEM_OPTION_ALLOCATED_NAME = (1 << 0), // the name pointer is a STRING |
| 31 | |
| 32 | // IMPORTANT: This is 1-bit - to add more change ITEM_OPTIONS_BITS |
| 33 | } ITEM_OPTIONS; |
| 34 | |
| 35 | typedef enum __attribute__ ((__packed__)) item_flags { |
| 36 | ITEM_FLAG_NONE = 0, |
| 37 | ITEM_FLAG_DELETED = (1 << 0), // this item is marked deleted, so it is not available for traversal (deleted from the index too) |
| 38 | ITEM_FLAG_BEING_CREATED = (1 << 1), // this item is currently being created - this flag is removed when construction finishes |
| 39 | |
| 40 | // IMPORTANT: This is 8-bit |
| 41 | } ITEM_FLAGS; |
| 42 | |
| 43 | #define item_flag_check(item, flag) (__atomic_load_n(&((item)->flags), __ATOMIC_RELAXED) & (flag)) |
| 44 | #define item_flag_set(item, flag) __atomic_or_fetch(&((item)->flags), flag, __ATOMIC_RELAXED) |
| 45 | #define item_flag_clear(item, flag) __atomic_and_fetch(&((item)->flags), ~(flag), __ATOMIC_RELAXED) |
| 46 | |
| 47 | #define item_shared_flag_check(item, flag) (__atomic_load_n(&((item)->shared->flags), __ATOMIC_RELAXED) & (flag)) |
| 48 | #define item_shared_flag_set(item, flag) __atomic_or_fetch(&((item)->shared->flags), flag, __ATOMIC_RELAXED) |
| 49 | #define item_shared_flag_clear(item, flag) __atomic_and_fetch(&((item)->shared->flags), ~(flag), __ATOMIC_RELAXED) |
| 50 | |
| 51 | #define ITEM_FLAGS_TYPE uint8_t |
| 52 | #define KEY_LEN_TYPE uint32_t |
| 53 | #define VALUE_LEN_TYPE uint32_t |
| 54 | |
| 55 | #define ITEM_OPTIONS_BITS 1 |
| 56 | #define KEY_LEN_BITS ((sizeof(KEY_LEN_TYPE) * 8) - (sizeof(ITEM_FLAGS_TYPE) * 8) - ITEM_OPTIONS_BITS) |
| 57 | #define KEY_LEN_MAX ((1 << KEY_LEN_BITS) - 1) |
| 58 | |
| 59 | #define VALUE_LEN_BITS ((sizeof(VALUE_LEN_TYPE) * 8) - (sizeof(ITEM_FLAGS_TYPE) * 8)) |
| 60 | #define VALUE_LEN_MAX ((1 << VALUE_LEN_BITS) - 1) |
| 61 | |
| 62 | |
| 63 | /* |
| 64 | * Every item in the dictionary has the following structure. |
| 65 | */ |
| 66 | |
| 67 | typedef struct dictionary_item_shared { |
| 68 | void *value; // the value of the dictionary item |
| 69 | |
| 70 | // the order of the following items is important! |
| 71 | // The total of their storage should be 64-bits |
| 72 | |
| 73 | REFCOUNT links; // how many links this item has |
| 74 | VALUE_LEN_TYPE value_len:VALUE_LEN_BITS; // the size of the value |
| 75 | ITEM_FLAGS_TYPE flags; // shared flags |
| 76 | } DICTIONARY_ITEM_SHARED; |
| 77 | |
| 78 | struct dictionary_item { |
| 79 | #ifdef FSANITIZE_ADDRESS |
| 80 | STACKTRACE_ARRAY stacktraces; // stack traces from all acquisition points |
| 81 | #endif |
| 82 | #if defined(FSANITIZE_ADDRESS) || defined(NETDATA_INTERNAL_CHECKS) |
| 83 | DICTIONARY *dict; // the dictionary this item belongs to |
| 84 | #endif |
| 85 | #ifdef NETDATA_INTERNAL_CHECKS |
| 86 | pid_t creator_pid; |
| 87 | pid_t deleter_pid; |
| 88 | pid_t ll_adder_pid; |
| 89 | pid_t ll_remover_pid; |
| 90 | #endif |
| 91 | |
| 92 | DICTIONARY_ITEM_SHARED *shared; |
| 93 | |
| 94 | struct dictionary_item *next; // a double linked list to allow fast insertions and deletions |
| 95 | struct dictionary_item *prev; |
| 96 | |
| 97 | union { |
| 98 | STRING *string_name; // the name of the dictionary item |
| 99 | char *caller_name; // the user supplied string pointer |
| 100 | // void *key_ptr; // binary key pointer |
| 101 | }; |
| 102 | |
| 103 | // the order of the following items is important! |
| 104 | // The total of their storage should be 64-bits |
| 105 | |
| 106 | REFCOUNT refcount; // the private reference counter |
| 107 | |
| 108 | KEY_LEN_TYPE key_len:KEY_LEN_BITS; // the size of key indexed (for strings, including the null terminator) |
| 109 | // this is (2^23 - 1) = 8.388.607 bytes max key length. |
| 110 | |
| 111 | ITEM_OPTIONS options:ITEM_OPTIONS_BITS; // permanent configuration options |
| 112 | // (no atomic operations on this - they never change) |
| 113 | |
| 114 | ITEM_FLAGS_TYPE flags; // runtime changing flags for this item (atomic operations on this) |
| 115 | // cannot be a bit field because of atomics. |
| 116 | }; |
| 117 | |
| 118 | struct dictionary_hooks { |
| 119 | REFCOUNT links; |
| 120 | usec_t last_master_deletion_us; |
| 121 | |
| 122 | dict_cb_insert_t insert_callback; |
| 123 | void *insert_callback_data; |
| 124 | |
| 125 | dict_cb_conflict_t conflict_callback; |
| 126 | void *conflict_callback_data; |
| 127 | |
| 128 | dict_cb_react_t react_callback; |
| 129 | void *react_callback_data; |
| 130 | |
| 131 | dict_cb_delete_t delete_callback; |
| 132 | void *delelte_callback_data; |
| 133 | }; |
| 134 | |
| 135 | struct dictionary { |
| 136 | #ifdef FSANITIZE_ADDRESS |
| 137 | STACKTRACE_ARRAY stacktraces; // stack traces from all acquisition points |
| 138 | #endif |
| 139 | |
| 140 | usec_t last_gc_run_us; |
| 141 | DICT_OPTIONS options; // the configuration flags of the dictionary (they never change - no atomics) |
| 142 | DICT_FLAGS flags; // run time flags for the dictionary (they change all the time - atomics needed) |
| 143 | |
| 144 | ARAL *value_aral; |
| 145 | |
| 146 | struct { // support for multiple indexing engines |
| 147 | Pvoid_t JudyHSArray; // the hash table |
| 148 | RW_SPINLOCK rw_spinlock; // protect the index |
| 149 | } index; |
| 150 | |
| 151 | struct { |
| 152 | DICTIONARY_ITEM *list; // the double linked list of all items in the dictionary |
| 153 | RW_SPINLOCK rw_spinlock; // protect the linked-list |
| 154 | pid_t writer_pid; // the gettid() of the writer |
| 155 | uint32_t writer_depth; // nesting of write locks |
| 156 | } items; |
| 157 | |
| 158 | struct dictionary_hooks *hooks; // pointer to external function callbacks to be called at certain points |
| 159 | struct dictionary_stats *stats; // statistics data, when DICT_OPTION_STATS is set |
| 160 | |
| 161 | DICTIONARY *master; // the master dictionary |
| 162 | DICTIONARY *next; // linked list for delayed destruction (garbage collection of whole dictionaries) |
| 163 | |
| 164 | uint32_t version; // the current version of the dictionary |
| 165 | // it is incremented when: |
| 166 | // - item added |
| 167 | // - item removed |
| 168 | // - item value reset |
| 169 | // - conflict callback returns true |
| 170 | // - function dictionary_version_increment() is called |
| 171 | |
| 172 | int32_t entries; // how many items are currently in the index (the linked list may have more) |
| 173 | int32_t referenced_items; // how many items of the dictionary are currently being used by 3rd parties |
| 174 | int32_t pending_deletion_items; // how many items of the dictionary have been deleted, but have not been removed yet |
| 175 | |
| 176 | #ifdef NETDATA_DICTIONARY_VALIDATE_POINTERS |
| 177 | netdata_mutex_t global_pointer_registry_mutex; |
| 178 | Pvoid_t global_pointer_registry; |
| 179 | #endif |
| 180 | }; |
| 181 | |
| 182 | // ---------------------------------------------------------------------------- |
| 183 | // forward definitions of functions used in reverse order in the code |
| 184 | |
| 185 | void garbage_collect_pending_deletes(DICTIONARY *dict); |
| 186 | static inline void item_linked_list_remove(DICTIONARY *dict, DICTIONARY_ITEM *item); |
| 187 | static size_t dict_item_free_with_hooks(DICTIONARY *dict, DICTIONARY_ITEM *item); |
| 188 | static inline const char *item_get_name(const DICTIONARY_ITEM *item); |
| 189 | static inline int hashtable_delete_unsafe(DICTIONARY *dict, const char *name, size_t name_len, DICTIONARY_ITEM *item); |
| 190 | static void item_release(DICTIONARY *dict, DICTIONARY_ITEM *item); |
| 191 | static bool dict_item_set_deleted(DICTIONARY *dict, DICTIONARY_ITEM *item); |
| 192 | |
| 193 | #define RC_ITEM_OK ( 0) |
| 194 | #define RC_ITEM_MARKED_FOR_DELETION (-1) // the item is marked for deletion |
| 195 | #define RC_ITEM_IS_CURRENTLY_BEING_DELETED (-2) // the item is currently being deleted |
| 196 | #define RC_ITEM_IS_CURRENTLY_BEING_CREATED (-3) // the item is currently being deleted |
| 197 | #define RC_ITEM_IS_REFERENCED (-4) // the item is currently referenced |
| 198 | #define item_check_and_acquire(dict, item) (item_check_and_acquire_advanced(dict, item, false) == RC_ITEM_OK) |
| 199 | static int item_check_and_acquire_advanced(DICTIONARY *dict, DICTIONARY_ITEM *item, bool having_index_lock); |
| 200 | #define item_is_not_referenced_and_can_be_removed(dict, item) (item_is_not_referenced_and_can_be_removed_advanced(dict, item) == RC_ITEM_OK) |
| 201 | static inline int item_is_not_referenced_and_can_be_removed_advanced(DICTIONARY *dict, DICTIONARY_ITEM *item); |
| 202 | |
| 203 | // ---------------------------------------------------------------------------- |
| 204 | // validate each pointer is indexed once - internal checks only |
| 205 | |
| 206 | #ifdef NETDATA_DICTIONARY_VALIDATE_POINTERS |
| 207 | static inline void pointer_index_init(DICTIONARY *dict __maybe_unused) { |
| 208 | netdata_mutex_init(&dict->global_pointer_registry_mutex); |
| 209 | } |
| 210 | |
| 211 | static inline void pointer_destroy_index(DICTIONARY *dict __maybe_unused) { |
| 212 | netdata_mutex_lock(&dict->global_pointer_registry_mutex); |
| 213 | JudyHSFreeArray(&dict->global_pointer_registry, PJE0); |
| 214 | netdata_mutex_unlock(&dict->global_pointer_registry_mutex); |
| 215 | } |
| 216 | static inline void pointer_add(DICTIONARY *dict __maybe_unused, DICTIONARY_ITEM *item __maybe_unused) { |
| 217 | netdata_mutex_lock(&dict->global_pointer_registry_mutex); |
| 218 | Pvoid_t *PValue = JudyHSIns(&dict->global_pointer_registry, &item, sizeof(void *), PJE0); |
| 219 | if(*PValue != NULL) |
| 220 | fatal("pointer already exists in registry"); |
| 221 | *PValue = item; |
| 222 | netdata_mutex_unlock(&dict->global_pointer_registry_mutex); |
| 223 | } |
| 224 | |
| 225 | static inline void pointer_check(DICTIONARY *dict __maybe_unused, DICTIONARY_ITEM *item __maybe_unused) { |
| 226 | netdata_mutex_lock(&dict->global_pointer_registry_mutex); |
| 227 | Pvoid_t *PValue = JudyHSGet(dict->global_pointer_registry, &item, sizeof(void *)); |
| 228 | if(PValue == NULL) |
| 229 | fatal("pointer is not found in registry"); |
| 230 | netdata_mutex_unlock(&dict->global_pointer_registry_mutex); |
| 231 | } |
| 232 | |
| 233 | static inline void pointer_del(DICTIONARY *dict __maybe_unused, DICTIONARY_ITEM *item __maybe_unused) { |
| 234 | netdata_mutex_lock(&dict->global_pointer_registry_mutex); |
| 235 | int ret = JudyHSDel(&dict->global_pointer_registry, &item, sizeof(void *), PJE0); |
| 236 | if(!ret) |
| 237 | fatal("pointer to be deleted does not exist in registry"); |
| 238 | netdata_mutex_unlock(&dict->global_pointer_registry_mutex); |
| 239 | } |
| 240 | #else // !NETDATA_DICTIONARY_VALIDATE_POINTERS |
| 241 | #define pointer_index_init(dict) debug_dummy() |
| 242 | #define pointer_destroy_index(dict) debug_dummy() |
| 243 | #define pointer_add(dict, item) debug_dummy() |
| 244 | #define pointer_check(dict, item) debug_dummy() |
| 245 | #define pointer_del(dict, item) debug_dummy() |
| 246 | #endif // !NETDATA_DICTIONARY_VALIDATE_POINTERS |
| 247 | |
| 248 | extern ARAL *dict_items_aral; |
| 249 | extern ARAL *dict_shared_items_aral; |
| 250 | |
| 251 | size_t dictionary_destroy_delayed_count(void); |
| 252 | |
| 253 | #include "dictionary-debug.h" |
| 254 | #include "dictionary-statistics.h" |
| 255 | #include "dictionary-locks.h" |
| 256 | #include "dictionary-refcount.h" |
| 257 | #include "dictionary-hashtable.h" |
| 258 | #include "dictionary-callbacks.h" |
| 259 | #include "dictionary-item.h" |
| 260 | |
| 261 | #endif //NETDATA_DICTIONARY_INTERNALS_H |