aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2019-03-18 17:17:43 +0100
committerHarald Welte <laforge@gnumonks.org>2019-06-04 11:01:21 +0200
commit1aa350a069512c486e78535780e4ece8dba4d724 (patch)
treebb0f92b6d680a7db908a917fa470d95aeaada030
parent171ef826e1489031bc48745f29fa2d4657bf165f (diff)
context: Add support for [per-thread] global talloc contexts
Rather than having applications maintain their own talloc cotexts, let's offer some root talloc contexts in libosmocore. Let's also make them per thread right from the beginning. This will help some multi-threaded applications to use talloc in a thread-safe way. Change-Id: Iae39cd57274bf6753ecaf186f229e582b42662e3
-rw-r--r--include/osmocom/core/select.h1
-rw-r--r--include/osmocom/core/talloc.h28
-rw-r--r--src/Makefile.am2
-rw-r--r--src/context.c51
-rw-r--r--src/select.c43
-rw-r--r--tests/Makefile.am5
-rw-r--r--tests/context/context_test.c84
-rw-r--r--tests/context/context_test.ok4
-rw-r--r--tests/testsuite.at6
9 files changed, 214 insertions, 10 deletions
diff --git a/include/osmocom/core/select.h b/include/osmocom/core/select.h
index e4787b0..a200b6f 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 191a463..c68a56c 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 e65e0c9..714b5ea 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 0000000..eade74c
--- /dev/null
+++ b/src/context.c
@@ -0,0 +1,51 @@
+/*! \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>
+
+__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_ctx_init("main");
+}
+
+/*! @} */
diff --git a/src/select.c b/src/select.c
index 7ce135f..d7679e9 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, bool volatile_ctx)
{
fd_set readset, writeset, exceptset;
int rc;
@@ -259,8 +257,41 @@ 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);
+ rc = osmo_fd_disp_fds(&readset, &writeset, &exceptset);
+
+ if (volatile_ctx) {
+ /* free all the children of the volatile 'select' scope context */
+ talloc_free_children(osmo_ctx->select);
+ } else{
+ if (talloc_total_size(osmo_ctx->select) != 0) {
+ LOGP(DLGLOBAL, LOGL_FATAL, "You cannot use the 'select' volatile "
+ "context if you don't use osmo_select_main_ctx()!\n");
+ OSMO_ASSERT(0);
+ }
+ }
+
+ return rc;
+}
+
+/*! 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)
+{
+ return _osmo_select_main(polling, false);
+}
+
+/*! 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)
+{
+ return _osmo_select_main(polling, true);
}
/*! find an osmo_fd based on the integer fd
diff --git a/tests/Makefile.am b/tests/Makefile.am
index a8a06c5..92edf75 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 0000000..e9a5559
--- /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 0000000..3984a39
--- /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 0fc9646..a043f0c 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