diff options
author | Samuel Thibault <samuel.thibault@ens-lyon.org> | 2018-04-02 01:43:22 +0200 |
---|---|---|
committer | Samuel Thibault <samuel.thibault@ens-lyon.org> | 2018-04-02 01:44:14 +0200 |
commit | 33574c17eefcf326c1cd30eb9f915ad26d3d9ef2 (patch) | |
tree | 2c1b143f2b7a0ceba9fba000b70dd2889d56e60f /htl | |
parent | 03e2aa50fd512449025bba8c244d16338d8526a4 (diff) | |
download | glibc-33574c17eefcf326c1cd30eb9f915ad26d3d9ef2.tar.gz glibc-33574c17eefcf326c1cd30eb9f915ad26d3d9ef2.tar.xz glibc-33574c17eefcf326c1cd30eb9f915ad26d3d9ef2.zip |
hurd: Add hurd thread library
Contributed by Agustina Arzille <avarzille@riseup.net> Amos Jeffries <squid3@treenet.co.nz> David Michael <fedora.dm0@gmail.com> Marco Gerards <marco@gnu.org> Marcus Brinkmann <marcus@gnu.org> Neal H. Walfield <neal@gnu.org> Pino Toscano <toscano.pino@tiscali.it> Richard Braun <rbraun@sceen.net> Roland McGrath <roland@gnu.org> Samuel Thibault <samuel.thibault@ens-lyon.org> Thomas DiModica <ricinwich@yahoo.com> Thomas Schwinge <tschwinge@gnu.org> * htl: New directory. * sysdeps/htl: New directory. * sysdeps/hurd/htl: New directory. * sysdeps/i386/htl: New directory. * sysdeps/mach/htl: New directory. * sysdeps/mach/hurd/htl: New directory. * sysdeps/mach/hurd/i386/htl: New directory. * nscd/Depend, resolv/Depend, rt/Depend: Add htl dependency. * sysdeps/mach/hurd/i386/Implies: Add mach/hurd/i386/htl imply. * sysdeps/mach/hurd/i386/libpthread.abilist: New file.
Diffstat (limited to 'htl')
50 files changed, 4126 insertions, 0 deletions
diff --git a/htl/Makefile b/htl/Makefile new file mode 100644 index 0000000000..3bde5f0800 --- /dev/null +++ b/htl/Makefile @@ -0,0 +1,237 @@ +# +# Copyright (C) 1994-2018 Free Software Foundation, Inc. +# +# 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +subdir := htl + +srcdir = . + +MICROKERNEL := mach +SYSDEPS := lockfile + +LCLHDRS := + +libpthread-routines := pt-attr pt-attr-destroy pt-attr-getdetachstate \ + pt-attr-getguardsize pt-attr-getinheritsched \ + pt-attr-getschedparam pt-attr-getschedpolicy pt-attr-getscope \ + pt-attr-getstack pt-attr-getstackaddr pt-attr-getstacksize \ + pt-attr-init pt-attr-setdetachstate pt-attr-setguardsize \ + pt-attr-setinheritsched pt-attr-setschedparam \ + pt-attr-setschedpolicy pt-attr-setscope pt-attr-setstack \ + pt-attr-setstackaddr pt-attr-setstacksize \ + \ + pt-barrier-destroy pt-barrier-init pt-barrier-wait \ + pt-barrier pt-barrierattr-destroy pt-barrierattr-init \ + pt-barrierattr-getpshared pt-barrierattr-setpshared \ + \ + pt-destroy-specific pt-init-specific \ + pt-key-create pt-key-delete \ + pt-getspecific pt-setspecific \ + \ + pt-once \ + \ + pt-alloc \ + pt-create \ + pt-getattr \ + pt-equal \ + pt-dealloc \ + pt-detach \ + pt-exit \ + pt-initialize \ + pt-join \ + pt-self \ + pt-sigmask \ + pt-spin-inlines \ + pt-cleanup \ + pt-setcancelstate \ + pt-setcanceltype \ + pt-testcancel \ + pt-cancel \ + \ + pt-mutexattr \ + pt-mutexattr-destroy pt-mutexattr-init \ + pt-mutexattr-getprioceiling pt-mutexattr-getprotocol \ + pt-mutexattr-getpshared pt-mutexattr-gettype \ + pt-mutexattr-setprioceiling pt-mutexattr-setprotocol \ + pt-mutexattr-setpshared pt-mutexattr-settype \ + pt-mutexattr-getrobust pt-mutexattr-setrobust \ + \ + pt-mutex-init pt-mutex-destroy \ + pt-mutex-lock pt-mutex-trylock pt-mutex-timedlock \ + pt-mutex-unlock \ + pt-mutex-transfer-np \ + pt-mutex-getprioceiling pt-mutex-setprioceiling \ + pt-mutex-consistent \ + \ + pt-rwlock-attr \ + pt-rwlockattr-init pt-rwlockattr-destroy \ + pt-rwlockattr-getpshared pt-rwlockattr-setpshared \ + \ + pt-rwlock-init pt-rwlock-destroy \ + pt-rwlock-rdlock pt-rwlock-tryrdlock \ + pt-rwlock-trywrlock pt-rwlock-wrlock \ + pt-rwlock-timedrdlock pt-rwlock-timedwrlock \ + pt-rwlock-unlock \ + \ + pt-cond \ + pt-condattr-init pt-condattr-destroy \ + pt-condattr-getclock pt-condattr-getpshared \ + pt-condattr-setclock pt-condattr-setpshared \ + \ + pt-cond-destroy pt-cond-init \ + pt-cond-brdcast \ + pt-cond-signal \ + pt-cond-wait \ + pt-cond-timedwait \ + pt-hurd-cond-wait \ + pt-hurd-cond-timedwait \ + \ + pt-stack-alloc \ + pt-thread-alloc \ + pt-thread-start \ + pt-thread-terminate \ + pt-startup \ + \ + pt-getconcurrency pt-setconcurrency \ + \ + pt-block \ + pt-timedblock \ + pt-wakeup \ + pt-docancel \ + pt-sysdep \ + pt-setup \ + pt-machdep \ + pt-spin \ + \ + pt-sigstate-init \ + pt-sigstate-destroy \ + pt-sigstate \ + \ + pt-atfork \ + old_pt-atfork \ + pt-kill \ + pt-getcpuclockid \ + \ + pt-getschedparam pt-setschedparam pt-setschedprio \ + pt-yield \ + \ + sem-close sem-destroy sem-getvalue sem-init sem-open \ + sem-post sem-timedwait sem-trywait sem-unlink \ + sem-wait \ + \ + shm-directory \ + \ + cthreads-compat \ + $(SYSDEPS) + +libpthread-static-only-routines = pt-atfork + +headers := \ + pthread.h \ + semaphore.h \ + \ + bits/pthread.h \ + bits/pthread-np.h \ + bits/pthreadtypes.h \ + bits/pthreadtypes-arch.h \ + bits/thread-shared-types.h \ + bits/types/struct___pthread_mutex.h \ + bits/types/struct___pthread_cond.h \ + bits/types/struct___pthread_condattr.h \ + bits/types/__pthread_spinlock_t.h \ + bits/spin-lock-inline.h \ + bits/cancelation.h \ + bits/types/struct___pthread_attr.h \ + bits/types/struct___pthread_barrierattr.h \ + bits/types/struct___pthread_barrier.h \ + bits/types/__pthread_key.h \ + bits/types/struct___pthread_once.h \ + bits/types/struct___pthread_mutexattr.h \ + bits/types/struct___pthread_rwlock.h \ + bits/types/struct___pthread_rwlockattr.h \ + bits/semaphore.h + +distribute := + +routines := forward libc_pthread_init alloca_cutoff +shared-only-routines = forward + +extra-libs := libpthread +extra-libs-others := $(extra-libs) +install-lib := libpthread.so + +include ../Makeconfig + +CFLAGS-lockfile.c = -D_IO_MTSAFE_IO + +all: # Make this the default target; it will be defined in Rules. + +subdir_install: $(inst_libdir)/libpthread2.a + +# XXX: If $(inst_libdir)/libpthread2.a is installed and +# $(inst_libdir)/libpthread is not, we can have some issues. +.PHONY: $(inst_libdir)/libpthread.a $(inst_libdir)/libpthread_pic.a + +# XXX: These rules are a hack. But it is better than messing with +# ../Makeconf at the moment. Note that the linker scripts +# $(srcdir)/libpthread.a and $(srcdir)/libpthread_pic.a get overwritten +# when building in $(srcdir) and not a seperate build directory. +$(inst_libdir)/libpthread2.a: $(inst_libdir)/libpthread.a + mv $< $@ + $(INSTALL_DATA) $(srcdir)/libpthread.a $< + +$(inst_libdir)/libpthread2_pic.a: $(inst_libdir)/libpthread_pic.a + mv $< $@ + $(INSTALL_DATA) $(srcdir)/libpthread_pic.a $< + +libc-link.so = $(common-objpfx)libc.so + +extra-B-pthread.so = -B$(common-objpfx)htl/ + +include ../Rules + +ifeq (yes,$(build-shared)) +# What we install as libpthread.so for programs to link against is in fact a +# link script. It contains references for the various libraries we need. +# The libpthread.so object is not complete since some functions are only +# defined in libpthread_nonshared.a. +# We need to use absolute paths since otherwise local copies (if they exist) +# of the files are taken by the linker. +install: $(inst_libdir)/libpthread.so + +$(inst_libdir)/libpthread.so: $(common-objpfx)format.lds \ + $(objpfx)libpthread.so$(libpthread.so-version) \ + $(inst_libdir)/$(patsubst %,$(libtype.oS),\ + $(libprefix)pthread) \ + $(+force) + (echo '/* GNU ld script';\ + echo ' Use the shared library, but some functions are only in';\ + echo ' the static library, so try that secondarily. */';\ + cat $<; \ + echo 'GROUP ( $(slibdir)/libpthread.so$(libpthread.so-version)' \ + '$(libdir)/$(patsubst %,$(libtype.oS),$(libprefix)pthread)'\ + ')' \ + ) > $@.new + mv -f $@.new $@ + +$(addprefix $(objpfx), \ + $(filter-out $(tests-static) $(xtests-static) $(tests-reverse) \ + $(tests-nolibpthread), \ + $(tests) $(xtests) $(test-srcs))): $(objpfx)libpthread.so \ + $(objpfx)libpthread_nonshared.a +endif + +generated += libpthread_nonshared.a diff --git a/htl/Versions b/htl/Versions new file mode 100644 index 0000000000..6a63a1b8a1 --- /dev/null +++ b/htl/Versions @@ -0,0 +1,156 @@ +libc { + GLIBC_2.21 { + pthread_attr_destroy; pthread_attr_getdetachstate; + pthread_attr_getinheritsched; pthread_attr_getschedparam; + pthread_attr_getschedpolicy; pthread_attr_getscope; pthread_attr_init; + pthread_attr_setdetachstate; pthread_attr_setinheritsched; + pthread_attr_setschedparam; pthread_attr_setschedpolicy; + pthread_attr_setscope; + pthread_condattr_destroy; pthread_condattr_init; + pthread_cond_broadcast; pthread_cond_destroy; + pthread_cond_init; pthread_cond_signal; pthread_cond_wait; + pthread_cond_timedwait; + pthread_equal; + pthread_exit; pthread_getschedparam; pthread_setschedparam; + pthread_mutex_destroy; pthread_mutex_init; + pthread_mutex_lock; pthread_mutex_trylock; pthread_mutex_unlock; + pthread_self; pthread_setcancelstate; pthread_setcanceltype; + __pthread_get_cleanup_stack; + } + GLIBC_2.22 { + __register_atfork; + } + GLIBC_PRIVATE { + __libc_alloca_cutoff; + __libc_pthread_init; + } +} + +libpthread { + GLIBC_2.2.6 { + _IO_flockfile; _IO_ftrylockfile; _IO_funlockfile; + } + GLIBC_2.12 { + __pthread_errorcheck_mutexattr; __pthread_recursive_mutexattr; + + __pthread_get_cleanup_stack; + + __pthread_mutex_transfer_np; + + _pthread_mutex_destroy; _pthread_mutex_init; + _pthread_mutex_lock; _pthread_mutex_trylock; _pthread_mutex_unlock; + _pthread_rwlock_destroy; _pthread_rwlock_init; + + _cthread_init_routine; + + cthread_detach; + cthread_fork; + cthread_keycreate; + cthread_getspecific; + cthread_setspecific; + __mutex_lock_solid; + __mutex_unlock_solid; + _cthreads_flockfile; + _cthreads_ftrylockfile; + _cthreads_funlockfile; + + flockfile; ftrylockfile; funlockfile; + + pthread_atfork; + + pthread_attr_destroy; pthread_attr_getdetachstate; + pthread_attr_getguardsize; pthread_attr_getinheritsched; + pthread_attr_getschedparam; pthread_attr_getschedpolicy; + pthread_attr_getscope; pthread_attr_getstack; pthread_attr_getstackaddr; + pthread_attr_getstacksize; pthread_attr_init; pthread_attr_setdetachstate; + pthread_attr_setguardsize; pthread_attr_setinheritsched; + pthread_attr_setschedparam; pthread_attr_setschedpolicy; + pthread_attr_setscope; pthread_attr_setstack; pthread_attr_setstackaddr; + pthread_attr_setstacksize; + + pthread_barrier_destroy; pthread_barrier_init; pthread_barrier_wait; + pthread_barrierattr_destroy; pthread_barrierattr_getpshared; + pthread_barrierattr_init; pthread_barrierattr_setpshared; + + pthread_cancel; + + pthread_cond_broadcast; pthread_cond_destroy; pthread_cond_init; + pthread_cond_signal; pthread_cond_timedwait; pthread_cond_wait; + + pthread_condattr_destroy; pthread_condattr_getclock; + pthread_condattr_getpshared; pthread_condattr_init; + pthread_condattr_setclock; pthread_condattr_setpshared; + + pthread_create; pthread_detach; pthread_equal; pthread_exit; + + pthread_getattr_np; + + pthread_getconcurrency; pthread_getcpuclockid; + pthread_getschedparam; pthread_getspecific; + + pthread_join; + + pthread_key_create; pthread_key_delete; + __pthread_key_create; + + pthread_kill; + __pthread_kill; + + pthread_mutex_destroy; pthread_mutex_getprioceiling; + pthread_mutex_init; pthread_mutex_lock; pthread_mutex_setprioceiling; + pthread_mutex_timedlock; pthread_mutex_transfer_np; + pthread_mutex_trylock; pthread_mutex_unlock; + + pthread_mutexattr_destroy; pthread_mutexattr_getprioceiling; + pthread_mutexattr_getprotocol; pthread_mutexattr_getpshared; + pthread_mutexattr_gettype; pthread_mutexattr_init; + pthread_mutexattr_setprioceiling; pthread_mutexattr_setprotocol; + pthread_mutexattr_setpshared; pthread_mutexattr_settype; + + pthread_once; + + pthread_rwlock_destroy; pthread_rwlock_init; pthread_rwlock_rdlock; + pthread_rwlock_timedrdlock; pthread_rwlock_timedwrlock; + pthread_rwlock_tryrdlock; pthread_rwlock_trywrlock; + pthread_rwlock_unlock; pthread_rwlock_wrlock; + + pthread_rwlockattr_destroy; pthread_rwlockattr_getpshared; + pthread_rwlockattr_init; pthread_rwlockattr_setpshared; + + pthread_self; + __pthread_self; + + pthread_setcancelstate; pthread_setcanceltype; + pthread_setconcurrency; pthread_setschedparam; + pthread_setschedprio; pthread_setspecific; + + pthread_sigmask; + pthread_testcancel; + pthread_yield; + + sem_close; sem_destroy; sem_getvalue; sem_init; sem_open; sem_post; + sem_timedwait; sem_trywait; sem_unlink; sem_wait; + + pthread_spin_destroy; pthread_spin_init; pthread_spin_lock; + pthread_spin_trylock; pthread_spin_unlock; + __pthread_spin_destroy; __pthread_spin_init; + __pthread_spin_lock; __pthread_spin_trylock; __pthread_spin_unlock; + _pthread_spin_lock; + } + GLIBC_2.21 { + pthread_hurd_cond_wait_np; + pthread_hurd_cond_timedwait_np; + } + GLIBC_PRIVATE { + __shm_directory; + __pthread_threads; + + __cthread_detach; + __cthread_fork; + __cthread_keycreate; + __cthread_getspecific; + __cthread_setspecific; + __pthread_getattr_np; + __pthread_attr_getstack; + } +} diff --git a/htl/alloca_cutoff.c b/htl/alloca_cutoff.c new file mode 100644 index 0000000000..4c183bd7fa --- /dev/null +++ b/htl/alloca_cutoff.c @@ -0,0 +1,26 @@ +/* Allocate a new thread structure. + Copyright (C) 2015-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#include <alloca.h> + +int +__libc_alloca_cutoff (size_t size) +{ + return size <= 65536; +} +libc_hidden_def (__libc_alloca_cutoff) diff --git a/htl/configure b/htl/configure new file mode 100644 index 0000000000..5983490dde --- /dev/null +++ b/htl/configure @@ -0,0 +1,2 @@ +libc_add_on_canonical=libpthread +libc_add_on_subdirs=. diff --git a/htl/configure.in b/htl/configure.in new file mode 100644 index 0000000000..4e140b1197 --- /dev/null +++ b/htl/configure.in @@ -0,0 +1,4 @@ +GLIBC_PROVIDES + +libc_add_on_canonical=libpthread +libc_add_on_subdirs=. diff --git a/htl/cthreads-compat.c b/htl/cthreads-compat.c new file mode 100644 index 0000000000..042ed64e1d --- /dev/null +++ b/htl/cthreads-compat.c @@ -0,0 +1,101 @@ +/* Compatibility routines for cthreads. + Copyright (C) 2000-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#include <assert.h> +#include <pthreadP.h> + +#define CTHREAD_KEY_INVALID (__cthread_key_t) -1 + +void +__cthread_detach (__cthread_t thread) +{ + int err; + + err = pthread_detach ((pthread_t) thread); + assert_perror (err); +} +weak_alias (__cthread_detach, cthread_detach) + +__cthread_t +__cthread_fork (__cthread_fn_t func, void *arg) +{ + pthread_t thread; + int err; + + err = pthread_create (&thread, NULL, func, arg); + assert_perror (err); + + return (__cthread_t) thread; +} +weak_alias (__cthread_fork, cthread_fork) + +int +__cthread_keycreate (__cthread_key_t *key) +{ + error_t err; + + err = pthread_key_create (key, 0); + if (err) + { + errno = err; + *key = CTHREAD_KEY_INVALID; + err = -1; + } + + return err; +} +weak_alias (__cthread_keycreate, cthread_keycreate) + +int +__cthread_getspecific (__cthread_key_t key, void **val) +{ + *val = pthread_getspecific (key); + return 0; +} +weak_alias (__cthread_getspecific, cthread_getspecific) + +int +__cthread_setspecific (__cthread_key_t key, void *val) +{ + error_t err; + + err = pthread_setspecific (key, (const void *) val); + if (err) + { + errno = err; + err = -1; + } + + return err; +} +weak_alias (__cthread_setspecific, cthread_setspecific) + +void +__mutex_lock_solid (void *lock) +{ + __pthread_mutex_lock (lock); +} + +void +__mutex_unlock_solid (void *lock) +{ + if (__pthread_spin_trylock (lock) != 0) + /* Somebody already got the lock, that one will manage waking up others */ + return; + __pthread_mutex_unlock (lock); +} diff --git a/htl/forward.c b/htl/forward.c new file mode 100644 index 0000000000..cb36ae2cb7 --- /dev/null +++ b/htl/forward.c @@ -0,0 +1,283 @@ +/* Libc stubs for pthread functions. Hurd pthread version. + Copyright (C) 2002-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <dlfcn.h> +#include <stdlib.h> +#include <shlib-compat.h> +#include <pthread-functions.h> +#include <libc-lock.h> +#include <fork.h> + +/* Pointers to the libc functions. */ +struct pthread_functions __libc_pthread_functions attribute_hidden; +int __libc_pthread_functions_init attribute_hidden; + + +#define FORWARD2(name, rettype, decl, params, defaction) \ +rettype \ +name decl \ +{ \ + if (!__libc_pthread_functions_init) \ + defaction; \ + \ + return PTHFCT_CALL (ptr_##name, params); \ +} + +/* Same as FORWARD2, only without return. */ +#define FORWARD_NORETURN(name, rettype, decl, params, defaction) \ +rettype \ +name decl \ +{ \ + if (!__libc_pthread_functions_init) \ + defaction; \ + \ + PTHFCT_CALL (ptr_##name, params); \ +} + +#define FORWARD(name, decl, params, defretval) \ + FORWARD2 (name, int, decl, params, return defretval) + +FORWARD (pthread_attr_destroy, (pthread_attr_t *attr), (attr), 0) + +FORWARD (pthread_attr_init, (pthread_attr_t *attr), (attr), 0) + +FORWARD (pthread_attr_getdetachstate, + (const pthread_attr_t *attr, int *detachstate), (attr, detachstate), + 0) +FORWARD (pthread_attr_setdetachstate, (pthread_attr_t *attr, int detachstate), + (attr, detachstate), 0) + +FORWARD (pthread_attr_getinheritsched, + (const pthread_attr_t *attr, int *inherit), (attr, inherit), 0) +FORWARD (pthread_attr_setinheritsched, (pthread_attr_t *attr, int inherit), + (attr, inherit), 0) + +FORWARD (pthread_attr_getschedparam, + (const pthread_attr_t *attr, struct sched_param *param), + (attr, param), 0) +FORWARD (pthread_attr_setschedparam, + (pthread_attr_t *attr, const struct sched_param *param), + (attr, param), 0) + +FORWARD (pthread_attr_getschedpolicy, + (const pthread_attr_t *attr, int *policy), (attr, policy), 0) +FORWARD (pthread_attr_setschedpolicy, (pthread_attr_t *attr, int policy), + (attr, policy), 0) + +FORWARD (pthread_attr_getscope, + (const pthread_attr_t *attr, int *scope), (attr, scope), 0) +FORWARD (pthread_attr_setscope, (pthread_attr_t *attr, int scope), + (attr, scope), 0) + + +FORWARD (pthread_condattr_destroy, (pthread_condattr_t *attr), (attr), 0) +FORWARD (pthread_condattr_init, (pthread_condattr_t *attr), (attr), 0) + + +FORWARD (pthread_cond_broadcast, (pthread_cond_t *cond), (cond), 0) +FORWARD (pthread_cond_destroy, (pthread_cond_t *cond), (cond), 0) +FORWARD (pthread_cond_init, + (pthread_cond_t *cond, const pthread_condattr_t *cond_attr), + (cond, cond_attr), 0) +FORWARD (pthread_cond_signal, (pthread_cond_t *cond), (cond), 0) +FORWARD (pthread_cond_wait, (pthread_cond_t *cond, pthread_mutex_t *mutex), + (cond, mutex), 0) +FORWARD (pthread_cond_timedwait, + (pthread_cond_t *cond, pthread_mutex_t *mutex, + const struct timespec *abstime), (cond, mutex, abstime), 0) + +FORWARD (pthread_equal, (pthread_t thread1, pthread_t thread2), + (thread1, thread2), 1) + + +/* Use an alias to avoid warning, as pthread_exit is declared noreturn. */ +FORWARD_NORETURN (__pthread_exit, void, (void *retval), (retval), + exit (EXIT_SUCCESS)) +strong_alias (__pthread_exit, pthread_exit); + + +FORWARD (pthread_getschedparam, + (pthread_t target_thread, int *policy, struct sched_param *param), + (target_thread, policy, param), 0) +FORWARD (pthread_setschedparam, + (pthread_t target_thread, int policy, + const struct sched_param *param), (target_thread, policy, param), 0) + + +FORWARD (pthread_mutex_destroy, (pthread_mutex_t *mutex), (mutex), 0) + +FORWARD (pthread_mutex_init, + (pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr), + (mutex, mutexattr), 0) + +FORWARD (pthread_mutex_lock, (pthread_mutex_t *mutex), (mutex), 0) + +FORWARD (pthread_mutex_unlock, (pthread_mutex_t *mutex), (mutex), 0) + + +FORWARD2 (pthread_self, pthread_t, (void), (), return 0) + + +FORWARD (__pthread_setcancelstate, (int state, int *oldstate), + (state, oldstate), 0) +strong_alias (__pthread_setcancelstate, pthread_setcancelstate); + +FORWARD (pthread_setcanceltype, (int type, int *oldtype), (type, oldtype), 0) + +struct __pthread_cancelation_handler *dummy_list; +FORWARD2 (__pthread_get_cleanup_stack, struct __pthread_cancelation_handler **, + (void), (), return &dummy_list); + + +/* Fork interaction */ + +struct atfork +{ + void (*prepare) (void); + void (*parent) (void); + void (*child) (void); + void *dso_handle; + struct atfork *prev; + struct atfork *next; +}; + +/* TODO: better locking */ +__libc_lock_define_initialized (static, atfork_lock); +static struct atfork *fork_handlers, *fork_last_handler; + +static void +atfork_pthread_prepare (void) +{ + struct atfork *handlers, *last_handler; + + __libc_lock_lock (atfork_lock); + handlers = fork_handlers; + last_handler = fork_last_handler; + __libc_lock_unlock (atfork_lock); + + if (last_handler == NULL) + return; + + while (1) + { + if (last_handler->prepare != NULL) + last_handler->prepare (); + if (last_handler == handlers) + break; + last_handler = last_handler->prev; + } +} +text_set_element (_hurd_atfork_prepare_hook, atfork_pthread_prepare); + +static void +atfork_pthread_parent (void) +{ + struct atfork *handlers; + + __libc_lock_lock (atfork_lock); + handlers = fork_handlers; + __libc_lock_unlock (atfork_lock); + + while (handlers != NULL) + { + if (handlers->parent != NULL) + handlers->parent (); + handlers = handlers->next; + } +} +text_set_element (_hurd_atfork_parent_hook, atfork_pthread_parent); + +static void +atfork_pthread_child (void) +{ + struct atfork *handlers; + + __libc_lock_lock (atfork_lock); + handlers = fork_handlers; + __libc_lock_unlock (atfork_lock); + + while (handlers != NULL) + { + if (handlers->child != NULL) + handlers->child (); + handlers = handlers->next; + } +} +text_set_element (_hurd_atfork_child_hook, atfork_pthread_child); + +int +__register_atfork (void (*prepare) (void), + void (*parent) (void), + void (*child) (void), + void *dso_handle) +{ + struct atfork *new = malloc (sizeof (*new)); + if (new == NULL) + return errno; + + new->prepare = prepare; + new->parent = parent; + new->child = child; + new->dso_handle = dso_handle; + new->prev = NULL; + + __libc_lock_lock (atfork_lock); + new->next = fork_handlers; + if (fork_handlers != NULL) + fork_handlers->prev = new; + fork_handlers = new; + if (fork_last_handler == NULL) + fork_last_handler = new; + __libc_lock_unlock (atfork_lock); + + return 0; +} +libc_hidden_def (__register_atfork) + +void +__unregister_atfork (void *dso_handle) +{ + struct atfork **handlers, *prev = NULL, *next; + __libc_lock_lock (atfork_lock); + handlers = &fork_handlers; + while (*handlers != NULL) + { + if ((*handlers)->dso_handle == dso_handle) + { + /* Drop this handler from the list. */ + if (*handlers == fork_last_handler) + { + /* Was last, new last is prev, if any. */ + fork_last_handler = prev; + } + + next = (*handlers)->next; + if (next != NULL) + next->prev = prev; + *handlers = next; + } + else + { + /* Just proceed to next handler. */ + prev = *handlers; + handlers = &prev->next; + } + } + __libc_lock_unlock (atfork_lock); +} diff --git a/htl/libc_pthread_init.c b/htl/libc_pthread_init.c new file mode 100644 index 0000000000..b6e96827a5 --- /dev/null +++ b/htl/libc_pthread_init.c @@ -0,0 +1,33 @@ +/* libc initialization for libpthread. Hurd pthread version. + Copyright (C) 2002-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <string.h> +#include <pthread-functions.h> + +void +__libc_pthread_init (const struct pthread_functions *functions) +{ +#ifdef SHARED + /* We copy the content of the variable pointed to by the FUNCTIONS + parameter to one in libc.so since this means access to the array + can be done with one memory access instead of two. */ + memcpy (&__libc_pthread_functions, functions, + sizeof (__libc_pthread_functions)); + __libc_pthread_functions_init = 1; +#endif +} diff --git a/htl/libpthread.a b/htl/libpthread.a new file mode 100644 index 0000000000..e5bd2cc229 --- /dev/null +++ b/htl/libpthread.a @@ -0,0 +1,22 @@ +/* pthread initializer is weak in glibc. It must be included if glibc + is to start threading. */ +EXTERN(_cthread_init_routine) + +/* Weak references in glibc that must be filled if glibc is to be + thread safe. */ +EXTERN(cthread_detach) +EXTERN(cthread_fork) +EXTERN(cthread_keycreate) +EXTERN(cthread_getspecific) +EXTERN(cthread_setspecific) +EXTERN(__mutex_lock_solid) +EXTERN(__mutex_unlock_solid) +/* For libio stream locking. */ +EXTERN(_cthreads_flockfile) +EXTERN(_cthreads_funlockfile) +EXTERN(_cthreads_ftrylockfile) +/* To get the sigthread stack layout on fork */ +EXTERN(pthread_getattr_np) +EXTERN(pthread_attr_getstack) + +GROUP(-lpthread2 -lrt) diff --git a/htl/libpthread_pic.a b/htl/libpthread_pic.a new file mode 100644 index 0000000000..33346b4b39 --- /dev/null +++ b/htl/libpthread_pic.a @@ -0,0 +1,22 @@ +/* pthread initializer is weak in glibc. It must be included if glibc + is to start threading. */ +EXTERN(_cthread_init_routine) + +/* Weak references in glibc that must be filled if glibc is to be + thread safe. */ +EXTERN(cthread_detach) +EXTERN(cthread_fork) +EXTERN(cthread_keycreate) +EXTERN(cthread_getspecific) +EXTERN(cthread_setspecific) +EXTERN(__mutex_lock_solid) +EXTERN(__mutex_unlock_solid) +/* For libio stream locking. */ +EXTERN(_cthreads_flockfile) +EXTERN(_cthreads_funlockfile) +EXTERN(_cthreads_ftrylockfile) +/* To get the sigthread stack layout on fork */ +EXTERN(pthread_getattr_np) +EXTERN(pthread_attr_getstack) + +GROUP(-lpthread2_pic) diff --git a/htl/lockfile.c b/htl/lockfile.c new file mode 100644 index 0000000000..9069496185 --- /dev/null +++ b/htl/lockfile.c @@ -0,0 +1,60 @@ +/* lockfile - Handle locking and unlocking of streams. Hurd pthread version. + Copyright (C) 2000-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#include <cthreads.h> +#include <pthread.h> /* Must come before <stdio.h>! */ +#include <stdio.h> + +void +_cthreads_flockfile (FILE *fp) +{ + _IO_lock_lock (*fp->_lock); +} + +void +_cthreads_funlockfile (FILE *fp) +{ + _IO_lock_unlock (*fp->_lock); +} + +int +_cthreads_ftrylockfile (FILE *fp) +{ + return __libc_lock_trylock_recursive (*fp->_lock); +} + +#undef _IO_flockfile +#undef _IO_funlockfile +#undef _IO_ftrylockfile +#undef flockfile +#undef funlockfile +#undef ftrylockfile + +void _IO_flockfile (FILE *) + __attribute__ ((alias ("_cthreads_flockfile"))); +void _IO_funlockfile (FILE *) + __attribute__ ((alias ("_cthreads_funlockfile"))); +int _IO_ftrylockfile (FILE *) + __attribute__ ((alias ("_cthreads_ftrylockfile"))); + +void flockfile (FILE *) + __attribute__ ((alias ("_cthreads_flockfile"))); +void funlockfile (FILE *) + __attribute__ ((alias ("_cthreads_funlockfile"))); +int ftrylockfile (FILE *) + __attribute__ ((alias ("_cthreads_ftrylockfile"))); diff --git a/htl/pt-alloc.c b/htl/pt-alloc.c new file mode 100644 index 0000000000..adab790d9d --- /dev/null +++ b/htl/pt-alloc.c @@ -0,0 +1,214 @@ +/* Allocate a new thread structure. + Copyright (C) 2000-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#include <assert.h> +#include <errno.h> +#include <pthread.h> +#include <stdlib.h> +#include <string.h> + +#include <pt-internal.h> + +/* This braindamage is necessary because the standard says that some + of the threads functions "shall fail" if "No thread could be found + corresponding to that specified by the given thread ID." */ + +/* Thread ID lookup table. */ +struct __pthread **__pthread_threads; + +/* The size of the thread ID lookup table. */ +int __pthread_max_threads; + +/* The total number of thread IDs currently in use, or on the list of + available thread IDs. */ +int __pthread_num_threads; + +/* A lock for the table, and the other variables above. */ +pthread_rwlock_t __pthread_threads_lock; + +/* List of thread structures corresponding to free thread IDs. */ +struct __pthread *__pthread_free_threads; +pthread_mutex_t __pthread_free_threads_lock; + +static inline error_t +initialize_pthread (struct __pthread *new) +{ + error_t err; + + err = __pthread_init_specific (new); + if (err) + return err; + + new->nr_refs = 1; + new->cancel_lock = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER; + new->cancel_hook = NULL; + new->cancel_hook_arg = NULL; + new->cancel_state = PTHREAD_CANCEL_ENABLE; + new->cancel_type = PTHREAD_CANCEL_DEFERRED; + new->cancel_pending = 0; + + new->state_lock = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER; + new->state_cond = (pthread_cond_t) PTHREAD_COND_INITIALIZER; + + new->cancelation_handlers = 0; + + memset (&new->res_state, '\0', sizeof (new->res_state)); + + new->tcb = NULL; + + new->next = 0; + new->prevp = 0; + + return 0; +} + + +/* Allocate a new thread structure and its pthread thread ID (but not + a kernel thread). */ +int +__pthread_alloc (struct __pthread **pthread) +{ + error_t err; + + struct __pthread *new; + struct __pthread **threads; + struct __pthread **old_threads; + int max_threads; + int new_max_threads; + + pthread_mutex_lock (&__pthread_free_threads_lock); + for (new = __pthread_free_threads; new; new = new->next) + { + /* There is no need to take NEW->STATE_LOCK: if NEW is on this + list, then it is protected by __PTHREAD_FREE_THREADS_LOCK + except in __pthread_dealloc where after it is added to the + list (with the lock held), it drops the lock and then sets + NEW->STATE and immediately stops using NEW. */ + if (new->state == PTHREAD_TERMINATED) + { + __pthread_dequeue (new); + break; + } + } + pthread_mutex_unlock (&__pthread_free_threads_lock); + + if (new) + { + if (new->tcb) + { + /* Drop old values */ + _dl_deallocate_tls (new->tcb, 1); + } + + err = initialize_pthread (new); + if (!err) + *pthread = new; + return err; + } + + /* Allocate a new thread structure. */ + new = malloc (sizeof (struct __pthread)); + if (new == NULL) + return ENOMEM; + + err = initialize_pthread (new); + if (err) + { + free (new); + return err; + } + +retry: + __pthread_rwlock_wrlock (&__pthread_threads_lock); + + if (__pthread_num_threads < __pthread_max_threads) + { + /* We have a free slot. Use the slot number plus one as the + thread ID for the new thread. */ + new->thread = 1 + __pthread_num_threads++; + __pthread_threads[new->thread - 1] = NULL; + + __pthread_rwlock_unlock (&__pthread_threads_lock); + + *pthread = new; + return 0; + } +#ifdef PTHREAD_THREADS_MAX + else if (__pthread_num_threads >= PTHREAD_THREADS_MAX) + { + /* We have reached the limit on the number of threads per process. */ + __pthread_rwlock_unlock (&__pthread_threads_lock); + + free (new); + return EAGAIN; + } +#endif + + /* We are going to enlarge the threads table. Save its current + size. We're going to release the lock before doing the necessary + memory allocation, since that's a potentially blocking operation. */ + max_threads = __pthread_max_threads; + + __pthread_rwlock_unlock (&__pthread_threads_lock); + + /* Allocate a new lookup table that's twice as large. */ + new_max_threads + = max_threads > 0 ? max_threads * 2 : _POSIX_THREAD_THREADS_MAX; + threads = malloc (new_max_threads * sizeof (struct __pthread *)); + if (threads == NULL) + { + free (new); + return ENOMEM; + } + + __pthread_rwlock_wrlock (&__pthread_threads_lock); + + /* Check if nobody else has already enlarged the table. */ + if (max_threads != __pthread_max_threads) + { + /* Yep, they did. */ + __pthread_rwlock_unlock (&__pthread_threads_lock); + + /* Free the newly allocated table and try again to allocate a slot. */ + free (threads); + goto retry; + } + + /* Copy over the contents of the old table. */ + memcpy (threads, __pthread_threads, + __pthread_max_threads * sizeof (struct __pthread *)); + + /* Save the location of the old table. We want to deallocate its + storage after we released the lock. */ + old_threads = __pthread_threads; + + /* Replace the table with the new one. */ + __pthread_max_threads = new_max_threads; + __pthread_threads = threads; + + /* And allocate ourselves one of the newly created slots. */ + new->thread = 1 + __pthread_num_threads++; + __pthread_threads[new->thread - 1] = NULL; + + __pthread_rwlock_unlock (&__pthread_threads_lock); + + free (old_threads); + + *pthread = new; + return 0; +} diff --git a/htl/pt-cancel.c b/htl/pt-cancel.c new file mode 100644 index 0000000000..9f877243b9 --- /dev/null +++ b/htl/pt-cancel.c @@ -0,0 +1,62 @@ +/* Cancel a thread. + Copyright (C) 2002-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#include <pthread.h> + +#include <pt-internal.h> + +int +pthread_cancel (pthread_t t) +{ + int err = 0; + struct __pthread *p; + + p = __pthread_getid (t); + if (p == NULL) + return ESRCH; + + __pthread_mutex_lock (&p->cancel_lock); + if (p->cancel_pending) + { + __pthread_mutex_unlock (&p->cancel_lock); + return 0; + } + + p->cancel_pending = 1; + + if (p->cancel_state != PTHREAD_CANCEL_ENABLE) + { + __pthread_mutex_unlock (&p->cancel_lock); + return 0; + } + + if (p->cancel_type == PTHREAD_CANCEL_ASYNCHRONOUS) + /* CANCEL_LOCK is unlocked by this call. */ + err = __pthread_do_cancel (p); + else + { + if (p->cancel_hook != NULL) + /* Thread blocking on a cancellation point. Invoke hook to unblock. + See __pthread_cond_timedwait_internal. */ + p->cancel_hook (p->cancel_hook_arg); + + __pthread_mutex_unlock (&p->cancel_lock); + } + + return err; +} diff --git a/htl/pt-cleanup.c b/htl/pt-cleanup.c new file mode 100644 index 0000000000..74c86b2bf4 --- /dev/null +++ b/htl/pt-cleanup.c @@ -0,0 +1,27 @@ +/* Add a cancelation handler to the stack. + Copyright (C) 2002-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#include <pthread.h> + +#include <pt-internal.h> + +struct __pthread_cancelation_handler ** +__pthread_get_cleanup_stack (void) +{ + return &_pthread_self ()->cancelation_handlers; +} diff --git a/htl/pt-create.c b/htl/pt-create.c new file mode 100644 index 0000000000..f5c06ffdaf --- /dev/null +++ b/htl/pt-create.c @@ -0,0 +1,246 @@ +/* Thread creation. + Copyright (C) 2000-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#include <assert.h> +#include <errno.h> +#include <pthread.h> +#include <signal.h> +#include <resolv.h> + +#include <atomic.h> +#include <hurd/resource.h> + +#include <pt-internal.h> + +#if IS_IN (libpthread) +# include <ctype.h> +#endif +#ifdef HAVE_USELOCALE +# include <locale.h> +#endif + +/* The total number of pthreads currently active. This is defined + here since it would be really stupid to have a threads-using + program that doesn't call `pthread_create'. */ +unsigned int __pthread_total; + + +/* The entry-point for new threads. */ +static void +entry_point (struct __pthread *self, void *(*start_routine) (void *), void *arg) +{ + ___pthread_self = self; + __resp = &self->res_state; + +#if IS_IN (libpthread) + /* Initialize pointers to locale data. */ + __ctype_init (); +#endif +#ifdef HAVE_USELOCALE + /* A fresh thread needs to be bound to the global locale. */ + uselocale (LC_GLOBAL_LOCALE); +#endif + + __pthread_startup (); + + pthread_exit (start_routine (arg)); +} + +/* Create a thread with attributes given by ATTR, executing + START_ROUTINE with argument ARG. */ +int +pthread_create (pthread_t * thread, const pthread_attr_t * attr, + void *(*start_routine) (void *), void *arg) +{ + int err; + struct __pthread *pthread; + + err = __pthread_create_internal (&pthread, attr, start_routine, arg); + if (!err) + *thread = pthread->thread; + else if (err == ENOMEM) + err = EAGAIN; + + return err; +} + +/* Internal version of pthread_create. See comment in + pt-internal.h. */ +int +__pthread_create_internal (struct __pthread **thread, + const pthread_attr_t * attr, + void *(*start_routine) (void *), void *arg) +{ + int err; + struct __pthread *pthread; + const struct __pthread_attr *setup; + sigset_t sigset; + size_t stacksize; + + /* Allocate a new thread structure. */ + err = __pthread_alloc (&pthread); + if (err) + goto failed; + + /* Use the default attributes if ATTR is NULL. */ + setup = attr ? attr : &__pthread_default_attr; + + stacksize = setup->__stacksize; + if (stacksize == 0) + { + struct rlimit rlim; + __getrlimit (RLIMIT_STACK, &rlim); + if (rlim.rlim_cur != RLIM_INFINITY) + stacksize = rlim.rlim_cur; + if (stacksize == 0) + stacksize = PTHREAD_STACK_DEFAULT; + } + + /* Initialize the thread state. */ + pthread->state = (setup->__detachstate == PTHREAD_CREATE_DETACHED + ? PTHREAD_DETACHED : PTHREAD_JOINABLE); + + if (setup->__stackaddr) + { + pthread->stackaddr = setup->__stackaddr; + + /* If the user supplied a stack, it is not our responsibility to + setup a stack guard. */ + pthread->guardsize = 0; + pthread->stack = 0; + } + else + { + /* Allocate a stack. */ + err = __pthread_stack_alloc (&pthread->stackaddr, + ((setup->__guardsize + __vm_page_size - 1) + / __vm_page_size) * __vm_page_size + + stacksize); + if (err) + goto failed_stack_alloc; + + pthread->guardsize = setup->__guardsize; + pthread->stack = 1; + } + + pthread->stacksize = stacksize; + + /* Allocate the kernel thread and other required resources. */ + err = __pthread_thread_alloc (pthread); + if (err) + goto failed_thread_alloc; + + pthread->tcb = _dl_allocate_tls (NULL); + if (pthread->tcb == NULL) + { + err = ENOMEM; + goto failed_thread_tls_alloc; + } + pthread->tcb->tcb = pthread->tcb; + + /* And initialize the rest of the machine context. This may include + additional machine- and system-specific initializations that + prove convenient. */ + err = __pthread_setup (pthread, entry_point, start_routine, arg); + if (err) + goto failed_setup; + + /* Initialize the system-specific signal state for the new + thread. */ + err = __pthread_sigstate_init (pthread); + if (err) + goto failed_sigstate; + + /* If the new thread is joinable, add a reference for the caller. */ + if (pthread->state == PTHREAD_JOINABLE) + pthread->nr_refs++; + + /* Set the new thread's signal mask and set the pending signals to + empty. POSIX says: "The signal mask shall be inherited from the + creating thread. The set of signals pending for the new thread + shall be empty." If the currnet thread is not a pthread then we + just inherit the process' sigmask. */ + if (__pthread_num_threads == 1) + err = sigprocmask (0, 0, &sigset); + else + err = __pthread_sigstate (_pthread_self (), 0, 0, &sigset, 0); + assert_perror (err); + + err = __pthread_sigstate (pthread, SIG_SETMASK, &sigset, 0, 1); + assert_perror (err); + + /* Increase the total number of threads. We do this before actually + starting the new thread, since the new thread might immediately + call `pthread_exit' which decreases the number of threads and + calls `exit' if the number of threads reaches zero. Increasing + the number of threads from within the new thread isn't an option + since this thread might return and call `pthread_exit' before the + new thread runs. */ + atomic_increment (&__pthread_total); + + /* Store a pointer to this thread in the thread ID lookup table. We + could use __thread_setid, however, we only lock for reading as no + other thread should be using this entry (we also assume that the + store is atomic). */ + __pthread_rwlock_rdlock (&__pthread_threads_lock); + __pthread_threads[pthread->thread - 1] = pthread; + __pthread_rwlock_unlock (&__pthread_threads_lock); + + /* At this point it is possible to guess our pthread ID. We have to + make sure that all functions taking a pthread_t argument can + handle the fact that this thread isn't really running yet. Since + the new thread might be passed its ID through pthread_create (to + avoid calling pthread_self), read it before starting the thread. */ + *thread = pthread; + + /* Schedule the new thread. */ + err = __pthread_thread_start (pthread); + if (err) + goto failed_starting; + + + return 0; + +failed_starting: + /* If joinable, a reference was added for the caller. */ + if (pthread->state == PTHREAD_JOINABLE) + __pthread_dealloc (pthread); + + __pthread_setid (pthread->thread, NULL); + atomic_decrement (&__pthread_total); +failed_sigstate: + __pthread_sigstate_destroy (pthread); +failed_setup: + _dl_deallocate_tls (pthread->tcb, 1); + pthread->tcb = NULL; +failed_thread_tls_alloc: + __pthread_thread_terminate (pthread); + + /* __pthread_thread_terminate has taken care of deallocating the stack and + the thread structure. */ + goto failed; +failed_thread_alloc: + if (pthread->stack) + __pthread_stack_dealloc (pthread->stackaddr, + ((setup->__guardsize + __vm_page_size - 1) + / __vm_page_size) * __vm_page_size + stacksize); +failed_stack_alloc: + __pthread_dealloc (pthread); +failed: + return err; +} diff --git a/htl/pt-dealloc.c b/htl/pt-dealloc.c new file mode 100644 index 0000000000..15e251ef46 --- /dev/null +++ b/htl/pt-dealloc.c @@ -0,0 +1,68 @@ +/* Deallocate a thread structure. + Copyright (C) 2000-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#include <assert.h> +#include <pthread.h> +#include <stdlib.h> + +#include <pt-internal.h> + +#include <atomic.h> + +/* List of thread structures corresponding to free thread IDs. */ +extern struct __pthread *__pthread_free_threads; +extern pthread_mutex_t __pthread_free_threads_lock; + + +/* Deallocate the thread structure for PTHREAD. */ +void +__pthread_dealloc (struct __pthread *pthread) +{ + assert (pthread->state != PTHREAD_TERMINATED); + + if (!atomic_decrement_and_test (&pthread->nr_refs)) + return; + + /* Withdraw this thread from the thread ID lookup table. */ + __pthread_setid (pthread->thread, NULL); + + /* Mark the thread as terminated. We broadcast the condition + here to prevent pthread_join from waiting for this thread to + exit where it was never really started. Such a call to + pthread_join is completely bogus, but unfortunately allowed + by the standards. */ + __pthread_mutex_lock (&pthread->state_lock); + if (pthread->state != PTHREAD_EXITED) + __pthread_cond_broadcast (&pthread->state_cond); + __pthread_mutex_unlock (&pthread->state_lock); + + /* We do not actually deallocate the thread structure, but add it to + a list of re-usable thread structures. */ + __pthread_mutex_lock (&__pthread_free_threads_lock); + __pthread_enqueue (&__pthread_free_threads, pthread); + __pthread_mutex_unlock (&__pthread_free_threads_lock); + + /* Setting PTHREAD->STATE to PTHREAD_TERMINATED makes this TCB + available for reuse. After that point, we can no longer assume + that PTHREAD is valid. + + Note that it is safe to not lock this update to PTHREAD->STATE: + the only way that it can now be accessed is in __pthread_alloc, + which reads this variable. */ + pthread->state = PTHREAD_TERMINATED; +} diff --git a/htl/pt-detach.c b/htl/pt-detach.c new file mode 100644 index 0000000000..773ed10722 --- /dev/null +++ b/htl/pt-detach.c @@ -0,0 +1,79 @@ +/* Detach a thread. + Copyright (C) 2000-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <pthread.h> +#include <stddef.h> + +#include <pt-internal.h> + +/* Indicate that the storage for THREAD can be reclaimed when it + terminates. */ +int +pthread_detach (pthread_t thread) +{ + struct __pthread *pthread; + int err = 0; + + /* Lookup the thread structure for THREAD. */ + pthread = __pthread_getid (thread); + if (pthread == NULL) + return ESRCH; + + __pthread_mutex_lock (&pthread->state_lock); + + switch (pthread->state) + { + case PTHREAD_JOINABLE: + /* THREAD still running. Mark it as detached such that its + resources can be reclaimed as soon as the thread exits. */ + pthread->state = PTHREAD_DETACHED; + + /* Broadcast the condition. This will make threads that are + waiting to join THREAD continue with hopefully disastrous + consequences instead of blocking indefinitely. */ + pthread_cond_broadcast (&pthread->state_cond); + __pthread_mutex_unlock (&pthread->state_lock); + + __pthread_dealloc (pthread); + break; + + case PTHREAD_EXITED: + __pthread_mutex_unlock (&pthread->state_lock); + + /* THREAD has already exited. PTHREAD remained after the thread + exited in order to provide the exit status, but it turns out + it won't be needed. */ + __pthread_dealloc (pthread); + break; + + case PTHREAD_TERMINATED: + /* Pretend THREAD wasn't there in the first place. */ + __pthread_mutex_unlock (&pthread->state_lock); + err = ESRCH; + break; + + default: + /* Thou shalt not detach non-joinable threads! */ + __pthread_mutex_unlock (&pthread->state_lock); + err = EINVAL; + break; + } + + return err; +} diff --git a/htl/pt-exit.c b/htl/pt-exit.c new file mode 100644 index 0000000000..8f3d755773 --- /dev/null +++ b/htl/pt-exit.c @@ -0,0 +1,111 @@ +/* Thread termination. + Copyright (C) 2000-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#include <assert.h> +#include <errno.h> +#include <pthread.h> +#include <stdlib.h> + +#include <pt-internal.h> + +#include <atomic.h> + + +/* Terminate the current thread and make STATUS available to any + thread that might join it. */ +void +__pthread_exit (void *status) +{ + struct __pthread *self = _pthread_self (); + struct __pthread_cancelation_handler **handlers; + int oldstate; + + /* Run any cancelation handlers. According to POSIX, the + cancellation cleanup handlers should be called with cancellation + disabled. */ + pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &oldstate); + + for (handlers = __pthread_get_cleanup_stack (); + *handlers != NULL; + *handlers = (*handlers)->__next) + (*handlers)->__handler ((*handlers)->__arg); + + pthread_setcancelstate (oldstate, &oldstate); + + /* Decrease the number of threads. We use an atomic operation to + make sure that only the last thread calls `exit'. */ + if (atomic_decrement_and_test (&__pthread_total)) + /* We are the last thread. */ + exit (0); + + /* Note that after this point the process can be terminated at any + point if another thread calls `pthread_exit' and happens to be + the last thread. */ + + __pthread_mutex_lock (&self->state_lock); + + if (self->cancel_state == PTHREAD_CANCEL_ENABLE && self->cancel_pending) + status = PTHREAD_CANCELED; + + switch (self->state) + { + default: + assert (!"Consistency error: unexpected self->state"); + abort (); + break; + + case PTHREAD_DETACHED: + __pthread_mutex_unlock (&self->state_lock); + + break; + + case PTHREAD_JOINABLE: + /* We need to stay around for a while since another thread + might want to join us. */ + self->state = PTHREAD_EXITED; + + /* We need to remember the exit status. A thread joining us + might ask for it. */ + self->status = status; + + /* Broadcast the condition. This will wake up threads that are + waiting to join us. */ + __pthread_cond_broadcast (&self->state_cond); + __pthread_mutex_unlock (&self->state_lock); + + break; + } + + /* Destroy any thread specific data. */ + __pthread_destroy_specific (self); + + /* Destroy any signal state. */ + __pthread_sigstate_destroy (self); + + /* Self terminating requires TLS, so defer the release of the TCB until + the thread structure is reused. */ + + /* Release kernel resources, including the kernel thread and the stack, + and drop the self reference. */ + __pthread_thread_terminate (self); + + /* NOTREACHED */ + abort (); +} + +strong_alias (__pthread_exit, pthread_exit); diff --git a/htl/pt-getattr.c b/htl/pt-getattr.c new file mode 100644 index 0000000000..b1ec5e0257 --- /dev/null +++ b/htl/pt-getattr.c @@ -0,0 +1,51 @@ +/* Thread attributes retrieval. + Copyright (C) 2008-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#include <assert.h> +#include <errno.h> +#include <pthread.h> + +#include <pt-internal.h> + +/* Initialize thread attribute *ATTR with attributes corresponding to the + already running thread THREAD. It shall be called on an uninitialized ATTR + and destroyed with pthread_attr_destroy when no longer needed. */ +int +__pthread_getattr_np (pthread_t thread, pthread_attr_t *attr) +{ + struct __pthread *pthread; + + pthread = __pthread_getid (thread); + if (pthread == NULL) + return ESRCH; + + /* Some attributes (schedparam, inheritsched, contentionscope and schedpolicy) + are not supported yet, so fill them with our default values. */ + *attr = __pthread_default_attr; + + attr->__stackaddr = pthread->stackaddr + + ((pthread->guardsize + __vm_page_size - 1) + / __vm_page_size * __vm_page_size); + attr->__stacksize = pthread->stacksize; + attr->__guardsize = pthread->guardsize; + attr->__detachstate = (pthread->state == PTHREAD_DETACHED + ? PTHREAD_CREATE_DETACHED : PTHREAD_CREATE_JOINABLE); + + return 0; +} +weak_alias (__pthread_getattr_np, pthread_getattr_np) diff --git a/htl/pt-initialize.c b/htl/pt-initialize.c new file mode 100644 index 0000000000..201810f1ca --- /dev/null +++ b/htl/pt-initialize.c @@ -0,0 +1,83 @@ +/* Initialize pthreads library. + Copyright (C) 2000-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#include <assert.h> +#include <string.h> + +#include <pt-internal.h> +#include <set-hooks.h> + +#include <pthread.h> +#include <pthread-functions.h> + +#if IS_IN (libpthread) +static const struct pthread_functions pthread_functions = { + .ptr_pthread_attr_destroy = __pthread_attr_destroy, + .ptr_pthread_attr_init = __pthread_attr_init, + .ptr_pthread_attr_getdetachstate = __pthread_attr_getdetachstate, + .ptr_pthread_attr_setdetachstate = __pthread_attr_setdetachstate, + .ptr_pthread_attr_getinheritsched = __pthread_attr_getinheritsched, + .ptr_pthread_attr_setinheritsched = __pthread_attr_setinheritsched, + .ptr_pthread_attr_getschedparam = __pthread_attr_getschedparam, + .ptr_pthread_attr_setschedparam = __pthread_attr_setschedparam, + .ptr_pthread_attr_getschedpolicy = __pthread_attr_getschedpolicy, + .ptr_pthread_attr_setschedpolicy = __pthread_attr_setschedpolicy, + .ptr_pthread_attr_getscope = __pthread_attr_getscope, + .ptr_pthread_attr_setscope = __pthread_attr_setscope, + .ptr_pthread_condattr_destroy = __pthread_condattr_destroy, + .ptr_pthread_condattr_init = __pthread_condattr_init, + .ptr_pthread_cond_broadcast = __pthread_cond_broadcast, + .ptr_pthread_cond_destroy = __pthread_cond_destroy, + .ptr_pthread_cond_init = __pthread_cond_init, + .ptr_pthread_cond_signal = __pthread_cond_signal, + .ptr_pthread_cond_wait = __pthread_cond_wait, + .ptr_pthread_cond_timedwait = __pthread_cond_timedwait, + .ptr_pthread_equal = __pthread_equal, + .ptr___pthread_exit = __pthread_exit, + .ptr_pthread_getschedparam = __pthread_getschedparam, + .ptr_pthread_setschedparam = __pthread_setschedparam, + .ptr_pthread_mutex_destroy = _pthread_mutex_destroy, + .ptr_pthread_mutex_init = _pthread_mutex_init, + .ptr_pthread_mutex_lock = __pthread_mutex_lock, + .ptr_pthread_mutex_trylock = __pthread_mutex_trylock, + .ptr_pthread_mutex_unlock = __pthread_mutex_unlock, + .ptr_pthread_self = __pthread_self, + .ptr___pthread_setcancelstate = __pthread_setcancelstate, + .ptr_pthread_setcanceltype = __pthread_setcanceltype, + .ptr___pthread_get_cleanup_stack = __pthread_get_cleanup_stack, + .ptr_pthread_once = __pthread_once, + .ptr_pthread_rwlock_rdlock = __pthread_rwlock_rdlock, + .ptr_pthread_rwlock_wrlock = __pthread_rwlock_wrlock, + .ptr_pthread_rwlock_unlock = __pthread_rwlock_unlock, + .ptr___pthread_key_create = __pthread_key_create, + .ptr___pthread_getspecific = __pthread_getspecific, + .ptr___pthread_setspecific = __pthread_setspecific, + .ptr__IO_flockfile = _cthreads_flockfile, + .ptr__IO_funlockfile = _cthreads_funlockfile, + .ptr__IO_ftrylockfile = _cthreads_ftrylockfile, +}; +#endif /* IS_IN (libpthread) */ + +/* Initialize the pthreads library. */ +void +___pthread_init (void) +{ +#if IS_IN (libpthread) + __libc_pthread_init (&pthread_functions); +#endif +} diff --git a/htl/pt-internal.h b/htl/pt-internal.h new file mode 100644 index 0000000000..22afc1fcce --- /dev/null +++ b/htl/pt-internal.h @@ -0,0 +1,324 @@ +/* Internal defenitions for pthreads library. + Copyright (C) 2000-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#ifndef _PT_INTERNAL_H +#define _PT_INTERNAL_H 1 + +#include <pthread.h> +#include <stddef.h> +#include <sched.h> +#include <signal.h> +#include <assert.h> +#include <bits/types/res_state.h> + +#include <atomic.h> + +#include <pt-key.h> + +#include <pt-sysdep.h> +#include <pt-machdep.h> + +#if IS_IN (libpthread) +# include <ldsodefs.h> +#endif + +/* Thread state. */ +enum pthread_state +{ + /* The thread is running and joinable. */ + PTHREAD_JOINABLE = 0, + /* The thread is running and detached. */ + PTHREAD_DETACHED, + /* A joinable thread exited and its return code is available. */ + PTHREAD_EXITED, + /* The thread structure is unallocated and available for reuse. */ + PTHREAD_TERMINATED +}; + +#ifndef PTHREAD_KEY_MEMBERS +# define PTHREAD_KEY_MEMBERS +#endif + +#ifndef PTHREAD_SYSDEP_MEMBERS +# define PTHREAD_SYSDEP_MEMBERS +#endif + +#if !(IS_IN (libpthread)) +/* Type of the TCB. */ +typedef struct +{ + void *tcb; /* Points to this structure. */ + void *dtv; /* Vector of pointers to TLS data. */ + thread_t self; /* This thread's control port. */ +} tcbhead_t; +#endif /* ! IS_IN (libpthread) */ + +/* This structure describes a POSIX thread. */ +struct __pthread +{ + /* Thread ID. */ + pthread_t thread; + + unsigned int nr_refs; /* Detached threads have a self reference only, + while joinable threads have two references. + These are used to keep the structure valid at + thread destruction. Detaching/joining a thread + drops a reference. */ + + /* Cancellation. */ + pthread_mutex_t cancel_lock; /* Protect cancel_xxx members. */ + void (*cancel_hook) (void *); /* Called to unblock a thread blocking + in a cancellation point (namely, + __pthread_cond_timedwait_internal). */ + void *cancel_hook_arg; + int cancel_state; + int cancel_type; + int cancel_pending; + struct __pthread_cancelation_handler *cancelation_handlers; + + /* Thread stack. */ + void *stackaddr; + size_t stacksize; + size_t guardsize; + int stack; /* Nonzero if the stack was allocated. */ + + /* Exit status. */ + void *status; + + /* Thread state. */ + enum pthread_state state; + pthread_mutex_t state_lock; /* Locks the state. */ + pthread_cond_t state_cond; /* Signalled when the state changes. */ + + /* Resolver state. */ + struct __res_state res_state; + + /* Thread context. */ + struct pthread_mcontext mcontext; + + PTHREAD_KEY_MEMBERS + + PTHREAD_SYSDEP_MEMBERS + + tcbhead_t *tcb; + + /* Queue links. Since PREVP is used to determine if a thread has been + awaken, it must be protected by the queue lock. */ + struct __pthread *next, **prevp; +}; + +/* Enqueue an element THREAD on the queue *HEAD. */ +static inline void +__pthread_enqueue (struct __pthread **head, struct __pthread *thread) +{ + assert (thread->prevp == 0); + + thread->next = *head; + thread->prevp = head; + if (*head) + (*head)->prevp = &thread->next; + *head = thread; +} + +/* Dequeue the element THREAD from the queue it is connected to. */ +static inline void +__pthread_dequeue (struct __pthread *thread) +{ + assert (thread); + assert (thread->prevp); + + if (thread->next) + thread->next->prevp = thread->prevp; + *thread->prevp = thread->next; + thread->prevp = 0; +} + +/* Iterate over QUEUE storing each element in ELEMENT. */ +#define __pthread_queue_iterate(queue, element) \ + for (struct __pthread *__pdi_next = (queue); \ + ((element) = __pdi_next) \ + && ((__pdi_next = __pdi_next->next), \ + 1); \ + ) + +/* Iterate over QUEUE dequeuing each element, storing it in + ELEMENT. */ +#define __pthread_dequeuing_iterate(queue, element) \ + for (struct __pthread *__pdi_next = (queue); \ + ((element) = __pdi_next) \ + && ((__pdi_next = __pdi_next->next), \ + ((element)->prevp = 0), \ + 1); \ + ) + +/* The total number of threads currently active. */ +extern unsigned int __pthread_total; + +/* The total number of thread IDs currently in use, or on the list of + available thread IDs. */ +extern int __pthread_num_threads; + +/* Concurrency hint. */ +extern int __pthread_concurrency; + +/* Array of __pthread structures and its lock. Indexed by the pthread + id minus one. (Why not just use the pthread id? Because some + brain-dead users of the pthread interface incorrectly assume that 0 + is an invalid pthread id.) */ +extern struct __pthread **__pthread_threads; +extern pthread_rwlock_t __pthread_threads_lock; + +#define __pthread_getid(thread) \ + ({ struct __pthread *__t; \ + __pthread_rwlock_rdlock (&__pthread_threads_lock); \ + __t = __pthread_threads[thread - 1]; \ + __pthread_rwlock_unlock (&__pthread_threads_lock); \ + __t; }) + +#define __pthread_setid(thread, pthread) \ + __pthread_rwlock_wrlock (&__pthread_threads_lock); \ + __pthread_threads[thread - 1] = pthread; \ + __pthread_rwlock_unlock (&__pthread_threads_lock); + +/* Similar to pthread_self, but returns the thread descriptor instead + of the thread ID. */ +#ifndef _pthread_self +extern struct __pthread *_pthread_self (void); +#endif + + +/* Initialize the pthreads library. */ +extern void ___pthread_init (void); + +/* Internal version of pthread_create. Rather than return the new + tid, we return the whole __pthread structure in *PTHREAD. */ +extern int __pthread_create_internal (struct __pthread **__restrict pthread, + const pthread_attr_t *__restrict attr, + void *(*start_routine) (void *), + void *__restrict arg); + +/* Allocate a new thread structure and a pthread thread ID (but not a + kernel thread or a stack). THREAD has one reference. */ +extern int __pthread_alloc (struct __pthread **thread); + +/* Deallocate the thread structure. This is the dual of + __pthread_alloc (N.B. it does not call __pthread_stack_dealloc nor + __pthread_thread_terminate). THREAD loses one reference and is + released if the reference counter drops to 0. */ +extern void __pthread_dealloc (struct __pthread *thread); + + +/* Allocate a stack of size STACKSIZE. The stack base shall be + returned in *STACKADDR. */ +extern int __pthread_stack_alloc (void **stackaddr, size_t stacksize); + +/* Deallocate the stack STACKADDR of size STACKSIZE. */ +extern void __pthread_stack_dealloc (void *stackaddr, size_t stacksize); + + +/* Setup thread THREAD's context. */ +extern int __pthread_setup (struct __pthread *__restrict thread, + void (*entry_point) (struct __pthread *, + void *(*)(void *), + void *), + void *(*start_routine) (void *), + void *__restrict arg); + + +/* Allocate a kernel thread (and any miscellaneous system dependent + resources) for THREAD; it must not be placed on the run queue. */ +extern int __pthread_thread_alloc (struct __pthread *thread); + +/* Start THREAD making it eligible to run. */ +extern int __pthread_thread_start (struct __pthread *thread); + +/* Terminate the kernel thread associated with THREAD, and deallocate its + stack as well as any other kernel resource associated with it. + In addition, THREAD looses one reference. + + This function can be called by any thread, including the target thread. + Since some resources that are destroyed along the kernel thread are + stored in thread-local variables, the conditions required for this + function to behave correctly are a bit unusual : as long as the target + thread hasn't been started, any thread can terminate it, but once it + has started, no other thread can terminate it, so that thread-local + variables created by that thread are correctly released. */ +extern void __pthread_thread_terminate (struct __pthread *thread); + + +/* Called by a thread just before it calls the provided start + routine. */ +extern void __pthread_startup (void); + +/* Block THREAD. */ +extern void __pthread_block (struct __pthread *thread); + +/* Block THREAD until *ABSTIME is reached. */ +extern error_t __pthread_timedblock (struct __pthread *__restrict thread, + const struct timespec *__restrict abstime, + clockid_t clock_id); + +/* Wakeup THREAD. */ +extern void __pthread_wakeup (struct __pthread *thread); + + +/* Perform a cancelation. The CANCEL_LOCK member of the given thread must + be locked before calling this function, which must unlock it. */ +extern int __pthread_do_cancel (struct __pthread *thread); + + +/* Initialize the thread specific data structures. THREAD must be the + calling thread. */ +extern error_t __pthread_init_specific (struct __pthread *thread); + +/* Call the destructors on all of the thread specific data in THREAD. + THREAD must be the calling thread. */ +extern void __pthread_destroy_specific (struct __pthread *thread); + + +/* Initialize newly create thread *THREAD's signal state data + structures. */ +extern error_t __pthread_sigstate_init (struct __pthread *thread); + +/* Destroy the signal state data structures associcated with thread + *THREAD. */ +extern void __pthread_sigstate_destroy (struct __pthread *thread); + +/* Modify thread *THREAD's signal state. */ +extern error_t __pthread_sigstate (struct __pthread *__restrict thread, int how, + const sigset_t *__restrict set, + sigset_t *__restrict oset, + int clear_pending); + + +/* Default thread attributes. */ +extern const struct __pthread_attr __pthread_default_attr; + +/* Default barrier attributes. */ +extern const struct __pthread_barrierattr __pthread_default_barrierattr; + +/* Default mutex attributes. */ +extern const struct __pthread_mutexattr __pthread_default_mutexattr; + +/* Default rdlock attributes. */ +extern const struct __pthread_rwlockattr __pthread_default_rwlockattr; + +/* Default condition attributes. */ +extern const struct __pthread_condattr __pthread_default_condattr; + +#endif /* pt-internal.h */ diff --git a/htl/pt-join.c b/htl/pt-join.c new file mode 100644 index 0000000000..329163d514 --- /dev/null +++ b/htl/pt-join.c @@ -0,0 +1,75 @@ +/* Wait for thread termination. + Copyright (C) 2000-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <pthread.h> +#include <stddef.h> + +#include <pt-internal.h> + +/* Make calling thread wait for termination of thread THREAD. Return + the exit status of the thread in *STATUS. */ +int +pthread_join (pthread_t thread, void **status) +{ + struct __pthread *pthread; + int err = 0; + + /* Lookup the thread structure for THREAD. */ + pthread = __pthread_getid (thread); + if (pthread == NULL) + return ESRCH; + + __pthread_mutex_lock (&pthread->state_lock); + pthread_cleanup_push ((void (*)(void *)) __pthread_mutex_unlock, + &pthread->state_lock); + + /* Rely on pthread_cond_wait being a cancellation point to make + pthread_join one too. */ + while (pthread->state == PTHREAD_JOINABLE) + __pthread_cond_wait (&pthread->state_cond, &pthread->state_lock); + + pthread_cleanup_pop (0); + + switch (pthread->state) + { + case PTHREAD_EXITED: + /* THREAD has already exited. Salvage its exit status. */ + if (status != NULL) + *status = pthread->status; + + __pthread_mutex_unlock (&pthread->state_lock); + + __pthread_dealloc (pthread); + break; + + case PTHREAD_TERMINATED: + /* Pretend THREAD wasn't there in the first place. */ + __pthread_mutex_unlock (&pthread->state_lock); + err = ESRCH; + break; + + default: + /* Thou shalt not join non-joinable threads! */ + __pthread_mutex_unlock (&pthread->state_lock); + err = EINVAL; + break; + } + + return err; +} diff --git a/htl/pt-self.c b/htl/pt-self.c new file mode 100644 index 0000000000..0f932ac1f4 --- /dev/null +++ b/htl/pt-self.c @@ -0,0 +1,33 @@ +/* Get calling thread's ID. + Copyright (C) 2000-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#include <pthread.h> + +#include <pt-internal.h> + +/* Return the thread ID of the calling thread. */ +pthread_t +__pthread_self (void) +{ + struct __pthread *self = _pthread_self (); + assert (self != NULL); + + return self->thread; +} + +strong_alias (__pthread_self, pthread_self); diff --git a/htl/pt-setcancelstate.c b/htl/pt-setcancelstate.c new file mode 100644 index 0000000000..de5c6880e6 --- /dev/null +++ b/htl/pt-setcancelstate.c @@ -0,0 +1,46 @@ +/* Set the cancel state for the calling thread. + Copyright (C) 2002-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#include <pthread.h> + +#include <pt-internal.h> + +int +__pthread_setcancelstate (int state, int *oldstate) +{ + struct __pthread *p = _pthread_self (); + + switch (state) + { + default: + return EINVAL; + case PTHREAD_CANCEL_ENABLE: + case PTHREAD_CANCEL_DISABLE: + break; + } + + __pthread_mutex_lock (&p->cancel_lock); + if (oldstate != NULL) + *oldstate = p->cancel_state; + p->cancel_state = state; + __pthread_mutex_unlock (&p->cancel_lock); + + return 0; +} + +strong_alias (__pthread_setcancelstate, pthread_setcancelstate); diff --git a/htl/pt-setcanceltype.c b/htl/pt-setcanceltype.c new file mode 100644 index 0000000000..0f29a43a9a --- /dev/null +++ b/htl/pt-setcanceltype.c @@ -0,0 +1,46 @@ +/* Set the cancel type for the calling thread. + Copyright (C) 2002-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#include <pthread.h> + +#include <pt-internal.h> + +int +__pthread_setcanceltype (int type, int *oldtype) +{ + struct __pthread *p = _pthread_self (); + + switch (type) + { + default: + return EINVAL; + case PTHREAD_CANCEL_DEFERRED: + case PTHREAD_CANCEL_ASYNCHRONOUS: + break; + } + + __pthread_mutex_lock (&p->cancel_lock); + if (oldtype != NULL) + *oldtype = p->cancel_type; + p->cancel_type = type; + __pthread_mutex_unlock (&p->cancel_lock); + + return 0; +} + +strong_alias (__pthread_setcanceltype, pthread_setcanceltype); diff --git a/htl/pt-sigmask.c b/htl/pt-sigmask.c new file mode 100644 index 0000000000..f55c508dfb --- /dev/null +++ b/htl/pt-sigmask.c @@ -0,0 +1,31 @@ +/* Get or set a thread's signal mask. + Copyright (C) 2000-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#include <pthread.h> +#include <signal.h> + +#include <pt-internal.h> + +int +pthread_sigmask (int how, const sigset_t *set, sigset_t *oset) +{ + struct __pthread *self = _pthread_self (); + + /* Do not clear SELF's pending signals. */ + return __pthread_sigstate (self, how, set, oset, 0); +} diff --git a/htl/pt-spin-inlines.c b/htl/pt-spin-inlines.c new file mode 100644 index 0000000000..379723524e --- /dev/null +++ b/htl/pt-spin-inlines.c @@ -0,0 +1,33 @@ +/* Spin locks non-inline functions. + Copyright (C) 2000-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +/* <bits/types/__pthread_spinlock_t.h> declares some extern inline functions. These + functions are declared additionally here for use when inlining is + not possible. */ + +#define _FORCE_INLINES +#define __PT_SPIN_INLINE /* empty */ + +#include <pthread.h> + +/* Weak aliases for the spin lock functions. */ +weak_alias (__pthread_spin_destroy, pthread_spin_destroy); +weak_alias (__pthread_spin_init, pthread_spin_init); +weak_alias (__pthread_spin_trylock, pthread_spin_trylock); +weak_alias (__pthread_spin_lock, pthread_spin_lock); +weak_alias (__pthread_spin_unlock, pthread_spin_unlock); diff --git a/htl/pt-testcancel.c b/htl/pt-testcancel.c new file mode 100644 index 0000000000..783a6a6001 --- /dev/null +++ b/htl/pt-testcancel.c @@ -0,0 +1,35 @@ +/* Add an explicit cancelation point. + Copyright (C) 2002-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#include <pthread.h> + +#include <pt-internal.h> + +void +pthread_testcancel (void) +{ + struct __pthread *p = _pthread_self (); + int cancelled; + + __pthread_mutex_lock (&p->cancel_lock); + cancelled = (p->cancel_state == PTHREAD_CANCEL_ENABLE) && p->cancel_pending; + __pthread_mutex_unlock (&p->cancel_lock); + + if (cancelled) + pthread_exit (PTHREAD_CANCELED); +} diff --git a/htl/pt-yield.c b/htl/pt-yield.c new file mode 100644 index 0000000000..0dee959b87 --- /dev/null +++ b/htl/pt-yield.c @@ -0,0 +1,26 @@ +/* Yield the processor to another thread or process. + Copyright (C) 2010-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#include <pthread.h> +#include <sched.h> + +int +pthread_yield (void) +{ + return sched_yield (); +} diff --git a/htl/shlib-versions b/htl/shlib-versions new file mode 100644 index 0000000000..98e07a6c33 --- /dev/null +++ b/htl/shlib-versions @@ -0,0 +1 @@ +libpthread=0.3 diff --git a/htl/tests/Makefile b/htl/tests/Makefile new file mode 100644 index 0000000000..7177ad181e --- /dev/null +++ b/htl/tests/Makefile @@ -0,0 +1,40 @@ +ifdef INSTALL_ROOT +INSTALL_ROOT_CPPFLAGS = -isystem $(INSTALL_ROOT)/include +INSTALL_ROOT_LDFLAGS = -L$(INSTALL_ROOT)/lib -Wl,-rpath,$(INSTALL_ROOT)/lib +endif + +CFLAGS=-Wall -g + +LDLIBS = -lpthread + +CHECK_SRC := test-1.c test-2.c test-3.c test-6.c test-7.c test-8.c \ + test-9.c test-10.c test-11.c test-12.c test-13.c test-14.c \ + test-15.c test-16.c test-17.c test-__pthread_destroy_specific-skip.c + +CHECK_OBJS := $(addsuffix .o,$(basename $(notdir $(CHECK_SRC)))) +CHECK_PROGS := $(basename $(notdir $(CHECK_SRC))) \ + $(addsuffix -static, $(basename $(CHECK_SRC))) + +%.o: %.c + $(CC) $(INSTALL_ROOT_CPPFLAGS) $(CPPFLAGS) $(CFLAGS) $< -c -o $@ + +%: %.o + $(CC) $(INSTALL_ROOT_LDFLAGS) $(LDFLAGS) $< -o $@ $(LDLIBS) + +%-static: %.o + $(CC) -static $(INSTALL_ROOT_LDFLAGS) $(LDFLAGS) $< -o $@ $(LDLIBS) + +check: $(CHECK_OBJS) $(CHECK_PROGS) + for i in $(CHECK_PROGS); do \ + echo -n Running $$i...\ ; \ + if ./$$i 2>&1 > $$i.out; \ + then \ + echo Success.; \ + else \ + echo Failure.; \ + fi \ + done + +clean: + rm -f $(CHECK_OBJS) $(CHECK_PROGS) \ + $(addsuffix .out,$(basename $(notdir $(CHECK_PROGS)))) \ No newline at end of file diff --git a/htl/tests/README b/htl/tests/README new file mode 100644 index 0000000000..230f1b24f0 --- /dev/null +++ b/htl/tests/README @@ -0,0 +1,6 @@ +Testing of installed package: + + $ [libpthread]/configure --prefix=[install_root] + $ make + $ make install + $ make -C [libpthread]/tests/ INSTALL_ROOT=[install_root] clean check diff --git a/htl/tests/test-1.c b/htl/tests/test-1.c new file mode 100644 index 0000000000..25263ee2f2 --- /dev/null +++ b/htl/tests/test-1.c @@ -0,0 +1,68 @@ +/* Test mutexes. + Copyright (C) 2000-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#define _GNU_SOURCE + +#include <pthread.h> +#include <assert.h> +#include <unistd.h> +#include <error.h> +#include <errno.h> +#include <stdio.h> + +#define THREADS 500 + +void * +foo (void *arg) +{ + pthread_mutex_t *mutex = arg; + pthread_mutex_lock (mutex); + pthread_mutex_unlock (mutex); + return mutex; +} + +int +main (int argc, char **argv) +{ + int i; + error_t err; + pthread_t tid[THREADS]; + pthread_mutex_t mutex[THREADS]; + + for (i = 0; i < THREADS; i++) + { + pthread_mutex_init (&mutex[i], 0); + pthread_mutex_lock (&mutex[i]); + err = pthread_create (&tid[i], 0, foo, &mutex[i]); + if (err) + error (1, err, "pthread_create"); + sched_yield (); + } + + for (i = THREADS - 1; i >= 0; i--) + { + void *ret; + pthread_mutex_unlock (&mutex[i]); + err = pthread_join (tid[i], &ret); + if (err) + error (1, err, "pthread_join"); + assert (ret == &mutex[i]); + } + + return 0; +} diff --git a/htl/tests/test-10.c b/htl/tests/test-10.c new file mode 100644 index 0000000000..31d3449d04 --- /dev/null +++ b/htl/tests/test-10.c @@ -0,0 +1,62 @@ +/* Test error checking mutexes. + Copyright (C) 2000-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#define _GNU_SOURCE + +#include <pthread.h> +#include <assert.h> +#include <error.h> +#include <errno.h> + +int +main (int argc, char **argv) +{ + error_t err; + pthread_mutexattr_t mattr; + pthread_mutex_t mutex; + + err = pthread_mutexattr_init (&mattr); + if (err) + error (1, err, "pthread_mutexattr_init"); + + err = pthread_mutexattr_settype (&mattr, PTHREAD_MUTEX_ERRORCHECK); + if (err) + error (1, err, "pthread_mutexattr_settype"); + + err = pthread_mutex_init (&mutex, &mattr); + if (err) + error (1, err, "pthread_mutex_init"); + + err = pthread_mutexattr_destroy (&mattr); + if (err) + error (1, err, "pthread_mutexattr_destroy"); + + err = pthread_mutex_lock (&mutex); + assert (err == 0); + + err = pthread_mutex_lock (&mutex); + assert (err == EDEADLK); + + err = pthread_mutex_unlock (&mutex); + assert (err == 0); + + err = pthread_mutex_unlock (&mutex); + assert (err == EPERM); + + return 0; +} diff --git a/htl/tests/test-11.c b/htl/tests/test-11.c new file mode 100644 index 0000000000..f8c7a427e8 --- /dev/null +++ b/htl/tests/test-11.c @@ -0,0 +1,159 @@ +/* Test rwlocks. + Copyright (C) 2000-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#define _GNU_SOURCE + +#include <pthread.h> +#include <assert.h> +#include <error.h> +#include <errno.h> + +#define THREADS 1 + +int a; +int b; + +/* Get a read lock and assert that a == b. */ +void * +test1 (void *arg) +{ + error_t err; + pthread_rwlock_t *lock = arg; + int i; + + for (i = 0; i < 200; i++) + { + err = pthread_rwlock_rdlock (lock); + assert (err == 0); + + assert (a == b); + + sched_yield (); + + assert (a == b); + + err = pthread_rwlock_unlock (lock); + assert (err == 0); + } + + return 0; +} + +int +main (int argc, char **argv) +{ + error_t err; + pthread_rwlockattr_t attr; + pthread_rwlock_t lock; + int pshared; + + int i; + pthread_t tid[THREADS]; + void *ret; + + err = pthread_rwlockattr_init (&attr); + if (err) + error (1, err, "pthread_rwlockattr_init"); + + err = pthread_rwlockattr_getpshared (&attr, &pshared); + if (err) + error (1, err, "pthread_rwlockattr_getpshared"); + + /* Assert the default state as mandated by POSIX. */ + assert (pshared == PTHREAD_PROCESS_PRIVATE); + + err = pthread_rwlockattr_setpshared (&attr, pshared); + if (err) + error (1, err, "pthread_rwlockattr_setpshared"); + + err = pthread_rwlock_init (&lock, &attr); + if (err) + error (1, err, "pthread_rwlock_init"); + + err = pthread_rwlockattr_destroy (&attr); + if (err) + error (1, err, "pthread_rwlockattr_destroy"); + + /* Now test the lock. */ + + for (i = 0; i < THREADS; i++) + { + err = pthread_create (&tid[i], 0, test1, &lock); + if (err) + error (1, err, "pthread_create"); + } + + for (i = 0; i < 10; i++) + { + sched_yield (); + + /* Get a write lock. */ + pthread_rwlock_wrlock (&lock); + /* Increment a and b giving other threads a chance to run in + between. */ + sched_yield (); + a++; + sched_yield (); + b++; + sched_yield (); + /* Unlock. */ + pthread_rwlock_unlock (&lock); + } + + for (i = 0; i < THREADS; i++) + { + err = pthread_join (tid[i], &ret); + if (err) + error (1, err, "pthread_join"); + } + + /* Read lock it. */ + err = pthread_rwlock_tryrdlock (&lock); + assert (err == 0); + + /* Try to write lock it. It should fail with EBUSY. */ + err = pthread_rwlock_trywrlock (&lock); + assert (err == EBUSY); + + /* Drop the read lock. */ + err = pthread_rwlock_unlock (&lock); + assert (err == 0); + + /* Get a write lock. */ + err = pthread_rwlock_trywrlock (&lock); + assert (err == 0); + + /* Fail trying to acquire another write lock. */ + err = pthread_rwlock_trywrlock (&lock); + assert (err == EBUSY); + + /* Try to get a read lock which should also fail. */ + err = pthread_rwlock_tryrdlock (&lock); + assert (err == EBUSY); + + /* Unlock it. */ + err = pthread_rwlock_unlock (&lock); + assert (err == 0); + + + err = pthread_rwlock_destroy (&lock); + if (err) + error (1, err, "pthread_rwlock_destroy"); + + return 0; +} diff --git a/htl/tests/test-12.c b/htl/tests/test-12.c new file mode 100644 index 0000000000..9676446182 --- /dev/null +++ b/htl/tests/test-12.c @@ -0,0 +1,45 @@ +/* Test concurrency level. + Copyright (C) 2000-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#define _GNU_SOURCE + +#include <pthread.h> +#include <assert.h> +#include <error.h> +#include <errno.h> + +int +main (int argc, char **argv) +{ + int i; + int err; + + i = pthread_getconcurrency (); + assert (i == 0); + + err = pthread_setconcurrency (-1); + assert (err == EINVAL); + + err = pthread_setconcurrency (4); + assert (err == 0); + + i = pthread_getconcurrency (); + assert (i == 4); + + return 0; +} diff --git a/htl/tests/test-13.c b/htl/tests/test-13.c new file mode 100644 index 0000000000..d14214981e --- /dev/null +++ b/htl/tests/test-13.c @@ -0,0 +1,82 @@ +/* Test condition attributes and pthread_cond_timedwait. + Copyright (C) 2000-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#define _GNU_SOURCE + +#include <pthread.h> +#include <stdio.h> +#include <assert.h> +#include <error.h> +#include <errno.h> +#include <sys/time.h> + +int +main (int argc, char **argv) +{ + error_t err; + int i; + pthread_condattr_t attr; + pthread_cond_t cond; + struct timespec ts; + pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; + struct timeval before, after; + int diff; + + err = pthread_condattr_init (&attr); + if (err) + error (1, err, "pthread_condattr_init"); + + err = pthread_condattr_getpshared (&attr, &i); + if (err) + error (1, err, "pthread_condattr_getpshared"); + assert (i == PTHREAD_PROCESS_PRIVATE); + + err = pthread_condattr_setpshared (&attr, PTHREAD_PROCESS_PRIVATE); + assert (err == 0); + + err = pthread_cond_init (&cond, &attr); + if (err) + error (1, err, "pthread_cond_init"); + + err = pthread_condattr_destroy (&attr); + if (err) + error (1, err, "pthread_condattr_destroy"); + + gettimeofday (&before, 0); + ts.tv_sec = before.tv_sec + 1; + ts.tv_nsec = before.tv_usec * 1000; + + printf ("Starting wait @ %d\n", (int) before.tv_sec); + + pthread_mutex_lock (&m); + err = pthread_cond_timedwait (&cond, &m, &ts); + + gettimeofday (&after, 0); + + printf ("End wait @ %d (err = %d)\n", (int) after.tv_sec, err); + + assert (err == ETIMEDOUT); + + diff = after.tv_sec * 1000000 + after.tv_usec + - before.tv_sec * 1000000 - before.tv_usec; + + if (diff < 900000 || diff > 1100000) + error (1, EGRATUITOUS, "pthread_cond_timedwait waited %d us", diff); + + return 0; +} diff --git a/htl/tests/test-14.c b/htl/tests/test-14.c new file mode 100644 index 0000000000..6e7cef12fe --- /dev/null +++ b/htl/tests/test-14.c @@ -0,0 +1,60 @@ +/* Test pthread_mutex_timedlock. + Copyright (C) 2000-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#define _GNU_SOURCE + +#include <pthread.h> +#include <stdio.h> +#include <assert.h> +#include <error.h> +#include <errno.h> +#include <sys/time.h> + +int +main (int argc, char **argv) +{ + error_t err; + struct timespec ts; + pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; + struct timeval before, after; + int diff; + + gettimeofday (&before, 0); + ts.tv_sec = before.tv_sec + 1; + ts.tv_nsec = before.tv_usec * 1000; + + printf ("Starting wait @ %d\n", (int) before.tv_sec); + + pthread_mutex_lock (&m); + /* A default mutex shall dead lock if locked twice. As such we do + not need spawn a second thread. */ + err = pthread_mutex_timedlock (&m, &ts); + assert (err == ETIMEDOUT); + + gettimeofday (&after, 0); + + printf ("End wait @ %d\n", (int) after.tv_sec); + + diff = after.tv_sec * 1000000 + after.tv_usec + - before.tv_sec * 1000000 - before.tv_usec; + + if (diff < 900000 || diff > 1100000) + error (1, EGRATUITOUS, "pthread_mutex_timedlock waited %d us", diff); + + return 0; +} diff --git a/htl/tests/test-15.c b/htl/tests/test-15.c new file mode 100644 index 0000000000..baf866aae4 --- /dev/null +++ b/htl/tests/test-15.c @@ -0,0 +1,102 @@ +/* Test pthread_rwlock_timedrdlock and pthread_rwlock_timedwrlock. + Copyright (C) 2000-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#define _GNU_SOURCE + +#include <pthread.h> +#include <stdio.h> +#include <assert.h> +#include <error.h> +#include <errno.h> +#include <sys/time.h> + +#define THREADS 10 + +pthread_rwlock_t rwlock; + +void * +test (void *arg) +{ + error_t err; + int foo = (int) arg; + struct timespec ts; + struct timeval before, after; + int diff; + + gettimeofday (&before, 0); + ts.tv_sec = before.tv_sec + 1; + ts.tv_nsec = before.tv_usec * 1000; + + printf ("Thread %d starting wait @ %d\n", pthread_self (), + (int) before.tv_sec); + + if (foo % 2 == 0) + err = pthread_rwlock_timedrdlock (&rwlock, &ts); + else + err = pthread_rwlock_timedwrlock (&rwlock, &ts); + + assert (err == ETIMEDOUT); + + gettimeofday (&after, 0); + + printf ("Thread %d ending wait @ %d\n", pthread_self (), (int) after.tv_sec); + + diff = after.tv_sec * 1000000 + after.tv_usec + - before.tv_sec * 1000000 - before.tv_usec; + + if (diff < 900000 || diff > 1100000) + error (1, EGRATUITOUS, "pthread_mutex_timedlock waited %d us", diff); + + return 0; +} + +int +main (int argc, char **argv) +{ + error_t err; + int i; + pthread_t tid[THREADS]; + + err = pthread_rwlock_init (&rwlock, 0); + if (err) + error (1, err, "pthread_rwlock_init"); + + /* Lock it so all the threads will block. */ + err = pthread_rwlock_wrlock (&rwlock); + assert (err == 0); + + for (i = 0; i < THREADS; i++) + { + err = pthread_create (&tid[i], 0, test, (void *) i); + if (err) + error (1, err, "pthread_create"); + } + + for (i = 0; i < THREADS; i++) + { + void *ret; + + err = pthread_join (tid[i], &ret); + if (err) + error (1, err, "pthread_join"); + + assert (ret == 0); + } + + return 0; +} diff --git a/htl/tests/test-16.c b/htl/tests/test-16.c new file mode 100644 index 0000000000..519aa84547 --- /dev/null +++ b/htl/tests/test-16.c @@ -0,0 +1,87 @@ +/* Test pthread_kill.c. + Copyright (C) 2000-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#define _GNU_SOURCE + +#include <pthread.h> +#include <signal.h> +#include <stdio.h> +#include <assert.h> +#include <error.h> +#include <errno.h> +#include <hurd/signal.h> + +pthread_t testthread; + +int i; + +void * +test (void *arg) +{ + error_t err; + + printf ("test: %d\n", pthread_self ()); + + err = pthread_kill (pthread_self (), SIGINFO); + if (err) + error (1, err, "pthread_kill"); + + /* To avoid using condition variables in a signal handler. */ + while (i == 0) + sched_yield (); + + return 0; +} + +static void +handler (int sig) +{ + assert (pthread_equal (pthread_self (), testthread)); + printf ("handler: %d\n", pthread_self ()); + i = 1; +} + +int +main (int argc, char **argv) +{ + error_t err; + struct sigaction sa; + void *ret; + + printf ("main: %d\n", pthread_self ()); + + sa.sa_handler = handler; + sa.sa_mask = 0; + sa.sa_flags = 0; + + err = sigaction (SIGINFO, &sa, 0); + if (err) + error (1, err, "sigaction"); + + err = pthread_create (&testthread, 0, test, 0); + if (err) + error (1, err, "pthread_create"); + + err = pthread_join (testthread, &ret); + if (err) + error (1, err, "pthread_join"); + + assert (ret == 0); + + return 0; +} diff --git a/htl/tests/test-17.c b/htl/tests/test-17.c new file mode 100644 index 0000000000..ffc90b5f74 --- /dev/null +++ b/htl/tests/test-17.c @@ -0,0 +1,73 @@ +/* Test that the key reuse inside libpthread does not cause thread + specific values to persist. + Copyright (C) 2000-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#define _GNU_SOURCE 1 + +#include <pthread.h> +#include <stdio.h> +#include <assert.h> +#include <errno.h> + +void +work (int iter) +{ + error_t err; + pthread_key_t key1; + pthread_key_t key2; + void *value1; + void *value2; + + printf ("work/%d: start\n", iter); + err = pthread_key_create (&key1, NULL); + assert (err == 0); + err = pthread_key_create (&key2, NULL); + assert (err == 0); + + value1 = pthread_getspecific (key1); + value2 = pthread_getspecific (key2); + printf ("work/%d: pre-setspecific: %p,%p\n", iter, value1, value2); + assert (value1 == NULL); + assert (value2 == NULL); + err = pthread_setspecific (key1, (void *) (0x100 + iter)); + assert (err == 0); + err = pthread_setspecific (key2, (void *) (0x200 + iter)); + assert (err == 0); + + value1 = pthread_getspecific (key1); + value2 = pthread_getspecific (key2); + printf ("work/%d: post-setspecific: %p,%p\n", iter, value1, value2); + assert (value1 == (void *) (0x100 + iter)); + assert (value2 == (void *) (0x200 + iter)); + + err = pthread_key_delete (key1); + assert (err == 0); + err = pthread_key_delete (key2); + assert (err == 0); +} + +int +main (int argc, char *argv[]) +{ + int i; + + for (i = 0; i < 8; ++i) + work (i + 1); + + return 0; +} diff --git a/htl/tests/test-2.c b/htl/tests/test-2.c new file mode 100644 index 0000000000..1194a3427a --- /dev/null +++ b/htl/tests/test-2.c @@ -0,0 +1,56 @@ +/* Test detachability. + Copyright (C) 2000-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#define _GNU_SOURCE + +#include <pthread.h> +#include <assert.h> +#include <error.h> +#include <errno.h> +#include <unistd.h> + +void * +thread (void *arg) +{ + while (1) + sched_yield (); +} + +int +main (int argc, char **argv) +{ + int err; + pthread_t tid; + void *ret; + + err = pthread_create (&tid, 0, thread, 0); + if (err) + error (1, err, "pthread_create"); + + err = pthread_detach (tid); + if (err) + error (1, err, "pthread_detach"); + + err = pthread_detach (tid); + assert (err == EINVAL); + + err = pthread_join (tid, &ret); + assert (err == EINVAL); + + return 0; +} diff --git a/htl/tests/test-3.c b/htl/tests/test-3.c new file mode 100644 index 0000000000..beed473d3c --- /dev/null +++ b/htl/tests/test-3.c @@ -0,0 +1,71 @@ +/* Test the thread attribute get and set methods. + Copyright (C) 2000-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#define _GNU_SOURCE + +#include <pthread.h> +#include <sched.h> +#include <assert.h> +#include <errno.h> + +int +main (int argc, char *argv[]) +{ + error_t err; + pthread_attr_t attr; + + int i; + struct sched_param sp; + void *p; + size_t sz; + + err = pthread_attr_init (&attr); + assert_perror (err); + + err = pthread_attr_destroy (&attr); + assert_perror (err); + + err = pthread_attr_init (&attr); + assert_perror (err); + +#define TEST1(foo, rv, v) \ + err = pthread_attr_get##foo (&attr, rv); \ + assert_perror (err); \ + \ + err = pthread_attr_set##foo (&attr, v); \ + assert_perror (err); + +#define TEST(foo, rv, v) TEST1(foo, rv, v) + + TEST (inheritsched, &i, i); + TEST (schedparam, &sp, &sp); + TEST (schedpolicy, &i, i); + TEST (scope, &i, i); + TEST (stackaddr, &p, p); + TEST (detachstate, &i, i); + TEST (guardsize, &sz, sz); + TEST (stacksize, &sz, sz); + + err = pthread_attr_getstack (&attr, &p, &sz); + assert_perror (err); + + err = pthread_attr_setstack (&attr, p, sz); + assert_perror (err); + + return 0; +} diff --git a/htl/tests/test-4.c b/htl/tests/test-4.c new file mode 100644 index 0000000000..b8c24b78f1 --- /dev/null +++ b/htl/tests/test-4.c @@ -0,0 +1,102 @@ +/* Test the stack guard. + Copyright (C) 2000-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#define _GNU_SOURCE + +#include <pthread.h> +#include <assert.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <stdio.h> + +size_t stacksize; + +void * +thr (void *arg) +{ + int i; + char *foo; + + foo = alloca (3 * stacksize / 4); + for (i = 0; i < sizeof foo; i++) + foo[i] = -1; + + return (void *) 1; +} + +int +main (int argc, char *argv[]) +{ + error_t err; + pid_t child; + + child = fork (); + switch (child) + { + case -1: + error (1, errno, "fork"); + break; + + case 0: + { + pthread_attr_t attr; + pthread_t tid; + void *ret; + + err = pthread_attr_init (&attr); + assert_perror (err); + + err = pthread_attr_getstacksize (&attr, &stacksize); + assert_perror (err); + + err = pthread_attr_setguardsize (&attr, stacksize / 2); + if (err == ENOTSUP) + { + printf ("Stack guard attribute not supported.\n"); + return 1; + } + assert_perror (err); + + err = pthread_create (&tid, &attr, thr, 0); + assert_perror (err); + + err = pthread_attr_destroy (&attr); + assert_perror (err); + + err = pthread_join (tid, &ret); + /* Should never be successful. */ + printf ("Thread did not segfault!?!\n"); + assert_perror (err); + return 0; + } + + default: + { + pid_t pid; + int status; + + pid = waitpid (child, &status, 0); + printf ("pid = %d; child = %d; status = %d\n", pid, child, status); + assert (pid == child); + assert (status != 0); + } + } + + return 0; +} diff --git a/htl/tests/test-5.c b/htl/tests/test-5.c new file mode 100644 index 0000000000..ef9691c869 --- /dev/null +++ b/htl/tests/test-5.c @@ -0,0 +1,91 @@ +/* Test signals. + Copyright (C) 2000-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#define _GNU_SOURCE + +#include <pthread.h> +#include <unistd.h> +#include <stdio.h> +#include <errno.h> +#include <error.h> +#include <assert.h> +#include <sys/resource.h> +#include <sys/wait.h> + +void * +thr (void *arg) +{ + *(int *) 0 = 0; + return 0; +} + +int foobar; + +int +main (int argc, char *argv[]) +{ + error_t err; + pid_t child; + + struct rlimit limit; + + limit.rlim_cur = 0; + limit.rlim_max = 0; + + err = setrlimit (RLIMIT_CORE, &limit); + if (err) + error (1, err, "setrlimit"); + + child = fork (); + switch (child) + { + case -1: + error (1, errno, "fork"); + break; + + case 0: + { + pthread_t tid; + void *ret; + + err = pthread_create (&tid, 0, thr, 0); + if (err) + error (1, err, "pthread_create"); + + err = pthread_join (tid, &ret); + assert_perror (err); + + /* Should have never returned. Our parent expects us to fail + thus we succeed and indicate the error. */ + return 0; + } + + default: + { + pid_t pid; + int status; + + pid = waitpid (child, &status, 0); + printf ("pid = %d; child = %d; status = %d\n", pid, child, status); + assert (pid == child); + assert (status != 0); + } + } + + return 0; +} diff --git a/htl/tests/test-6.c b/htl/tests/test-6.c new file mode 100644 index 0000000000..e25d419e6f --- /dev/null +++ b/htl/tests/test-6.c @@ -0,0 +1,114 @@ +/* Test barriers. + Copyright (C) 2000-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#define _GNU_SOURCE + +#include <pthread.h> +#include <stdio.h> +#include <error.h> +#include <assert.h> +#include <errno.h> + +#define THREADS 500 +#define WAITS 3 + +void * +dowait (void *arg) +{ + pthread_barrier_t *barrier = arg; + int ret; + + ret = pthread_barrier_wait (barrier); + printf ("%d ", pthread_self ()); + return (void *) ret; +} + +int +main (int argc, char **argv) +{ + pthread_barrierattr_t attr; + pthread_barrier_t barrier; + + int i, j; + error_t err; + pthread_t tid[THREADS]; + + int havesyncs; + + err = pthread_barrierattr_init (&attr); + if (err) + error (1, err, "pthread_barrierattr_init"); + + err = pthread_barrierattr_getpshared (&attr, &i); + if (err) + error (1, err, "pthread_barrierattr_getpshared"); + assert (i == PTHREAD_PROCESS_PRIVATE || i == PTHREAD_PROCESS_SHARED); + + err = pthread_barrierattr_setpshared (&attr, PTHREAD_PROCESS_PRIVATE); + if (err) + error (1, err, "pthread_barrierattr_setpshared"); + + err = pthread_barrier_init (&barrier, &attr, THREADS + 1); + if (err) + error (1, err, "pthread_barrier_init"); + + for (j = 0; j < WAITS; j++) + { + + for (i = 0; i < THREADS; i++) + { + err = pthread_create (&tid[i], 0, dowait, &barrier); + if (err) + error (1, err, "pthread_create (%d)", i); + } + + printf ("Manager will now call pthread_barrier_wait.\n"); + + havesyncs + = pthread_barrier_wait (&barrier) == PTHREAD_BARRIER_SERIAL_THREAD + ? 1 : 0; + + for (i = THREADS - 1; i >= 0; i--) + { + void *ret; + err = pthread_join (tid[i], &ret); + if (err) + error (1, err, "pthread_join"); + + switch ((int) ret) + { + case 0: + break; + + case PTHREAD_BARRIER_SERIAL_THREAD: + havesyncs++; + break; + + default: + assert (!"Unknown value returned from pthread_barrier_wait."); + break; + } + } + + printf ("\n"); + + assert (havesyncs == 1); + } + + return 0; +} diff --git a/htl/tests/test-7.c b/htl/tests/test-7.c new file mode 100644 index 0000000000..4823de2486 --- /dev/null +++ b/htl/tests/test-7.c @@ -0,0 +1,89 @@ +/* Test Thread-Specific Data. + Copyright (C) 2000-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#define _GNU_SOURCE + +#include <pthread.h> +#include <assert.h> +#include <stdio.h> +#include <error.h> +#include <errno.h> + +#define THREADS 10 +#define KEYS 400 + +pthread_key_t key[KEYS]; + +void * +thr (void *arg) +{ + error_t err; + int i; + + for (i = 0; i < KEYS; i++) + { + printf ("pthread_getspecific(%d).\n", key[i]); + assert (pthread_getspecific (key[i]) == NULL); + printf ("pthread_setspecific(%d, %d).\n", key[i], pthread_self ()); + err = pthread_setspecific (key[i], (void *) pthread_self ()); + printf ("pthread_setspecific(%d, %d) => %d.\n", key[i], pthread_self (), + err); + assert_perror (err); + } + + return 0; +} + +int +main (int argc, char **argv) +{ + error_t err; + int i; + pthread_t tid[THREADS]; + + void des (void *val) + { + assert ((pthread_t) val == pthread_self ()); + } + + assert (pthread_getspecific ((pthread_key_t) 0) == NULL); + assert (pthread_setspecific ((pthread_key_t) 0, (void *) 0x1) == EINVAL); + + for (i = 0; i < KEYS; i++) + err = pthread_key_create (&key[i], des); + + for (i = 0; i < THREADS; i++) + { + err = pthread_create (&tid[i], 0, thr, 0); + if (err) + error (1, err, "pthread_create (%d)", i); + } + + for (i = 0; i < THREADS; i++) + { + void *ret; + + err = pthread_join (tid[i], &ret); + if (err) + error (1, err, "pthread_join"); + + assert (ret == 0); + } + + return 0; +} diff --git a/htl/tests/test-8.c b/htl/tests/test-8.c new file mode 100644 index 0000000000..9cf74a70c4 --- /dev/null +++ b/htl/tests/test-8.c @@ -0,0 +1,78 @@ +/* Test pthread_once. + Copyright (C) 2000-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#define _GNU_SOURCE + +#include <pthread.h> +#include <assert.h> +#include <error.h> +#include <errno.h> + +#define THREADS 10 + +pthread_once_t inc_var_once = PTHREAD_ONCE_INIT; +int var; + +void +inc_var (void) +{ + var++; +} + +void * +thr (void *arg) +{ + int i; + + for (i = 0; i < 500; i++) + pthread_once (&inc_var_once, inc_var); + + return 0; +} + +int +main (int argc, char **argv) +{ + error_t err; + int i; + pthread_t tid[THREADS]; + + for (i = 0; i < THREADS; i++) + { + err = pthread_create (&tid[i], 0, thr, 0); + if (err) + error (1, err, "pthread_create (%d)", i); + } + + assert (thr (0) == 0); + + for (i = 0; i < THREADS; i++) + { + void *ret; + + err = pthread_join (tid[i], &ret); + if (err) + error (1, err, "pthread_join"); + + assert (ret == 0); + } + + assert (var == 1); + + return 0; +} diff --git a/htl/tests/test-9.c b/htl/tests/test-9.c new file mode 100644 index 0000000000..ad6bc36de5 --- /dev/null +++ b/htl/tests/test-9.c @@ -0,0 +1,104 @@ +/* Test recursive mutexes. + Copyright (C) 2000-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#define _GNU_SOURCE + +#include <pthread.h> +#include <assert.h> +#include <error.h> +#include <errno.h> + +#define THREADS 10 + +int foo; + +void * +thr (void *arg) +{ + int i; + + pthread_mutex_lock (arg); + + foo = pthread_self (); + + for (i = 0; i < 500; i++) + pthread_mutex_lock (arg); + for (i = 0; i < 500; i++) + pthread_mutex_unlock (arg); + + assert (foo == pthread_self ()); + + pthread_mutex_unlock (arg); + + return 0; +} + +int +main (int argc, char **argv) +{ + error_t err; + int i; + pthread_t tid[THREADS]; + pthread_mutexattr_t mattr; + pthread_mutex_t mutex; + + err = pthread_mutexattr_init (&mattr); + if (err) + error (1, err, "pthread_mutexattr_init"); + + err = pthread_mutexattr_settype (&mattr, PTHREAD_MUTEX_RECURSIVE); + if (err) + error (1, err, "pthread_mutexattr_settype"); + + err = pthread_mutex_init (&mutex, &mattr); + if (err) + error (1, err, "pthread_mutex_init"); + + err = pthread_mutexattr_destroy (&mattr); + if (err) + error (1, err, "pthread_mutexattr_destroy"); + + pthread_mutex_lock (&mutex); + pthread_mutex_lock (&mutex); + pthread_mutex_unlock (&mutex); + pthread_mutex_unlock (&mutex); + + for (i = 0; i < THREADS; i++) + { + err = pthread_create (&tid[i], 0, thr, &mutex); + if (err) + error (1, err, "pthread_create (%d)", i); + } + + for (i = 0; i < THREADS; i++) + { + void *ret; + + err = pthread_join (tid[i], &ret); + if (err) + error (1, err, "pthread_join"); + + assert (ret == 0); + } + + err = pthread_mutex_destroy (&mutex); + if (err) + error (1, err, "pthread_mutex_destroy"); + + return 0; +} diff --git a/htl/tests/test-__pthread_destroy_specific-skip.c b/htl/tests/test-__pthread_destroy_specific-skip.c new file mode 100644 index 0000000000..c54ccad88d --- /dev/null +++ b/htl/tests/test-__pthread_destroy_specific-skip.c @@ -0,0 +1,100 @@ +/* Check that __pthread_destroy_specific works correctly if it has to skip + unused slots. + Copyright (C) 2000-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see <http://www.gnu.org/licenses/>. */ + +#define _GNU_SOURCE + +#include <error.h> +#include <pthread.h> +#include <stdio.h> + + +#define N_k 42 + +static volatile int v; + +static void +d (void *x) +{ + int *i = (int *) x; + + if (v != *i) + error (1, 0, "FAILED %d %d", v, *i); + v += 2; + + printf ("%s %d\n", __FUNCTION__, *i); + fflush (stdout); +} + +static void * +test (void *x) +{ + pthread_key_t k[N_k]; + static int k_v[N_k]; + + int err, i; + + for (i = 0; i < N_k; i += 1) + { + err = pthread_key_create (&k[i], &d); + if (err != 0) + error (1, err, "pthread_key_create %d", i); + } + + for (i = 0; i < N_k; i += 1) + { + k_v[i] = i; + err = pthread_setspecific (k[i], &k_v[i]); + if (err != 0) + error (1, err, "pthread_setspecific %d", i); + } + + /* Delete every even key. */ + for (i = 0; i < N_k; i += 2) + { + err = pthread_key_delete (k[i]); + if (err != 0) + error (1, err, "pthread_key_delete %d", i); + } + + v = 1; + pthread_exit (NULL); + + return NULL; +} + + +int +main (void) +{ + pthread_t tid; + int err; + + err = pthread_create (&tid, 0, test, NULL); + if (err != 0) + error (1, err, "pthread_create"); + + err = pthread_join (tid, NULL); + if (err) + error (1, err, "pthread_join"); + + if (v != N_k + 1) + error (1, 0, "FAILED END %d %d", v, N_k + 1); + + return 0; +} |