summaryrefslogtreecommitdiffstats
path: root/lib/libthr
diff options
context:
space:
mode:
authorsjg <sjg@FreeBSD.org>2014-11-19 01:07:58 +0000
committersjg <sjg@FreeBSD.org>2014-11-19 01:07:58 +0000
commitb137080f19736ee33fede2e88bb54438604cf86b (patch)
tree377ac0ac449528621eb192cd245adadb5fd53668 /lib/libthr
parentab21a29eb607d4dfe389b965fbdee27558e791aa (diff)
parent4a8d07956d121238d006d34ffe7d6269744e8b1a (diff)
downloadFreeBSD-src-b137080f19736ee33fede2e88bb54438604cf86b.zip
FreeBSD-src-b137080f19736ee33fede2e88bb54438604cf86b.tar.gz
Merge from head@274682
Diffstat (limited to 'lib/libthr')
-rw-r--r--lib/libthr/Makefile2
-rw-r--r--lib/libthr/Makefile.amd646
-rw-r--r--lib/libthr/Makefile.i3866
-rw-r--r--lib/libthr/libthr.3223
-rw-r--r--lib/libthr/tests/Makefile58
-rw-r--r--lib/libthr/tests/dlopen/Makefile30
-rw-r--r--lib/libthr/tests/dlopen/dso/Makefile19
-rw-r--r--lib/libthr/thread/thr_cond.c2
-rw-r--r--lib/libthr/thread/thr_init.c11
-rw-r--r--lib/libthr/thread/thr_stack.c7
10 files changed, 358 insertions, 6 deletions
diff --git a/lib/libthr/Makefile b/lib/libthr/Makefile
index cfcc41e..5cbd0aa 100644
--- a/lib/libthr/Makefile
+++ b/lib/libthr/Makefile
@@ -64,4 +64,6 @@ SYMLINKS+=lib${LIB}_p.a ${LIBDIR}/libpthread_p.a
CFLAGS+=-DSYSCALL_COMPAT
.endif
+.include <bsd.arch.inc.mk>
+
.include <bsd.lib.mk>
diff --git a/lib/libthr/Makefile.amd64 b/lib/libthr/Makefile.amd64
new file mode 100644
index 0000000..dd0f5b0
--- /dev/null
+++ b/lib/libthr/Makefile.amd64
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+.if ${MK_TESTS} != "no"
+SUBDIR+= tests
+.endif
+
diff --git a/lib/libthr/Makefile.i386 b/lib/libthr/Makefile.i386
new file mode 100644
index 0000000..dd0f5b0
--- /dev/null
+++ b/lib/libthr/Makefile.i386
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+.if ${MK_TESTS} != "no"
+SUBDIR+= tests
+.endif
+
diff --git a/lib/libthr/libthr.3 b/lib/libthr/libthr.3
index bfbebec..4b636ce 100644
--- a/lib/libthr/libthr.3
+++ b/lib/libthr/libthr.3
@@ -1,6 +1,11 @@
.\" Copyright (c) 2005 Robert N. M. Watson
+.\" Copyright (c) 2014 The FreeBSD Foundation, Inc.
.\" All rights reserved.
.\"
+.\" Part of this documentation was written by
+.\" Konstantin Belousov <kib@FreeBSD.org> under sponsorship
+.\" from the FreeBSD Foundation.
+.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
@@ -24,7 +29,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd October 19, 2007
+.Dd September 26, 2014
.Dt LIBTHR 3
.Os
.Sh NAME
@@ -45,8 +50,222 @@ has been optimized for use by applications expecting system scope thread
semantics, and can provide significant performance improvements
compared to
.Lb libkse .
+.Pp
+The library is tightly integrated with the run-time link editor
+.Xr ld-elf.so.1 1
+and
+.Lb libc ;
+all three components must be built from the same source tree.
+Mixing
+.Li libc
+and
+.Nm
+libraries from different versions of
+.Fx
+is not supported.
+The run-time linker
+.Xr ld-elf.so.1 1
+has some code to ensure backward-compatibility with older versions of
+.Nm .
+.Pp
+The man page documents the quirks and tunables of the
+.Nm .
+When linking with
+.Li -lpthread ,
+the run-time dependency
+.Li libthr.so.3
+is recorded in the produced object.
+.Sh MUTEX ACQUISITION
+A locked mutex (see
+.Xr pthread_mutex_lock 3 )
+is represented by a volatile variable of type
+.Dv lwpid_t ,
+which records the global system identifier of the thread
+owning the lock.
+.Nm
+performs a contested mutex acquisition in three stages, each of which
+is more resource-consuming than the previous.
+The first two stages are only applied for a mutex of
+.Dv PTHREAD_MUTEX_ADAPTIVE_NP
+type and
+.Dv PTHREAD_PRIO_NONE
+protocol (see
+.Xr pthread_mutexattr 3 ) .
+.Pp
+First, on SMP systems, a spin loop
+is performed, where the library attempts to acquire the lock by
+.Xr atomic 9
+operations.
+The loop count is controlled by the
+.Ev LIBPTHREAD_SPINLOOPS
+environment variable, with a default value of 2000.
+.Pp
+If the spin loop
+was unable to acquire the mutex, a yield loop
+is executed, performing the same
+.Xr atomic 9
+acquisition attempts as the spin loop,
+but each attempt is followed by a yield of the CPU time
+of the thread using the
+.Xr sched_yield 2
+syscall.
+By default, the yield loop
+is not executed.
+This is controlled by the
+.Ev LIBPTHREAD_YIELDLOOPS
+environment variable.
+.Pp
+If both the spin and yield loops
+failed to acquire the lock, the thread is taken off the CPU and
+put to sleep in the kernel with the
+.Xr umtx 2
+syscall.
+The kernel wakes up a thread and hands the ownership of the lock to
+the woken thread when the lock becomes available.
+.Sh THREAD STACKS
+Each thread is provided with a private user-mode stack area
+used by the C runtime.
+The size of the main (initial) thread stack is set by the kernel, and is
+controlled by the
+.Dv RLIMIT_STACK
+process resource limit (see
+.Xr getrlimit 2 ) .
+.Pp
+By default, the main thread's stack size is equal to the value of
+.Dv RLIMIT_STACK
+for the process.
+If the
+.Ev LIBPTHREAD_SPLITSTACK_MAIN
+environment variable is present in the process environment
+(its value does not matter),
+the main thread's stack is reduced to 4MB on 64bit architectures, and to
+2MB on 32bit architectures, when the threading library is initialized.
+The rest of the address space area which has been reserved by the
+kernel for the initial process stack is used for non-initial thread stacks
+in this case.
+The presence of the
+.Ev LIBPTHREAD_BIGSTACK_MAIN
+environment variable overrides
+.Ev LIBPTHREAD_SPLITSTACK_MAIN ;
+it is kept for backward-compatibility.
+.Pp
+The size of stacks for threads created by the process at run-time
+with the
+.Xr pthread_create 3
+call is controlled by thread attributes: see
+.Xr pthread_attr 3 ,
+in particular, the
+.Xr pthread_attr_setstacksize 3 ,
+.Xr pthread_attr_setguardsize 3
+and
+.Xr pthread_attr_setstackaddr 3
+functions.
+If no attributes for the thread stack size are specified, the default
+non-initial thread stack size is 2MB for 64bit architectures, and 1MB
+for 32bit architectures.
+.Sh RUN-TIME SETTINGS
+The following environment variables are recognized by
+.Nm
+and adjust the operation of the library at run-time:
+.Bl -tag -width LIBPTHREAD_SPLITSTACK_MAIN
+.It Ev LIBPTHREAD_BIGSTACK_MAIN
+Disables the reduction of the initial thread stack enabled by
+.Ev LIBPTHREAD_SPLITSTACK_MAIN .
+.It Ev LIBPTHREAD_SPLITSTACK_MAIN
+Causes a reduction of the initial thread stack, as described in the
+section
+.Sx THREAD STACKS .
+This was the default behaviour of
+.Nm
+before
+.Fx 11.0 .
+.It Ev LIBPTHREAD_SPINLOOPS
+The integer value of the variable overrides the default count of
+iterations in the
+.Li spin loop
+of the mutex acquisition.
+The default count is 2000, set by the
+.Dv MUTEX_ADAPTIVE_SPINS
+constant in the
+.Nm
+sources.
+.It Ev LIBPTHREAD_YIELDLOOPS
+A non-zero integer value enables the yield loop
+in the process of the mutex acquisition.
+The value is the count of loop operations.
+.It Ev LIBPTHREAD_QUEUE_FIFO
+The integer value of the variable specifies how often blocked
+threads are inserted at the head of the sleep queue, instead of its tail.
+Bigger values reduce the frequency of the FIFO discipline.
+The value must be between 0 and 255.
+.El
+.Sh INTERACTION WITH RUN-TIME LINKER
+The
+.Nm
+library must appear before
+.Li libc
+in the global order of depended objects.
+.Pp
+Loading
+.Nm
+with the
+.Xr dlopen 3
+call in the process after the program binary is activated
+is not supported, and causes miscellaneous and hard-to-diagnose misbehaviour.
+This is due to
+.Nm
+interposing several important
+.Li libc
+symbols to provide thread-safe services.
+In particular,
+.Dv errno
+and the locking stubs from
+.Li libc
+are affected.
+This requirement is currently not enforced.
+.Pp
+If the program loads any modules at run-time, and those modules may require
+threading services, the main program binary must be linked with
+.Li libpthread ,
+even if it does not require any services from the library.
+.Pp
+.Nm
+cannot be unloaded; the
+.Xr dlclose 3
+function does not perform any action when called with a handle for
+.Nm .
+One of the reasons is that the interposing of
+.Li libc
+functions cannot be undone.
+.Sh SIGNALS
+The implementation also interposes the user-installed
+.Xr signal 3
+handlers.
+This interposing is done to postpone signal delivery to threads which
+entered (libthr-internal) critical sections, where the calling
+of the user-provided signal handler is unsafe.
+An example of such a situation is owning the internal library lock.
+When a signal is delivered while the signal handler cannot be safely
+called, the call is postponed and performed until after the exit from
+the critical section.
+This should be taken into account when interpreting
+.Xr ktrace 1
+logs.
.Sh SEE ALSO
-.Xr pthread 3
+.Xr ktrace 1 ,
+.Xr ld-elf.so.1 1 ,
+.Xr getrlimit 2 ,
+.Xr umtx 2 ,
+.Xr dlclose 3 ,
+.Xr dlopen 3 ,
+.Xr errno 3 ,
+.Xr getenv 3 ,
+.Xr libc 3 ,
+.Xr pthread_attr 3 ,
+.Xr pthread_attr_setstacksize 3 ,
+.Xr pthread_create 3 ,
+.Xr signal 3 ,
+.Xr atomic 9
.Sh AUTHORS
.An -nosplit
The
diff --git a/lib/libthr/tests/Makefile b/lib/libthr/tests/Makefile
new file mode 100644
index 0000000..50f07f0
--- /dev/null
+++ b/lib/libthr/tests/Makefile
@@ -0,0 +1,58 @@
+# $FreeBSD$
+
+OBJTOP= ${.OBJDIR:H:H:H}
+SRCTOP= ${.CURDIR:H:H:H}
+TESTSRC= ${SRCTOP}/contrib/netbsd-tests/lib/libpthread
+
+TESTSDIR= ${TESTSBASE}/lib/libthr
+
+# TODO: t_name (missing pthread_getname_np support in FreeBSD)
+NETBSD_ATF_TESTS_C= barrier_test
+NETBSD_ATF_TESTS_C+= cond_test
+NETBSD_ATF_TESTS_C+= condwait_test
+NETBSD_ATF_TESTS_C+= detach_test
+NETBSD_ATF_TESTS_C+= equal_test
+NETBSD_ATF_TESTS_C+= fork_test
+NETBSD_ATF_TESTS_C+= fpu_test
+NETBSD_ATF_TESTS_C+= join_test
+NETBSD_ATF_TESTS_C+= kill_test
+NETBSD_ATF_TESTS_C+= mutex_test
+NETBSD_ATF_TESTS_C+= once_test
+NETBSD_ATF_TESTS_C+= preempt_test
+NETBSD_ATF_TESTS_C+= rwlock_test
+NETBSD_ATF_TESTS_C+= sem_test
+NETBSD_ATF_TESTS_C+= sigmask_test
+NETBSD_ATF_TESTS_C+= sigsuspend_test
+NETBSD_ATF_TESTS_C+= siglongjmp_test
+NETBSD_ATF_TESTS_C+= sleep_test
+NETBSD_ATF_TESTS_C+= swapcontext_test
+
+NETBSD_ATF_TESTS_SH= atexit_test
+NETBSD_ATF_TESTS_SH+= cancel_test
+NETBSD_ATF_TESTS_SH+= exit_test
+NETBSD_ATF_TESTS_SH+= resolv_test
+
+DPADD+= ${LIBPTHREAD}
+LDADD+= -lpthread
+DPADD.fpu_test+= ${LIBM}
+LDADD.fpu_test+= -lm
+DPADD.sem_test+= ${LIBRT}
+LDADD.sem_test+= -lrt
+
+BINDIR= ${TESTSDIR}
+
+PROGS= h_atexit
+PROGS+= h_cancel
+PROGS+= h_exit
+PROGS+= h_resolv
+
+FILESDIR= ${TESTSDIR}
+FILES= d_mach
+
+TESTS_SUBDIRS= dlopen
+
+.include <netbsd-tests.test.mk>
+
+CFLAGS.condwait_test+= -I${SRCTOP}/contrib/netbsd-tests/lib/libc/gen
+
+.include <bsd.test.mk>
diff --git a/lib/libthr/tests/dlopen/Makefile b/lib/libthr/tests/dlopen/Makefile
new file mode 100644
index 0000000..0764bfa
--- /dev/null
+++ b/lib/libthr/tests/dlopen/Makefile
@@ -0,0 +1,30 @@
+# $FreeBSD$
+
+OBJTOP= ${.OBJDIR:H:H:H:H}
+SRCTOP= ${.CURDIR:H:H:H:H}
+TESTSRC= ${SRCTOP}/contrib/netbsd-tests/lib/libpthread/dlopen
+
+.include <bsd.own.mk>
+
+TESTSDIR= ${TESTSBASE}/lib/libthr/dlopen
+
+CFLAGS+= -DTESTDIR=\"${TESTSDIR:Q}/\"
+LDFLAGS+= -L${.OBJDIR}/dso -Wl,-rpath=${TESTDIR}
+
+.if !defined(NO_PIC)
+SUBDIR+= dso
+
+NETBSD_ATF_TESTS_C= dlopen_test
+NETBSD_ATF_TESTS_C+= main_pthread_create_test
+# XXX: this blocks running the testcase
+#NETBSD_ATF_TESTS_C+= dso_pthread_create_test
+
+.for t in dlopen_test main_pthread_create_test
+DPADD.$t+= ${LIBPTHREAD}
+LDADD.$t+= -lpthread
+.endfor
+.endif
+
+.include <netbsd-tests.test.mk>
+
+.include <bsd.test.mk>
diff --git a/lib/libthr/tests/dlopen/dso/Makefile b/lib/libthr/tests/dlopen/dso/Makefile
new file mode 100644
index 0000000..080dec9
--- /dev/null
+++ b/lib/libthr/tests/dlopen/dso/Makefile
@@ -0,0 +1,19 @@
+# $FreeBSD$
+
+OBJTOP= ${.OBJDIR:H:H:H:H:H}
+SRCTOP= ${.CURDIR:H:H:H:H:H}
+TESTSRC= ${SRCTOP}/contrib/netbsd-tests/lib/libpthread/dlopen/dso
+
+SHLIB= h_pthread_dlopen
+SHLIB_MAJOR= 1
+SHLIB_NAME= h_pthread_dlopen.so.${SHLIB_MAJOR}
+SRCS= h_pthread_dlopen.c
+
+DPADD+= ${LIBPTHREAD}
+LDADD+= -lpthread
+
+LIBDIR= ${TESTSBASE}/lib/libthr/dlopen
+
+.include <netbsd-tests.test.mk>
+
+.include <bsd.lib.mk>
diff --git a/lib/libthr/thread/thr_cond.c b/lib/libthr/thread/thr_cond.c
index 6af15db..71b4293 100644
--- a/lib/libthr/thread/thr_cond.c
+++ b/lib/libthr/thread/thr_cond.c
@@ -150,7 +150,7 @@ _pthread_cond_destroy(pthread_cond_t *cond)
}
/*
- * Cancellation behaivor:
+ * Cancellation behavior:
* Thread may be canceled at start, if thread is canceled, it means it
* did not get a wakeup from pthread_cond_signal(), otherwise, it is
* not canceled.
diff --git a/lib/libthr/thread/thr_init.c b/lib/libthr/thread/thr_init.c
index 937d83f..6d6a532 100644
--- a/lib/libthr/thread/thr_init.c
+++ b/lib/libthr/thread/thr_init.c
@@ -37,6 +37,7 @@
#include <sys/types.h>
#include <sys/signalvar.h>
#include <sys/ioctl.h>
+#include <sys/resource.h>
#include <sys/sysctl.h>
#include <sys/ttycom.h>
#include <sys/mman.h>
@@ -441,9 +442,10 @@ init_main_thread(struct pthread *thread)
static void
init_private(void)
{
+ struct rlimit rlim;
size_t len;
int mib[2];
- char *env;
+ char *env, *env_bigstack, *env_splitstack;
_thr_umutex_init(&_mutex_static_lock);
_thr_umutex_init(&_cond_static_lock);
@@ -471,6 +473,13 @@ init_private(void)
len = sizeof (_usrstack);
if (sysctl(mib, 2, &_usrstack, &len, NULL, 0) == -1)
PANIC("Cannot get kern.usrstack from sysctl");
+ env_bigstack = getenv("LIBPTHREAD_BIGSTACK_MAIN");
+ env_splitstack = getenv("LIBPTHREAD_SPLITSTACK_MAIN");
+ if (env_bigstack != NULL || env_splitstack == NULL) {
+ if (getrlimit(RLIMIT_STACK, &rlim) == -1)
+ PANIC("Cannot get stack rlimit");
+ _thr_stack_initial = rlim.rlim_cur;
+ }
len = sizeof(_thr_is_smp);
sysctlbyname("kern.smp.cpus", &_thr_is_smp, &len, NULL, 0);
_thr_is_smp = (_thr_is_smp > 1);
diff --git a/lib/libthr/thread/thr_stack.c b/lib/libthr/thread/thr_stack.c
index 15a9c82..e5d149e 100644
--- a/lib/libthr/thread/thr_stack.c
+++ b/lib/libthr/thread/thr_stack.c
@@ -246,7 +246,10 @@ _thr_stack_alloc(struct pthread_attr *attr)
THREAD_LIST_UNLOCK(curthread);
}
else {
- /* Allocate a stack from usrstack. */
+ /*
+ * Allocate a stack from or below usrstack, depending
+ * on the LIBPTHREAD_BIGSTACK_MAIN env variable.
+ */
if (last_stack == NULL)
last_stack = _usrstack - _thr_stack_initial -
_thr_guard_default;
@@ -268,7 +271,7 @@ _thr_stack_alloc(struct pthread_attr *attr)
/* Map the stack and guard page together, and split guard
page from allocated space: */
- if ((stackaddr = mmap(stackaddr, stacksize+guardsize,
+ if ((stackaddr = mmap(stackaddr, stacksize + guardsize,
_rtld_get_stack_prot(), MAP_STACK,
-1, 0)) != MAP_FAILED &&
(guardsize == 0 ||
OpenPOWER on IntegriCloud