blob: 95af30823c539140b1b52e110d6db18fc9957c31 [file] [log] [blame]
Neels Hofmeyr0e8df1c2019-02-11 20:32:25 +01001/* Test implementation for osmo_use_count API. */
2/*
3 * (C) 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
4 *
5 * All Rights Reserved
6 *
7 * Author: Neels Hofmeyr <neels@hofmeyr.de>
8 *
9 * SPDX-License-Identifier: GPL-2.0+
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License along
22 * with this program; if not, write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 */
25
26#include <stdio.h>
27#include <errno.h>
28#include <string.h>
29#include <limits.h>
30
31#include <osmocom/core/logging.h>
32#include <osmocom/core/application.h>
33#include <osmocom/core/fsm.h>
34
35#include <osmocom/core/use_count.h>
36
37static void *ctx = NULL;
38
39#define log(fmt, args...) fprintf(stderr, fmt, ##args)
40
41enum {
42 DFOO,
43};
44
45#define FOO_USE_BARRING "barring"
46#define FOO_USE_FIGHTING "fighting"
47#define FOO_USE_KUNG "kungfoo"
48#define FOO_USE_RELEASING "releasing"
49
50LLIST_HEAD(all_foo);
51
52struct foo {
53 struct llist_head entry;
54 struct osmo_fsm_inst *fi;
55 struct osmo_use_count use_count;
56 struct osmo_use_count_entry use_count_buf[10];
57};
58
59enum foo_fsm_events {
60 FOO_EV_UNUSED,
61};
62
63static char name_buf[1024];
64#define use_count_name(UL) osmo_use_count_name_buf(name_buf, sizeof(name_buf), UL)
65
66int foo_use_cb(struct osmo_use_count_entry *use_count_entry, int32_t old_use_count,
67 const char *file, int line)
68{
69 struct osmo_use_count *use_count = use_count_entry->use_count;
70 struct foo *foo = use_count->talloc_object;
71 const char *use = use_count_entry->use;
72 int32_t new_use_count = use_count_entry->count;
73
74 if (use && (!strcmp(use, FOO_USE_BARRING) || !strcmp(use, FOO_USE_RELEASING))
75 && new_use_count > 1) {
76 LOGPFSMLSRC(foo->fi, LOGL_ERROR, file, line,
77 "Attempt to get more than one %s\n", use);
78 /* Fix the use count */
79 use_count_entry->count = 1;
80 return -ERANGE;
81 }
82
83 LOGPFSMLSRC(foo->fi, LOGL_NOTICE, file, line, "%s %+d %s: now used by %s\n",
84 foo->fi->id, new_use_count - old_use_count, use ? : "NULL", use_count_name(use_count));
85
86 if (new_use_count < 0) {
87 LOGPFSMLSRC(foo->fi, LOGL_ERROR, file, line, "Negative use count on %s: %s\n",
88 use ? : "NULL", use_count_name(use_count));
89 /* Let it pass for the sake of this test */
90 }
91
92 if (osmo_use_count_total(use_count) == 0)
93 osmo_fsm_inst_dispatch(foo->fi, FOO_EV_UNUSED, NULL);
94 return 0;
95}
96
97#define foo_get_put(FOO, USE, CHANGE) do { \
98 int rc = osmo_use_count_get_put(&(FOO)->use_count, USE, CHANGE); \
99 if (rc) \
100 log("osmo_use_count_get_put(%s, %s, %d) returned error: %d %s\n", \
101 (FOO)->fi->id, USE ? : "NULL", CHANGE, rc, strerror(-rc)); \
102 } while(0)
103
104#define foo_get(FOO, USE) foo_get_put(FOO, USE, 1)
105#define foo_put(FOO, USE) foo_get_put(FOO, USE, -1)
106
107enum foo_fsm_states {
108 FOO_ST_IN_USE,
109 FOO_ST_IN_RELEASE,
110};
111
112void foo_fsm_in_use(struct osmo_fsm_inst *fi, uint32_t event, void *data)
113{
114 OSMO_ASSERT(event == FOO_EV_UNUSED);
115 osmo_fsm_inst_state_chg(fi, FOO_ST_IN_RELEASE, 0, 0);
116}
117
118void foo_fsm_in_release_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
119{
120 struct foo *foo = fi->priv;
121 foo_get(foo, FOO_USE_RELEASING);
122}
123
124void foo_fsm_in_release(struct osmo_fsm_inst *fi, uint32_t event, void *data)
125{
126 OSMO_ASSERT(event == FOO_EV_UNUSED);
127 osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
128}
129
130
131#define S(x) (1 << (x))
132
133static const struct osmo_fsm_state foo_fsm_states[] = {
134 [FOO_ST_IN_USE] = {
135 .name = "IN_USE",
136 .in_event_mask = 0
137 | S(FOO_EV_UNUSED)
138 ,
139 .out_state_mask = 0
140 | S(FOO_ST_IN_RELEASE)
141 ,
142 .action = foo_fsm_in_use,
143 },
144 [FOO_ST_IN_RELEASE] = {
145 .name = "IN_RELEASE",
146 .in_event_mask = 0
147 | S(FOO_EV_UNUSED)
148 ,
149 .out_state_mask = 0
150 ,
151 .onenter = foo_fsm_in_release_onenter,
152 .action = foo_fsm_in_release,
153 },
154};
155
156static const struct value_string foo_fsm_event_names[] = {
157 OSMO_VALUE_STRING(FOO_EV_UNUSED),
158 {}
159};
160
161void foo_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
162{
163 struct foo *foo = fi->priv;
164 llist_del(&foo->entry);
165}
166
167static struct osmo_fsm foo_fsm = {
168 .name = "foo",
169 .states = foo_fsm_states,
170 .event_names = foo_fsm_event_names,
171 .num_states = ARRAY_SIZE(foo_fsm_states),
172 .log_subsys = DFOO,
173 .cleanup = foo_cleanup,
174};
175
176static struct foo *foo_alloc(const char *name, size_t static_entries)
177{
178 struct foo *foo;
179 struct osmo_fsm_inst *fi = osmo_fsm_inst_alloc(&foo_fsm, ctx, NULL, LOGL_DEBUG, name);
180 OSMO_ASSERT(fi);
181 OSMO_ASSERT(static_entries <= ARRAY_SIZE(foo->use_count_buf));
182
183 foo = talloc_zero(fi, struct foo);
184 *foo = (struct foo){
185 .fi = fi,
186 .use_count = {
187 .talloc_object = foo,
188 .use_cb = foo_use_cb,
189 },
190 };
191 fi->priv = foo;
192
193 osmo_use_count_make_static_entries(&foo->use_count, foo->use_count_buf, static_entries);
194
195 llist_add_tail(&foo->entry, &all_foo);
196 return foo;
197}
198
199void print_foos()
200{
201 int count = 0;
202 struct foo *foo;
203 fprintf(stderr, "\nall use counts:\n");
204 llist_for_each_entry(foo, &all_foo, entry) {
205 fprintf(stderr, "%s: %s\n", foo->fi->id, use_count_name(&foo->use_count));
206 count++;
207 }
208 fprintf(stderr, "%d foos\n\n", count);
209}
210
211static void test_use_count_fsm()
212{
213 struct foo *a, *b, *c;
214 log("\n%s()\n", __func__);
215
216 a = foo_alloc("a", 0);
217 b = foo_alloc("b", 2);
218 c = foo_alloc("c", 10);
219 print_foos();
220
221 log("A few gets and puts, logging source file information\n");
222 log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_BASENAME);
223 foo_get(a, FOO_USE_BARRING);
224
225 foo_get(b, FOO_USE_BARRING);
226 foo_get(b, FOO_USE_FIGHTING);
227
228 print_foos();
229
230 log("Attempt to get more than one on limited 'barring' user:\n");
231 foo_get(b, FOO_USE_BARRING);
232 print_foos();
233
234 log("Put away one user of b\n");
235 foo_put(b, FOO_USE_BARRING);
236 print_foos();
237
238 log("(no longer log source file information)\n");
239 log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
240
241 log("Test null use token\n");
242 foo_get(a, NULL);
243 print_foos();
244 foo_put(a, NULL);
245 print_foos();
246
247 log("Put away last user of a, goes to RELEASING state and waits for a hypothetic async release process\n");
248 foo_put(a, FOO_USE_BARRING);
249 print_foos();
250
251 log("Async releasing of a is done, will dealloc\n");
252 foo_put(a, FOO_USE_RELEASING);
253 print_foos();
254
255 log("Use b multiple times\n");
256 foo_get(b, FOO_USE_KUNG);
257 foo_get(b, FOO_USE_KUNG);
258
259 foo_put(b, FOO_USE_KUNG);
260 foo_get(b, FOO_USE_KUNG);
261
262 foo_get(b, FOO_USE_KUNG);
263 print_foos();
264
265 log("Test range: set kung-fu to INT32_MAX-1, then get three more; total count gets max-clamped to INT32_MAX\n");
266 foo_get_put(b, FOO_USE_KUNG, INT32_MAX-1 - osmo_use_count_by(&b->use_count, FOO_USE_KUNG));
267 print_foos();
268 foo_get(b, FOO_USE_KUNG);
269 foo_get(b, FOO_USE_KUNG);
270 foo_get(b, FOO_USE_KUNG);
271 foo_get_put(b, FOO_USE_FIGHTING, 2);
272 foo_get_put(b, FOO_USE_KUNG, -3);
273 foo_put(b, FOO_USE_KUNG);
274 foo_put(b, FOO_USE_KUNG);
275 foo_get(b, FOO_USE_FIGHTING);
276 foo_get(b, FOO_USE_FIGHTING);
277 foo_get(b, FOO_USE_FIGHTING);
278 print_foos();
279
280 log("Release all uses of b\n");
281 foo_get_put(b, FOO_USE_KUNG, - osmo_use_count_by(&b->use_count, FOO_USE_KUNG));
282 foo_get_put(b, FOO_USE_FIGHTING, - osmo_use_count_by(&b->use_count, FOO_USE_FIGHTING));
283
284 log("Signal async release as done\n");
285 foo_put(b, FOO_USE_RELEASING);
286 print_foos();
287
288 log("Release something not gotten before: a get/put bug goes into negative count\n");
289 foo_put(c, FOO_USE_KUNG);
290 print_foos();
291 log("More negative\n");
292 foo_put(c, FOO_USE_KUNG);
293 foo_put(c, FOO_USE_KUNG);
294 print_foos();
295
296 log("Also release c\n");
297 foo_get_put(c, FOO_USE_KUNG, 4);
298 foo_put(c, FOO_USE_KUNG);
299 log("Signal async release as done\n");
300 foo_put(c, FOO_USE_RELEASING);
301 print_foos();
302}
303
304static const struct log_info_cat default_categories[] = {
305 [DFOO] = {
306 .name = "DFOO",
307 .description = "FOO",
308 .enabled = 1, .loglevel = LOGL_DEBUG,
309 },
310};
311
312static const struct log_info log_info = {
313 .cat = default_categories,
314 .num_cat = ARRAY_SIZE(default_categories),
315};
316
317
318int main(int argc, char **argv)
319{
320 ctx = talloc_named_const(NULL, 0, "use_count_test.c");
321
322 osmo_fsm_log_addr(false);
323
324 osmo_init_logging2(ctx, &log_info);
325
326 log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_BASENAME);
327 log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
328 log_set_print_filename_pos(osmo_stderr_target, LOG_FILENAME_POS_LINE_END);
329 log_set_print_category(osmo_stderr_target, 1);
330 log_set_print_category_hex(osmo_stderr_target, 0);
331 log_set_print_level(osmo_stderr_target, 1);
332 log_set_use_color(osmo_stderr_target, 0);
333
Harald Welteae5016f2019-12-01 13:38:20 +0100334 OSMO_ASSERT(osmo_fsm_register(&foo_fsm) == 0);
Neels Hofmeyr0e8df1c2019-02-11 20:32:25 +0100335
336 test_use_count_fsm();
337
338 return EXIT_SUCCESS;
339}