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
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);
+}