<p>Neels Hofmeyr <strong>merged</strong> this change.</p><p><a href="https://gerrit.osmocom.org/13121">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Harald Welte: Looks good to me, approved
  Jenkins Builder: Verified

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">add osmo_use_count API<br><br>Provide a common implementation of use counting that supports naming each user<br>as well as counting more than just one use per user, depending on the rules the<br>caller implies.<br><br>In osmo-msc, we were originally using a simple int counter to see whether a<br>connection is still in use or should be discarded. For clarity, we later added<br>names to each user in the form of a bitmask of flags, to figure out exactly<br>which users are still active: for logging and to debug double get / double put<br>bugs. This however is still not adequate, since there may be more than one CM<br>Service Request pending. Also, it is a specialized implementation that is not<br>re-usable.<br><br>With this generalized implementation, we can:<br><br>- fix the problem of inadequate counting of multiple concurrent CM Service<br>  Requests (more than one use count per user category),<br>- directly use arbitrary names for uses like __func__ or "foo" (no need to<br>  define enums and value_string[]s),<br>- re-use the same code for e.g. vlr_subscr and get fairly detailed VLR<br>  susbscriber usage logging for free.<br><br>Change-Id: Ife31e6798b4e728a23913179e346552a7dd338c0<br>---<br>M include/Makefile.am<br>A include/osmocom/core/use_count.h<br>M src/Makefile.am<br>A src/use_count.c<br>M tests/Makefile.am<br>M tests/testsuite.at<br>A tests/use_count/use_count_test.c<br>A tests/use_count/use_count_test.err<br>A tests/use_count/use_count_test.ok<br>9 files changed, 1,031 insertions(+), 0 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/include/Makefile.am b/include/Makefile.am</span><br><span>index 6ed7fe6..7b9e347 100644</span><br><span>--- a/include/Makefile.am</span><br><span>+++ b/include/Makefile.am</span><br><span>@@ -53,6 +53,7 @@</span><br><span>                        osmocom/core/utils.h \</span><br><span>                        osmocom/core/write_queue.h \</span><br><span>                        osmocom/core/sockaddr_str.h \</span><br><span style="color: hsl(120, 100%, 40%);">+                       osmocom/core/use_count.h \</span><br><span>                        osmocom/crypt/auth.h \</span><br><span>                        osmocom/crypt/gprs_cipher.h \</span><br><span>                 osmocom/ctrl/control_cmd.h \</span><br><span>diff --git a/include/osmocom/core/use_count.h b/include/osmocom/core/use_count.h</span><br><span>new file mode 100644</span><br><span>index 0000000..6a4bf1f</span><br><span>--- /dev/null</span><br><span>+++ b/include/osmocom/core/use_count.h</span><br><span>@@ -0,0 +1,228 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \file use_count.h</span><br><span style="color: hsl(120, 100%, 40%);">+ * Generic object usage counter API (get, put and deallocate on zero count).</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * (C) 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Author: Neels Hofmeyr <neels@hofmeyr.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * SPDX-License-Identifier: GPL-2.0+</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 2 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU General Public License along</span><br><span style="color: hsl(120, 100%, 40%);">+ * with this program; if not, write to the Free Software Foundation, Inc.,</span><br><span style="color: hsl(120, 100%, 40%);">+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#pragma once</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdint.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdlib.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/linuxlist.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \defgroup use_count  Use Counter</span><br><span style="color: hsl(120, 100%, 40%);">+ * @{</span><br><span style="color: hsl(120, 100%, 40%);">+ * \file use_count.h</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_use_count_entry;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Invoked when a use count changes.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * The implementation is free to trigger actions on arbitrary use count changes, typically to free the</span><br><span style="color: hsl(120, 100%, 40%);">+ * use_count->talloc_object when the total use count reaches zero.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * The implementation may modify use_count_entry->count, for example for handling of get()/put() bugs, to clamp specific use</span><br><span style="color: hsl(120, 100%, 40%);">+ * tokens to specific counts, or to prevent the caller from put()ting into negative counts. When returning an error,</span><br><span style="color: hsl(120, 100%, 40%);">+ * there is no implicit undo -- if errors need to be corrected, this function is responsible for that.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Be aware: use token strings are not copied, and use count entries usually remain listed also when they reach a zero</span><br><span style="color: hsl(120, 100%, 40%);">+ * count. This is trivially perfectly ok when using string literals as use tokens. It is also possible to use</span><br><span style="color: hsl(120, 100%, 40%);">+ * dynamically allocated string tokens, but should a use token string become invalid memory when reaching zero count, it</span><br><span style="color: hsl(120, 100%, 40%);">+ * is the responsibility of this function to set the use_count_entry->use = NULL; this is required to avoid subsequent</span><br><span style="color: hsl(120, 100%, 40%);">+ * osmo_use_count_get_put() invocations from calling strcmp() on invalid memory. (Setting use = NULL cannot be done</span><br><span style="color: hsl(120, 100%, 40%);">+ * implicitly after this callback invocation, because callback implementations are allowed to completely deallocate the</span><br><span style="color: hsl(120, 100%, 40%);">+ * talloc_object and the use_count list entries, and setting use = NULL after that would be a use-after-free.)</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] use_count_entry  Use count entry that is being modified.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] old_use_count  Use count the item had before the change in use count.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] file  Source file string, passed in as __FILE__ from macro osmo_use_count_get_put().</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] line  Source file line, passed in as __LINE__ from macro osmo_use_count_get_put().</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return 0 on success, negative if any undesired use count is reached; this rc will be returned by</span><br><span style="color: hsl(120, 100%, 40%);">+ *         osmo_use_count_get_put().</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+typedef int (* osmo_use_count_cb_t )(struct osmo_use_count_entry *use_count_entry, int32_t old_use_count,</span><br><span style="color: hsl(120, 100%, 40%);">+                                   const char *file, int line);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Use counter state for one used object, managing N distinct named counters.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Manage any number of uses of an object, with name tokens given to each use.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * A typical use tracking done by a single instance of this struct may look like:</span><br><span style="color: hsl(120, 100%, 40%);">+ * "VLR subscr MSISDN-23 + SMS-receiver: now used by 6 (attached,2*SMS-receiver,SMS-pending,SMS,Paging)"</span><br><span style="color: hsl(120, 100%, 40%);">+ * (This is a DREF log statement from an osmo-msc run delivering an SMS.)</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Use tokens are given as const char* strings. Typically string literals like "foo", __func__, or also NULL. Tokens may</span><br><span style="color: hsl(120, 100%, 40%);">+ * be dynamically allocated or static char[] buffers as long as they are guaranteed to remain unchanged while referenced</span><br><span style="color: hsl(120, 100%, 40%);">+ * by an osmo_use_count_entry. (Breakage occurs if one token magically changes to equal another listed token.)</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Instead of using string literals in the code directly, callers should use a #define, so that typos are caught at</span><br><span style="color: hsl(120, 100%, 40%);">+ * compile time rather than introducing obscure failures that are hard to spot for humans -- don't use foo_get("bar")</span><br><span style="color: hsl(120, 100%, 40%);">+ * and foo_put("bar"), but '#define FOO_USE_BAR "bar"' for foo_get(FOO_USE_BAR) and foo_put(FOO_USE_BAR).</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Counts are int32_t values, a separate count per use token string. Counts can be negative, though in the typical use</span><br><span style="color: hsl(120, 100%, 40%);">+ * case are only positive or 0. Enforcing a range is entirely up to the osmo_use_count_cb_t() implementation.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * The talloc_object must be a pointer eligible to be a talloc context, i.e. either obtained from a function like</span><br><span style="color: hsl(120, 100%, 40%);">+ * talloc_zero() or NULL. talloc_object is typically a pointer to the object that this struct is a member of.  Use count</span><br><span style="color: hsl(120, 100%, 40%);">+ * entries may be allocated as talloc children of this (see also "Avoiding dynamic allocation" below).</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * The use_cb() implementation allows to trigger actions when reaching specific use counts, e.g. deallocate when</span><br><span style="color: hsl(120, 100%, 40%);">+ * reaching a total sum across all use tokens of zero.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * On initialization, this struct can be left fully zero initialized (the llist_head use_counts is implicitly</span><br><span style="color: hsl(120, 100%, 40%);">+ * initialized upon the first osmo_use_count_get_put()). Usually, set only a talloc_object and a use_cb, though neither</span><br><span style="color: hsl(120, 100%, 40%);">+ * is strictly required.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Avoiding dynamic allocation: dynamic allocation can be avoided completely by providing sufficient static use count</span><br><span style="color: hsl(120, 100%, 40%);">+ * entries with osmo_use_count_make_static_entries(). Otherwise, each new use token will dynamically allocate a new</span><br><span style="color: hsl(120, 100%, 40%);">+ * osmo_use_count_entry; note that once allocated, these entries stay around even if they reached an entry count of</span><br><span style="color: hsl(120, 100%, 40%);">+ * zero, and will be re-used for subsequent use count tokens. So even if not using osmo_use_count_make_static_entries(),</span><br><span style="color: hsl(120, 100%, 40%);">+ * each osmo_use_count will keep dynamic allocations at a minimum. See also the documentation for osmo_use_count_cb_t.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * List traversal considerations: your typical use count list would max at about six entries in practice. Traversing six</span><br><span style="color: hsl(120, 100%, 40%);">+ * llist->next pointers is less effort than doing a common strlen().</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Obtaining the total use count: osmo_use_count_total() traverses all use token entries and forms a sum. It is trivial</span><br><span style="color: hsl(120, 100%, 40%);">+ * to keep a separate total count that completely avoids the need for calling this function, which is entirely up to the</span><br><span style="color: hsl(120, 100%, 40%);">+ * individual osmo_use_count_cb_t() implementation. The optimization gained is usually not worth it, though.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Use token comparison considerations: strcmp() to compare use tokens is a fairly good tradeoff:</span><br><span style="color: hsl(120, 100%, 40%);">+ * - when the strings differ, strcmp() usually exits on the first or second character.</span><br><span style="color: hsl(120, 100%, 40%);">+ * - when the strings are identical, they are usually the exact same char* address (from compile-time string constant),</span><br><span style="color: hsl(120, 100%, 40%);">+ *   meaning that strcmp() is completely skipped.</span><br><span style="color: hsl(120, 100%, 40%);">+ *   (quote: "if (e->use == use || (use && e->use && !strcmp(e->use, use)))")</span><br><span style="color: hsl(120, 100%, 40%);">+ * - if we specified compile-time string constant use as requirement, we wouldn't need strcmp() at all, but this</span><br><span style="color: hsl(120, 100%, 40%);">+ *   minuscule overhead has the benefit of complete correctness for any kinds of use token strings.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Example:</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *     struct foo {</span><br><span style="color: hsl(120, 100%, 40%);">+ *             struct osmo_use_count use_count;</span><br><span style="color: hsl(120, 100%, 40%);">+ *     };</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *     // Convenience macros for struct foo instances. These are strict about use count errors.</span><br><span style="color: hsl(120, 100%, 40%);">+ *     #define foo_get(FOO, USE) OSMO_ASSERT( osmo_use_count_get_put(&(FOO)->use_count, USE, 1) == 0 );</span><br><span style="color: hsl(120, 100%, 40%);">+ *     #define foo_put(FOO, USE) OSMO_ASSERT( osmo_use_count_get_put(&(FOO)->use_count, USE, -1) == 0 );</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *     int foo_use_cb(struct osmo_use_count_entry *use_count_entry, int32_t old_use_count, const char *file, int line)</span><br><span style="color: hsl(120, 100%, 40%);">+ *     {</span><br><span style="color: hsl(120, 100%, 40%);">+ *             struct foo *foo = use_count_entry->use_count->talloc_object;</span><br><span style="color: hsl(120, 100%, 40%);">+ *             if (osmo_use_count_total(&use_count_entry->use_count) == 0)</span><br><span style="color: hsl(120, 100%, 40%);">+ *                     talloc_free(foo);</span><br><span style="color: hsl(120, 100%, 40%);">+ *     }</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *     // The function name is a convenient use token:</span><br><span style="color: hsl(120, 100%, 40%);">+ *     void rx_stop_baz_request(struct foo *foo)</span><br><span style="color: hsl(120, 100%, 40%);">+ *     {</span><br><span style="color: hsl(120, 100%, 40%);">+ *             foo_get(foo, __func__);</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *             foo_put(foo, "baz");</span><br><span style="color: hsl(120, 100%, 40%);">+ *             printf("Stopped Bazing (%p)\n", foo);</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *             foo_put(foo, __func__);</span><br><span style="color: hsl(120, 100%, 40%);">+ *     }</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *     void use_count_example()</span><br><span style="color: hsl(120, 100%, 40%);">+ *     {</span><br><span style="color: hsl(120, 100%, 40%);">+ *             struct foo *foo = talloc_zero(ctx, struct foo);</span><br><span style="color: hsl(120, 100%, 40%);">+ *             *foo = (struct foo){</span><br><span style="color: hsl(120, 100%, 40%);">+ *                     .use_count = {</span><br><span style="color: hsl(120, 100%, 40%);">+ *                             .talloc_object = foo,</span><br><span style="color: hsl(120, 100%, 40%);">+ *                             .use_cb = foo_use_cb,</span><br><span style="color: hsl(120, 100%, 40%);">+ *                     },</span><br><span style="color: hsl(120, 100%, 40%);">+ *             };</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *             foo_get(foo, "bar");       // one osmo_use_count_entry was allocated</span><br><span style="color: hsl(120, 100%, 40%);">+ *             foo_get(foo, "baz");       // a second osmo_use_count_entry was allocated</span><br><span style="color: hsl(120, 100%, 40%);">+ *             foo_get(foo, "baz");       // still two entries</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *             printf("use: %s\n", osmo_use_count_name_buf(namebuf, sizeof(namebuf), &foo->use_count));</span><br><span style="color: hsl(120, 100%, 40%);">+ *             // "use: 3 (bar,2*baz)"</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *             foo_put(foo, "bar");       // still two entries, one entry is idle ("bar"=0)</span><br><span style="color: hsl(120, 100%, 40%);">+ *             foo_put(foo, "baz");</span><br><span style="color: hsl(120, 100%, 40%);">+ *             rx_stop_baz_request(foo);</span><br><span style="color: hsl(120, 100%, 40%);">+ *             // Final "baz" was put(), foo_use_cb() deallocated object foo, as well as all use count entries.</span><br><span style="color: hsl(120, 100%, 40%);">+ *     };</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_use_count {</span><br><span style="color: hsl(120, 100%, 40%);">+        /*! Context to talloc-allocate use count entries from (if at all necessary); back-pointer to the owning object</span><br><span style="color: hsl(120, 100%, 40%);">+         * for osmo_use_count_cb_t implementations. */</span><br><span style="color: hsl(120, 100%, 40%);">+        void *talloc_object;</span><br><span style="color: hsl(120, 100%, 40%);">+  /*! If not NULL, this is invoked for each use count change. */</span><br><span style="color: hsl(120, 100%, 40%);">+        osmo_use_count_cb_t use_cb;</span><br><span style="color: hsl(120, 100%, 40%);">+   /*! List of use tokens. No need to touch this, the llist is initialized implicitly. */</span><br><span style="color: hsl(120, 100%, 40%);">+        struct llist_head use_counts;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! One named counter in the list managed by osmo_use_count.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Gets created as necessary by osmo_use_count_get_put(). The total current use count of an object is the sum of all</span><br><span style="color: hsl(120, 100%, 40%);">+ * individual osmo_use_count_entry->count.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *      object            <--backpointer-+</span><br><span style="color: hsl(120, 100%, 40%);">+ *     t|  .osmo_use_count               |</span><br><span style="color: hsl(120, 100%, 40%);">+ *     a|     .talloc_object ------------+</span><br><span style="color: hsl(120, 100%, 40%);">+ *     l|     .use_counts llist:          use   count</span><br><span style="color: hsl(120, 100%, 40%);">+ *     l|->      - osmo_use_count_entry: "foo"  1</span><br><span style="color: hsl(120, 100%, 40%);">+ *     o|->      - osmo_use_count_entry: "bar"  3</span><br><span style="color: hsl(120, 100%, 40%);">+ *     c|->      - osmo_use_count_entry: "baz"  0  (currently unused entry)</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_use_count_entry {</span><br><span style="color: hsl(120, 100%, 40%);">+        /*! Entry in osmo_use_count->use_counts. */</span><br><span style="color: hsl(120, 100%, 40%);">+        struct llist_head entry;</span><br><span style="color: hsl(120, 100%, 40%);">+      /*! Parent use count and backpointer to the talloc_object. */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct osmo_use_count *use_count;</span><br><span style="color: hsl(120, 100%, 40%);">+     /*! Use token string that was passed to osmo_use_count_get_put(). */</span><br><span style="color: hsl(120, 100%, 40%);">+  const char *use;</span><br><span style="color: hsl(120, 100%, 40%);">+      /*! Current use count amount for only this use token string.</span><br><span style="color: hsl(120, 100%, 40%);">+   * If zero, this entry is currently unused and kept around to avoid frequent de-/allocation. */</span><br><span style="color: hsl(120, 100%, 40%);">+       int32_t count;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Change the use count for a given use token.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param USE_LIST  A struct osmo_use_count*, e.g. &my_obj->use_count.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param USE  A use token: arbitrary string (const char*). This must remain valid memory, e.g. string constants.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param CHANGE  Signed integer value to add to the use count: positive means get(), negative means put().</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return Negative on range violations or USE_LIST == NULL, the use_cb()'s return value, or 0 on success.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define osmo_use_count_get_put(USE_LIST, USE, CHANGE) \</span><br><span style="color: hsl(120, 100%, 40%);">+    _osmo_use_count_get_put(USE_LIST, USE, CHANGE, __FILE__, __LINE__)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int _osmo_use_count_get_put(struct osmo_use_count *uc, const char *use, int32_t change,</span><br><span style="color: hsl(120, 100%, 40%);">+                     const char *file, int line);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const char *osmo_use_count_name_buf(char *buf, size_t buf_len, const struct osmo_use_count *uc);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int32_t osmo_use_count_total(const struct osmo_use_count *uc);</span><br><span style="color: hsl(120, 100%, 40%);">+int32_t osmo_use_count_by(const struct osmo_use_count *uc, const char *use);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_use_count_entry *osmo_use_count_find(const struct osmo_use_count *uc, const char *use);</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_use_count_free(struct osmo_use_count_entry *use_count_entry);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_use_count_make_static_entries(struct osmo_use_count *uc, struct osmo_use_count_entry *buf,</span><br><span style="color: hsl(120, 100%, 40%);">+                                  size_t buf_n_entries);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! @} */</span><br><span>diff --git a/src/Makefile.am b/src/Makefile.am</span><br><span>index 1fae8b0..aaf2233 100644</span><br><span>--- a/src/Makefile.am</span><br><span>+++ b/src/Makefile.am</span><br><span>@@ -26,6 +26,7 @@</span><br><span>                      isdnhdlc.c \</span><br><span>                         tdef.c \</span><br><span>                     sockaddr_str.c \</span><br><span style="color: hsl(120, 100%, 40%);">+                      use_count.c \</span><br><span>                        $(NULL)</span><br><span> </span><br><span> if HAVE_SSSE3</span><br><span>diff --git a/src/use_count.c b/src/use_count.c</span><br><span>new file mode 100644</span><br><span>index 0000000..453d2ae</span><br><span>--- /dev/null</span><br><span>+++ b/src/use_count.c</span><br><span>@@ -0,0 +1,279 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \file use_count.c</span><br><span style="color: hsl(120, 100%, 40%);">+ * Generic object usage counter Implementation (get, put and deallocate on zero count).</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * (C) 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Author: Neels Hofmeyr <neels@hofmeyr.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * SPDX-License-Identifier: GPL-2.0+</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 2 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU General Public License along</span><br><span style="color: hsl(120, 100%, 40%);">+ * with this program; if not, write to the Free Software Foundation, Inc.,</span><br><span style="color: hsl(120, 100%, 40%);">+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <errno.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <inttypes.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <string.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/linuxlist.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/utils.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/use_count.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \addtogroup use_count</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Generic object usage counter (get, put and deallocate on zero count).</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * For an example and a detailed description, see struct osmo_use_count.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * @{</span><br><span style="color: hsl(120, 100%, 40%);">+ * \file use_count.c</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Add two int32_t but make sure to min- and max-clamp at INT32_MIN and INT32_MAX, respectively. */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline bool count_safe(int32_t *val_p, int32_t add)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  int32_t val = *val_p;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       /* A simpler implementation would just let the integer overflow and compare with previous value afterwards, but</span><br><span style="color: hsl(120, 100%, 40%);">+        * that causes runtime errors in the address sanitizer. So let's just do this without tricks. */</span><br><span style="color: hsl(120, 100%, 40%);">+  if (add < 0 && val < 0 && val - INT32_MIN < -add) {</span><br><span style="color: hsl(120, 100%, 40%);">+          *val_p = INT32_MIN;</span><br><span style="color: hsl(120, 100%, 40%);">+           return false;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (add > 0 && val > 0 && INT32_MAX - val < add) {</span><br><span style="color: hsl(120, 100%, 40%);">+           *val_p = INT32_MAX;</span><br><span style="color: hsl(120, 100%, 40%);">+           return false;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   *val_p = val + add;</span><br><span style="color: hsl(120, 100%, 40%);">+   return true;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Return the sum of all use counts, min- and max-clamped at INT32_MIN and INT32_MAX.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] uc  Use counts to sum up.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return Accumulated counts, or 0 if uc is NULL.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int32_t osmo_use_count_total(const struct osmo_use_count *uc)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct osmo_use_count_entry *e;</span><br><span style="color: hsl(120, 100%, 40%);">+       int32_t total = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!uc || !uc->use_counts.next)</span><br><span style="color: hsl(120, 100%, 40%);">+           return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   llist_for_each_entry(e, &uc->use_counts, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+              count_safe(&total, e->count);</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+     return total;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Return use count by a single use token.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] uc  Use counts to look up in.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] use  Use token.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return Use count, or 0 if uc is NULL or use token is not present.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int32_t osmo_use_count_by(const struct osmo_use_count *uc, const char *use)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       const struct osmo_use_count_entry *e;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!uc)</span><br><span style="color: hsl(120, 100%, 40%);">+              return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     e = osmo_use_count_find(uc, use);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!e)</span><br><span style="color: hsl(120, 100%, 40%);">+               return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     return e->count;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Write a comprehensive listing of use counts to a string buffer.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Reads like "12 (3*barring,fighting,8*kungfoo)".</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[inout] buf  Destination buffer.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] buf_len  sizeof(buf).</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] uc  Use counts to print.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return buf, always nul-terminated (except when buf_len < 1).</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+const char *osmo_use_count_name_buf(char *buf, size_t buf_len, const struct osmo_use_count *uc)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     int32_t count = osmo_use_count_total(uc);</span><br><span style="color: hsl(120, 100%, 40%);">+     struct osmo_strbuf sb = { .buf = buf, .len = buf_len };</span><br><span style="color: hsl(120, 100%, 40%);">+       struct osmo_use_count_entry *e;</span><br><span style="color: hsl(120, 100%, 40%);">+       bool first;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_STRBUF_PRINTF(sb, "%" PRId32 " (", count);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ first = true;</span><br><span style="color: hsl(120, 100%, 40%);">+ llist_for_each_entry(e, &uc->use_counts, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+              if (!e->count)</span><br><span style="color: hsl(120, 100%, 40%);">+                     continue;</span><br><span style="color: hsl(120, 100%, 40%);">+             if (!first)</span><br><span style="color: hsl(120, 100%, 40%);">+                   OSMO_STRBUF_PRINTF(sb, ",");</span><br><span style="color: hsl(120, 100%, 40%);">+                first = false;</span><br><span style="color: hsl(120, 100%, 40%);">+                if (e->count != 1)</span><br><span style="color: hsl(120, 100%, 40%);">+                 OSMO_STRBUF_PRINTF(sb, "%" PRId32 "*", e->count);</span><br><span style="color: hsl(120, 100%, 40%);">+              OSMO_STRBUF_PRINTF(sb, "%s", e->use ? : "NULL");</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (first)</span><br><span style="color: hsl(120, 100%, 40%);">+            OSMO_STRBUF_PRINTF(sb, "-");</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_STRBUF_PRINTF(sb, ")");</span><br><span style="color: hsl(120, 100%, 40%);">+        return buf;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Return a use token's use count entry -- probably you want osmo_use_count_by() instead.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] uc  Use counts to look up in.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] use  Use token.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return matching entry, or NULL if not present.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_use_count_entry *osmo_use_count_find(const struct osmo_use_count *uc, const char *use)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct osmo_use_count_entry *e;</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!uc->use_counts.next)</span><br><span style="color: hsl(120, 100%, 40%);">+          return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  llist_for_each_entry(e, &uc->use_counts, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+              if (e->use == use || (use && e->use && !strcmp(e->use, use)))</span><br><span style="color: hsl(120, 100%, 40%);">+                        return e;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Find a use count entry that currently has zero count, and re-use that for this new use token. */</span><br><span style="color: hsl(120, 100%, 40%);">+static struct osmo_use_count_entry *osmo_use_count_repurpose_zero_entry(struct osmo_use_count *uc, const char *use)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct osmo_use_count_entry *e;</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!uc->use_counts.next)</span><br><span style="color: hsl(120, 100%, 40%);">+          return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  llist_for_each_entry(e, &uc->use_counts, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+              if (!e->count) {</span><br><span style="color: hsl(120, 100%, 40%);">+                   e->use = use;</span><br><span style="color: hsl(120, 100%, 40%);">+                      return e;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Allocate a new use count entry, happens implicitly in osmo_use_count_get_put(). */</span><br><span style="color: hsl(120, 100%, 40%);">+static struct osmo_use_count_entry *osmo_use_count_create(struct osmo_use_count *uc, const char *use)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct osmo_use_count_entry *e = talloc_zero(uc->talloc_object, struct osmo_use_count_entry);</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!e)</span><br><span style="color: hsl(120, 100%, 40%);">+               return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  *e = (struct osmo_use_count_entry){</span><br><span style="color: hsl(120, 100%, 40%);">+           .use_count = uc,</span><br><span style="color: hsl(120, 100%, 40%);">+              .use = use,</span><br><span style="color: hsl(120, 100%, 40%);">+   };</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!uc->use_counts.next)</span><br><span style="color: hsl(120, 100%, 40%);">+          INIT_LLIST_HEAD(&uc->use_counts);</span><br><span style="color: hsl(120, 100%, 40%);">+      llist_add_tail(&e->entry, &uc->use_counts);</span><br><span style="color: hsl(120, 100%, 40%);">+     return e;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Deallocate a use count entry.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Normally, this is not necessary -- it is ok and even desirable to leave use count entries around even when they reach</span><br><span style="color: hsl(120, 100%, 40%);">+ * a count of zero, until the use_count->talloc_object deallocates and removes all of them in one flush. This avoids</span><br><span style="color: hsl(120, 100%, 40%);">+ * repeated allocation and deallocation for use tokens, because use count entries that have reached zero count are</span><br><span style="color: hsl(120, 100%, 40%);">+ * repurposed for any other use tokens. A cleanup makes sense only if a very large number of differing use tokens surged</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the same time, and the owning object will not be deallocated soon; if so, this should be done by the</span><br><span style="color: hsl(120, 100%, 40%);">+ * osmo_use_count_cb_t implementation.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * osmo_use_count_free() must *not* be called on use count entries that were added by</span><br><span style="color: hsl(120, 100%, 40%);">+ * osmo_use_count_make_static_entries(). This is the responsibility of the osmo_use_count_cb_t() implementation.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] use_count_entry  Use count entry to unlist and free.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_use_count_free(struct osmo_use_count_entry *use_count_entry)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!use_count_entry)</span><br><span style="color: hsl(120, 100%, 40%);">+         return;</span><br><span style="color: hsl(120, 100%, 40%);">+       llist_del(&use_count_entry->entry);</span><br><span style="color: hsl(120, 100%, 40%);">+    talloc_free(use_count_entry);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Implementation for osmo_use_count_get_put(), which can also be directly invoked to pass source file information. For</span><br><span style="color: hsl(120, 100%, 40%);">+ * arguments besides file and line, see osmo_use_count_get_put().</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] file  Source file path, as in __FILE__.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] line  Source file line, as in __LINE__.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int _osmo_use_count_get_put(struct osmo_use_count *uc, const char *use, int32_t change,</span><br><span style="color: hsl(120, 100%, 40%);">+                       const char *file, int line)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct osmo_use_count_entry *e;</span><br><span style="color: hsl(120, 100%, 40%);">+       int32_t old_use_count;</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!change)</span><br><span style="color: hsl(120, 100%, 40%);">+          return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   e = osmo_use_count_find(uc, use);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!e)</span><br><span style="color: hsl(120, 100%, 40%);">+               e = osmo_use_count_repurpose_zero_entry(uc, use);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!e)</span><br><span style="color: hsl(120, 100%, 40%);">+               e = osmo_use_count_create(uc, use);</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!e)</span><br><span style="color: hsl(120, 100%, 40%);">+               return -ENOMEM;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!e->count) {</span><br><span style="color: hsl(120, 100%, 40%);">+           /* move to end */</span><br><span style="color: hsl(120, 100%, 40%);">+             llist_del(&e->entry);</span><br><span style="color: hsl(120, 100%, 40%);">+          llist_add_tail(&e->entry, &uc->use_counts);</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   old_use_count = e->count;</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!count_safe(&e->count, change)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          e->count = old_use_count;</span><br><span style="color: hsl(120, 100%, 40%);">+          return -ERANGE;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (uc->use_cb)</span><br><span style="color: hsl(120, 100%, 40%);">+            return uc->use_cb(e, old_use_count, file, line);</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Add N static use token entries to avoid dynamic allocation of use count tokens.</span><br><span style="color: hsl(120, 100%, 40%);">+ * When not using this function, use count entries are talloc allocated from uc->talloc_object as talloc context. This</span><br><span style="color: hsl(120, 100%, 40%);">+ * means that there are small dynamic allocations for each use count token. osmo_use_count_get_put() normally leaves</span><br><span style="color: hsl(120, 100%, 40%);">+ * zero-count entries around and re-purposes them later, so the number of small allocations is at most the number of</span><br><span style="color: hsl(120, 100%, 40%);">+ * concurrent differently-named uses of the same object. If that is not enough, this function allows completely avoiding</span><br><span style="color: hsl(120, 100%, 40%);">+ * dynamic use count allocations, by adding N static entries with a zero count and a NULL use token.  They will be used</span><br><span style="color: hsl(120, 100%, 40%);">+ * by osmo_use_count_get_put(), and, if the caller avoids using osmo_use_count_free(), the osmo_use_count implementation</span><br><span style="color: hsl(120, 100%, 40%);">+ * never deallocates them. The idea is that the entries are members of the uc->talloc_object, or that they will by other</span><br><span style="color: hsl(120, 100%, 40%);">+ * means be implicitly deallocated by the talloc_object. It is fine to call</span><br><span style="color: hsl(120, 100%, 40%);">+ * osmo_use_count_make_static_entries(buf_n_entries=N) and later have more than N concurrent uses, i.e. it is no problem</span><br><span style="color: hsl(120, 100%, 40%);">+ * to mix static and dynamic entries. To completely avoid dynamic use count entries, N has to >= the maximum number of</span><br><span style="color: hsl(120, 100%, 40%);">+ * concurrent differently-named uses that will occur in the lifetime of the talloc_object.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *    struct my_object {</span><br><span style="color: hsl(120, 100%, 40%);">+ *            struct osmo_use_count use_count;</span><br><span style="color: hsl(120, 100%, 40%);">+ *            struct osmo_use_count_entry use_count_buf[3]; // planning for 3 concurrent users</span><br><span style="color: hsl(120, 100%, 40%);">+ *    };</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *    void example() {</span><br><span style="color: hsl(120, 100%, 40%);">+ *            struct my_object *o = talloc_zero(ctx, struct my_object);</span><br><span style="color: hsl(120, 100%, 40%);">+ *            osmo_use_count_make_static_entries(&o->use_count, o->use_count_buf, ARRAY_SIZE(o->use_count_buf));</span><br><span style="color: hsl(120, 100%, 40%);">+ *    }</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_use_count_make_static_entries(struct osmo_use_count *uc, struct osmo_use_count_entry *buf,</span><br><span style="color: hsl(120, 100%, 40%);">+                                    size_t buf_n_entries)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      size_t idx;</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!uc->use_counts.next)</span><br><span style="color: hsl(120, 100%, 40%);">+          INIT_LLIST_HEAD(&uc->use_counts);</span><br><span style="color: hsl(120, 100%, 40%);">+      for (idx = 0; idx < buf_n_entries; idx++) {</span><br><span style="color: hsl(120, 100%, 40%);">+                struct osmo_use_count_entry *e = &buf[idx];</span><br><span style="color: hsl(120, 100%, 40%);">+               *e = (struct osmo_use_count_entry){</span><br><span style="color: hsl(120, 100%, 40%);">+                   .use_count = uc,</span><br><span style="color: hsl(120, 100%, 40%);">+              };</span><br><span style="color: hsl(120, 100%, 40%);">+            llist_add_tail(&e->entry, &uc->use_counts);</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! @} */</span><br><span>diff --git a/tests/Makefile.am b/tests/Makefile.am</span><br><span>index 88bcd7e..d123ee2 100644</span><br><span>--- a/tests/Makefile.am</span><br><span>+++ b/tests/Makefile.am</span><br><span>@@ -31,6 +31,7 @@</span><br><span>                tdef/tdef_vty_test_config_subnode                      \</span><br><span>             tdef/tdef_vty_test_dynamic                             \</span><br><span>             sockaddr_str/sockaddr_str_test                         \</span><br><span style="color: hsl(120, 100%, 40%);">+              use_count/use_count_test                               \</span><br><span>             $(NULL)</span><br><span> </span><br><span> if ENABLE_MSGFILE</span><br><span>@@ -240,6 +241,9 @@</span><br><span> sockaddr_str_sockaddr_str_test_SOURCES = sockaddr_str/sockaddr_str_test.c</span><br><span> sockaddr_str_sockaddr_str_test_LDADD = $(LDADD)</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+use_count_use_count_test_SOURCES = use_count/use_count_test.c</span><br><span style="color: hsl(120, 100%, 40%);">+use_count_use_count_test_LDADD = $(LDADD)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # The `:;' works around a Bash 3.2 bug when the output is not writeable.</span><br><span> $(srcdir)/package.m4: $(top_srcdir)/configure.ac</span><br><span>  :;{ \</span><br><span>@@ -309,6 +313,7 @@</span><br><span>       tdef/tdef_vty_test_config_subnode.vty \</span><br><span>              tdef/tdef_vty_test_dynamic.vty \</span><br><span>             sockaddr_str/sockaddr_str_test.ok \</span><br><span style="color: hsl(120, 100%, 40%);">+           use_count/use_count_test.ok use_count/use_count_test.err \</span><br><span>           $(NULL)</span><br><span> </span><br><span> DISTCLEANFILES = atconfig atlocal conv/gsm0503_test_vectors.c</span><br><span>diff --git a/tests/testsuite.at b/tests/testsuite.at</span><br><span>index db2003f..5a6e802 100644</span><br><span>--- a/tests/testsuite.at</span><br><span>+++ b/tests/testsuite.at</span><br><span>@@ -337,3 +337,10 @@</span><br><span> cat $abs_srcdir/sockaddr_str/sockaddr_str_test.ok > expout</span><br><span> AT_CHECK([$abs_top_builddir/tests/sockaddr_str/sockaddr_str_test], [0], [expout], [ignore])</span><br><span> AT_CLEANUP</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AT_SETUP([use_count])</span><br><span style="color: hsl(120, 100%, 40%);">+AT_KEYWORDS([use_count])</span><br><span style="color: hsl(120, 100%, 40%);">+cat $abs_srcdir/use_count/use_count_test.ok > expout</span><br><span style="color: hsl(120, 100%, 40%);">+cat $abs_srcdir/use_count/use_count_test.err > experr</span><br><span style="color: hsl(120, 100%, 40%);">+AT_CHECK([$abs_top_builddir/tests/use_count/use_count_test], [0], [expout], [experr])</span><br><span style="color: hsl(120, 100%, 40%);">+AT_CLEANUP</span><br><span>diff --git a/tests/use_count/use_count_test.c b/tests/use_count/use_count_test.c</span><br><span>new file mode 100644</span><br><span>index 0000000..0b081c9</span><br><span>--- /dev/null</span><br><span>+++ b/tests/use_count/use_count_test.c</span><br><span>@@ -0,0 +1,339 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* Test implementation for osmo_use_count API. */</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * (C) 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Author: Neels Hofmeyr <neels@hofmeyr.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * SPDX-License-Identifier: GPL-2.0+</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 2 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU General Public License along</span><br><span style="color: hsl(120, 100%, 40%);">+ * with this program; if not, write to the Free Software Foundation, Inc.,</span><br><span style="color: hsl(120, 100%, 40%);">+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdio.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <errno.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <string.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <limits.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/logging.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/application.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/fsm.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/use_count.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void *ctx = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define log(fmt, args...) fprintf(stderr, fmt, ##args)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum {</span><br><span style="color: hsl(120, 100%, 40%);">+     DFOO,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define FOO_USE_BARRING "barring"</span><br><span style="color: hsl(120, 100%, 40%);">+#define FOO_USE_FIGHTING "fighting"</span><br><span style="color: hsl(120, 100%, 40%);">+#define FOO_USE_KUNG "kungfoo"</span><br><span style="color: hsl(120, 100%, 40%);">+#define FOO_USE_RELEASING "releasing"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+LLIST_HEAD(all_foo);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct foo {</span><br><span style="color: hsl(120, 100%, 40%);">+    struct llist_head entry;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct osmo_fsm_inst *fi;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct osmo_use_count use_count;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct osmo_use_count_entry use_count_buf[10];</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum foo_fsm_events {</span><br><span style="color: hsl(120, 100%, 40%);">+   FOO_EV_UNUSED,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static char name_buf[1024];</span><br><span style="color: hsl(120, 100%, 40%);">+#define use_count_name(UL) osmo_use_count_name_buf(name_buf, sizeof(name_buf), UL)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int foo_use_cb(struct osmo_use_count_entry *use_count_entry, int32_t old_use_count,</span><br><span style="color: hsl(120, 100%, 40%);">+         const char *file, int line)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct osmo_use_count *use_count = use_count_entry->use_count;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct foo *foo = use_count->talloc_object;</span><br><span style="color: hsl(120, 100%, 40%);">+        const char *use = use_count_entry->use;</span><br><span style="color: hsl(120, 100%, 40%);">+    int32_t new_use_count = use_count_entry->count;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  if (use && (!strcmp(use, FOO_USE_BARRING) || !strcmp(use, FOO_USE_RELEASING))</span><br><span style="color: hsl(120, 100%, 40%);">+     && new_use_count > 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+                LOGPFSMLSRC(foo->fi, LOGL_ERROR, file, line,</span><br><span style="color: hsl(120, 100%, 40%);">+                           "Attempt to get more than one %s\n", use);</span><br><span style="color: hsl(120, 100%, 40%);">+              /* Fix the use count */</span><br><span style="color: hsl(120, 100%, 40%);">+               use_count_entry->count = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                return -ERANGE;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   LOGPFSMLSRC(foo->fi, LOGL_NOTICE, file, line, "%s %+d %s: now used by %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                 foo->fi->id, new_use_count - old_use_count, use ? : "NULL", use_count_name(use_count));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (new_use_count < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+           LOGPFSMLSRC(foo->fi, LOGL_ERROR, file, line, "Negative use count on %s: %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                       use ? : "NULL", use_count_name(use_count));</span><br><span style="color: hsl(120, 100%, 40%);">+             /* Let it pass for the sake of this test */</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (osmo_use_count_total(use_count) == 0)</span><br><span style="color: hsl(120, 100%, 40%);">+             osmo_fsm_inst_dispatch(foo->fi, FOO_EV_UNUSED, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+      return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define foo_get_put(FOO, USE, CHANGE) do { \</span><br><span style="color: hsl(120, 100%, 40%);">+          int rc = osmo_use_count_get_put(&(FOO)->use_count, USE, CHANGE); \</span><br><span style="color: hsl(120, 100%, 40%);">+             if (rc) \</span><br><span style="color: hsl(120, 100%, 40%);">+                     log("osmo_use_count_get_put(%s, %s, %d) returned error: %d %s\n", \</span><br><span style="color: hsl(120, 100%, 40%);">+                     (FOO)->fi->id, USE ? : "NULL", CHANGE, rc, strerror(-rc)); \</span><br><span style="color: hsl(120, 100%, 40%);">+      } while(0)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define foo_get(FOO, USE) foo_get_put(FOO, USE, 1)</span><br><span style="color: hsl(120, 100%, 40%);">+#define foo_put(FOO, USE) foo_get_put(FOO, USE, -1)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum foo_fsm_states {</span><br><span style="color: hsl(120, 100%, 40%);">+        FOO_ST_IN_USE,</span><br><span style="color: hsl(120, 100%, 40%);">+        FOO_ST_IN_RELEASE,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void foo_fsm_in_use(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_ASSERT(event == FOO_EV_UNUSED);</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_fsm_inst_state_chg(fi, FOO_ST_IN_RELEASE, 0, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void foo_fsm_in_release_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct foo *foo = fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+        foo_get(foo, FOO_USE_RELEASING);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void foo_fsm_in_release(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_ASSERT(event == FOO_EV_UNUSED);</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define S(x)       (1 << (x))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const struct osmo_fsm_state foo_fsm_states[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+   [FOO_ST_IN_USE] = {</span><br><span style="color: hsl(120, 100%, 40%);">+           .name = "IN_USE",</span><br><span style="color: hsl(120, 100%, 40%);">+           .in_event_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(FOO_EV_UNUSED)</span><br><span style="color: hsl(120, 100%, 40%);">+                    ,</span><br><span style="color: hsl(120, 100%, 40%);">+             .out_state_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                   | S(FOO_ST_IN_RELEASE)</span><br><span style="color: hsl(120, 100%, 40%);">+                        ,</span><br><span style="color: hsl(120, 100%, 40%);">+             .action = foo_fsm_in_use,</span><br><span style="color: hsl(120, 100%, 40%);">+     },</span><br><span style="color: hsl(120, 100%, 40%);">+    [FOO_ST_IN_RELEASE] = {</span><br><span style="color: hsl(120, 100%, 40%);">+               .name = "IN_RELEASE",</span><br><span style="color: hsl(120, 100%, 40%);">+               .in_event_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(FOO_EV_UNUSED)</span><br><span style="color: hsl(120, 100%, 40%);">+                    ,</span><br><span style="color: hsl(120, 100%, 40%);">+             .out_state_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                   ,</span><br><span style="color: hsl(120, 100%, 40%);">+             .onenter = foo_fsm_in_release_onenter,</span><br><span style="color: hsl(120, 100%, 40%);">+                .action = foo_fsm_in_release,</span><br><span style="color: hsl(120, 100%, 40%);">+ },</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const struct value_string foo_fsm_event_names[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+  OSMO_VALUE_STRING(FOO_EV_UNUSED),</span><br><span style="color: hsl(120, 100%, 40%);">+     {}</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void foo_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct foo *foo = fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+        llist_del(&foo->entry);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct osmo_fsm foo_fsm = {</span><br><span style="color: hsl(120, 100%, 40%);">+       .name = "foo",</span><br><span style="color: hsl(120, 100%, 40%);">+      .states = foo_fsm_states,</span><br><span style="color: hsl(120, 100%, 40%);">+     .event_names = foo_fsm_event_names,</span><br><span style="color: hsl(120, 100%, 40%);">+   .num_states = ARRAY_SIZE(foo_fsm_states),</span><br><span style="color: hsl(120, 100%, 40%);">+     .log_subsys = DFOO,</span><br><span style="color: hsl(120, 100%, 40%);">+   .cleanup = foo_cleanup,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct foo *foo_alloc(const char *name, size_t static_entries)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct foo *foo;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct osmo_fsm_inst *fi = osmo_fsm_inst_alloc(&foo_fsm, ctx, NULL, LOGL_DEBUG, name);</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_ASSERT(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(static_entries <= ARRAY_SIZE(foo->use_count_buf));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        foo = talloc_zero(fi, struct foo);</span><br><span style="color: hsl(120, 100%, 40%);">+    *foo = (struct foo){</span><br><span style="color: hsl(120, 100%, 40%);">+          .fi = fi,</span><br><span style="color: hsl(120, 100%, 40%);">+             .use_count = {</span><br><span style="color: hsl(120, 100%, 40%);">+                        .talloc_object = foo,</span><br><span style="color: hsl(120, 100%, 40%);">+                 .use_cb = foo_use_cb,</span><br><span style="color: hsl(120, 100%, 40%);">+         },</span><br><span style="color: hsl(120, 100%, 40%);">+    };</span><br><span style="color: hsl(120, 100%, 40%);">+    fi->priv = foo;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_use_count_make_static_entries(&foo->use_count, foo->use_count_buf, static_entries);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  llist_add_tail(&foo->entry, &all_foo);</span><br><span style="color: hsl(120, 100%, 40%);">+     return foo;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void print_foos()</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        int count = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct foo *foo;</span><br><span style="color: hsl(120, 100%, 40%);">+      fprintf(stderr, "\nall use counts:\n");</span><br><span style="color: hsl(120, 100%, 40%);">+     llist_for_each_entry(foo, &all_foo, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+              fprintf(stderr, "%s: %s\n", foo->fi->id, use_count_name(&foo->use_count));</span><br><span style="color: hsl(120, 100%, 40%);">+             count++;</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+     fprintf(stderr, "%d foos\n\n", count);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void test_use_count_fsm()</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct foo *a, *b, *c;</span><br><span style="color: hsl(120, 100%, 40%);">+        log("\n%s()\n", __func__);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        a = foo_alloc("a", 0);</span><br><span style="color: hsl(120, 100%, 40%);">+      b = foo_alloc("b", 2);</span><br><span style="color: hsl(120, 100%, 40%);">+      c = foo_alloc("c", 10);</span><br><span style="color: hsl(120, 100%, 40%);">+     print_foos();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       log("A few gets and puts, logging source file information\n");</span><br><span style="color: hsl(120, 100%, 40%);">+      log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_BASENAME);</span><br><span style="color: hsl(120, 100%, 40%);">+   foo_get(a, FOO_USE_BARRING);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        foo_get(b, FOO_USE_BARRING);</span><br><span style="color: hsl(120, 100%, 40%);">+  foo_get(b, FOO_USE_FIGHTING);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       print_foos();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       log("Attempt to get more than one on limited 'barring' user:\n");</span><br><span style="color: hsl(120, 100%, 40%);">+   foo_get(b, FOO_USE_BARRING);</span><br><span style="color: hsl(120, 100%, 40%);">+  print_foos();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       log("Put away one user of b\n");</span><br><span style="color: hsl(120, 100%, 40%);">+    foo_put(b, FOO_USE_BARRING);</span><br><span style="color: hsl(120, 100%, 40%);">+  print_foos();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       log("(no longer log source file information)\n");</span><br><span style="color: hsl(120, 100%, 40%);">+   log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     log("Test null use token\n");</span><br><span style="color: hsl(120, 100%, 40%);">+       foo_get(a, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+     print_foos();</span><br><span style="color: hsl(120, 100%, 40%);">+ foo_put(a, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+     print_foos();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       log("Put away last user of a, goes to RELEASING state and waits for a hypothetic async release process\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ foo_put(a, FOO_USE_BARRING);</span><br><span style="color: hsl(120, 100%, 40%);">+  print_foos();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       log("Async releasing of a is done, will dealloc\n");</span><br><span style="color: hsl(120, 100%, 40%);">+        foo_put(a, FOO_USE_RELEASING);</span><br><span style="color: hsl(120, 100%, 40%);">+        print_foos();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       log("Use b multiple times\n");</span><br><span style="color: hsl(120, 100%, 40%);">+      foo_get(b, FOO_USE_KUNG);</span><br><span style="color: hsl(120, 100%, 40%);">+     foo_get(b, FOO_USE_KUNG);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   foo_put(b, FOO_USE_KUNG);</span><br><span style="color: hsl(120, 100%, 40%);">+     foo_get(b, FOO_USE_KUNG);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   foo_get(b, FOO_USE_KUNG);</span><br><span style="color: hsl(120, 100%, 40%);">+     print_foos();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       log("Test range: set kung-fu to INT32_MAX-1, then get three more; total count gets max-clamped to INT32_MAX\n");</span><br><span style="color: hsl(120, 100%, 40%);">+    foo_get_put(b, FOO_USE_KUNG, INT32_MAX-1 - osmo_use_count_by(&b->use_count, FOO_USE_KUNG));</span><br><span style="color: hsl(120, 100%, 40%);">+    print_foos();</span><br><span style="color: hsl(120, 100%, 40%);">+ foo_get(b, FOO_USE_KUNG);</span><br><span style="color: hsl(120, 100%, 40%);">+     foo_get(b, FOO_USE_KUNG);</span><br><span style="color: hsl(120, 100%, 40%);">+     foo_get(b, FOO_USE_KUNG);</span><br><span style="color: hsl(120, 100%, 40%);">+     foo_get_put(b, FOO_USE_FIGHTING, 2);</span><br><span style="color: hsl(120, 100%, 40%);">+  foo_get_put(b, FOO_USE_KUNG, -3);</span><br><span style="color: hsl(120, 100%, 40%);">+     foo_put(b, FOO_USE_KUNG);</span><br><span style="color: hsl(120, 100%, 40%);">+     foo_put(b, FOO_USE_KUNG);</span><br><span style="color: hsl(120, 100%, 40%);">+     foo_get(b, FOO_USE_FIGHTING);</span><br><span style="color: hsl(120, 100%, 40%);">+ foo_get(b, FOO_USE_FIGHTING);</span><br><span style="color: hsl(120, 100%, 40%);">+ foo_get(b, FOO_USE_FIGHTING);</span><br><span style="color: hsl(120, 100%, 40%);">+ print_foos();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       log("Release all uses of b\n");</span><br><span style="color: hsl(120, 100%, 40%);">+     foo_get_put(b, FOO_USE_KUNG, - osmo_use_count_by(&b->use_count, FOO_USE_KUNG));</span><br><span style="color: hsl(120, 100%, 40%);">+        foo_get_put(b, FOO_USE_FIGHTING, - osmo_use_count_by(&b->use_count, FOO_USE_FIGHTING));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      log("Signal async release as done\n");</span><br><span style="color: hsl(120, 100%, 40%);">+      foo_put(b, FOO_USE_RELEASING);</span><br><span style="color: hsl(120, 100%, 40%);">+        print_foos();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       log("Release something not gotten before: a get/put bug goes into negative count\n");</span><br><span style="color: hsl(120, 100%, 40%);">+       foo_put(c, FOO_USE_KUNG);</span><br><span style="color: hsl(120, 100%, 40%);">+     print_foos();</span><br><span style="color: hsl(120, 100%, 40%);">+ log("More negative\n");</span><br><span style="color: hsl(120, 100%, 40%);">+     foo_put(c, FOO_USE_KUNG);</span><br><span style="color: hsl(120, 100%, 40%);">+     foo_put(c, FOO_USE_KUNG);</span><br><span style="color: hsl(120, 100%, 40%);">+     print_foos();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       log("Also release c\n");</span><br><span style="color: hsl(120, 100%, 40%);">+    foo_get_put(c, FOO_USE_KUNG, 4);</span><br><span style="color: hsl(120, 100%, 40%);">+      foo_put(c, FOO_USE_KUNG);</span><br><span style="color: hsl(120, 100%, 40%);">+     log("Signal async release as done\n");</span><br><span style="color: hsl(120, 100%, 40%);">+      foo_put(c, FOO_USE_RELEASING);</span><br><span style="color: hsl(120, 100%, 40%);">+        print_foos();</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const struct log_info_cat default_categories[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+ [DFOO] = {</span><br><span style="color: hsl(120, 100%, 40%);">+            .name = "DFOO",</span><br><span style="color: hsl(120, 100%, 40%);">+             .description = "FOO",</span><br><span style="color: hsl(120, 100%, 40%);">+               .enabled = 1, .loglevel = LOGL_DEBUG,</span><br><span style="color: hsl(120, 100%, 40%);">+ },</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const struct log_info log_info = {</span><br><span style="color: hsl(120, 100%, 40%);">+   .cat = default_categories,</span><br><span style="color: hsl(120, 100%, 40%);">+    .num_cat = ARRAY_SIZE(default_categories),</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int main(int argc, char **argv)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        ctx = talloc_named_const(NULL, 0, "use_count_test.c");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    osmo_fsm_log_addr(false);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   osmo_init_logging2(ctx, &log_info);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_BASENAME);</span><br><span style="color: hsl(120, 100%, 40%);">+   log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);</span><br><span style="color: hsl(120, 100%, 40%);">+       log_set_print_filename_pos(osmo_stderr_target, LOG_FILENAME_POS_LINE_END);</span><br><span style="color: hsl(120, 100%, 40%);">+    log_set_print_category(osmo_stderr_target, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+        log_set_print_category_hex(osmo_stderr_target, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+    log_set_print_level(osmo_stderr_target, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+   log_set_use_color(osmo_stderr_target, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   osmo_fsm_register(&foo_fsm);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    test_use_count_fsm();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       return EXIT_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span>diff --git a/tests/use_count/use_count_test.err b/tests/use_count/use_count_test.err</span><br><span>new file mode 100644</span><br><span>index 0000000..97e74a5</span><br><span>--- /dev/null</span><br><span>+++ b/tests/use_count/use_count_test.err</span><br><span>@@ -0,0 +1,171 @@</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+test_use_count_fsm()</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO DEBUG foo(a){IN_USE}: Allocated</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO DEBUG foo(b){IN_USE}: Allocated</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO DEBUG foo(c){IN_USE}: Allocated</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+all use counts:</span><br><span style="color: hsl(120, 100%, 40%);">+a: 0 (-)</span><br><span style="color: hsl(120, 100%, 40%);">+b: 0 (-)</span><br><span style="color: hsl(120, 100%, 40%);">+c: 0 (-)</span><br><span style="color: hsl(120, 100%, 40%);">+3 foos</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+A few gets and puts, logging source file information</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO NOTICE foo(a){IN_USE}: a +1 barring: now used by 1 (barring) (use_count_test.c:223)</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO NOTICE foo(b){IN_USE}: b +1 barring: now used by 1 (barring) (use_count_test.c:225)</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO NOTICE foo(b){IN_USE}: b +1 fighting: now used by 2 (barring,fighting) (use_count_test.c:226)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+all use counts:</span><br><span style="color: hsl(120, 100%, 40%);">+a: 1 (barring)</span><br><span style="color: hsl(120, 100%, 40%);">+b: 2 (barring,fighting)</span><br><span style="color: hsl(120, 100%, 40%);">+c: 0 (-)</span><br><span style="color: hsl(120, 100%, 40%);">+3 foos</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Attempt to get more than one on limited 'barring' user:</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO ERROR foo(b){IN_USE}: Attempt to get more than one barring (use_count_test.c:231)</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_use_count_get_put(b, barring, 1) returned error: -34 Numerical result out of range</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+all use counts:</span><br><span style="color: hsl(120, 100%, 40%);">+a: 1 (barring)</span><br><span style="color: hsl(120, 100%, 40%);">+b: 2 (barring,fighting)</span><br><span style="color: hsl(120, 100%, 40%);">+c: 0 (-)</span><br><span style="color: hsl(120, 100%, 40%);">+3 foos</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Put away one user of b</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO NOTICE foo(b){IN_USE}: b -1 barring: now used by 1 (fighting) (use_count_test.c:235)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+all use counts:</span><br><span style="color: hsl(120, 100%, 40%);">+a: 1 (barring)</span><br><span style="color: hsl(120, 100%, 40%);">+b: 1 (fighting)</span><br><span style="color: hsl(120, 100%, 40%);">+c: 0 (-)</span><br><span style="color: hsl(120, 100%, 40%);">+3 foos</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+(no longer log source file information)</span><br><span style="color: hsl(120, 100%, 40%);">+Test null use token</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO NOTICE foo(a){IN_USE}: a +1 NULL: now used by 2 (barring,NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+all use counts:</span><br><span style="color: hsl(120, 100%, 40%);">+a: 2 (barring,NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+b: 1 (fighting)</span><br><span style="color: hsl(120, 100%, 40%);">+c: 0 (-)</span><br><span style="color: hsl(120, 100%, 40%);">+3 foos</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO NOTICE foo(a){IN_USE}: a -1 NULL: now used by 1 (barring)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+all use counts:</span><br><span style="color: hsl(120, 100%, 40%);">+a: 1 (barring)</span><br><span style="color: hsl(120, 100%, 40%);">+b: 1 (fighting)</span><br><span style="color: hsl(120, 100%, 40%);">+c: 0 (-)</span><br><span style="color: hsl(120, 100%, 40%);">+3 foos</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Put away last user of a, goes to RELEASING state and waits for a hypothetic async release process</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO NOTICE foo(a){IN_USE}: a -1 barring: now used by 0 (-)</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO DEBUG foo(a){IN_USE}: Received Event FOO_EV_UNUSED</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO DEBUG foo(a){IN_USE}: state_chg to IN_RELEASE</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO NOTICE foo(a){IN_RELEASE}: a +1 releasing: now used by 1 (releasing)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+all use counts:</span><br><span style="color: hsl(120, 100%, 40%);">+a: 1 (releasing)</span><br><span style="color: hsl(120, 100%, 40%);">+b: 1 (fighting)</span><br><span style="color: hsl(120, 100%, 40%);">+c: 0 (-)</span><br><span style="color: hsl(120, 100%, 40%);">+3 foos</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Async releasing of a is done, will dealloc</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO NOTICE foo(a){IN_RELEASE}: a -1 releasing: now used by 0 (-)</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO DEBUG foo(a){IN_RELEASE}: Received Event FOO_EV_UNUSED</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO DEBUG foo(a){IN_RELEASE}: Terminating (cause = OSMO_FSM_TERM_REGULAR)</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO DEBUG foo(a){IN_RELEASE}: Freeing instance</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO DEBUG foo(a){IN_RELEASE}: Deallocated</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+all use counts:</span><br><span style="color: hsl(120, 100%, 40%);">+b: 1 (fighting)</span><br><span style="color: hsl(120, 100%, 40%);">+c: 0 (-)</span><br><span style="color: hsl(120, 100%, 40%);">+2 foos</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Use b multiple times</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO NOTICE foo(b){IN_USE}: b +1 kungfoo: now used by 2 (fighting,kungfoo)</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO NOTICE foo(b){IN_USE}: b +1 kungfoo: now used by 3 (fighting,2*kungfoo)</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO NOTICE foo(b){IN_USE}: b -1 kungfoo: now used by 2 (fighting,kungfoo)</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO NOTICE foo(b){IN_USE}: b +1 kungfoo: now used by 3 (fighting,2*kungfoo)</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO NOTICE foo(b){IN_USE}: b +1 kungfoo: now used by 4 (fighting,3*kungfoo)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+all use counts:</span><br><span style="color: hsl(120, 100%, 40%);">+b: 4 (fighting,3*kungfoo)</span><br><span style="color: hsl(120, 100%, 40%);">+c: 0 (-)</span><br><span style="color: hsl(120, 100%, 40%);">+2 foos</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Test range: set kung-fu to INT32_MAX-1, then get three more; total count gets max-clamped to INT32_MAX</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO NOTICE foo(b){IN_USE}: b +2147483643 kungfoo: now used by 2147483647 (fighting,2147483646*kungfoo)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+all use counts:</span><br><span style="color: hsl(120, 100%, 40%);">+b: 2147483647 (fighting,2147483646*kungfoo)</span><br><span style="color: hsl(120, 100%, 40%);">+c: 0 (-)</span><br><span style="color: hsl(120, 100%, 40%);">+2 foos</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO NOTICE foo(b){IN_USE}: b +1 kungfoo: now used by 2147483647 (fighting,2147483647*kungfoo)</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_use_count_get_put(b, kungfoo, 1) returned error: -34 Numerical result out of range</span><br><span style="color: hsl(120, 100%, 40%);">+osmo_use_count_get_put(b, kungfoo, 1) returned error: -34 Numerical result out of range</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO NOTICE foo(b){IN_USE}: b +2 fighting: now used by 2147483647 (3*fighting,2147483647*kungfoo)</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO NOTICE foo(b){IN_USE}: b -3 kungfoo: now used by 2147483647 (3*fighting,2147483644*kungfoo)</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO NOTICE foo(b){IN_USE}: b -1 kungfoo: now used by 2147483646 (3*fighting,2147483643*kungfoo)</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO NOTICE foo(b){IN_USE}: b -1 kungfoo: now used by 2147483645 (3*fighting,2147483642*kungfoo)</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO NOTICE foo(b){IN_USE}: b +1 fighting: now used by 2147483646 (4*fighting,2147483642*kungfoo)</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO NOTICE foo(b){IN_USE}: b +1 fighting: now used by 2147483647 (5*fighting,2147483642*kungfoo)</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO NOTICE foo(b){IN_USE}: b +1 fighting: now used by 2147483647 (6*fighting,2147483642*kungfoo)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+all use counts:</span><br><span style="color: hsl(120, 100%, 40%);">+b: 2147483647 (6*fighting,2147483642*kungfoo)</span><br><span style="color: hsl(120, 100%, 40%);">+c: 0 (-)</span><br><span style="color: hsl(120, 100%, 40%);">+2 foos</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Release all uses of b</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO NOTICE foo(b){IN_USE}: b -2147483642 kungfoo: now used by 6 (6*fighting)</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO NOTICE foo(b){IN_USE}: b -6 fighting: now used by 0 (-)</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO DEBUG foo(b){IN_USE}: Received Event FOO_EV_UNUSED</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO DEBUG foo(b){IN_USE}: state_chg to IN_RELEASE</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO NOTICE foo(b){IN_RELEASE}: b +1 releasing: now used by 1 (releasing)</span><br><span style="color: hsl(120, 100%, 40%);">+Signal async release as done</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO NOTICE foo(b){IN_RELEASE}: b -1 releasing: now used by 0 (-)</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO DEBUG foo(b){IN_RELEASE}: Received Event FOO_EV_UNUSED</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO DEBUG foo(b){IN_RELEASE}: Terminating (cause = OSMO_FSM_TERM_REGULAR)</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO DEBUG foo(b){IN_RELEASE}: Freeing instance</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO DEBUG foo(b){IN_RELEASE}: Deallocated</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+all use counts:</span><br><span style="color: hsl(120, 100%, 40%);">+c: 0 (-)</span><br><span style="color: hsl(120, 100%, 40%);">+1 foos</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Release something not gotten before: a get/put bug goes into negative count</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO NOTICE foo(c){IN_USE}: c -1 kungfoo: now used by -1 (-1*kungfoo)</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO ERROR foo(c){IN_USE}: Negative use count on kungfoo: -1 (-1*kungfoo)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+all use counts:</span><br><span style="color: hsl(120, 100%, 40%);">+c: -1 (-1*kungfoo)</span><br><span style="color: hsl(120, 100%, 40%);">+1 foos</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+More negative</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO NOTICE foo(c){IN_USE}: c -1 kungfoo: now used by -2 (-2*kungfoo)</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO ERROR foo(c){IN_USE}: Negative use count on kungfoo: -2 (-2*kungfoo)</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO NOTICE foo(c){IN_USE}: c -1 kungfoo: now used by -3 (-3*kungfoo)</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO ERROR foo(c){IN_USE}: Negative use count on kungfoo: -3 (-3*kungfoo)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+all use counts:</span><br><span style="color: hsl(120, 100%, 40%);">+c: -3 (-3*kungfoo)</span><br><span style="color: hsl(120, 100%, 40%);">+1 foos</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Also release c</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO NOTICE foo(c){IN_USE}: c +4 kungfoo: now used by 1 (kungfoo)</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO NOTICE foo(c){IN_USE}: c -1 kungfoo: now used by 0 (-)</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO DEBUG foo(c){IN_USE}: Received Event FOO_EV_UNUSED</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO DEBUG foo(c){IN_USE}: state_chg to IN_RELEASE</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO NOTICE foo(c){IN_RELEASE}: c +1 releasing: now used by 1 (releasing)</span><br><span style="color: hsl(120, 100%, 40%);">+Signal async release as done</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO NOTICE foo(c){IN_RELEASE}: c -1 releasing: now used by 0 (-)</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO DEBUG foo(c){IN_RELEASE}: Received Event FOO_EV_UNUSED</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO DEBUG foo(c){IN_RELEASE}: Terminating (cause = OSMO_FSM_TERM_REGULAR)</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO DEBUG foo(c){IN_RELEASE}: Freeing instance</span><br><span style="color: hsl(120, 100%, 40%);">+DFOO DEBUG foo(c){IN_RELEASE}: Deallocated</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+all use counts:</span><br><span style="color: hsl(120, 100%, 40%);">+0 foos</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>diff --git a/tests/use_count/use_count_test.ok b/tests/use_count/use_count_test.ok</span><br><span>new file mode 100644</span><br><span>index 0000000..e69de29</span><br><span>--- /dev/null</span><br><span>+++ b/tests/use_count/use_count_test.ok</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/13121">change 13121</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/13121"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: libosmocore </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-MessageType: merged </div>
<div style="display:none"> Gerrit-Change-Id: Ife31e6798b4e728a23913179e346552a7dd338c0 </div>
<div style="display:none"> Gerrit-Change-Number: 13121 </div>
<div style="display:none"> Gerrit-PatchSet: 9 </div>
<div style="display:none"> Gerrit-Owner: Neels Hofmeyr <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Harald Welte <laforge@gnumonks.org> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder (1000002) </div>
<div style="display:none"> Gerrit-Reviewer: Neels Hofmeyr <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Pau Espin Pedrol <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Vadim Yanitskiy <axilirator@gmail.com> </div>
<div style="display:none"> Gerrit-CC: Max <suraev@alumni.ntnu.no> </div>