blob: 9714403de12d0ccd9c84fc0c6b01df5f76bc969b [file] [log] [blame]
Neels Hofmeyr0e8df1c2019-02-11 20:32:25 +01001/*! \file use_count.c
2 * Generic object usage counter Implementation (get, put and deallocate on zero count).
3 */
4/*
5 * (C) 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
6 *
7 * All Rights Reserved
8 *
9 * Author: Neels Hofmeyr <neels@hofmeyr.de>
10 *
11 * SPDX-License-Identifier: GPL-2.0+
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
Neels Hofmeyr0e8df1c2019-02-11 20:32:25 +010022 */
23
24#include <errno.h>
25#include <inttypes.h>
26#include <string.h>
27
28#include <osmocom/core/linuxlist.h>
29#include <osmocom/core/utils.h>
30#include <osmocom/core/use_count.h>
31
32/*! \addtogroup use_count
33 *
34 * Generic object usage counter (get, put and deallocate on zero count).
35 *
36 * For an example and a detailed description, see struct osmo_use_count.
37 *
38 * @{
39 * \file use_count.c
40 */
41
42/*! Add two int32_t but make sure to min- and max-clamp at INT32_MIN and INT32_MAX, respectively. */
43static inline bool count_safe(int32_t *val_p, int32_t add)
44{
45 int32_t val = *val_p;
46
47 /* A simpler implementation would just let the integer overflow and compare with previous value afterwards, but
48 * that causes runtime errors in the address sanitizer. So let's just do this without tricks. */
49 if (add < 0 && val < 0 && val - INT32_MIN < -add) {
50 *val_p = INT32_MIN;
51 return false;
52 }
53
54 if (add > 0 && val > 0 && INT32_MAX - val < add) {
55 *val_p = INT32_MAX;
56 return false;
57 }
58
59 *val_p = val + add;
60 return true;
61}
62
63/*! Return the sum of all use counts, min- and max-clamped at INT32_MIN and INT32_MAX.
64 * \param[in] uc Use counts to sum up.
65 * \return Accumulated counts, or 0 if uc is NULL.
66 */
67int32_t osmo_use_count_total(const struct osmo_use_count *uc)
68{
69 struct osmo_use_count_entry *e;
70 int32_t total = 0;
71
72 if (!uc || !uc->use_counts.next)
73 return 0;
74
75 llist_for_each_entry(e, &uc->use_counts, entry) {
76 count_safe(&total, e->count);
77 }
78 return total;
79}
80
81/*! Return use count by a single use token.
82 * \param[in] uc Use counts to look up in.
83 * \param[in] use Use token.
84 * \return Use count, or 0 if uc is NULL or use token is not present.
85 */
86int32_t osmo_use_count_by(const struct osmo_use_count *uc, const char *use)
87{
88 const struct osmo_use_count_entry *e;
89 if (!uc)
90 return 0;
91 e = osmo_use_count_find(uc, use);
92 if (!e)
93 return 0;
94 return e->count;
95}
96
97/*! Write a comprehensive listing of use counts to a string buffer.
98 * Reads like "12 (3*barring,fighting,8*kungfoo)".
99 * \param[inout] buf Destination buffer.
100 * \param[in] buf_len sizeof(buf).
101 * \param[in] uc Use counts to print.
102 * \return buf, always nul-terminated (except when buf_len < 1).
103 */
104const char *osmo_use_count_name_buf(char *buf, size_t buf_len, const struct osmo_use_count *uc)
105{
Neels Hofmeyr9277b972020-09-15 00:48:36 +0000106 osmo_use_count_to_str_buf(buf, buf_len, uc);
107 return buf;
108}
109
110/*! Write a comprehensive listing of use counts to a string buffer.
111 * Reads like "12 (3*barring,fighting,8*kungfoo)".
112 * \param[inout] buf Destination buffer.
113 * \param[in] buf_len sizeof(buf).
114 * \param[in] uc Use counts to print.
115 * \return number of bytes that would be written, like snprintf().
116 */
117int osmo_use_count_to_str_buf(char *buf, size_t buf_len, const struct osmo_use_count *uc)
118{
Neels Hofmeyr0e8df1c2019-02-11 20:32:25 +0100119 int32_t count = osmo_use_count_total(uc);
120 struct osmo_strbuf sb = { .buf = buf, .len = buf_len };
121 struct osmo_use_count_entry *e;
122 bool first;
123
124 OSMO_STRBUF_PRINTF(sb, "%" PRId32 " (", count);
125
Neels Hofmeyr6b5f1de2020-09-29 01:32:28 +0000126 if (!uc->use_counts.next)
127 goto uninitialized;
128
Neels Hofmeyr0e8df1c2019-02-11 20:32:25 +0100129 first = true;
130 llist_for_each_entry(e, &uc->use_counts, entry) {
131 if (!e->count)
132 continue;
133 if (!first)
134 OSMO_STRBUF_PRINTF(sb, ",");
135 first = false;
136 if (e->count != 1)
137 OSMO_STRBUF_PRINTF(sb, "%" PRId32 "*", e->count);
138 OSMO_STRBUF_PRINTF(sb, "%s", e->use ? : "NULL");
139 }
140 if (first)
141 OSMO_STRBUF_PRINTF(sb, "-");
Neels Hofmeyr6b5f1de2020-09-29 01:32:28 +0000142
143uninitialized:
Neels Hofmeyr0e8df1c2019-02-11 20:32:25 +0100144 OSMO_STRBUF_PRINTF(sb, ")");
Neels Hofmeyr9277b972020-09-15 00:48:36 +0000145 return sb.chars_needed;
146}
147
148/*! Write a comprehensive listing of use counts to a talloc allocated string buffer.
149 * Reads like "12 (3*barring,fighting,8*kungfoo)".
150 * \param[in] ctx talloc pool to allocate from.
151 * \param[in] uc Use counts to print.
152 * \return buf, always nul-terminated.
153 */
154char *osmo_use_count_to_str_c(void *ctx, const struct osmo_use_count *uc)
155{
156 OSMO_NAME_C_IMPL(ctx, 32, "ERROR", osmo_use_count_to_str_buf, uc)
Neels Hofmeyr0e8df1c2019-02-11 20:32:25 +0100157}
158
159/* Return a use token's use count entry -- probably you want osmo_use_count_by() instead.
160 * \param[in] uc Use counts to look up in.
161 * \param[in] use Use token.
162 * \return matching entry, or NULL if not present.
163 */
164struct osmo_use_count_entry *osmo_use_count_find(const struct osmo_use_count *uc, const char *use)
165{
166 struct osmo_use_count_entry *e;
167 if (!uc->use_counts.next)
168 return NULL;
169 llist_for_each_entry(e, &uc->use_counts, entry) {
170 if (e->use == use || (use && e->use && !strcmp(e->use, use)))
171 return e;
172 }
173 return NULL;
174}
175
176/*! Find a use count entry that currently has zero count, and re-use that for this new use token. */
177static struct osmo_use_count_entry *osmo_use_count_repurpose_zero_entry(struct osmo_use_count *uc, const char *use)
178{
179 struct osmo_use_count_entry *e;
180 if (!uc->use_counts.next)
181 return NULL;
182 llist_for_each_entry(e, &uc->use_counts, entry) {
183 if (!e->count) {
184 e->use = use;
185 return e;
186 }
187 }
188 return NULL;
189}
190
191/*! Allocate a new use count entry, happens implicitly in osmo_use_count_get_put(). */
192static struct osmo_use_count_entry *osmo_use_count_create(struct osmo_use_count *uc, const char *use)
193{
194 struct osmo_use_count_entry *e = talloc_zero(uc->talloc_object, struct osmo_use_count_entry);
195 if (!e)
196 return NULL;
197 *e = (struct osmo_use_count_entry){
198 .use_count = uc,
199 .use = use,
200 };
201 if (!uc->use_counts.next)
202 INIT_LLIST_HEAD(&uc->use_counts);
203 llist_add_tail(&e->entry, &uc->use_counts);
204 return e;
205}
206
207/*! Deallocate a use count entry.
208 * Normally, this is not necessary -- it is ok and even desirable to leave use count entries around even when they reach
209 * a count of zero, until the use_count->talloc_object deallocates and removes all of them in one flush. This avoids
210 * repeated allocation and deallocation for use tokens, because use count entries that have reached zero count are
211 * repurposed for any other use tokens. A cleanup makes sense only if a very large number of differing use tokens surged
212 * at the same time, and the owning object will not be deallocated soon; if so, this should be done by the
213 * osmo_use_count_cb_t implementation.
214 *
215 * osmo_use_count_free() must *not* be called on use count entries that were added by
216 * osmo_use_count_make_static_entries(). This is the responsibility of the osmo_use_count_cb_t() implementation.
217 *
218 * \param[in] use_count_entry Use count entry to unlist and free.
219 */
220void osmo_use_count_free(struct osmo_use_count_entry *use_count_entry)
221{
222 if (!use_count_entry)
223 return;
224 llist_del(&use_count_entry->entry);
225 talloc_free(use_count_entry);
226}
227
228/*! Implementation for osmo_use_count_get_put(), which can also be directly invoked to pass source file information. For
229 * arguments besides file and line, see osmo_use_count_get_put().
230 * \param[in] file Source file path, as in __FILE__.
231 * \param[in] line Source file line, as in __LINE__.
232 */
233int _osmo_use_count_get_put(struct osmo_use_count *uc, const char *use, int32_t change,
234 const char *file, int line)
235{
236 struct osmo_use_count_entry *e;
237 int32_t old_use_count;
Daniel Willmannd5150a62022-08-17 16:00:39 +0200238 if (!uc)
239 return -EINVAL;
Neels Hofmeyr0e8df1c2019-02-11 20:32:25 +0100240 if (!change)
241 return 0;
242
243 e = osmo_use_count_find(uc, use);
244 if (!e)
245 e = osmo_use_count_repurpose_zero_entry(uc, use);
246 if (!e)
247 e = osmo_use_count_create(uc, use);
248 if (!e)
249 return -ENOMEM;
250
251 if (!e->count) {
252 /* move to end */
253 llist_del(&e->entry);
254 llist_add_tail(&e->entry, &uc->use_counts);
255 }
256
257 old_use_count = e->count;
258 if (!count_safe(&e->count, change)) {
259 e->count = old_use_count;
260 return -ERANGE;
261 }
262
263 if (uc->use_cb)
264 return uc->use_cb(e, old_use_count, file, line);
265 return 0;
266}
267
268/*! Add N static use token entries to avoid dynamic allocation of use count tokens.
269 * When not using this function, use count entries are talloc allocated from uc->talloc_object as talloc context. This
270 * means that there are small dynamic allocations for each use count token. osmo_use_count_get_put() normally leaves
271 * zero-count entries around and re-purposes them later, so the number of small allocations is at most the number of
272 * concurrent differently-named uses of the same object. If that is not enough, this function allows completely avoiding
273 * dynamic use count allocations, by adding N static entries with a zero count and a NULL use token. They will be used
274 * by osmo_use_count_get_put(), and, if the caller avoids using osmo_use_count_free(), the osmo_use_count implementation
275 * never deallocates them. The idea is that the entries are members of the uc->talloc_object, or that they will by other
276 * means be implicitly deallocated by the talloc_object. It is fine to call
277 * osmo_use_count_make_static_entries(buf_n_entries=N) and later have more than N concurrent uses, i.e. it is no problem
278 * to mix static and dynamic entries. To completely avoid dynamic use count entries, N has to >= the maximum number of
279 * concurrent differently-named uses that will occur in the lifetime of the talloc_object.
280 *
281 * struct my_object {
282 * struct osmo_use_count use_count;
283 * struct osmo_use_count_entry use_count_buf[3]; // planning for 3 concurrent users
284 * };
285 *
286 * void example() {
287 * struct my_object *o = talloc_zero(ctx, struct my_object);
288 * osmo_use_count_make_static_entries(&o->use_count, o->use_count_buf, ARRAY_SIZE(o->use_count_buf));
289 * }
290 */
291void osmo_use_count_make_static_entries(struct osmo_use_count *uc, struct osmo_use_count_entry *buf,
292 size_t buf_n_entries)
293{
294 size_t idx;
295 if (!uc->use_counts.next)
296 INIT_LLIST_HEAD(&uc->use_counts);
297 for (idx = 0; idx < buf_n_entries; idx++) {
298 struct osmo_use_count_entry *e = &buf[idx];
299 *e = (struct osmo_use_count_entry){
300 .use_count = uc,
301 };
302 llist_add_tail(&e->entry, &uc->use_counts);
303 }
304}
305
306/*! @} */