libosmocore 1.13.1.1-afb4 Osmocom core library

Data Fields
osmo_use_count Struct Reference
Use Counter

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

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

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

- include/osmocom/core/use_count.h