Neels Hofmeyr | 0e8df1c | 2019-02-11 20:32:25 +0100 | [diff] [blame] | 1 | /* 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 Hofmeyr | 0e8df1c | 2019-02-11 20:32:25 +0100 | [diff] [blame] | 20 | */ |
| 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 | |
| 33 | static void *ctx = NULL; |
| 34 | |
| 35 | #define log(fmt, args...) fprintf(stderr, fmt, ##args) |
| 36 | |
| 37 | enum { |
| 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 | |
| 46 | LLIST_HEAD(all_foo); |
| 47 | |
| 48 | struct 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 | |
| 55 | enum foo_fsm_events { |
| 56 | FOO_EV_UNUSED, |
| 57 | }; |
| 58 | |
| 59 | static char name_buf[1024]; |
| 60 | #define use_count_name(UL) osmo_use_count_name_buf(name_buf, sizeof(name_buf), UL) |
| 61 | |
| 62 | int 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 | |
| 103 | enum foo_fsm_states { |
| 104 | FOO_ST_IN_USE, |
| 105 | FOO_ST_IN_RELEASE, |
| 106 | }; |
| 107 | |
| 108 | void 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 | |
| 114 | void 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 | |
| 120 | void 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 | |
| 129 | static 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 | |
| 152 | static const struct value_string foo_fsm_event_names[] = { |
| 153 | OSMO_VALUE_STRING(FOO_EV_UNUSED), |
| 154 | {} |
| 155 | }; |
| 156 | |
| 157 | void 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 | |
| 163 | static 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 | |
| 172 | static 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 Welte | e61d459 | 2022-11-03 11:05:58 +0100 | [diff] [blame] | 195 | void print_foos(void) |
Neels Hofmeyr | 0e8df1c | 2019-02-11 20:32:25 +0100 | [diff] [blame] | 196 | { |
| 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 Welte | e61d459 | 2022-11-03 11:05:58 +0100 | [diff] [blame] | 207 | static void test_use_count_fsm(void) |
Neels Hofmeyr | 0e8df1c | 2019-02-11 20:32:25 +0100 | [diff] [blame] | 208 | { |
| 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 | |
| 300 | static const struct log_info_cat default_categories[] = { |
| 301 | [DFOO] = { |
| 302 | .name = "DFOO", |
| 303 | .description = "FOO", |
| 304 | .enabled = 1, .loglevel = LOGL_DEBUG, |
| 305 | }, |
| 306 | }; |
| 307 | |
| 308 | static const struct log_info log_info = { |
| 309 | .cat = default_categories, |
| 310 | .num_cat = ARRAY_SIZE(default_categories), |
| 311 | }; |
| 312 | |
| 313 | |
| 314 | int 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 Welte | ae5016f | 2019-12-01 13:38:20 +0100 | [diff] [blame] | 330 | OSMO_ASSERT(osmo_fsm_register(&foo_fsm) == 0); |
Neels Hofmeyr | 0e8df1c | 2019-02-11 20:32:25 +0100 | [diff] [blame] | 331 | |
| 332 | test_use_count_fsm(); |
| 333 | |
| 334 | return EXIT_SUCCESS; |
| 335 | } |