libosmocore 1.10.0.64-ff3d
Osmocom core library
|
Use counter state for one used object, managing N distinct named counters. More...
#include <use_count.h>
Data Fields | |
void * | talloc_object |
Context to talloc-allocate use count entries from (if at all necessary); back-pointer to the owning object for osmo_use_count_cb_t implementations. More... | |
osmo_use_count_cb_t | use_cb |
If not NULL, this is invoked for each use count change. More... | |
struct llist_head | use_counts |
List of use tokens. More... | |
Use counter state for one used object, managing N distinct named counters.
Manage any number of uses of an object, with name tokens given to each use.
A typical use tracking done by a single instance of this struct may look like: "VLR subscr MSISDN-23 + SMS-receiver: now used by 6 (attached,2*SMS-receiver,SMS-pending,SMS,Paging)" (This is a DREF log statement from an osmo-msc run delivering an SMS.)
Use tokens are given as const char* strings. Typically string literals like "foo", func, or also NULL. Tokens may be dynamically allocated or static char[] buffers as long as they are guaranteed to remain unchanged while referenced by an osmo_use_count_entry. (Breakage occurs if one token magically changes to equal another listed token.)
Instead of using string literals in the code directly, callers should use a #define, so that typos are caught at compile time rather than introducing obscure failures that are hard to spot for humans – don't use foo_get("bar") and foo_put("bar"), but '#define FOO_USE_BAR "bar"' for foo_get(FOO_USE_BAR) and foo_put(FOO_USE_BAR).
Counts are int32_t values, a separate count per use token string. Counts can be negative, though in the typical use case are only positive or 0. Enforcing a range is entirely up to the osmo_use_count_cb_t() implementation.
The talloc_object must be a pointer eligible to be a talloc context, i.e. either obtained from a function like talloc_zero() or NULL. talloc_object is typically a pointer to the object that this struct is a member of. Use count entries may be allocated as talloc children of this (see also "Avoiding dynamic allocation" below).
The use_cb() implementation allows to trigger actions when reaching specific use counts, e.g. deallocate when reaching a total sum across all use tokens of zero.
On initialization, this struct can be left fully zero initialized (the llist_head use_counts is implicitly initialized upon the first osmo_use_count_get_put()). Usually, set only a talloc_object and a use_cb, though neither is strictly required.
Avoiding dynamic allocation: dynamic allocation can be avoided completely by providing sufficient static use count entries with osmo_use_count_make_static_entries(). Otherwise, each new use token will dynamically allocate a new osmo_use_count_entry; note that once allocated, these entries stay around even if they reached an entry count of zero, and will be re-used for subsequent use count tokens. So even if not using osmo_use_count_make_static_entries(), each osmo_use_count will keep dynamic allocations at a minimum. See also the documentation for osmo_use_count_cb_t.
List traversal considerations: your typical use count list would max at about six entries in practice. Traversing six llist->next pointers is less effort than doing a common strlen().
Obtaining the total use count: osmo_use_count_total() traverses all use token entries and forms a sum. It is trivial to keep a separate total count that completely avoids the need for calling this function, which is entirely up to the individual osmo_use_count_cb_t() implementation. The optimization gained is usually not worth it, though.
Use token comparison considerations: strcmp() to compare use tokens is a fairly good tradeoff:
Example:
struct foo { struct osmo_use_count use_count; }; // Convenience macros for struct foo instances. These are strict about use count errors. #define foo_get(FOO, USE) OSMO_ASSERT( osmo_use_count_get_put(&(FOO)->use_count, USE, 1) == 0 ); #define foo_put(FOO, USE) OSMO_ASSERT( osmo_use_count_get_put(&(FOO)->use_count, USE, -1) == 0 ); int foo_use_cb(struct osmo_use_count_entry *use_count_entry, int32_t old_use_count, const char *file, int line) { struct foo *foo = use_count_entry->use_count->talloc_object; if (osmo_use_count_total(use_count_entry->use_count) == 0) talloc_free(foo); return 0; } // The function name is a convenient use token: void rx_stop_baz_request(struct foo *foo) { foo_get(foo, __func__); foo_put(foo, "baz"); printf("Stopped Bazing (%p)\n", foo); foo_put(foo, __func__); } void use_count_example() { struct foo *foo = talloc_zero(ctx, struct foo); *foo = (struct foo){ .use_count = { .talloc_object = foo, .use_cb = foo_use_cb, }, }; foo_get(foo, "bar"); // one osmo_use_count_entry was allocated foo_get(foo, "baz"); // a second osmo_use_count_entry was allocated foo_get(foo, "baz"); // still two entries printf("use: %s\n", osmo_use_count_name_buf(namebuf, sizeof(namebuf), &foo->use_count)); // "use: 3 (bar,2*baz)" foo_put(foo, "bar"); // still two entries, one entry is idle ("bar"=0) foo_put(foo, "baz"); rx_stop_baz_request(foo); // Final "baz" was put(), foo_use_cb() deallocated object foo, as well as all use count entries. };
void* osmo_use_count::talloc_object |
Context to talloc-allocate use count entries from (if at all necessary); back-pointer to the owning object for osmo_use_count_cb_t implementations.
Referenced by osmo_use_count_create().
osmo_use_count_cb_t osmo_use_count::use_cb |
If not NULL, this is invoked for each use count change.
Referenced by _osmo_use_count_get_put().
struct llist_head osmo_use_count::use_counts |
List of use tokens.
No need to touch this, the llist is initialized implicitly.
Referenced by _osmo_use_count_get_put(), osmo_use_count_create(), osmo_use_count_find(), osmo_use_count_make_static_entries(), osmo_use_count_repurpose_zero_entry(), osmo_use_count_to_str_buf(), and osmo_use_count_total().