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