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 --- 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 + 85 files changed, 3613 insertions(+), 893 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 (limited to 'libpthread') 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 %w0,%%gs" : : "r" (ctx.gs)); - self = thread_self(); - PDEBUG("got called in i386-mode for %u\n", self); -#endif - THREAD_SETMEM(self, p_signal, sig); - if (THREAD_GETMEM(self, p_signal_jmp) != NULL) - siglongjmp(*THREAD_GETMEM(self, p_signal_jmp), 1); + pthread_descr self = thread_self(); + THREAD_SETMEM(self, p_signal, sig); + if (THREAD_GETMEM(self, p_signal_jmp) != NULL) + siglongjmp(*THREAD_GETMEM(self, p_signal_jmp), 1); } /* The handler for the CANCEL signal checks for cancellation @@ -608,33 +644,48 @@ static void pthread_handle_sigrestart(int sig, struct sigcontext ctx) For the thread manager thread, redirect the signal to __pthread_manager_sighandler. */ -#ifndef __i386__ static void pthread_handle_sigcancel(int sig) { pthread_descr self = thread_self(); sigjmp_buf * jmpbuf; -#else -static void pthread_handle_sigcancel(int sig, struct sigcontext ctx) -{ - pthread_descr self; - sigjmp_buf * jmpbuf; - asm volatile ("movw %w0,%%gs" : : "r" (ctx.gs)); - self = thread_self(); -#endif + if (self == &__pthread_manager_thread) { +#ifdef THREAD_SELF + /* A new thread might get a cancel signal before it is fully + initialized, so that the thread register might still point to the + manager thread. Double check that this is really the manager + thread. */ + pthread_descr real_self = thread_self_stack(); + if (real_self == &__pthread_manager_thread) + { + __pthread_manager_sighandler(sig); + return; + } + /* Oops, thread_self() isn't working yet.. */ + self = real_self; +# ifdef INIT_THREAD_SELF + INIT_THREAD_SELF(self, self->p_nr); +# endif +#else __pthread_manager_sighandler(sig); return; +#endif } - if (__pthread_exit_requested) { + if (__builtin_expect (__pthread_exit_requested, 0)) { /* 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) + if (self == __pthread_main_thread) { +#ifdef USE_TLS + waitpid(__pthread_manager_thread->p_pid, NULL, __WCLONE); +#else waitpid(__pthread_manager_thread.p_pid, NULL, __WCLONE); +#endif + } _exit(__pthread_exit_code); } - if (THREAD_GETMEM(self, p_canceled) + if (__builtin_expect (THREAD_GETMEM(self, p_canceled), 0) && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) { if (THREAD_GETMEM(self, p_canceltype) == PTHREAD_CANCEL_ASYNCHRONOUS) pthread_exit(PTHREAD_CANCELED); @@ -698,7 +749,7 @@ void __pthread_kill_other_threads_np(void) { struct sigaction sa; /* Terminate all other threads and thread manager */ - pthread_exit_process(0, NULL); + pthread_onexit_process(0, NULL); /* Make current thread the main thread in case the calling thread changes its mind, does not exec(), and creates new threads instead. */ __pthread_reset_main_thread(); @@ -732,65 +783,188 @@ int __pthread_getconcurrency(void) } weak_alias (__pthread_getconcurrency, pthread_getconcurrency) -void __pthread_set_own_extricate_if(pthread_descr self, pthread_extricate_if *peif) -{ - __pthread_lock(self->p_lock, self); - THREAD_SETMEM(self, p_extricate, peif); - __pthread_unlock(self->p_lock); -} /* Primitives for controlling thread execution */ void __pthread_wait_for_restart_signal(pthread_descr self) { - sigset_t mask; + sigset_t mask; - sigprocmask(SIG_SETMASK, NULL, &mask); /* Get current signal mask */ - sigdelset(&mask, __pthread_sig_restart); /* Unblock the restart signal */ - do { - self->p_signal = 0; - PDEBUG("temporary block mask = %x\n", mask); - sigsuspend(&mask); /* Wait for signal */ - PDEBUG(" *** after sigsuspend *** \n"); - } while (self->p_signal !=__pthread_sig_restart ); -} + sigprocmask(SIG_SETMASK, NULL, &mask); /* Get current signal mask */ + sigdelset(&mask, __pthread_sig_restart); /* Unblock the restart signal */ + THREAD_SETMEM(self, p_signal, 0); + do { + sigsuspend(&mask); /* Wait for signal */ + } while (THREAD_GETMEM(self, p_signal) !=__pthread_sig_restart); -#ifdef __NR_rt_sigaction -void __pthread_restart_new(pthread_descr th) -{ - kill(th->p_pid, __pthread_sig_restart); + READ_MEMORY_BARRIER(); /* See comment in __pthread_restart_new */ } -/* There is no __pthread_suspend_new because it would just - be a wasteful wrapper for __pthread_wait_for_restart_signal */ -#if 0 -void __pthread_suspend_new(pthread_descr th) -{ - __pthread_wait_for_restart_signal(th); -} -#endif - -#else -/* The _old variants are for 2.0 and early 2.1 kernels which don't have RT signals. +#ifndef __NR_rt_sigaction +/* The _old variants are for 2.0 and early 2.1 kernels which don't have RT + signals. On these kernels, we use SIGUSR1 and SIGUSR2 for restart and cancellation. Since the restart signal does not queue, we use an atomic counter to create queuing semantics. This is needed to resolve a rare race condition in pthread_cond_timedwait_relative. */ + void __pthread_restart_old(pthread_descr th) { - if (atomic_increment(&th->p_resume_count) == -1) - kill(th->p_pid, __pthread_sig_restart); + if