libosmocore 1.9.0.196-9975
Osmocom core library
osmo_use_count Struct Reference

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...
 

Detailed Description

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:

  • when the strings differ, strcmp() usually exits on the first or second character.
  • when the strings are identical, they are usually the exact same char* address (from compile-time string constant), meaning that strcmp() is completely skipped. (quote: "if (e->use == use || (use && e->use && !strcmp(e->use, use)))")
  • if we specified compile-time string constant use as requirement, we wouldn't need strcmp() at all, but this minuscule overhead has the benefit of complete correctness for any kinds of use token strings.

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.
};

Field Documentation

◆ talloc_object

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().

◆ use_cb

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().

◆ use_counts

struct llist_head osmo_use_count::use_counts

The documentation for this struct was generated from the following file: