From 187dd78d7bd1c03fcf16e54a30314512d38e1a4a Mon Sep 17 00:00:00 2001 From: Eric Andersen Date: Thu, 27 Feb 2003 18:13:05 +0000 Subject: Major update for pthreads, based in large part on improvements from glibc 2.3. This should make threads much more efficient. -Erik --- include/semaphore.h | 36 +- libc/sysdeps/linux/common/bits/initspin.h | 15 +- libc/sysdeps/linux/common/bits/pthreadtypes.h | 4 +- libpthread/Makefile | 4 +- libpthread/linuxthreads/condvar.c | 308 +++-------- libpthread/linuxthreads/internals.h | 37 +- libpthread/linuxthreads/join.c | 18 +- libpthread/linuxthreads/manager.c | 100 +++- libpthread/linuxthreads/mutex.c | 216 ++++++-- libpthread/linuxthreads/pthread.c | 416 ++++++++++---- libpthread/linuxthreads/restart.h | 26 +- libpthread/linuxthreads/semaphore.c | 159 ++++-- libpthread/linuxthreads/semaphore.h | 36 +- libpthread/linuxthreads/signals.c | 3 +- libpthread/linuxthreads/spinlock.c | 616 +++++++++++++++++++-- libpthread/linuxthreads/spinlock.h | 132 ++++- libpthread/linuxthreads/sysdeps/alpha/pt-machine.h | 45 +- libpthread/linuxthreads/sysdeps/alpha/stackinfo.h | 28 + libpthread/linuxthreads/sysdeps/arm/pt-machine.h | 19 +- .../linuxthreads/sysdeps/arm/sigcontextinfo.h | 75 ++- libpthread/linuxthreads/sysdeps/arm/stackinfo.h | 28 + libpthread/linuxthreads/sysdeps/cris/pt-machine.h | 20 +- libpthread/linuxthreads/sysdeps/cris/stackinfo.h | 28 + .../linuxthreads/sysdeps/i386/i686/pt-machine.h | 38 +- libpthread/linuxthreads/sysdeps/i386/pt-machine.h | 27 +- .../linuxthreads/sysdeps/i386/sigcontextinfo.h | 45 +- libpthread/linuxthreads/sysdeps/i386/stackinfo.h | 28 + libpthread/linuxthreads/sysdeps/i386/tls.h | 183 ++++++ libpthread/linuxthreads/sysdeps/i386/useldt.h | 213 +++++-- libpthread/linuxthreads/sysdeps/m68k/pt-machine.h | 27 +- libpthread/linuxthreads/sysdeps/m68k/stackinfo.h | 28 + libpthread/linuxthreads/sysdeps/mips/pt-machine.h | 111 ++-- libpthread/linuxthreads/sysdeps/mips/stackinfo.h | 28 + .../linuxthreads/sysdeps/powerpc/pt-machine.h | 82 ++- .../linuxthreads/sysdeps/powerpc/stackinfo.h | 28 + .../linuxthreads/sysdeps/pthread/bits/libc-lock.h | 327 +++++++++++ .../linuxthreads/sysdeps/pthread/bits/libc-tsd.h | 35 +- libpthread/linuxthreads/sysdeps/pthread/tls.h | 81 +++ libpthread/linuxthreads/sysdeps/sh/pt-machine.h | 33 +- libpthread/linuxthreads/sysdeps/sh/stackinfo.h | 28 + libpthread/linuxthreads/sysdeps/sh/tls.h | 115 ++++ libpthread/linuxthreads/sysdeps/sparc/stackinfo.h | 28 + libpthread/linuxthreads_db/Banner | 1 + libpthread/linuxthreads_db/ChangeLog | 190 ++++++- libpthread/linuxthreads_db/Makefile | 27 +- libpthread/linuxthreads_db/Versions | 6 + libpthread/linuxthreads_db/proc_service.h | 16 +- libpthread/linuxthreads_db/td_init.c | 4 +- libpthread/linuxthreads_db/td_log.c | 6 +- libpthread/linuxthreads_db/td_symbol_list.c | 40 +- libpthread/linuxthreads_db/td_ta_clear_event.c | 4 +- libpthread/linuxthreads_db/td_ta_delete.c | 4 +- libpthread/linuxthreads_db/td_ta_enable_stats.c | 4 +- libpthread/linuxthreads_db/td_ta_event_addr.c | 2 +- libpthread/linuxthreads_db/td_ta_event_getmsg.c | 5 +- libpthread/linuxthreads_db/td_ta_get_nthreads.c | 2 +- libpthread/linuxthreads_db/td_ta_get_ph.c | 4 +- libpthread/linuxthreads_db/td_ta_get_stats.c | 4 +- libpthread/linuxthreads_db/td_ta_map_id2thr.c | 19 +- libpthread/linuxthreads_db/td_ta_map_lwp2thr.c | 16 +- libpthread/linuxthreads_db/td_ta_new.c | 18 +- libpthread/linuxthreads_db/td_ta_reset_stats.c | 4 +- libpthread/linuxthreads_db/td_ta_set_event.c | 4 +- libpthread/linuxthreads_db/td_ta_setconcurrency.c | 4 +- libpthread/linuxthreads_db/td_ta_thr_iter.c | 36 +- libpthread/linuxthreads_db/td_ta_tsd_iter.c | 5 +- libpthread/linuxthreads_db/td_thr_clear_event.c | 9 +- libpthread/linuxthreads_db/td_thr_dbresume.c | 4 +- libpthread/linuxthreads_db/td_thr_dbsuspend.c | 4 +- libpthread/linuxthreads_db/td_thr_event_enable.c | 22 +- libpthread/linuxthreads_db/td_thr_event_getmsg.c | 9 +- libpthread/linuxthreads_db/td_thr_get_info.c | 21 +- libpthread/linuxthreads_db/td_thr_getfpregs.c | 8 +- libpthread/linuxthreads_db/td_thr_getgregs.c | 9 +- libpthread/linuxthreads_db/td_thr_getxregs.c | 4 +- libpthread/linuxthreads_db/td_thr_getxregsize.c | 4 +- libpthread/linuxthreads_db/td_thr_set_event.c | 9 +- libpthread/linuxthreads_db/td_thr_setfpregs.c | 9 +- libpthread/linuxthreads_db/td_thr_setgregs.c | 9 +- libpthread/linuxthreads_db/td_thr_setprio.c | 4 +- libpthread/linuxthreads_db/td_thr_setsigpending.c | 4 +- libpthread/linuxthreads_db/td_thr_setxregs.c | 4 +- libpthread/linuxthreads_db/td_thr_sigsetmask.c | 4 +- libpthread/linuxthreads_db/td_thr_tls_get_addr.c | 70 +++ libpthread/linuxthreads_db/td_thr_tsd.c | 12 +- libpthread/linuxthreads_db/td_thr_validate.c | 21 +- libpthread/linuxthreads_db/thread_db.h | 37 +- libpthread/linuxthreads_db/thread_dbP.h | 5 + 88 files changed, 3644 insertions(+), 917 deletions(-) create mode 100644 libpthread/linuxthreads/sysdeps/alpha/stackinfo.h create mode 100644 libpthread/linuxthreads/sysdeps/arm/stackinfo.h create mode 100644 libpthread/linuxthreads/sysdeps/cris/stackinfo.h create mode 100644 libpthread/linuxthreads/sysdeps/i386/stackinfo.h create mode 100644 libpthread/linuxthreads/sysdeps/i386/tls.h create mode 100644 libpthread/linuxthreads/sysdeps/m68k/stackinfo.h create mode 100644 libpthread/linuxthreads/sysdeps/mips/stackinfo.h create mode 100644 libpthread/linuxthreads/sysdeps/powerpc/stackinfo.h create mode 100644 libpthread/linuxthreads/sysdeps/pthread/bits/libc-lock.h create mode 100644 libpthread/linuxthreads/sysdeps/pthread/tls.h create mode 100644 libpthread/linuxthreads/sysdeps/sh/stackinfo.h create mode 100644 libpthread/linuxthreads/sysdeps/sh/tls.h create mode 100644 libpthread/linuxthreads/sysdeps/sparc/stackinfo.h create mode 100644 libpthread/linuxthreads_db/Banner create mode 100644 libpthread/linuxthreads_db/td_thr_tls_get_addr.c diff --git a/include/semaphore.h b/include/semaphore.h index 84742233b..9c283c864 100644 --- a/include/semaphore.h +++ b/include/semaphore.h @@ -17,6 +17,10 @@ #include #include +#ifdef __USE_XOPEN2K +# define __need_timespec +# include +#endif #ifndef _PTHREAD_DESCR_DEFINED /* Thread descriptors. Needed for `sem_t' definition. */ @@ -27,11 +31,7 @@ typedef struct _pthread_descr_struct *_pthread_descr; /* System specific semaphore definition. */ typedef struct { - struct - { - long int status; - int spinlock; - } __sem_lock; + struct _pthread_fastlock __sem_lock; int __sem_value; _pthread_descr __sem_waiting; } sem_t; @@ -49,31 +49,39 @@ __BEGIN_DECLS /* Initialize semaphore object SEM to VALUE. If PSHARED then share it with other processes. */ -extern int sem_init __P ((sem_t *__sem, int __pshared, unsigned int __value)); +extern int sem_init (sem_t *__sem, int __pshared, unsigned int __value) __THROW; /* Free resources associated with semaphore object SEM. */ -extern int sem_destroy __P ((sem_t *__sem)); +extern int sem_destroy (sem_t *__sem) __THROW; /* Open a named semaphore NAME with open flaot OFLAG. */ -extern sem_t *sem_open __P ((__const char *__name, int __oflag, ...)); +extern sem_t *sem_open (__const char *__name, int __oflag, ...) __THROW; /* Close descriptor for named semaphore SEM. */ -extern int sem_close __P ((sem_t *__sem)); +extern int sem_close (sem_t *__sem) __THROW; /* Remove named semaphore NAME. */ -extern int sem_unlink __P ((__const char *__name)); +extern int sem_unlink (__const char *__name) __THROW; /* Wait for SEM being posted. */ -extern int sem_wait __P ((sem_t *__sem)); +extern int sem_wait (sem_t *__sem) __THROW; + +#ifdef __USE_XOPEN2K +/* Similar to `sem_wait' but wait only until ABSTIME. */ +extern int sem_timedwait (sem_t *__restrict __sem, + __const struct timespec *__restrict __abstime) + __THROW; +#endif /* Test whether SEM is posted. */ -extern int sem_trywait __P ((sem_t *__sem)); +extern int sem_trywait (sem_t *__sem) __THROW; /* Post SEM. */ -extern int sem_post __P ((sem_t *__sem)); +extern int sem_post (sem_t *__sem) __THROW; /* Get current value of SEM and store it in *SVAL. */ -extern int sem_getvalue __P ((sem_t *__sem, int *__sval)); +extern int sem_getvalue (sem_t *__restrict __sem, int *__restrict __sval) + __THROW; __END_DECLS diff --git a/libc/sysdeps/linux/common/bits/initspin.h b/libc/sysdeps/linux/common/bits/initspin.h index 257fed3c5..a19ec077e 100644 --- a/libc/sysdeps/linux/common/bits/initspin.h +++ b/libc/sysdeps/linux/common/bits/initspin.h @@ -3,16 +3,16 @@ 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 + 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 - Library General Public License for more details. + Lesser General Public License for more details. - You should have received a copy of the GNU Library General Public + You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ @@ -20,8 +20,9 @@ /* Initial value of a spinlock. Most platforms should use zero, unless they only implement a "test and clear" operation instead of the usual "test and set". */ -#define __ATOMIC_LOCK_INIT 0 +#define __LT_SPINLOCK_INIT 0 /* Macros for lock initializers, using the above definition. */ -#define __LOCK_INITIALIZER { 0, 0 } -#define __ATOMIC_INITIALIZER { 0, 0 } +#define __LOCK_INITIALIZER { 0, __LT_SPINLOCK_INIT } +#define __ALT_LOCK_INITIALIZER { 0, __LT_SPINLOCK_INIT } +#define __ATOMIC_INITIALIZER { 0, __LT_SPINLOCK_INIT } diff --git a/libc/sysdeps/linux/common/bits/pthreadtypes.h b/libc/sysdeps/linux/common/bits/pthreadtypes.h index 441734c9d..3ee5c4813 100644 --- a/libc/sysdeps/linux/common/bits/pthreadtypes.h +++ b/libc/sysdeps/linux/common/bits/pthreadtypes.h @@ -22,13 +22,11 @@ #define __need_schedparam #include -typedef int __atomic_lock_t; - /* Fast locks (not abstract because mutexes and conditions aren't abstract). */ struct _pthread_fastlock { long int __status; /* "Free" or "taken" or head of waiting list */ - __atomic_lock_t __spinlock; /* Used by compare_and_swap emulation. Also, + int __spinlock; /* Used by compare_and_swap emulation. Also, adaptive SMP lock stores spin count here. */ }; diff --git a/libpthread/Makefile b/libpthread/Makefile index 567ceb983..89d039246 100644 --- a/libpthread/Makefile +++ b/libpthread/Makefile @@ -25,7 +25,7 @@ LIBPTHREAD_SHARED=libpthread.so LIBPTHREAD_SHARED_FULLNAME=libpthread-$(MAJOR_VERSION).$(MINOR_VERSION).$(SUBLEVEL).so LIBTHREAD_DB=libthread_db.a -LIBTHREAD_DB_SHARED=libthread_db.so +LIBTHREAD_DB_SHARED=libthread_db.so.1 LIBTHREAD_DB_SHARED_FULLNAME=libthread_db-$(MAJOR_VERSION).$(MINOR_VERSION).$(SUBLEVEL).so DIRS= @@ -101,8 +101,6 @@ shared: all install -m 644 $(LIBTHREAD_DB_SHARED_FULLNAME) $(TOPDIR)lib; \ (cd $(TOPDIR)lib && ln -sf $(LIBTHREAD_DB_SHARED_FULLNAME) \ $(LIBTHREAD_DB_SHARED)); \ - (cd $(TOPDIR)lib && ln -sf $(LIBTHREAD_DB_SHARED_FULLNAME) \ - $(LIBTHREAD_DB_SHARED).$(MAJOR_VERSION)); \ fi; tags: diff --git a/libpthread/linuxthreads/condvar.c b/libpthread/linuxthreads/condvar.c index 8bc114e3c..f9c46a331 100644 --- a/libpthread/linuxthreads/condvar.c +++ b/libpthread/linuxthreads/condvar.c @@ -25,22 +25,6 @@ #include "queue.h" #include "restart.h" -static int pthread_cond_timedwait_relative_old(pthread_cond_t *, - pthread_mutex_t *, const struct timespec *); - -static int pthread_cond_timedwait_relative_new(pthread_cond_t *, - pthread_mutex_t *, const struct timespec *); - -static int (*pthread_cond_tw_rel)(pthread_cond_t *, pthread_mutex_t *, - const struct timespec *) = pthread_cond_timedwait_relative_old; - -/* initialize this module */ -void __pthread_init_condvar(int rt_sig_available) -{ - if (rt_sig_available) - pthread_cond_tw_rel = pthread_cond_timedwait_relative_new; -} - int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) { @@ -76,12 +60,20 @@ int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) volatile pthread_descr self = thread_self(); pthread_extricate_if extr; int already_canceled = 0; + int spurious_wakeup_count; + + /* Check whether the mutex is locked and owned by this thread. */ + if (mutex->__m_kind != PTHREAD_MUTEX_TIMED_NP + && mutex->__m_kind != PTHREAD_MUTEX_ADAPTIVE_NP + && mutex->__m_owner != self) + return EINVAL; /* Set up extrication interface */ extr.pu_object = cond; extr.pu_extricate_func = cond_extricate_func; /* Register extrication interface */ + THREAD_SETMEM(self, p_condvar_avail, 0); __pthread_set_own_extricate_if(self, &extr); /* Atomically enqueue thread for waiting, but only if it is not @@ -106,7 +98,21 @@ int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) pthread_mutex_unlock(mutex); - suspend(self); + spurious_wakeup_count = 0; + while (1) + { + suspend(self); + if (THREAD_GETMEM(self, p_condvar_avail) == 0 + && (THREAD_GETMEM(self, p_woken_by_cancel) == 0 + || THREAD_GETMEM(self, p_cancelstate) != PTHREAD_CANCEL_ENABLE)) + { + /* Count resumes that don't belong to us. */ + spurious_wakeup_count++; + continue; + } + break; + } + __pthread_set_own_extricate_if(self, 0); /* Check for cancellation again, to provide correct cancellation @@ -119,31 +125,36 @@ int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) pthread_exit(PTHREAD_CANCELED); } + /* Put back any resumes we caught that don't belong to us. */ + while (spurious_wakeup_count--) + restart(self); + pthread_mutex_lock(mutex); return 0; } -/* The following function is used on kernels that don't have rt signals. - SIGUSR1 is used as the restart signal. The different code is needed - because that ordinary signal does not queue. */ - static int -pthread_cond_timedwait_relative_old(pthread_cond_t *cond, +pthread_cond_timedwait_relative(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec * abstime) { volatile pthread_descr self = thread_self(); - sigset_t unblock, initial_mask; int already_canceled = 0; - int was_signalled = 0; - sigjmp_buf jmpbuf; pthread_extricate_if extr; + int spurious_wakeup_count; + + /* Check whether the mutex is locked and owned by this thread. */ + if (mutex->__m_kind != PTHREAD_MUTEX_TIMED_NP + && mutex->__m_kind != PTHREAD_MUTEX_ADAPTIVE_NP + && mutex->__m_owner != self) + return EINVAL; /* Set up extrication interface */ extr.pu_object = cond; extr.pu_extricate_func = cond_extricate_func; /* Register extrication interface */ + THREAD_SETMEM(self, p_condvar_avail, 0); __pthread_set_own_extricate_if(self, &extr); /* Enqueue to wait on the condition and check for cancellation. */ @@ -162,202 +173,40 @@ pthread_cond_timedwait_relative_old(pthread_cond_t *cond, pthread_mutex_unlock(mutex); - if (atomic_decrement(&self->p_resume_count) == 0) { - /* Set up a longjmp handler for the restart signal, unblock - the signal and sleep. */ - - if (sigsetjmp(jmpbuf, 1) == 0) { - THREAD_SETMEM(self, p_signal_jmp, &jmpbuf); - THREAD_SETMEM(self, p_signal, 0); - /* Unblock the restart signal */ - sigemptyset(&unblock); - sigaddset(&unblock, __pthread_sig_restart); - sigprocmask(SIG_UNBLOCK, &unblock, &initial_mask); - - while (1) { - struct timeval now; - struct timespec reltime; - - /* Compute a time offset relative to now. */ - gettimeofday (&now, NULL); - reltime.tv_nsec = abstime->tv_nsec - now.tv_usec * 1000; - reltime.tv_sec = abstime->tv_sec - now.tv_sec; - if (reltime.tv_nsec < 0) { - reltime.tv_nsec += 1000000000; - reltime.tv_sec -= 1; - } - - /* Sleep for the required duration. If woken by a signal, resume waiting - as required by Single Unix Specification. */ - if (reltime.tv_sec < 0 || __libc_nanosleep(&reltime, NULL) == 0) - break; - } - - /* Block the restart signal again */ - sigprocmask(SIG_SETMASK, &initial_mask, NULL); - was_signalled = 0; - } else { - was_signalled = 1; - } - THREAD_SETMEM(self, p_signal_jmp, NULL); - } - - /* Now was_signalled is true if we exited the above code - due to the delivery of a restart signal. In that case, - we know we have been dequeued and resumed and that the - resume count is balanced. Otherwise, there are some - cases to consider. First, try to bump up the resume count - back to zero. If it goes to 1, it means restart() was - invoked on this thread. The signal must be consumed - and the count bumped down and everything is cool. - Otherwise, no restart was delivered yet, so we remove - the thread from the queue. If this succeeds, it's a clear - case of timeout. If we fail to remove from the queue, then we - must wait for a restart. */ - - if (!was_signalled) { - if (atomic_increment(&self->p_resume_count) != -1) { - __pthread_wait_for_restart_signal(self); - atomic_decrement(&self->p_resume_count); /* should be zero now! */ - } else { - int was_on_queue; - __pthread_lock(&cond->__c_lock, self); - was_on_queue = remove_from_queue(&cond->__c_waiting, self); - __pthread_unlock(&cond->__c_lock); - - if (was_on_queue) { - __pthread_set_own_extricate_if(self, 0); - pthread_mutex_lock(mutex); - return ETIMEDOUT; - } - - suspend(self); - } - } - - __pthread_set_own_extricate_if(self, 0); - - /* The remaining logic is the same as in other cancellable waits, - such as pthread_join sem_wait or pthread_cond wait. */ - - if (THREAD_GETMEM(self, p_woken_by_cancel) - && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) { - THREAD_SETMEM(self, p_woken_by_cancel, 0); - pthread_mutex_lock(mutex); - pthread_exit(PTHREAD_CANCELED); - } - - pthread_mutex_lock(mutex); - return 0; -} - -/* The following function is used on new (late 2.1 and 2.2 and higher) kernels - that have rt signals which queue. */ - -static int -pthread_cond_timedwait_relative_new(pthread_cond_t *cond, - pthread_mutex_t *mutex, - const struct timespec * abstime) -{ - volatile pthread_descr self = thread_self(); - sigset_t unblock, initial_mask; - int already_canceled = 0; - int was_signalled = 0; - sigjmp_buf jmpbuf; - pthread_extricate_if extr; - - /* Set up extrication interface */ - extr.pu_object = cond; - extr.pu_extricate_func = cond_extricate_func; - - /* Register extrication interface */ - __pthread_set_own_extricate_if(self, &extr); - - /* Enqueue to wait on the condition and check for cancellation. */ - __pthread_lock(&cond->__c_lock, self); - if (!(THREAD_GETMEM(self, p_canceled) - && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE)) - enqueue(&cond->__c_waiting, self); - else - already_canceled = 1; - __pthread_unlock(&cond->__c_lock); + spurious_wakeup_count = 0; + while (1) + { + if (!timedsuspend(self, abstime)) { + int was_on_queue; - if (already_canceled) { - __pthread_set_own_extricate_if(self, 0); - pthread_exit(PTHREAD_CANCELED); - } + /* __pthread_lock will queue back any spurious restarts that + may happen to it. */ - pthread_mutex_unlock(mutex); + __pthread_lock(&cond->__c_lock, self); + was_on_queue = remove_from_queue(&cond->__c_waiting, self); + __pthread_unlock(&cond->__c_lock); - /* Set up a longjmp handler for the restart signal, unblock - the signal and sleep. */ - - if (sigsetjmp(jmpbuf, 1) == 0) { - THREAD_SETMEM(self, p_signal_jmp, &jmpbuf); - THREAD_SETMEM(self, p_signal, 0); - /* Unblock the restart signal */ - sigemptyset(&unblock); - sigaddset(&unblock, __pthread_sig_restart); - sigprocmask(SIG_UNBLOCK, &unblock, &initial_mask); - - while (1) { - struct timeval now; - struct timespec reltime; - - /* Compute a time offset relative to now. */ - gettimeofday (&now, NULL); - reltime.tv_nsec = abstime->tv_nsec - now.tv_usec * 1000; - reltime.tv_sec = abstime->tv_sec - now.tv_sec; - if (reltime.tv_nsec < 0) { - reltime.tv_nsec += 1000000000; - reltime.tv_sec -= 1; + if (was_on_queue) { + __pthread_set_own_extricate_if(self, 0); + pthread_mutex_lock(mutex); + return ETIMEDOUT; } - /* Sleep for the required duration. If woken by a signal, - resume waiting as required by Single Unix Specification. */ - if (reltime.tv_sec < 0 || __libc_nanosleep(&reltime, NULL) == 0) - break; + /* Eat the outstanding restart() from the signaller */ + suspend(self); } - /* Block the restart signal again */ - sigprocmask(SIG_SETMASK, &initial_mask, NULL); - was_signalled = 0; - } else { - was_signalled = 1; - } - THREAD_SETMEM(self, p_signal_jmp, NULL); - - /* Now was_signalled is true if we exited the above code - due to the delivery of a restart signal. In that case, - everything is cool. We have been removed from the queue - by the other thread, and consumed its signal. - - Otherwise we this thread woke up spontaneously, or due to a signal other - than restart. The next thing to do is to try to remove the thread - from the queue. This may fail due to a race against another thread - trying to do the same. In the failed case, we know we were signalled, - and we may also have to consume a restart signal. */ - - if (!was_signalled) { - int was_on_queue; - - /* __pthread_lock will queue back any spurious restarts that - may happen to it. */ - - __pthread_lock(&cond->__c_lock, self); - was_on_queue = remove_from_queue(&cond->__c_waiting, self); - __pthread_unlock(&cond->__c_lock); - - if (was_on_queue) { - __pthread_set_own_extricate_if(self, 0); - pthread_mutex_lock(mutex); - return ETIMEDOUT; + if (THREAD_GETMEM(self, p_condvar_avail) == 0 + && (THREAD_GETMEM(self, p_woken_by_cancel) == 0 + || THREAD_GETMEM(self, p_cancelstate) != PTHREAD_CANCEL_ENABLE)) + { + /* Count resumes that don't belong to us. */ + spurious_wakeup_count++; + continue; + } + break; } - /* Eat the outstanding restart() from the signaller */ - suspend(self); - } - __pthread_set_own_extricate_if(self, 0); /* The remaining logic is the same as in other cancellable waits, @@ -370,6 +219,10 @@ pthread_cond_timedwait_relative_new(pthread_cond_t *cond, pthread_exit(PTHREAD_CANCELED); } + /* Put back any resumes we caught that don't belong to us. */ + while (spurious_wakeup_count--) + restart(self); + pthread_mutex_lock(mutex); return 0; } @@ -378,7 +231,7 @@ int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec * abstime) { /* Indirect call through pointer! */ - return pthread_cond_tw_rel(cond, mutex, abstime); + return pthread_cond_timedwait_relative(cond, mutex, abstime); } int pthread_cond_signal(pthread_cond_t *cond) @@ -388,7 +241,11 @@ int pthread_cond_signal(pthread_cond_t *cond) __pthread_lock(&cond->__c_lock, NULL); th = dequeue(&cond->__c_waiting); __pthread_unlock(&cond->__c_lock); - if (th != NULL) restart(th); + if (th != NULL) { + th->p_condvar_avail = 1; + WRITE_MEMORY_BARRIER(); + restart(th); + } return 0; } @@ -402,7 +259,11 @@ int pthread_cond_broadcast(pthread_cond_t *cond) cond->__c_waiting = NULL; __pthread_unlock(&cond->__c_lock); /* Now signal each process in the queue */ - while ((th = dequeue(&tosignal)) != NULL) restart(th); + while ((th = dequeue(&tosignal)) != NULL) { + th->p_condvar_avail = 1; + WRITE_MEMORY_BARRIER(); + restart(th); + } return 0; } @@ -418,19 +279,18 @@ int pthread_condattr_destroy(pthread_condattr_t *attr) int pthread_condattr_getpshared (const pthread_condattr_t *attr, int *pshared) { - *pshared = PTHREAD_PROCESS_PRIVATE; - return 0; + *pshared = PTHREAD_PROCESS_PRIVATE; + return 0; } int pthread_condattr_setpshared (pthread_condattr_t *attr, int pshared) { - if (pshared != PTHREAD_PROCESS_PRIVATE && pshared != PTHREAD_PROCESS_SHARED) - return EINVAL; + if (pshared != PTHREAD_PROCESS_PRIVATE && pshared != PTHREAD_PROCESS_SHARED) + return EINVAL; - /* For now it is not possible to share a conditional variable. */ - if (pshared != PTHREAD_PROCESS_PRIVATE) - return ENOSYS; + /* For now it is not possible to shared a conditional variable. */ + if (pshared != PTHREAD_PROCESS_PRIVATE) + return ENOSYS; - return 0; + return 0; } - diff --git a/libpthread/linuxthreads/internals.h b/libpthread/linuxthreads/internals.h index ffa52aff3..45439f363 100644 --- a/libpthread/linuxthreads/internals.h +++ b/libpthread/linuxthreads/internals.h @@ -29,6 +29,9 @@ #include "semaphore.h" #include "../linuxthreads_db/thread_dbP.h" +/* Pretend to be glibc 2.3 as far as gdb is concerned */ +#define VERSION "2.3" + #ifndef THREAD_GETMEM # define THREAD_GETMEM(descr, member) descr->member #endif @@ -159,6 +162,8 @@ struct _pthread_descr_struct { struct pthread_atomic p_resume_count; /* number of times restart() was called on thread */ char p_woken_by_cancel; /* cancellation performed wakeup */ + char p_condvar_avail; /* flag if conditional variable became avail */ + char p_sem_avail; /* flag if semaphore became available */ pthread_extricate_if *p_extricate; /* See above */ pthread_readlock_info *p_readlock_list; /* List of readlock info structs */ pthread_readlock_info *p_readlock_free; /* Free list of structs */ @@ -186,7 +191,7 @@ struct pthread_request { pthread_descr req_thread; /* Thread doing the request */ enum { /* Request kind */ REQ_CREATE, REQ_FREE, REQ_PROCESS_EXIT, REQ_MAIN_THREAD_EXIT, - REQ_POST, REQ_DEBUG + REQ_POST, REQ_DEBUG, REQ_KICK } req_kind; union { /* Arguments for request */ struct { /* For REQ_CREATE: */ @@ -337,6 +342,21 @@ static inline int invalid_handle(pthread_handle h, pthread_t id) #define CURRENT_STACK_FRAME ({ char __csf; &__csf; }) #endif +/* If MEMORY_BARRIER isn't defined in pt-machine.h, assume the + architecture doesn't need a memory barrier instruction (e.g. Intel + x86). Still we need the compiler to respect the barrier and emit + all outstanding operations which modify memory. Some architectures + distinguish between full, read and write barriers. */ +#ifndef MEMORY_BARRIER +#define MEMORY_BARRIER() asm ("" : : : "memory") +#endif +#ifndef READ_MEMORY_BARRIER +#define READ_MEMORY_BARRIER() MEMORY_BARRIER() +#endif +#ifndef WRITE_MEMORY_BARRIER +#define WRITE_MEMORY_BARRIER() MEMORY_BARRIER() +#endif + /* Recover thread descriptor for the current thread */ extern pthread_descr __pthread_find_self (void) __attribute__ ((const)); @@ -425,7 +445,6 @@ void __pthread_manager_sighandler(int sig); void __pthread_reset_main_thread(void); void __fresetlockfiles(void); void __pthread_manager_adjust_prio(int thread_prio); -void __pthread_set_own_extricate_if(pthread_descr self, pthread_extricate_if *peif); void __pthread_initialize_minimal (void); extern int __pthread_attr_setguardsize __P ((pthread_attr_t *__attr, @@ -446,15 +465,15 @@ extern int __pthread_mutexattr_gettype __P ((__const pthread_mutexattr_t *__attr int *__kind)); extern void __pthread_kill_other_threads_np __P ((void)); -void __pthread_restart_old(pthread_descr th); -void __pthread_suspend_old(pthread_descr self); - -void __pthread_restart_new(pthread_descr th); -void __pthread_suspend_new(pthread_descr self); +extern void __pthread_restart_old(pthread_descr th); +extern void __pthread_suspend_old(pthread_descr self); +extern int __pthread_timedsuspend_old(pthread_descr self, const struct timespec *abs); -void __pthread_wait_for_restart_signal(pthread_descr self); +extern void __pthread_restart_new(pthread_descr th); +extern void __pthread_suspend_new(pthread_descr self); +extern int __pthread_timedsuspend_new(pthread_descr self, const struct timespec *abs); -void __pthread_init_condvar(int rt_sig_available); +extern void __pthread_wait_for_restart_signal(pthread_descr self); /* Global pointers to old or new suspend functions */ diff --git a/libpthread/linuxthreads/join.c b/libpthread/linuxthreads/join.c index ccb11b124..cc2dc4ddc 100644 --- a/libpthread/linuxthreads/join.c +++ b/libpthread/linuxthreads/join.c @@ -14,9 +14,12 @@ /* Thread termination and joining */ +#include +#define __USE_GNU #include #include #include +#include #include "pthread.h" #include "internals.h" #include "spinlock.h" @@ -74,8 +77,13 @@ PDEBUG("joining = %p, pid=%d\n", joining, joining->p_pid); if (self == __pthread_main_thread && __pthread_manager_request >= 0) { request.req_thread = self; request.req_kind = REQ_MAIN_THREAD_EXIT; - __libc_write(__pthread_manager_request, (char *)&request, sizeof(request)); + TEMP_FAILURE_RETRY(__libc_write(__pthread_manager_request, + (char *)&request, sizeof(request))); suspend(self); + /* Main thread flushes stdio streams and runs atexit functions. + * It also calls a handler within LinuxThreads which sends a process exit + * request to the thread manager. */ + exit(0); } /* Exit the process (but don't flush stdio streams, and don't run atexit functions). */ @@ -168,8 +176,8 @@ PDEBUG("after suspend\n"); request.req_thread = self; request.req_kind = REQ_FREE; request.req_args.free.thread_id = thread_id; - __libc_write(__pthread_manager_request, - (char *) &request, sizeof(request)); + TEMP_FAILURE_RETRY(__libc_write(__pthread_manager_request, + (char *) &request, sizeof(request))); } return 0; } @@ -206,8 +214,8 @@ int pthread_detach(pthread_t thread_id) request.req_thread = thread_self(); request.req_kind = REQ_FREE; request.req_args.free.thread_id = thread_id; - __libc_write(__pthread_manager_request, - (char *) &request, sizeof(request)); + TEMP_FAILURE_RETRY(__libc_write(__pthread_manager_request, + (char *) &request, sizeof(request))); } return 0; } diff --git a/libpthread/linuxthreads/manager.c b/libpthread/linuxthreads/manager.c index 1b513ca92..f1c9b93af 100644 --- a/libpthread/linuxthreads/manager.c +++ b/libpthread/linuxthreads/manager.c @@ -18,6 +18,8 @@ #define __getpid getpid #define __getpagesize getpagesize +#include +#define __USE_GNU #include #include #include @@ -50,8 +52,8 @@ /* Array of active threads. Entry 0 is reserved for the initial thread. */ struct pthread_handle_struct __pthread_handles[PTHREAD_THREADS_MAX] = -{ { LOCK_INITIALIZER, &__pthread_initial_thread, 0}, - { LOCK_INITIALIZER, &__pthread_manager_thread, 0}, /* All NULLs */ }; +{ { __LOCK_INITIALIZER, &__pthread_initial_thread, 0}, + { __LOCK_INITIALIZER, &__pthread_manager_thread, 0}, /* All NULLs */ }; /* For debugging purposes put the maximum number of threads in a variable. */ const int __linuxthreads_pthread_threads_max = PTHREAD_THREADS_MAX; @@ -120,7 +122,7 @@ int __pthread_manager(void *arg) #else struct pollfd ufd; #endif - sigset_t mask; + sigset_t manager_mask; int n; struct pthread_request request; @@ -131,15 +133,19 @@ int __pthread_manager(void *arg) /* Set the error variable. */ __pthread_manager_thread.p_errnop = &__pthread_manager_thread.p_errno; __pthread_manager_thread.p_h_errnop = &__pthread_manager_thread.p_h_errno; + /* Block all signals except __pthread_sig_cancel and SIGTRAP */ - sigfillset(&mask); - sigdelset(&mask, __pthread_sig_cancel); /* for thread termination */ - sigdelset(&mask, SIGTRAP); /* for debugging purposes */ - sigprocmask(SIG_SETMASK, &mask, NULL); + sigfillset(&manager_mask); + sigdelset(&manager_mask, __pthread_sig_cancel); /* for thread termination */ + sigdelset(&manager_mask, SIGTRAP); /* for debugging purposes */ + if (__pthread_threads_debug && __pthread_sig_debug > 0) + sigdelset(&manager_mask, __pthread_sig_debug); + sigprocmask(SIG_SETMASK, &manager_mask, NULL); /* Raise our priority to match that of main thread */ __pthread_manager_adjust_prio(__pthread_main_thread->p_priority); /* Synchronize debugging of the thread manager */ - n = __libc_read(reqfd, (char *)&request, sizeof(request)); + n = TEMP_FAILURE_RETRY(__libc_read(reqfd, (char *)&request, + sizeof(request))); ASSERT(n == sizeof(request) && request.req_kind == REQ_DEBUG); #ifndef USE_SELECT ufd.fd = reqfd; @@ -201,17 +207,25 @@ PDEBUG("got REQ_FREE\n"); break; case REQ_PROCESS_EXIT: PDEBUG("got REQ_PROCESS_EXIT from %d, exit code = %d\n", - request.req_thread, request.req_args.exit.code); + request.req_thread, request.req_args.exit.code); pthread_handle_exit(request.req_thread, request.req_args.exit.code); break; case REQ_MAIN_THREAD_EXIT: PDEBUG("got REQ_MAIN_THREAD_EXIT\n"); main_thread_exiting = 1; + /* Reap children in case all other threads died and the signal handler + went off before we set main_thread_exiting to 1, and therefore did + not do REQ_KICK. */ + pthread_reap_children(); + if (__pthread_main_thread->p_nextlive == __pthread_main_thread) { restart(__pthread_main_thread); - return 0; - } + /* The main thread will now call exit() which will trigger an + __on_exit handler, which in turn will send REQ_PROCESS_EXIT + to the thread manager. In case you are wondering how the + manager terminates from its loop here. */ + } break; case REQ_POST: PDEBUG("got REQ_POST\n"); @@ -221,10 +235,14 @@ PDEBUG("got REQ_POST\n"); PDEBUG("got REQ_DEBUG\n"); /* Make gdb aware of new thread and gdb will restart the new thread when it is ready to handle the new thread. */ - if (__pthread_threads_debug && __pthread_sig_debug > 0) + if (__pthread_threads_debug && __pthread_sig_debug > 0) { PDEBUG("about to call raise(__pthread_sig_debug)\n"); raise(__pthread_sig_debug); - break; + } + case REQ_KICK: + /* This is just a prod to get the manager to reap some + threads right away, avoiding a potential delay at shutdown. */ + break; } } } @@ -246,8 +264,9 @@ int __pthread_manager_event(void *arg) } /* Process creation */ - -static int pthread_start_thread(void *arg) +static int +__attribute__ ((noreturn)) +pthread_start_thread(void *arg) { pthread_descr self = (pthread_descr) arg; struct pthread_request request; @@ -282,8 +301,8 @@ PDEBUG("\n"); if (__pthread_threads_debug && __pthread_sig_debug > 0) { request.req_thread = self; request.req_kind = REQ_DEBUG; - __libc_write(__pthread_manager_request, - (char *) &request, sizeof(request)); + TEMP_FAILURE_RETRY(__libc_write(__pthread_manager_request, + (char *) &request, sizeof(request))); suspend(self); } /* Run the thread code */ @@ -291,10 +310,11 @@ PDEBUG("\n"); p_start_args.arg)); /* Exit with the given return value */ pthread_exit(outcome); - return 0; } -static int pthread_start_thread_event(void *arg) +static int +__attribute__ ((noreturn)) +pthread_start_thread_event(void *arg) { pthread_descr self = (pthread_descr) arg; @@ -310,7 +330,7 @@ static int pthread_start_thread_event(void *arg) __pthread_unlock (THREAD_GETMEM(self, p_lock)); /* Continue with the real function. */ - return pthread_start_thread (arg); + pthread_start_thread (arg); } static int pthread_allocate_stack(const pthread_attr_t *attr, @@ -454,6 +474,7 @@ static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr, char *guardaddr = NULL; size_t guardsize = 0; int pagesize = __getpagesize(); + int saved_errno = 0; /* First check whether we have to change the policy and if yes, whether we can do this. Normally this should be done by examining the @@ -549,8 +570,10 @@ static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr, /* We have to report this event. */ pid = clone(pthread_start_thread_event, (void **) new_thread, - CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | - __pthread_sig_cancel, new_thread); + CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | + __pthread_sig_cancel, new_thread); + + saved_errno = errno; if (pid != -1) { /* Now fill in the information about the new thread in @@ -577,9 +600,10 @@ static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr, if (pid == 0) { PDEBUG("cloning new_thread = %p\n", new_thread); - pid = clone(pthread_start_thread, (void **) new_thread, - CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | - __pthread_sig_cancel, new_thread); + pid = clone(pthread_start_thread, (void **) new_thread, + CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | + __pthread_sig_cancel, new_thread); + saved_errno = errno; } /* Check if cloning succeeded */ if (pid == -1) { @@ -714,15 +738,15 @@ static void pthread_exited(pid_t pid) /* If we have to signal this event do it now. */ if (th->p_report_events) { - /* See whether TD_DEATH is in any of the mask. */ + /* See whether TD_REAP is in any of the mask. */ int idx = __td_eventword (TD_REAP); uint32_t mask = __td_eventmask (TD_REAP); if ((mask & (__pthread_threads_events.event_bits[idx] | th->p_eventbuf.eventmask.event_bits[idx])) != 0) { - /* Yep, we have to signal the death. */ - th->p_eventbuf.eventnum = TD_DEATH; + /* Yep, we have to signal the reapage. */ + th->p_eventbuf.eventnum = TD_REAP; th->p_eventbuf.eventdata = th; __pthread_last_event = th; @@ -742,7 +766,7 @@ static void pthread_exited(pid_t pid) if (main_thread_exiting && __pthread_main_thread->p_nextlive == __pthread_main_thread) { restart(__pthread_main_thread); - _exit(0); + /* Same logic as REQ_MAIN_THREAD_EXIT. */ } } @@ -837,7 +861,23 @@ static void pthread_handle_exit(pthread_descr issuing_thread, int exitcode) void __pthread_manager_sighandler(int sig) { - terminated_children = 1; + int kick_manager = terminated_children == 0 && main_thread_exiting; + terminated_children = 1; + + /* If the main thread is terminating, kick the thread manager loop + each time some threads terminate. This eliminates a two second + shutdown delay caused by the thread manager sleeping in the + call to __poll(). Instead, the thread manager is kicked into + action, reaps the outstanding threads and resumes the main thread + so that it can complete the shutdown. */ + + if (kick_manager) { + struct pthread_request request; + request.req_thread = 0; + request.req_kind = REQ_KICK; + TEMP_FAILURE_RETRY(__libc_write(__pthread_manager_request, + (char *) &request, sizeof(request))); + } } /* Adjust priority of thread manager so that it always run at a priority diff --git a/libpthread/linuxthreads/mutex.c b/libpthread/linuxthreads/mutex.c index 2ab9e7e55..3c97ea7d6 100644 --- a/libpthread/linuxthreads/mutex.c +++ b/libpthread/linuxthreads/mutex.c @@ -12,41 +12,51 @@ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ /* GNU Library General Public License for more details. */ -/* changes for uClibc: remove strong_alias'es and define the real symbol */ - /* Mutexes */ -#include -#define __USE_GNU +#include #include #include #include +#include #include "pthread.h" #include "internals.h" #include "spinlock.h" #include "queue.h" #include "restart.h" -int pthread_mutex_init(pthread_mutex_t * mutex, +int __pthread_mutex_init(pthread_mutex_t * mutex, const pthread_mutexattr_t * mutex_attr) { __pthread_init_lock(&mutex->__m_lock); mutex->__m_kind = - mutex_attr == NULL ? PTHREAD_MUTEX_ADAPTIVE_NP : mutex_attr->__mutexkind; + mutex_attr == NULL ? PTHREAD_MUTEX_TIMED_NP : mutex_attr->__mutexkind; mutex->__m_count = 0; mutex->__m_owner = NULL; return 0; } -//strong_alias (__pthread_mutex_init, pthread_mutex_init) +strong_alias (__pthread_mutex_init, pthread_mutex_init) -int pthread_mutex_destroy(pthread_mutex_t * mutex) +int __pthread_mutex_destroy(pthread_mutex_t * mutex) { - if (mutex->__m_lock.__status != 0) return EBUSY; - return 0; + switch (mutex->__m_kind) { + case PTHREAD_MUTEX_ADAPTIVE_NP: + case PTHREAD_MUTEX_RECURSIVE_NP: + if ((mutex->__m_lock.__status & 1) != 0) + return EBUSY; + return 0; + case PTHREAD_MUTEX_ERRORCHECK_NP: + case PTHREAD_MUTEX_TIMED_NP: + if (mutex->__m_lock.__status != 0) + return EBUSY; + return 0; + default: + return EINVAL; + } } -//strong_alias (__pthread_mutex_destroy, pthread_mutex_destroy) +strong_alias (__pthread_mutex_destroy, pthread_mutex_destroy) -int pthread_mutex_trylock(pthread_mutex_t * mutex) +int __pthread_mutex_trylock(pthread_mutex_t * mutex) { pthread_descr self; int retcode; @@ -68,18 +78,21 @@ int pthread_mutex_trylock(pthread_mutex_t * mutex) } return retcode; case PTHREAD_MUTEX_ERRORCHECK_NP: - retcode = __pthread_trylock(&mutex->__m_lock); + retcode = __pthread_alt_trylock(&mutex->__m_lock); if (retcode == 0) { mutex->__m_owner = thread_self(); } return retcode; + case PTHREAD_MUTEX_TIMED_NP: + retcode = __pthread_alt_trylock(&mutex->__m_lock); + return retcode; default: return EINVAL; } } -//strong_alias (__pthread_mutex_trylock, pthread_mutex_trylock) +strong_alias (__pthread_mutex_trylock, pthread_mutex_trylock) -int pthread_mutex_lock(pthread_mutex_t * mutex) +int __pthread_mutex_lock(pthread_mutex_t * mutex) { pthread_descr self; @@ -100,22 +113,71 @@ int pthread_mutex_lock(pthread_mutex_t * mutex) case PTHREAD_MUTEX_ERRORCHECK_NP: self = thread_self(); if (mutex->__m_owner == self) return EDEADLK; + __pthread_alt_lock(&mutex->__m_lock, self); + mutex->__m_owner = self; + return 0; + case PTHREAD_MUTEX_TIMED_NP: + __pthread_alt_lock(&mutex->__m_lock, NULL); + return 0; + default: + return EINVAL; + } +} +strong_alias (__pthread_mutex_lock, pthread_mutex_lock) + +int __pthread_mutex_timedlock (pthread_mutex_t *mutex, + const struct timespec *abstime) +{ + pthread_descr self; + int res; + + if (__builtin_expect (abstime->tv_nsec, 0) < 0 + || __builtin_expect (abstime->tv_nsec, 0) >= 1000000000) + return EINVAL; + + switch(mutex->__m_kind) { + case PTHREAD_MUTEX_ADAPTIVE_NP: + __pthread_lock(&mutex->__m_lock, NULL); + return 0; + case PTHREAD_MUTEX_RECURSIVE_NP: + self = thread_self(); + if (mutex->__m_owner == self) { + mutex->__m_count++; + return 0; + } __pthread_lock(&mutex->__m_lock, self); mutex->__m_owner = self; + mutex->__m_count = 0; return 0; + case PTHREAD_MUTEX_ERRORCHECK_NP: + self = thread_self(); + if (mutex->__m_owner == self) return EDEADLK; + res = __pthread_alt_timedlock(&mutex->__m_lock, self, abstime); + if (res != 0) + { + mutex->__m_owner = self; + return 0; + } + return ETIMEDOUT; + case PTHREAD_MUTEX_TIMED_NP: + /* Only this type supports timed out lock. */ + return (__pthread_alt_timedlock(&mutex->__m_lock, NULL, abstime) + ? 0 : ETIMEDOUT); default: return EINVAL; } } -//strong_alias (__pthread_mutex_lock, pthread_mutex_lock) +strong_alias (__pthread_mutex_timedlock, pthread_mutex_timedlock) -int pthread_mutex_unlock(pthread_mutex_t * mutex) +int __pthread_mutex_unlock(pthread_mutex_t * mutex) { switch (mutex->__m_kind) { case PTHREAD_MUTEX_ADAPTIVE_NP: __pthread_unlock(&mutex->__m_lock); return 0; case PTHREAD_MUTEX_RECURSIVE_NP: + if (mutex->__m_owner != thread_self()) + return EPERM; if (mutex->__m_count > 0) { mutex->__m_count--; return 0; @@ -127,38 +189,42 @@ int pthread_mutex_unlock(pthread_mutex_t * mutex) if (mutex->__m_owner != thread_self() || mutex->__m_lock.__status == 0) return EPERM; mutex->__m_owner = NULL; - __pthread_unlock(&mutex->__m_lock); + __pthread_alt_unlock(&mutex->__m_lock); + return 0; + case PTHREAD_MUTEX_TIMED_NP: + __pthread_alt_unlock(&mutex->__m_lock); return 0; default: return EINVAL; } } -//strong_alias (__pthread_mutex_unlock, pthread_mutex_unlock) +strong_alias (__pthread_mutex_unlock, pthread_mutex_unlock) -int pthread_mutexattr_init(pthread_mutexattr_t *attr) +int __pthread_mutexattr_init(pthread_mutexattr_t *attr) { - attr->__mutexkind = PTHREAD_MUTEX_ADAPTIVE_NP; + attr->__mutexkind = PTHREAD_MUTEX_TIMED_NP; return 0; } -//strong_alias (__pthread_mutexattr_init, pthread_mutexattr_init) +strong_alias (__pthread_mutexattr_init, pthread_mutexattr_init) -int pthread_mutexattr_destroy(pthread_mutexattr_t *attr) +int __pthread_mutexattr_destroy(pthread_mutexattr_t *attr) { return 0; } -//strong_alias (__pthread_mutexattr_destroy, pthread_mutexattr_destroy) +strong_alias (__pthread_mutexattr_destroy, pthread_mutexattr_destroy) int __pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind) { if (kind != PTHREAD_MUTEX_ADAPTIVE_NP && kind != PTHREAD_MUTEX_RECURSIVE_NP - && kind != PTHREAD_MUTEX_ERRORCHECK_NP) + && kind != PTHREAD_MUTEX_ERRORCHECK_NP + && kind != PTHREAD_MUTEX_TIMED_NP) return EINVAL; attr->__mutexkind = kind; return 0; } weak_alias (__pthread_mutexattr_settype, pthread_mutexattr_settype) -weak_alias ( __pthread_mutexattr_settype, __pthread_mutexattr_setkind_np) +strong_alias ( __pthread_mutexattr_settype, __pthread_mutexattr_setkind_np) weak_alias (__pthread_mutexattr_setkind_np, pthread_mutexattr_setkind_np) int __pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *kind) @@ -167,24 +233,27 @@ int __pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *kind) return 0; } weak_alias (__pthread_mutexattr_gettype, pthread_mutexattr_gettype) -weak_alias (__pthread_mutexattr_gettype, __pthread_mutexattr_getkind_np) +strong_alias (__pthread_mutexattr_gettype, __pthread_mutexattr_getkind_np) weak_alias (__pthread_mutexattr_getkind_np, pthread_mutexattr_getkind_np) -int __pthread_mutexattr_getpshared (const pthread_mutexattr_t *attr, int *pshared) +int __pthread_mutexattr_getpshared (const pthread_mutexattr_t *attr, + int *pshared) { - *pshared = PTHREAD_PROCESS_PRIVATE; - return 0; + *pshared = PTHREAD_PROCESS_PRIVATE; + return 0; } weak_alias (__pthread_mutexattr_getpshared, pthread_mutexattr_getpshared) int __pthread_mutexattr_setpshared (pthread_mutexattr_t *attr, int pshared) { - if (pshared != PTHREAD_PROCESS_PRIVATE && pshared != PTHREAD_PROCESS_SHARED) - return EINVAL; - /* For now it is not possible to shared a conditional variable. */ - if (pshared != PTHREAD_PROCESS_PRIVATE) - return ENOSYS; - return 0; + if (pshared != PTHREAD_PROCESS_PRIVATE && pshared != PTHREAD_PROCESS_SHARED) + return EINVAL; + + /* For now it is not possible to shared a conditional variable. */ + if (pshared != PTHREAD_PROCESS_PRIVATE) + return ENOSYS; + + return 0; } weak_alias (__pthread_mutexattr_setpshared, pthread_mutexattr_setpshared) @@ -192,30 +261,97 @@ weak_alias (__pthread_mutexattr_setpshared, pthread_mutexattr_setpshared) static pthread_mutex_t once_masterlock = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t once_finished = PTHREAD_COND_INITIALIZER; +static int fork_generation = 0; /* Child process increments this after fork. */ enum { NEVER = 0, IN_PROGRESS = 1, DONE = 2 }; +/* If a thread is canceled while calling the init_routine out of + pthread once, this handler will reset the once_control variable + to the NEVER state. */ + +static void pthread_once_cancelhandler(void *arg) +{ + pthread_once_t *once_control = arg; + + pthread_mutex_lock(&once_masterlock); + *once_control = NEVER; + pthread_mutex_unlock(&once_masterlock); + pthread_cond_broadcast(&once_finished); +} + int __pthread_once(pthread_once_t * once_control, void (*init_routine)(void)) { + /* flag for doing the condition broadcast outside of mutex */ + int state_changed; + /* Test without locking first for speed */ - if (*once_control == DONE) return 0; + if (*once_control == DONE) { + READ_MEMORY_BARRIER(); + return 0; + } /* Lock and test again */ + + state_changed = 0; + pthread_mutex_lock(&once_masterlock); + + /* If this object was left in an IN_PROGRESS state in a parent + process (indicated by stale generation field), reset it to NEVER. */ + if ((*once_control & 3) == IN_PROGRESS && (*once_control & ~3) != fork_generation) + *once_control = NEVER; + /* If init_routine is being called from another routine, wait until it completes. */ - while (*once_control == IN_PROGRESS) { + while ((*once_control & 3) == IN_PROGRESS) { pthread_cond_wait(&once_finished, &once_masterlock); } /* Here *once_control is stable and either NEVER or DONE. */ if (*once_control == NEVER) { - *once_control = IN_PROGRESS; + *once_control = IN_PROGRESS | fork_generation; pthread_mutex_unlock(&once_masterlock); + pthread_cleanup_push(pthread_once_cancelhandler, once_control); init_routine(); + pthread_cleanup_pop(0); pthread_mutex_lock(&once_masterlock); + WRITE_MEMORY_BARRIER(); *once_control = DONE; - pthread_cond_broadcast(&once_finished); + state_changed = 1; } pthread_mutex_unlock(&once_masterlock); + + if (state_changed) + pthread_cond_broadcast(&once_finished); + return 0; } strong_alias (__pthread_once, pthread_once) + +/* + * Handle the state of the pthread_once mechanism across forks. The + * once_masterlock is acquired in the parent process prior to a fork to ensure + * that no thread is in the critical region protected by the lock. After the + * fork, the lock is released. In the child, the lock and the condition + * variable are simply reset. The child also increments its generation + * counter which lets pthread_once calls detect stale IN_PROGRESS states + * and reset them back to NEVER. + */ + +void __pthread_once_fork_prepare(void) +{ + pthread_mutex_lock(&once_masterlock); +} + +void __pthread_once_fork_parent(void) +{ + pthread_mutex_unlock(&once_masterlock); +} + +void __pthread_once_fork_child(void) +{ + pthread_mutex_init(&once_masterlock, NULL); + pthread_cond_init(&once_finished, NULL); + if (fork_generation <= INT_MAX - 4) + fork_generation += 4; /* leave least significant two bits zero */ + else + fork_generation = 0; +} diff --git a/libpthread/linuxthreads/pthread.c b/libpthread/linuxthreads/pthread.c index 12ee2fd98..61f6b582a 100644 --- a/libpthread/linuxthreads/pthread.c +++ b/libpthread/linuxthreads/pthread.c @@ -16,6 +16,7 @@ #define __FORCE_GLIBC #include +#define __USE_GNU #include #include /* for h_errno */ #include @@ -90,8 +91,10 @@ struct _pthread_descr_struct __pthread_initial_thread = { 0, /* Always index 0 */ 0, /* int p_report_events */ {{{0, }}, 0, NULL}, /* td_eventbuf_t p_eventbuf */ - ATOMIC_INITIALIZER, /* struct pthread_atomic p_resume_count */ + __ATOMIC_INITIALIZER, /* struct pthread_atomic p_resume_count */ 0, /* char p_woken_by_cancel */ + 0, /* char p_condvar_avail */ + 0, /* char p_sem_avail */ NULL, /* struct pthread_extricate_if *p_extricate */ NULL, /* pthread_readlock_info *p_readlock_list; */ NULL, /* pthread_readlock_info *p_readlock_free; */ @@ -140,8 +143,10 @@ struct _pthread_descr_struct __pthread_manager_thread = { 1, /* Always index 1 */ 0, /* int p_report_events */ {{{0, }}, 0, NULL}, /* td_eventbuf_t p_eventbuf */ - ATOMIC_INITIALIZER, /* struct pthread_atomic p_resume_count */ + __ATOMIC_INITIALIZER, /* struct pthread_atomic p_resume_count */ 0, /* char p_woken_by_cancel */ + 0, /* char p_condvar_avail */ + 0, /* char p_sem_avail */ NULL, /* struct pthread_extricate_if *p_extricate */ NULL, /* pthread_readlock_info *p_readlock_list; */ NULL, /* pthread_readlock_info *p_readlock_free; */ @@ -189,45 +194,79 @@ int __pthread_exit_code = 0; const int __pthread_threads_max = PTHREAD_THREADS_MAX; const int __pthread_sizeof_handle = sizeof(struct pthread_handle_struct); -const int __pthread_offsetof_descr = offsetof(struct pthread_handle_struct, - h_descr); +const int __pthread_offsetof_descr = offsetof(struct pthread_handle_struct, h_descr); const int __pthread_offsetof_pid = offsetof(struct _pthread_descr_struct, p_pid); const int __linuxthreads_pthread_sizeof_descr = sizeof(struct _pthread_descr_struct); +const int __linuxthreads_initial_report_events; -/* Forward declarations */ +const char __linuxthreads_version[] = VERSION; -static void pthread_exit_process(int retcode, void *arg); -#ifndef __i386__ +/* Forward declarations */ +static void pthread_onexit_process(int retcode, void *arg); static void pthread_handle_sigcancel(int sig); static void pthread_handle_sigrestart(int sig); -#else -static void pthread_handle_sigcancel(int sig, struct sigcontext ctx); -static void pthread_handle_sigrestart(int sig, struct sigcontext ctx); -#endif static void pthread_handle_sigdebug(int sig); +int __pthread_timedsuspend_new(pthread_descr self, const struct timespec *abstime); /* Signal numbers used for the communication. In these variables we keep track of the used variables. If the platform does not support any real-time signals we will define the values to some unreasonable value which will signal failing of all the functions below. */ -#ifdef __NR_rt_sigaction +#ifndef __NR_rt_sigaction +static int current_rtmin = -1; +static int current_rtmax = -1; +int __pthread_sig_restart = SIGUSR1; +int __pthread_sig_cancel = SIGUSR2; +int __pthread_sig_debug; +#else + +#if __SIGRTMAX - __SIGRTMIN >= 3 +static int current_rtmin = __SIGRTMIN + 3; +static int current_rtmax = __SIGRTMAX; int __pthread_sig_restart = __SIGRTMIN; int __pthread_sig_cancel = __SIGRTMIN + 1; int __pthread_sig_debug = __SIGRTMIN + 2; void (*__pthread_restart)(pthread_descr) = __pthread_restart_new; void (*__pthread_suspend)(pthread_descr) = __pthread_wait_for_restart_signal; +int (*__pthread_timedsuspend)(pthread_descr, const struct timespec *) = __pthread_timedsuspend_new; #else +static int current_rtmin = __SIGRTMIN; +static int current_rtmax = __SIGRTMAX; int __pthread_sig_restart = SIGUSR1; int __pthread_sig_cancel = SIGUSR2; -int __pthread_sig_debug = 0; -/* Pointers that select new or old suspend/resume functions - based on availability of rt signals. */ +int __pthread_sig_debug; void (*__pthread_restart)(pthread_descr) = __pthread_restart_old; void (*__pthread_suspend)(pthread_descr) = __pthread_suspend_old; +int (*__pthread_timedsuspend)(pthread_descr, const struct timespec *) = __pthread_timedsuspend_old; + +#endif + +/* Return number of available real-time signal with highest priority. */ +int __libc_current_sigrtmin (void) +{ + return current_rtmin; +} + +/* Return number of available real-time signal with lowest priority. */ +int __libc_current_sigrtmax (void) +{ + return current_rtmax; +} + +/* Allocate real-time signal with highest/lowest available + priority. Please note that we don't use a lock since we assume + this function to be called at program start. */ +int __libc_allocate_rtsig (int high) +{ + if (current_rtmin == -1 || current_rtmin > current_rtmax) + /* We don't have anymore signal available. */ + return -1; + return high ? current_rtmin++ : current_rtmax--; +} #endif /* Initialize the pthread library. @@ -307,39 +346,27 @@ static void pthread_initialize(void) /* Setup signal handlers for the initial thread. Since signal handlers are shared between threads, these settings will be inherited by all other threads. */ -#ifndef __i386__ sa.sa_handler = pthread_handle_sigrestart; -#else - sa.sa_handler = (__sighandler_t) pthread_handle_sigrestart; -#endif sigemptyset(&sa.sa_mask); sa.sa_flags = 0; __libc_sigaction(__pthread_sig_restart, &sa, NULL); -#ifndef __i386__ sa.sa_handler = pthread_handle_sigcancel; -#else - sa.sa_handler = (__sighandler_t) pthread_handle_sigcancel; -#endif - sa.sa_flags = 0; + // sa.sa_flags = 0; __libc_sigaction(__pthread_sig_cancel, &sa, NULL); if (__pthread_sig_debug > 0) { - sa.sa_handler = pthread_handle_sigdebug; - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - __libc_sigaction(__pthread_sig_debug, &sa, NULL); + sa.sa_handler = pthread_handle_sigdebug; + sigemptyset(&sa.sa_mask); + // sa.sa_flags = 0; + __libc_sigaction(__pthread_sig_debug, &sa, NULL); } /* Initially, block __pthread_sig_restart. Will be unblocked on demand. */ sigemptyset(&mask); sigaddset(&mask, __pthread_sig_restart); -PDEBUG("block mask = %x\n", mask); sigprocmask(SIG_BLOCK, &mask, NULL); /* Register an exit function to kill all other threads. */ /* Do it early so that user-registered atexit functions are called - before pthread_exit_process. */ - on_exit(pthread_exit_process, NULL); -#ifdef __NR_rt_sigaction - __pthread_init_condvar(1); -#endif + before pthread_onexit_process. */ + on_exit(pthread_onexit_process, NULL); } void __pthread_initialize(void) @@ -351,6 +378,7 @@ int __pthread_initialize_manager(void) { int manager_pipe[2]; int pid; + int report_events; struct pthread_request request; /* If basic initialization not done yet (e.g. we're called from a @@ -379,7 +407,18 @@ int __pthread_initialize_manager(void) } /* Start the thread manager */ pid = 0; - if (__pthread_initial_thread.p_report_events) +#ifdef USE_TLS + if (__linuxthreads_initial_report_events != 0) + THREAD_SETMEM (((pthread_descr) NULL), p_report_events, + __linuxthreads_initial_report_events); + report_events = THREAD_GETMEM (((pthread_descr) NULL), p_report_events); +#else + if (__linuxthreads_initial_report_events != 0) + __pthread_initial_thread.p_report_events + = __linuxthreads_initial_report_events; + report_events = __pthread_initial_thread.p_report_events; +#endif + if (__builtin_expect (report_events, 0)) { /* It's a bit more complicated. We have to report the creation of the manager thread. */ @@ -391,7 +430,7 @@ int __pthread_initialize_manager(void) != 0) { - __pthread_lock(__pthread_manager_thread.p_lock, NULL); + __pthread_lock(__pthread_manager_thread.p_lock, NULL); pid = clone(__pthread_manager_event, (void **) __pthread_manager_thread_tos, @@ -405,7 +444,7 @@ int __pthread_initialize_manager(void) the new thread do this since we don't know whether it was already scheduled when we send the event. */ __pthread_manager_thread.p_eventbuf.eventdata = - &__pthread_manager_thread; + &__pthread_manager_thread; __pthread_manager_thread.p_eventbuf.eventnum = TD_CREATE; __pthread_last_event = &__pthread_manager_thread; __pthread_manager_thread.p_tid = 2* PTHREAD_THREADS_MAX + 1; @@ -434,6 +473,7 @@ int __pthread_initialize_manager(void) __pthread_manager_reader = manager_pipe[0]; /* reading end */ __pthread_manager_thread.p_tid = 2* PTHREAD_THREADS_MAX + 1; __pthread_manager_thread.p_pid = pid; + /* Make gdb aware of new thread manager */ if (__pthread_threads_debug && __pthread_sig_debug > 0) { @@ -445,7 +485,8 @@ int __pthread_initialize_manager(void) /* Synchronize debugging of the thread manager */ PDEBUG("send REQ_DEBUG to manager thread\n"); request.req_kind = REQ_DEBUG; - __libc_write(__pthread_manager_request, (char *) &request, sizeof(request)); + TEMP_FAILURE_RETRY(__libc_write(__pthread_manager_request, + (char *) &request, sizeof(request))); return 0; } @@ -467,7 +508,8 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr, sigprocmask(SIG_SETMASK, (const sigset_t *) NULL, &request.req_args.create.mask); PDEBUG("write REQ_CREATE to manager thread\n"); - __libc_write(__pthread_manager_request, (char *) &request, sizeof(request)); + TEMP_FAILURE_RETRY(__libc_write(__pthread_manager_request, + (char *) &request, sizeof(request))); PDEBUG("before suspend(self)\n"); suspend(self); PDEBUG("after suspend(self)\n"); @@ -562,45 +604,39 @@ int pthread_getschedparam(pthread_t thread, int *policy, /* Process-wide exit() request */ -static void pthread_exit_process(int retcode, void *arg) +static void pthread_onexit_process(int retcode, void *arg) { - struct pthread_request request; - pthread_descr self = thread_self(); - - if (__pthread_manager_request >= 0) { - request.req_thread = self; - request.req_kind = REQ_PROCESS_EXIT; - request.req_args.exit.code = retcode; - __libc_write(__pthread_manager_request, - (char *) &request, sizeof(request)); - suspend(self); - /* Main thread should accumulate times for thread manager and its - children, so that timings for main thread account for all threads. */ - if (self == __pthread_main_thread) - waitpid(__pthread_manager_thread.p_pid, NULL, __WCLONE); - } + struct pthread_request request; + pthread_descr self = thread_self(); + + if (__pthread_manager_request >= 0) { + request.req_thread = self; + request.req_kind = REQ_PROCESS_EXIT; + request.req_args.exit.code = retcode; + TEMP_FAILURE_RETRY(__libc_write(__pthread_manager_request, + (char *) &request, sizeof(request))); + suspend(self); + /* Main thread should accumulate times for thread manager and its + children, so that timings for main thread account for all threads. */ + if (self == __pthread_main_thread) { + waitpid(__pthread_manager_thread.p_pid, NULL, __WCLONE); + /* Since all threads have been asynchronously terminated + * (possibly holding locks), free cannot be used any more. */ + __pthread_manager_thread_bos = __pthread_manager_thread_tos = NULL; + } + } } /* The handler for the RESTART signal just records the signal received in the thread descriptor, and optionally performs a siglongjmp (for pthread_cond_timedwait). */ -#ifndef __i386__ static void pthread_handle_sigrestart(int sig) { - pthread_descr self = thread_self(); - PDEBUG("got called in non-i386 mode for %u\n", self); -#else -static void pthread_handle_sigrestart(int sig, struct sigcontext ctx) -{ - pthread_descr self; - asm volatile ("movw