Add the thread.arena mallctl.

Make it possible for each thread to manage which arena it is associated
with.

Implement the 'tests' and 'check' build targets.
diff --git a/.gitignore b/.gitignore
index d9e49f8..8e95c7a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,4 +12,6 @@
 /jemalloc/src/jemalloc\.h
 /jemalloc/src/jemalloc_defs\.h
 /jemalloc/src/*.[od]
+/jemalloc/test/*.[od]
+/jemalloc/test/*.out
 /jemalloc/VERSION
diff --git a/jemalloc/Makefile.in b/jemalloc/Makefile.in
index ac9b782..a7acc96 100644
--- a/jemalloc/Makefile.in
+++ b/jemalloc/Makefile.in
@@ -47,6 +47,10 @@
 	@objroot@lib/libjemalloc@install_suffix@.so \
 	@objroot@lib/libjemalloc@install_suffix@_pic.a
 MAN3 := @objroot@doc/jemalloc@install_suffix@.3
+CTESTS :=
+ifeq (1, @enable_tls@)
+CTESTS += @srcroot@test/thread_arena.c
+endif
 
 .PHONY: all dist install check clean distclean relclean
 
@@ -69,12 +73,22 @@
 
 @objroot@lib/libjemalloc@install_suffix@.so.$(REV) : $(CSRCS:@srcroot@%.c=@objroot@%.o)
 	@mkdir -p $(@D)
-	$(CC) -shared -Wl,-soname,$(@F) -o $@ $+ $(LDFLAGS) $(LIBS)
+	$(CC) -shared -Wl,-soname,$(@F) $(RPATH_EXTRA:%=@RPATH@%) -o $@ $+ $(LDFLAGS) $(LIBS)
 
 @objroot@lib/libjemalloc@install_suffix@_pic.a : $(CSRCS:@srcroot@%.c=@objroot@%.o)
 	@mkdir -p $(@D)
 	ar crus $@ $+
 
+@objroot@test/%.o: @srcroot@test/%.c
+	@mkdir -p $(@D)
+	$(CC) $(CFLAGS) -c $(CPPFLAGS) -o $@ $<
+	@$(SHELL) -ec "$(CC) -MM $(CPPFLAGS) $< | sed \"s/\($(subst /,\/,$(notdir $(basename $@)))\)\.o\([ :]*\)/$(subst /,\/,$(strip $(dir $@)))\1.o \2/g\" > $(@:%.o=%.d)"
+
+@objroot@test/%: @objroot@test/%.o \
+		 @objroot@lib/libjemalloc@install_suffix@.so
+	@mkdir -p $(@D)
+	$(CC) -o $@ $< @RPATH@@objroot@lib -L@objroot@lib -ljemalloc
+
 install_bin:
 	install -d $(BINDIR)
 	@for b in $(BINS); do \
@@ -104,11 +118,43 @@
 
 install: install_bin install_include install_lib install_man
 
-check:
+tests: $(CTESTS:@srcroot@%.c=@objroot@%)
+
+check: tests
+	@mkdir -p @objroot@test
+	@$(SHELL) -c 'total=0; \
+		failures=0; \
+		echo "========================================="; \
+		for t in $(CTESTS:@srcroot@%.c=@objroot@%); do \
+			total=`expr $$total + 1`; \
+			/bin/echo -n "$${t} ... "; \
+			$${t} @abs_srcroot@ @abs_objroot@ \
+			  > @objroot@$${t}.out 2>&1; \
+			if test -e "@srcroot@$${t}.exp"; then \
+				diff -u @srcroot@$${t}.exp \
+				  @objroot@$${t}.out >/dev/null 2>&1; \
+				fail=$$?; \
+				if test "$${fail}" -eq "1" ; then \
+					failures=`expr $${failures} + 1`; \
+					echo "*** FAIL ***"; \
+				else \
+					echo "pass"; \
+				fi; \
+			else \
+				echo "*** FAIL *** (.exp file is missing)"; \
+				failures=`expr $${failures} + 1`; \
+			fi; \
+		done; \
+		echo "========================================="; \
+		echo "Failures: $${failures}/$${total}"'
 
 clean:
 	rm -f $(CSRCS:@srcroot@%.c=@objroot@%.o)
 	rm -f $(CSRCS:@srcroot@%.c=@objroot@%.d)
+	rm -f $(CTESTS:@srcroot@%.c=@objroot@%)
+	rm -f $(CTESTS:@srcroot@%.c=@objroot@%.o)
+	rm -f $(CTESTS:@srcroot@%.c=@objroot@%.d)
+	rm -f $(CTESTS:@srcroot@%.c=@objroot@%.out)
 	rm -f $(DSOS)
 
 distclean: clean
diff --git a/jemalloc/configure.ac b/jemalloc/configure.ac
index ce6e679..bf16596 100644
--- a/jemalloc/configure.ac
+++ b/jemalloc/configure.ac
@@ -647,9 +647,14 @@
               AC_MSG_RESULT([no])
               enable_tls="0")
 fi
+AC_SUBST([enable_tls])
 if test "x${enable_tls}" = "x0" ; then
   AC_DEFINE_UNQUOTED([NO_TLS], [ ])
+  roff_tls=".\\\" "
+else
+  roff_tls=""
 fi
+AC_SUBST([roff_tls])
 
 dnl Finish tcache-related definitions, now that TLS configuration is done.
 if test "x$enable_tls" = "x0" ; then
diff --git a/jemalloc/doc/jemalloc.3.in b/jemalloc/doc/jemalloc.3.in
index cf5cb5e..d2d5b77 100644
--- a/jemalloc/doc/jemalloc.3.in
+++ b/jemalloc/doc/jemalloc.3.in
@@ -669,6 +669,19 @@
 @roff_tcache@find manual flushing useful.
 .Ed
 .\"-----------------------------------------------------------------------------
+@roff_tls@.It Sy "thread.arena (unsigned) rw"
+@roff_tls@.Bd -ragged -offset indent -compact
+@roff_tls@Get or set the arena associated with the calling thread.
+@roff_tls@The arena index must be less than the maximum number of arenas (see
+@roff_tls@the
+@roff_tls@.Dq arenas.narenas
+@roff_tls@mallctl).
+@roff_tls@If the specified arena was not initialized beforehand (see the
+@roff_tls@.Dq arenas.initialized
+@roff_tls@mallctl), it will be automatically initialized as a side effect of
+@roff_tls@calling this interface.
+@roff_tls@.Ed
+.\"-----------------------------------------------------------------------------
 .It Sy "config.debug (bool) r-"
 .Bd -ragged -offset indent -compact
 --enable-debug was specified during build configuration.
diff --git a/jemalloc/src/ctl.c b/jemalloc/src/ctl.c
index ffb732d..128883f 100644
--- a/jemalloc/src/ctl.c
+++ b/jemalloc/src/ctl.c
@@ -41,6 +41,9 @@
 #ifdef JEMALLOC_TCACHE
 CTL_PROTO(tcache_flush)
 #endif
+#ifndef NO_TLS
+CTL_PROTO(thread_arena)
+#endif
 CTL_PROTO(config_debug)
 CTL_PROTO(config_dss)
 CTL_PROTO(config_dynamic_page_shift)
@@ -210,6 +213,12 @@
 };
 #endif
 
+#ifndef NO_TLS
+static const ctl_node_t	thread_node[] = {
+	{NAME("arena"),		CTL(thread_arena)}
+};
+#endif
+
 static const ctl_node_t	config_node[] = {
 	{NAME("debug"),			CTL(config_debug)},
 	{NAME("dss"),			CTL(config_dss)},
@@ -448,6 +457,9 @@
 #ifdef JEMALLOC_TCACHE
 	{NAME("tcache"),	CHILD(tcache)},
 #endif
+#ifndef NO_TLS
+	{NAME("thread"),	CHILD(thread)},
+#endif
 	{NAME("config"),	CHILD(config)},
 	{NAME("opt"),		CHILD(opt)},
 	{NAME("arenas"),	CHILD(arenas)},
@@ -1042,6 +1054,46 @@
 }
 #endif
 
+#ifndef NO_TLS
+static int
+thread_arena_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
+    void *newp, size_t newlen)
+{
+	int ret;
+	unsigned newind, oldind;
+
+	newind = oldind = choose_arena()->ind;
+	WRITE(oldind, unsigned);
+	READ(newind, unsigned);
+	if (newind != oldind) {
+		arena_t *arena;
+
+		if (newind >= narenas) {
+			/* New arena index is out of range. */
+			ret = EFAULT;
+			goto RETURN;
+		}
+
+		/* Initialize arena if necessary. */
+		malloc_mutex_lock(&arenas_lock);
+		if ((arena = arenas[newind]) == NULL)
+			arena = arenas_extend(newind);
+		malloc_mutex_unlock(&arenas_lock);
+		if (arena == NULL) {
+			ret = EAGAIN;
+			goto RETURN;
+		}
+
+		/* Set new arena association. */
+		arenas_map = arena;
+	}
+
+	ret = 0;
+RETURN:
+	return (ret);
+}
+#endif
+
 /******************************************************************************/
 
 #ifdef JEMALLOC_DEBUG
diff --git a/jemalloc/test/thread_arena.c b/jemalloc/test/thread_arena.c
new file mode 100644
index 0000000..99e9669
--- /dev/null
+++ b/jemalloc/test/thread_arena.c
@@ -0,0 +1,69 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <string.h>
+
+#include "jemalloc/jemalloc.h"
+
+void *
+thread_start(void *arg)
+{
+	unsigned main_arena_ind = *(unsigned *)arg;
+	unsigned arena_ind;
+	size_t size;
+	int err;
+
+	malloc(1);
+
+	size = sizeof(arena_ind);
+	if ((err = mallctl("thread.arena", &arena_ind, &size,
+	    &main_arena_ind, sizeof(main_arena_ind)))) {
+		fprintf(stderr, "%s(): Error in mallctl(): %s\n", __func__,
+		    strerror(err));
+		return (void *)1;
+	}
+
+	return (NULL);
+}
+
+int
+main(void)
+{
+	int ret = 0;
+	unsigned arena_ind;
+	size_t size;
+	int err;
+	pthread_t thread;
+
+	fprintf(stderr, "Test begin\n");
+
+	malloc(1);
+
+	size = sizeof(arena_ind);
+	if ((err = mallctl("thread.arena", &arena_ind, &size, NULL, 0))) {
+		fprintf(stderr, "%s(): Error in mallctl(): %s\n", __func__,
+		    strerror(err));
+		ret = 1;
+		goto RETURN;
+	}
+
+	if (pthread_create(&thread, NULL, thread_start, (void *)&arena_ind)
+	    != 0) {
+		fprintf(stderr, "%s(): Error in pthread_create()\n", __func__);
+		ret = 1;
+		goto RETURN;
+	}
+	pthread_join(thread, (void *)&ret);
+
+	if (pthread_create(&thread, NULL, thread_start, (void *)&arena_ind)
+	    != 0) {
+		fprintf(stderr, "%s(): Error in pthread_create()\n", __func__);
+		ret = 1;
+		goto RETURN;
+	}
+	pthread_join(thread, (void *)&ret);
+
+RETURN:
+	fprintf(stderr, "Test end\n");
+	return (ret);
+}
diff --git a/jemalloc/test/thread_arena.exp b/jemalloc/test/thread_arena.exp
new file mode 100644
index 0000000..369a88d
--- /dev/null
+++ b/jemalloc/test/thread_arena.exp
@@ -0,0 +1,2 @@
+Test begin
+Test end