mirror of
https://github.com/netdata/netdata.git
synced 2025-04-07 14:55:41 +00:00

* rrdset - in progress * rrdset optimal constructor; rrdset conflict * rrdset final touches * re-organization of rrdset object members * prevent use-after-free * dictionary dfe supports also counting of iterations * rrddim managed by dictionary * rrd.h cleanup * DICTIONARY_ITEM now is referencing actual dictionary items in the code * removed rrdset linked list * Revert "removed rrdset linked list" This reverts commit 690d6a588b4b99619c2c5e10f84e8f868ae6def5. * removed rrdset linked list * added comments * Switch chart uuid to static allocation in rrdset Remove unused functions * rrdset_archive() and friends... * always create rrdfamily * enable ml_free_dimension * rrddim_foreach done with dfe * most custom rrddim loops replaced with rrddim_foreach * removed accesses to rrddim->dimensions * removed locks that are no longer needed * rrdsetvar is now managed by the dictionary * set rrdset is rrdsetvar, fixes https://github.com/netdata/netdata/pull/13646#issuecomment-1242574853 * conflict callback of rrdsetvar now properly checks if it has to reset the variable * dictionary registered callbacks accept as first parameter the DICTIONARY_ITEM * dictionary dfe now uses internal counter to report; avoided excess variables defined with dfe * dictionary walkthrough callbacks get dictionary acquired items * dictionary reference counters that can be dupped from zero * added advanced functions for get and del * rrdvar managed by dictionaries * thread safety for rrdsetvar * faster rrdvar initialization * rrdvar string lengths should match in all add, del, get functions * rrdvar internals hidden from the rest of the world * rrdvar is now acquired throughout netdata * hide the internal structures of rrdsetvar * rrdsetvar is now acquired through out netdata * rrddimvar managed by dictionary; rrddimvar linked list removed; rrddimvar structures hidden from the rest of netdata * better error handling * dont create variables if not initialized for health * dont create variables if not initialized for health again * rrdfamily is now managed by dictionaries; references of it are acquired dictionary items * type checking on acquired objects * rrdcalc renaming of functions * type checking for rrdfamily_acquired * rrdcalc managed by dictionaries * rrdcalc double free fix * host rrdvars is always needed * attempt to fix deadlock 1 * attempt to fix deadlock 2 * Remove unused variable * attempt to fix deadlock 3 * snprintfz * rrdcalc index in rrdset fix * Stop storing active charts and computing chart hashes * Remove store active chart function * Remove compute chart hash function * Remove sql_store_chart_hash function * Remove store_active_dimension function * dictionary delayed destruction * formatting and cleanup * zero dictionary base on rrdsetvar * added internal error to log delayed destructions of dictionaries * typo in rrddimvar * added debugging info to dictionary * debug info * fix for rrdcalc keys being empty * remove forgotten unlock * remove deadlock * Switch to metadata version 5 and drop chart_hash chart_hash_map chart_active dimension_active v_chart_hash * SQL cosmetic changes * do not busy wait while destroying a referenced dictionary * remove deadlock * code cleanup; re-organization; * fast cleanup and flushing of dictionaries * number formatting fixes * do not delete configured alerts when archiving a chart * rrddim obsolete linked list management outside dictionaries * removed duplicate contexts call * fix crash when rrdfamily is not initialized * dont keep rrddimvar referenced * properly cleanup rrdvar * removed some locks * Do not attempt to cleanup chart_hash / chart_hash_map * rrdcalctemplate managed by dictionary * register callbacks on the right dictionary * removed some more locks * rrdcalc secondary index replaced with linked-list; rrdcalc labels updates are now executed by health thread * when looking up for an alarm look using both chart id and chart name * host initialization a bit more modular * init rrdlabels on host update * preparation for dictionary views * improved comment * unused variables without internal checks * service threads isolation and worker info * more worker info in service thread * thread cancelability debugging with internal checks * strings data races addressed; fixes https://github.com/netdata/netdata/issues/13647 * dictionary modularization * Remove unused SQL statement definition * unit-tested thread safety of dictionaries; removed data race conditions on dictionaries and strings; dictionaries now can detect if the caller is holds a write lock and automatically all the calls become their unsafe versions; all direct calls to unsafe version is eliminated * remove worker_is_idle() from the exit of service functions, because we lose the lock time between loops * rewritten dictionary to have 2 separate locks, one for indexing and another for traversal * Update collectors/cgroups.plugin/sys_fs_cgroup.c Co-authored-by: Vladimir Kobal <vlad@prokk.net> * Update collectors/cgroups.plugin/sys_fs_cgroup.c Co-authored-by: Vladimir Kobal <vlad@prokk.net> * Update collectors/proc.plugin/proc_net_dev.c Co-authored-by: Vladimir Kobal <vlad@prokk.net> * fix memory leak in rrdset cache_dir * minor dictionary changes * dont use index locks in single threaded * obsolete dict option * rrddim options and flags separation; rrdset_done() optimization to keep array of reference pointers to rrddim; * fix jump on uninitialized value in dictionary; remove double free of cache_dir * addressed codacy findings * removed debugging code * use the private refcount on dictionaries * make dictionary item desctructors work on dictionary destruction; strictier control on dictionary API; proper cleanup sequence on rrddim; * more dictionary statistics * global statistics about dictionary operations, memory, items, callbacks * dictionary support for views - missing the public API * removed warning about unused parameter * chart and context name for cloud * chart and context name for cloud, again * dictionary statistics fixed; first implementation of dictionary views - not currently used * only the master can globally delete an item * context needs netdata prefix * fix context and chart it of spins * fix for host variables when health is not enabled * run garbage collector on item insert too * Fix info message; remove extra "using" * update dict unittest for new placement of garbage collector * we need RRDHOST->rrdvars for maintaining custom host variables * Health initialization needs the host->host_uuid * split STRING to its own files; no code changes other than that * initialize health unconditionally * unit tests do not pollute the global scope with their variables * Skip initialization when creating archived hosts on startup. When a child connects it will initialize properly Co-authored-by: Stelios Fragkakis <52996999+stelfrag@users.noreply.github.com> Co-authored-by: Vladimir Kobal <vlad@prokk.net>
316 lines
16 KiB
C
316 lines
16 KiB
C
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
#ifndef NETDATA_DICTIONARY_H
|
|
#define NETDATA_DICTIONARY_H 1
|
|
|
|
#include "../libnetdata.h"
|
|
|
|
|
|
/*
|
|
* Netdata DICTIONARY features:
|
|
*
|
|
* CLONE or LINK
|
|
* Names and Values in the dictionary can be cloned or linked.
|
|
* In clone mode, the dictionary does all the memory management.
|
|
* The default is clone for both names and values.
|
|
* Set DICT_OPTION_NAME_LINK_DONT_CLONE to link names.
|
|
* Set DICT_OPTION_VALUE_LINK_DONT_CLONE to link names.
|
|
*
|
|
* ORDERED
|
|
* Items are ordered in the order they are added (new items are appended at the end).
|
|
* You may reverse the order by setting the flag DICT_OPTION_ADD_IN_FRONT.
|
|
*
|
|
* LOOKUP
|
|
* The dictionary uses JudyHS to maintain a very fast randomly accessible hash table.
|
|
*
|
|
* MULTI-THREADED and SINGLE-THREADED
|
|
* Each dictionary may be single threaded (no locks), or multi-threaded (multiple readers or one writer).
|
|
* The default is multi-threaded. Add the flag DICT_OPTION_SINGLE_THREADED for single-threaded.
|
|
*
|
|
* WALK-THROUGH and FOREACH traversal
|
|
* The dictionary can be traversed on read or write mode, either with a callback (walkthrough) or with
|
|
* a loop (foreach).
|
|
*
|
|
* In write mode traversal, the caller may delete only the current item, but may add as many items as needed.
|
|
*
|
|
*/
|
|
|
|
#ifdef DICTIONARY_INTERNALS
|
|
#define DICTFE_CONST
|
|
#define DICT_ITEM_CONST
|
|
#else
|
|
#define DICTFE_CONST const
|
|
#define DICT_ITEM_CONST const
|
|
#endif
|
|
|
|
typedef struct dictionary DICTIONARY;
|
|
typedef struct dictionary_item DICTIONARY_ITEM;
|
|
|
|
typedef enum dictionary_options {
|
|
DICT_OPTION_NONE = 0, // the default is the opposite of all below
|
|
DICT_OPTION_SINGLE_THREADED = (1 << 0), // don't use any locks (default: use locks)
|
|
DICT_OPTION_VALUE_LINK_DONT_CLONE = (1 << 1), // don't copy the value, just point to the one provided (default: copy)
|
|
DICT_OPTION_NAME_LINK_DONT_CLONE = (1 << 2), // don't copy the name, just point to the one provided (default: copy)
|
|
DICT_OPTION_DONT_OVERWRITE_VALUE = (1 << 3), // don't overwrite values of dictionary items (default: overwrite)
|
|
DICT_OPTION_ADD_IN_FRONT = (1 << 4), // add dictionary items at the front of the linked list (default: at the end)
|
|
} DICT_OPTIONS;
|
|
|
|
struct dictionary_stats {
|
|
const char *name; // the name of the category
|
|
|
|
struct {
|
|
size_t active; // the number of active dictionaries
|
|
size_t deleted; // the number of dictionaries queued for destruction
|
|
} dictionaries;
|
|
|
|
struct {
|
|
long entries; // active items in the dictionary
|
|
long pending_deletion; // pending deletion items in the dictionary
|
|
long referenced; // referenced items in the dictionary
|
|
} items;
|
|
|
|
struct {
|
|
size_t creations; // dictionary creations
|
|
size_t destructions; // dictionary destructions
|
|
size_t flushes; // dictionary flushes
|
|
size_t traversals; // dictionary foreach
|
|
size_t walkthroughs; // dictionary walkthrough
|
|
size_t garbage_collections; // dictionary garbage collections
|
|
size_t searches; // item searches
|
|
size_t inserts; // item inserts
|
|
size_t resets; // item resets
|
|
size_t deletes; // item deletes
|
|
} ops;
|
|
|
|
struct {
|
|
size_t inserts; // number of times the insert callback is called
|
|
size_t conflicts; // number of times the insert conflict is called
|
|
size_t reacts; // number of times the insert react is called
|
|
size_t deletes; // number of times the insert delete is called
|
|
} callbacks;
|
|
|
|
// memory
|
|
struct {
|
|
long indexed; // bytes of keys indexed (indication of the index size)
|
|
long values; // bytes of caller structures
|
|
long dict; // bytes of the structures dictionary needs
|
|
} memory;
|
|
|
|
// spin locks
|
|
struct {
|
|
size_t use; // number of times a reference to item had to spin to acquire it or ignore it
|
|
size_t search; // number of times a successful search result had to be thrown away
|
|
size_t insert; // number of times an insertion to the hash table had to be repeated
|
|
} spin_locks;
|
|
};
|
|
|
|
// Create a dictionary
|
|
#ifdef NETDATA_INTERNAL_CHECKS
|
|
#define dictionary_create(options) dictionary_create_advanced_with_trace(options, NULL, __FUNCTION__, __LINE__, __FILE__)
|
|
#define dictionary_create_advanced(options, stats) dictionary_create_advanced_with_trace(options, stats, __FUNCTION__, __LINE__, __FILE__)
|
|
extern DICTIONARY *dictionary_create_advanced_with_trace(DICT_OPTIONS options, struct dictionary_stats *stats, const char *function, size_t line, const char *file);
|
|
#else
|
|
#define dictionary_create(options) dictionary_create_advanced(options, NULL);
|
|
extern DICTIONARY *dictionary_create_advanced(DICT_OPTIONS options, struct dictionary_stats *stats);
|
|
#endif
|
|
|
|
// Create a view on a dictionary
|
|
#ifdef NETDATA_INTERNAL_CHECKS
|
|
#define dictionary_create_view(master) dictionary_create_view_with_trace(master, __FUNCTION__, __LINE__, __FILE__)
|
|
extern DICTIONARY *dictionary_create_view_with_trace(DICTIONARY *master, const char *function, size_t line, const char *file);
|
|
#else
|
|
extern DICTIONARY *dictionary_create_view(DICTIONARY *master);
|
|
#endif
|
|
|
|
// an insert callback to be called just after an item is added to the dictionary
|
|
// this callback is called while the dictionary is write locked!
|
|
extern void dictionary_register_insert_callback(DICTIONARY *dict, void (*ins_callback)(const DICTIONARY_ITEM *item, void *value, void *data), void *data);
|
|
|
|
// a delete callback to be called just before an item is deleted forever
|
|
// this callback is called while the dictionary is write locked!
|
|
extern void dictionary_register_delete_callback(DICTIONARY *dict, void (*del_callback)(const DICTIONARY_ITEM *item, void *value, void *data), void *data);
|
|
|
|
// a merge callback to be called when DICT_OPTION_DONT_OVERWRITE_VALUE
|
|
// and an item is already found in the dictionary - the dictionary does nothing else in this case
|
|
// the old_value will remain in the dictionary - the new_value is ignored
|
|
// The callback should return true if the value has been updated (it increases the dictionary version).
|
|
extern void dictionary_register_conflict_callback(DICTIONARY *dict, bool (*conflict_callback)(const DICTIONARY_ITEM *item, void *old_value, void *new_value, void *data), void *data);
|
|
|
|
// a reaction callback to be called after every item insertion or conflict
|
|
// after the constructors have finished and the items are fully available for use
|
|
// and the dictionary is not write locked anymore
|
|
extern void dictionary_register_react_callback(DICTIONARY *dict, void (*react_callback)(const DICTIONARY_ITEM *item, void *value, void *data), void *data);
|
|
|
|
// Destroy a dictionary
|
|
// Returns the number of bytes freed
|
|
// The returned value will not include name/key sizes
|
|
// Registered delete callbacks will be run for each item in the dictionary.
|
|
extern size_t dictionary_destroy(DICTIONARY *dict);
|
|
|
|
// Empties a dictionary
|
|
// Referenced items will survive, but are not offered anymore.
|
|
// Registered delete callbacks will be run for each item in the dictionary.
|
|
extern void dictionary_flush(DICTIONARY *dict);
|
|
|
|
extern void dictionary_version_increment(DICTIONARY *dict);
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Set an item in the dictionary
|
|
//
|
|
// - if an item with the same name does not exist, create one
|
|
// - if an item with the same name exists, then:
|
|
// a) if DICT_OPTION_DONT_OVERWRITE_VALUE is set, just return the existing value (ignore the new value)
|
|
// else b) reset the value to the new value passed at the call
|
|
//
|
|
// When DICT_OPTION_VALUE_LINK_DONT_CLONE is set, the value is linked, otherwise it is copied
|
|
// When DICT_OPTION_NAME_LINK_DONT_CLONE is set, the name is linked, otherwise it is copied
|
|
//
|
|
// When neither DICT_OPTION_VALUE_LINK_DONT_CLONE nor DICT_OPTION_NAME_LINK_DONT_CLONE are set, all the
|
|
// memory management for names and values is done by the dictionary.
|
|
//
|
|
// Passing NULL as value, the dictionary will callocz() the newly allocated value, otherwise it will copy it.
|
|
// Passing 0 as value_len, the dictionary will set the value to NULL (no allocations for value will be made).
|
|
#define dictionary_set(dict, name, value, value_len) dictionary_set_advanced(dict, name, -1, value, value_len, NULL)
|
|
extern void *dictionary_set_advanced(DICTIONARY *dict, const char *name, ssize_t name_len, void *value, size_t value_len, void *constructor_data);
|
|
|
|
#define dictionary_set_and_acquire_item(dict, name, value, value_len) dictionary_set_and_acquire_item_advanced(dict, name, -1, value, value_len, NULL)
|
|
extern 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);
|
|
|
|
// set an item in a dictionary view
|
|
#define dictionary_view_set_and_acquire_item(dict, name, master_item) dictionary_view_set_and_acquire_item_advanced(dict, name, -1, master_item)
|
|
extern 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);
|
|
#define dictionary_view_set(dict, name, master_item) dictionary_view_set_advanced(dict, name, -1, master_item)
|
|
extern void *dictionary_view_set_advanced(DICTIONARY *dict, const char *name, ssize_t name_len, DICTIONARY_ITEM *master_item);
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Get an item from the dictionary
|
|
// If it returns NULL, the item is not found
|
|
|
|
#define dictionary_get(dict, name) dictionary_get_advanced(dict, name, -1)
|
|
extern void *dictionary_get_advanced(DICTIONARY *dict, const char *name, ssize_t name_len);
|
|
|
|
#define dictionary_get_and_acquire_item(dict, name) dictionary_get_and_acquire_item_advanced(dict, name, -1)
|
|
extern DICT_ITEM_CONST DICTIONARY_ITEM *dictionary_get_and_acquire_item_advanced(DICTIONARY *dict, const char *name, ssize_t name_len);
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Delete an item from the dictionary
|
|
// returns true if the item was found and has been deleted
|
|
// returns false if the item was not found in the index
|
|
|
|
#define dictionary_del(dict, name) dictionary_del_advanced(dict, name, -1)
|
|
extern bool dictionary_del_advanced(DICTIONARY *dict, const char *name, ssize_t name_len);
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// reference counters management
|
|
|
|
extern void dictionary_acquired_item_release(DICTIONARY *dict, DICT_ITEM_CONST DICTIONARY_ITEM *item);
|
|
|
|
extern DICT_ITEM_CONST DICTIONARY_ITEM *dictionary_acquired_item_dup(DICTIONARY *dict, DICT_ITEM_CONST DICTIONARY_ITEM *item);
|
|
|
|
extern const char *dictionary_acquired_item_name(DICT_ITEM_CONST DICTIONARY_ITEM *item);
|
|
extern void *dictionary_acquired_item_value(DICT_ITEM_CONST DICTIONARY_ITEM *item);
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Traverse (walk through) the items of the dictionary.
|
|
// The order of traversal is currently the order of insertion.
|
|
//
|
|
// The callback function may return a negative number to stop the traversal,
|
|
// in which case that negative value is returned to the caller.
|
|
//
|
|
// If all callback calls return zero or positive numbers, the sum of all of
|
|
// them is returned to the caller.
|
|
//
|
|
// You cannot alter the dictionary from inside a dictionary_walkthrough_read() - deadlock!
|
|
// You can only delete the current item from inside a dictionary_walkthrough_write() - you can add as many as you want.
|
|
//
|
|
#define dictionary_walkthrough_read(dict, callback, data) dictionary_walkthrough_rw(dict, 'r', callback, data)
|
|
#define dictionary_walkthrough_write(dict, callback, data) dictionary_walkthrough_rw(dict, 'w', callback, data)
|
|
extern int dictionary_walkthrough_rw(DICTIONARY *dict, char rw, int (*callback)(const DICTIONARY_ITEM *item, void *value, void *data), void *data);
|
|
|
|
#define dictionary_sorted_walkthrough_read(dict, callback, data) dictionary_sorted_walkthrough_rw(dict, 'r', callback, data)
|
|
#define dictionary_sorted_walkthrough_write(dict, callback, data) dictionary_sorted_walkthrough_rw(dict, 'w', callback, data)
|
|
int dictionary_sorted_walkthrough_rw(DICTIONARY *dict, char rw, int (*callback)(const DICTIONARY_ITEM *item, void *entry, void *data), void *data);
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Traverse with foreach
|
|
//
|
|
// Use like this:
|
|
//
|
|
// DICTFE dfe = {};
|
|
// for(MY_ITEM *item = dfe_start_read(&dfe, dict); item ; item = dfe_next(&dfe)) {
|
|
// // do things with the item and its dfe.name
|
|
// }
|
|
// dfe_done(&dfe);
|
|
//
|
|
// You cannot alter the dictionary from within a dfe_read_start() - deadlock!
|
|
// You can only delete the current item from inside a dfe_start_write() - you can add as many as you want.
|
|
//
|
|
|
|
#define DICTIONARY_LOCK_READ 'r'
|
|
#define DICTIONARY_LOCK_WRITE 'w'
|
|
#define DICTIONARY_LOCK_REENTRANT 'z'
|
|
|
|
typedef DICTFE_CONST struct dictionary_foreach {
|
|
DICTIONARY *dict; // the dictionary upon we work
|
|
|
|
DICTIONARY_ITEM *item; // the item we work on, to remember the position we are at
|
|
// this can be used with dictionary_acquired_item_dup() to
|
|
// acquire the currently working item.
|
|
|
|
DICTFE_CONST char *name; // the dictionary name of the last item used
|
|
void *value; // the dictionary value of the last item used
|
|
// same as the return value of dictfe_start() and dictfe_next()
|
|
|
|
size_t counter; // counts the number of iterations made, starting from zero
|
|
|
|
char rw; // the lock mode 'r' or 'w'
|
|
} DICTFE;
|
|
|
|
#define dfe_start_read(dict, value) dfe_start_rw(dict, value, DICTIONARY_LOCK_READ)
|
|
#define dfe_start_write(dict, value) dfe_start_rw(dict, value, DICTIONARY_LOCK_WRITE)
|
|
#define dfe_start_reentrant(dict, value) dfe_start_rw(dict, value, DICTIONARY_LOCK_REENTRANT)
|
|
|
|
#define dfe_start_rw(dict, value, mode) \
|
|
do { \
|
|
DICTFE value ## _dfe = {}; \
|
|
(void)(value); /* needed to avoid warning when looping without using this */ \
|
|
for((value) = dictionary_foreach_start_rw(&value ## _dfe, (dict), (mode)); \
|
|
(value ## _dfe.item) ; \
|
|
(value) = dictionary_foreach_next(&value ## _dfe)) \
|
|
{
|
|
|
|
#define dfe_done(value) \
|
|
} \
|
|
dictionary_foreach_done(&value ## _dfe); \
|
|
} while(0)
|
|
|
|
extern void *dictionary_foreach_start_rw(DICTFE *dfe, DICTIONARY *dict, char rw);
|
|
extern void *dictionary_foreach_next(DICTFE *dfe);
|
|
extern void dictionary_foreach_done(DICTFE *dfe);
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Get statistics about the dictionary
|
|
|
|
extern size_t dictionary_version(DICTIONARY *dict);
|
|
extern size_t dictionary_entries(DICTIONARY *dict);
|
|
extern size_t dictionary_referenced_items(DICTIONARY *dict);
|
|
extern long int dictionary_stats_for_registry(DICTIONARY *dict);
|
|
|
|
// for all cases that the caller does not provide a stats structure, this is where they are accumulated.
|
|
extern struct dictionary_stats dictionary_stats_category_other;
|
|
|
|
extern int dictionary_unittest(size_t entries);
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// THREAD CACHE
|
|
|
|
extern void *thread_cache_entry_get_or_set(void *key,
|
|
ssize_t key_length,
|
|
void *value,
|
|
void *(*transform_the_value_before_insert)(void *key, size_t key_length, void *value));
|
|
|
|
extern void thread_cache_destroy(void);
|
|
|
|
#endif /* NETDATA_DICTIONARY_H */
|