diff options
-rw-r--r-- | include/osmocom/core/select.h | 1 | ||||
-rw-r--r-- | include/osmocom/core/talloc.h | 28 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/context.c | 52 | ||||
-rw-r--r-- | src/select.c | 40 | ||||
-rw-r--r-- | tests/Makefile.am | 5 | ||||
-rw-r--r-- | tests/context/context_test.c | 84 | ||||
-rw-r--r-- | tests/context/context_test.ok | 4 | ||||
-rw-r--r-- | tests/testsuite.at | 6 |
9 files changed, 213 insertions, 9 deletions
diff --git a/include/osmocom/core/select.h b/include/osmocom/core/select.h index e4787b09..a200b6f3 100644 --- a/include/osmocom/core/select.h +++ b/include/osmocom/core/select.h @@ -51,6 +51,7 @@ int osmo_fd_register(struct osmo_fd *fd); void osmo_fd_unregister(struct osmo_fd *fd); void osmo_fd_close(struct osmo_fd *fd); int osmo_select_main(int polling); +int osmo_select_main_ctx(int polling); struct osmo_fd *osmo_fd_get_by_fd(int fd); diff --git a/include/osmocom/core/talloc.h b/include/osmocom/core/talloc.h index 191a463f..c68a56cf 100644 --- a/include/osmocom/core/talloc.h +++ b/include/osmocom/core/talloc.h @@ -1,5 +1,27 @@ -/*! \file talloc.h - * Convenience wrapper. libosmocore used to ship its own internal copy of - * talloc, before libtalloc became a standard component on most systems */ +/*! \file talloc.h */ #pragma once #include <talloc.h> + +/*! per-thread talloc contexts. This works around the problem that talloc is not + * thread-safe. However, one can simply have a different set of talloc contexts for each + * thread, and ensure that allocations made on one thread are always only free'd on that + * very same thread. + * WARNING: Users must make sure they free() on the same thread as they allocate!! */ +struct osmo_talloc_contexts { + /*! global per-thread talloc context. */ + void *global; + /*! volatile select-dispatch context. This context is completely free'd and + * re-created every time the main select loop in osmo_select_main() returns from + * select(2) and calls per-fd callback functions. This allows users of this + * facility to allocate temporary objects like string buffers, message buffers + * and the like which are automatically free'd when going into the next select() + * system call */ + void *select; +}; + +extern __thread struct osmo_talloc_contexts *osmo_ctx; + +/* short-hand #defines for the osmo talloc contexts (OTC) that can be used to pass + * to the various _c functions like msgb_alloc_c() */ +#define OTC_GLOBAL (osmo_ctx->global) +#define OTC_SELECT (osmo_ctx->select) diff --git a/src/Makefile.am b/src/Makefile.am index f9378100..245eb6d5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -13,7 +13,7 @@ endif lib_LTLIBRARIES = libosmocore.la libosmocore_la_LIBADD = $(BACKTRACE_LIB) $(TALLOC_LIBS) $(LIBRARY_RT) -libosmocore_la_SOURCES = timer.c timer_gettimeofday.c timer_clockgettime.c \ +libosmocore_la_SOURCES = context.c timer.c timer_gettimeofday.c timer_clockgettime.c \ select.c signal.c msgb.c bits.c \ bitvec.c bitcomp.c counter.c fsm.c \ write_queue.c utils.c socket.c \ diff --git a/src/context.c b/src/context.c new file mode 100644 index 00000000..bad012bd --- /dev/null +++ b/src/context.c @@ -0,0 +1,52 @@ +/*! \file context.c + * talloc context handling. + * + * (C) 2019 by Harald Welte <laforge@gnumonks.org> + * All Rights Reserverd. + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ +#include <string.h> +#include <errno.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/utils.h> + +__thread struct osmo_talloc_contexts *osmo_ctx; + +int osmo_ctx_init(const char *id) +{ + osmo_ctx = talloc_named(NULL, sizeof(*osmo_ctx), "global-%s", id); + if (!osmo_ctx) + return -ENOMEM; + memset(osmo_ctx, 0, sizeof(*osmo_ctx)); + osmo_ctx->global = osmo_ctx; + osmo_ctx->select = talloc_named_const(osmo_ctx->global, 0, "select"); + if (!osmo_ctx->select) { + talloc_free(osmo_ctx); + return -ENOMEM; + } + return 0; +} + +/* initialize osmo_ctx on main tread */ +static __attribute__((constructor)) void on_dso_load_ctx(void) +{ + OSMO_ASSERT(osmo_ctx_init("main") == 0); +} + +/*! @} */ diff --git a/src/select.c b/src/select.c index 7ce135f5..394a59dd 100644 --- a/src/select.c +++ b/src/select.c @@ -36,6 +36,8 @@ #include <osmocom/core/linuxlist.h> #include <osmocom/core/timer.h> #include <osmocom/core/logging.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/utils.h> #include "../config.h" @@ -233,11 +235,7 @@ restart: return work; } -/*! select main loop integration - * \param[in] polling should we pollonly (1) or block on select (0) - * \returns 0 if no fd handled; 1 if fd handled; negative in case of error - */ -int osmo_select_main(int polling) +static int _osmo_select_main(int polling) { fd_set readset, writeset, exceptset; int rc; @@ -259,10 +257,42 @@ int osmo_select_main(int polling) /* fire timers */ osmo_timers_update(); + OSMO_ASSERT(osmo_ctx->select); + /* call registered callback functions */ return osmo_fd_disp_fds(&readset, &writeset, &exceptset); } +/*! select main loop integration + * \param[in] polling should we pollonly (1) or block on select (0) + * \returns 0 if no fd handled; 1 if fd handled; negative in case of error + */ +int osmo_select_main(int polling) +{ + int rc = _osmo_select_main(polling); +#ifndef EMBEDDED + if (talloc_total_size(osmo_ctx->select) != 0) { + osmo_panic("You cannot use the 'select' volatile " + "context if you don't use osmo_select_main_ctx()!\n"); + } +#endif + return rc; +} + +#ifndef EMBEDDED +/*! select main loop integration with temporary select-dispatch talloc context + * \param[in] polling should we pollonly (1) or block on select (0) + * \returns 0 if no fd handled; 1 if fd handled; negative in case of error + */ +int osmo_select_main_ctx(int polling) +{ + int rc = _osmo_select_main(polling); + /* free all the children of the volatile 'select' scope context */ + talloc_free_children(osmo_ctx->select); + return rc; +} +#endif + /*! find an osmo_fd based on the integer fd * \param[in] fd file descriptor to use as search key * \returns \ref osmo_fd for \ref fd; NULL in case it doesn't exist */ diff --git a/tests/Makefile.am b/tests/Makefile.am index a8a06c53..92edf752 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -32,6 +32,7 @@ check_PROGRAMS = timer/timer_test sms/sms_test ussd/ussd_test \ tdef/tdef_vty_test_dynamic \ sockaddr_str/sockaddr_str_test \ use_count/use_count_test \ + context/context_test \ $(NULL) if ENABLE_MSGFILE @@ -251,6 +252,9 @@ sockaddr_str_sockaddr_str_test_LDADD = $(LDADD) use_count_use_count_test_SOURCES = use_count/use_count_test.c use_count_use_count_test_LDADD = $(LDADD) +context_context_test_SOURCES = context/context_test.c +context_context_test_LDADD = $(LDADD) + # The `:;' works around a Bash 3.2 bug when the output is not writeable. $(srcdir)/package.m4: $(top_srcdir)/configure.ac :;{ \ @@ -322,6 +326,7 @@ EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE) \ tdef/tdef_vty_test_dynamic.vty \ sockaddr_str/sockaddr_str_test.ok \ use_count/use_count_test.ok use_count/use_count_test.err \ + context/context_test.ok \ $(NULL) DISTCLEANFILES = atconfig atlocal conv/gsm0503_test_vectors.c diff --git a/tests/context/context_test.c b/tests/context/context_test.c new file mode 100644 index 00000000..e9a55593 --- /dev/null +++ b/tests/context/context_test.c @@ -0,0 +1,84 @@ +#include <stdint.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> + +#include <sys/eventfd.h> + +#include <osmocom/core/logging.h> +#include <osmocom/core/application.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/select.h> +#include <osmocom/core/talloc.h> + +static struct osmo_fd g_evfd; + +static void *alloc_res_select; +static void *alloc_res_global; + +static int destructor_called; + +static int talloc_destructor(void *ptr) +{ + printf("destructor was called automatically\n"); + /* ensure the destructor is only called for the chunk allocated from the + * volatile select context */ + OSMO_ASSERT(ptr == alloc_res_select); + destructor_called += 1; + return 0; +} + +static int evfd_cb(struct osmo_fd *ofd, unsigned int what) +{ + uint64_t rval; + int rc; + + rc = read(ofd->fd, &rval, sizeof(rval)); + OSMO_ASSERT(rc == sizeof(rval)); + + printf("allocating from select context\n"); + alloc_res_select = talloc_named_const(OTC_SELECT, 23, "alloc_select"); + OSMO_ASSERT(alloc_res_select); + talloc_set_destructor(alloc_res_select, talloc_destructor); + + printf("allocating from global context\n"); + alloc_res_global = talloc_named_const(OTC_GLOBAL, 42, "alloc_global"); + OSMO_ASSERT(alloc_res_global); + talloc_set_destructor(alloc_res_global, talloc_destructor); + return 0; +} + +const struct log_info_cat default_categories[] = { +}; + +static struct log_info info = { + .cat = default_categories, + .num_cat = ARRAY_SIZE(default_categories), +}; + +int main(int argc, char **argv) +{ + int rc; + + osmo_init_logging2(OTC_GLOBAL, &info); + + rc = eventfd(0, 0); + OSMO_ASSERT(rc >= 0); + osmo_fd_setup(&g_evfd, rc, OSMO_FD_READ, evfd_cb, NULL, 0); + osmo_fd_register(&g_evfd); + + /* make sure the select loop will immediately call the callback */ + uint64_t val = 1; + rc = write(g_evfd.fd, &val, sizeof(val)); + OSMO_ASSERT(rc == sizeof(val)); + + /* enter osmo_select_main_ctx() once */ + printf("entering osmo_select_main\n"); + osmo_select_main_ctx(1); + + /* the allocation must have happened, and the destructor must have been called + * automatically exactly once */ + OSMO_ASSERT(destructor_called == 1); + + exit(0); +} diff --git a/tests/context/context_test.ok b/tests/context/context_test.ok new file mode 100644 index 00000000..3984a397 --- /dev/null +++ b/tests/context/context_test.ok @@ -0,0 +1,4 @@ +entering osmo_select_main +allocating from select context +allocating from global context +destructor was called automatically diff --git a/tests/testsuite.at b/tests/testsuite.at index 0fc96467..a043f0c7 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -350,3 +350,9 @@ cat $abs_srcdir/use_count/use_count_test.ok > expout cat $abs_srcdir/use_count/use_count_test.err > experr AT_CHECK([$abs_top_builddir/tests/use_count/use_count_test], [0], [expout], [experr]) AT_CLEANUP + +AT_SETUP([context]) +AT_KEYWORDS([context]) +cat $abs_srcdir/context/context_test.ok > expout +AT_CHECK([$abs_top_builddir/tests/context/context_test], [0], [expout], [ignore]) +AT_CLEANUP |