diff --git a/ChangeLog b/ChangeLog index 3c3d8a9a54..2522b1095b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2001-04-10 Martin Schwidefsky + + * sysdeps/unix/sysv/linux/s390/s390-32/getcontext.S: Fix return + value of getcontext. + * sysdeps/unix/sysv/linux/s390/s390-64/getcontext.S: Likewise. + * sysdeps/unix/sysv/linux/s390/s390-32/setcontext.S: Fix return + value of setcontext. + * sysdeps/unix/sysv/linux/s390/s390-64/setcontext.S: Likewise. + * sysdeps/unix/sysv/linux/s390/swapcontext.c: Skip setcontext + call by changing the saved context. + 2001-04-10 Ulrich Drepper * sysdeps/alpha/stackinfo.h: New file. diff --git a/linuxthreads/ChangeLog b/linuxthreads/ChangeLog index de4047c5ac..917ab715f1 100644 --- a/linuxthreads/ChangeLog +++ b/linuxthreads/ChangeLog @@ -1,3 +1,36 @@ +2001-04-10 Ulrich Drepper + + * join.c (pthread_exit): Move code to new function __pthread_do_exit + which takes an extra parameter with the current frame pointer. + Call new function with CURRENT_STACK_FRAME. + (__pthread_do_exit): New function. Call __pthread_perform_cleanup + with the new parameter. + (pthread_join): Call __pthread_do_exit instead of pthread_exit. + * cancel.c (__pthread_perform_cleanup): Takes extra parameter. Use + this parameter as the initial value the cleanup handler records are + compared against. No active cleanup handler record must have an + address lower than the previous one and the initial record must be + above (below on PA) the frame address passed in. + (pthread_setcancelstate): Call __pthread_do_exit instead of + pthread_exit. + (pthread_setcanceltype): Likewise. + (pthread_testcancel): Likewise. + (_pthread_cleanup_pop_restore): Likewise. + * condvar.c (pthread_cond_wait): Likewise. + (pthread_cond_timedwait_relative): Likewise. + * manager.c (pthread_start_thread): Likewise. + * oldsemaphore.c (__old_sem_wait): Likewise. + * pthread.c (pthread_handle_sigcancel): Likewise. + * semaphore.c (__new_sem_wait): Likewise. + (sem_timedwait): Likewise. + * ptlongjmp.c (pthread_cleanup_upto): Also use current stack frame + to limit the cleanup handlers which get run. + * internals.h: Add prototype for __pthread_do_exit. Adjust prototype + for __pthread_perform_cleanup. + + * Makefile (tests): Add tst-cancel. + * tst-cancel.c: New file. + 2001-04-08 Hans-Peter Nilsson * sysdeps/cris/pt-machine.h: New file. diff --git a/linuxthreads/Makefile b/linuxthreads/Makefile index 8ecbd22d56..b719ff8f21 100644 --- a/linuxthreads/Makefile +++ b/linuxthreads/Makefile @@ -56,7 +56,7 @@ endif librt-tests = ex10 ex11 tests = ex1 ex2 ex3 ex4 ex5 ex6 ex7 ex8 ex9 $(librt-tests) ex12 ex13 joinrace \ tststack $(tests-nodelete-$(have-z-nodelete)) ecmutex ex14 ex15 ex16 \ - ex17 + ex17 tst-cancel ifeq (yes,$(build-shared)) tests-nodelete-yes = unload diff --git a/linuxthreads/cancel.c b/linuxthreads/cancel.c index 0ae0d12887..ed67a6845f 100644 --- a/linuxthreads/cancel.c +++ b/linuxthreads/cancel.c @@ -20,6 +20,9 @@ #include "internals.h" #include "spinlock.h" #include "restart.h" +#include + +#include int pthread_setcancelstate(int state, int * oldstate) { @@ -31,7 +34,7 @@ int pthread_setcancelstate(int state, int * oldstate) if (THREAD_GETMEM(self, p_canceled) && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE && THREAD_GETMEM(self, p_canceltype) == PTHREAD_CANCEL_ASYNCHRONOUS) - pthread_exit(PTHREAD_CANCELED); + __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME); return 0; } @@ -45,7 +48,7 @@ int pthread_setcanceltype(int type, int * oldtype) if (THREAD_GETMEM(self, p_canceled) && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE && THREAD_GETMEM(self, p_canceltype) == PTHREAD_CANCEL_ASYNCHRONOUS) - pthread_exit(PTHREAD_CANCELED); + __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME); return 0; } @@ -112,7 +115,7 @@ void pthread_testcancel(void) pthread_descr self = thread_self(); if (THREAD_GETMEM(self, p_canceled) && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) - pthread_exit(PTHREAD_CANCELED); + __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME); } void _pthread_cleanup_push(struct _pthread_cleanup_buffer * buffer, @@ -155,15 +158,27 @@ void _pthread_cleanup_pop_restore(struct _pthread_cleanup_buffer * buffer, if (THREAD_GETMEM(self, p_canceled) && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE && THREAD_GETMEM(self, p_canceltype) == PTHREAD_CANCEL_ASYNCHRONOUS) - pthread_exit(PTHREAD_CANCELED); + __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME); } -void __pthread_perform_cleanup(void) +void __pthread_perform_cleanup(char *currentframe) { pthread_descr self = thread_self(); struct _pthread_cleanup_buffer * c; + for (c = THREAD_GETMEM(self, p_cleanup); c != NULL; c = c->__prev) - c->__routine(c->__arg); + { +#if _STACK_GROWS_DOWN + if ((char *) c <= currentframe) + break; +#elif _STACK_GROWS_UP + if ((char *) c >= currentframe) + break; +#else +# error "Define either _STACK_GROWS_DOWN or _STACK_GROWS_UP" +#endif + c->__routine(c->__arg); + } /* And the TSD which needs special help. */ if (THREAD_GETMEM(self, p_libc_specific[_LIBC_TSD_KEY_RPC_VARS]) != NULL) diff --git a/linuxthreads/condvar.c b/linuxthreads/condvar.c index f9c46a3316..fd0db50fa2 100644 --- a/linuxthreads/condvar.c +++ b/linuxthreads/condvar.c @@ -93,7 +93,7 @@ int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) if (already_canceled) { __pthread_set_own_extricate_if(self, 0); - pthread_exit(PTHREAD_CANCELED); + __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME); } pthread_mutex_unlock(mutex); @@ -122,7 +122,7 @@ int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) && 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_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME); } /* Put back any resumes we caught that don't belong to us. */ @@ -168,7 +168,7 @@ pthread_cond_timedwait_relative(pthread_cond_t *cond, if (already_canceled) { __pthread_set_own_extricate_if(self, 0); - pthread_exit(PTHREAD_CANCELED); + __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME); } pthread_mutex_unlock(mutex); @@ -216,7 +216,7 @@ pthread_cond_timedwait_relative(pthread_cond_t *cond, && 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_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME); } /* Put back any resumes we caught that don't belong to us. */ diff --git a/linuxthreads/internals.h b/linuxthreads/internals.h index ba0c14d29d..998f7788e8 100644 --- a/linuxthreads/internals.h +++ b/linuxthreads/internals.h @@ -428,8 +428,10 @@ static inline pthread_descr thread_self (void) /* Internal global functions */ +extern void __pthread_do_exit (void *retval, char *currentframe) + __attribute__ ((__noreturn__)); extern void __pthread_destroy_specifics (void); -extern void __pthread_perform_cleanup (void); +extern void __pthread_perform_cleanup (char *currentframe); extern void __pthread_init_max_stacksize (void); extern int __pthread_initialize_manager (void); extern void __pthread_message (char * fmt, ...); diff --git a/linuxthreads/join.c b/linuxthreads/join.c index bbcebe12ae..a0cdb41cd3 100644 --- a/linuxthreads/join.c +++ b/linuxthreads/join.c @@ -24,6 +24,11 @@ #include "restart.h" void pthread_exit(void * retval) +{ + __pthread_do_exit (retval, CURRENT_STACK_FRAME); +} + +void __pthread_do_exit(void *retval, char *currentframe) { pthread_descr self = thread_self(); pthread_descr joining; @@ -33,7 +38,7 @@ void pthread_exit(void * retval) contain cancellation points */ THREAD_SETMEM(self, p_canceled, 0); /* Call cleanup functions and destroy the thread-specific data */ - __pthread_perform_cleanup(); + __pthread_perform_cleanup(currentframe); __pthread_destroy_specifics(); /* Store return value */ __pthread_lock(THREAD_GETMEM(self, p_lock), self); @@ -144,7 +149,7 @@ int pthread_join(pthread_t thread_id, void ** thread_return) if (already_canceled) { __pthread_set_own_extricate_if(self, 0); - pthread_exit(PTHREAD_CANCELED); + __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME); } suspend(self); @@ -155,7 +160,7 @@ int pthread_join(pthread_t thread_id, void ** thread_return) 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_exit(PTHREAD_CANCELED); + __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME); } __pthread_lock(&handle->h_lock, self); } diff --git a/linuxthreads/manager.c b/linuxthreads/manager.c index 567ba658d1..431e149212 100644 --- a/linuxthreads/manager.c +++ b/linuxthreads/manager.c @@ -262,7 +262,7 @@ static int pthread_start_thread(void *arg) outcome = self->p_start_args.start_routine(THREAD_GETMEM(self, p_start_args.arg)); /* Exit with the given return value */ - pthread_exit(outcome); + __pthread_do_exit(outcome, CURRENT_STACK_FRAME); return 0; } diff --git a/linuxthreads/oldsemaphore.c b/linuxthreads/oldsemaphore.c index da5272cd24..2099b8bbda 100644 --- a/linuxthreads/oldsemaphore.c +++ b/linuxthreads/oldsemaphore.c @@ -144,7 +144,7 @@ int __old_sem_wait(old_sem_t * sem) } } } - pthread_exit(PTHREAD_CANCELED); + __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME); } } } diff --git a/linuxthreads/pthread.c b/linuxthreads/pthread.c index 479531b5a4..ff9c083010 100644 --- a/linuxthreads/pthread.c +++ b/linuxthreads/pthread.c @@ -817,7 +817,7 @@ static void pthread_handle_sigcancel(int sig) 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); + __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME); jmpbuf = THREAD_GETMEM(self, p_cancel_jmp); if (jmpbuf != NULL) { THREAD_SETMEM(self, p_cancel_jmp, NULL); diff --git a/linuxthreads/ptlongjmp.c b/linuxthreads/ptlongjmp.c index 1c12508101..68b9235cb8 100644 --- a/linuxthreads/ptlongjmp.c +++ b/linuxthreads/ptlongjmp.c @@ -18,6 +18,7 @@ #include #include "pthread.h" #include "internals.h" +#include /* These functions are not declared anywhere since they shouldn't be used at another place but here. */ @@ -31,11 +32,29 @@ static void pthread_cleanup_upto(__jmp_buf target) { pthread_descr self = thread_self(); struct _pthread_cleanup_buffer * c; + char *currentframe = CURRENT_STACK_FRAME; for (c = THREAD_GETMEM(self, p_cleanup); c != NULL && _JMPBUF_UNWINDS(target, c); c = c->__prev) - c->__routine(c->__arg); + { +#if _STACK_GROWS_DOWN + if ((char *) c <= currentframe) + { + c = NULL; + break; + } +#elif _STACK_GROWS_UP + if ((char *) c >= currentframe) + { + c = NULL; + break; + } +#else +# error "Define either _STACK_GROWS_DOWN or _STACK_GROWS_UP" +#endif + c->__routine(c->__arg); + } THREAD_SETMEM(self, p_cleanup, c); if (THREAD_GETMEM(self, p_in_sighandler) && _JMPBUF_UNWINDS(target, THREAD_GETMEM(self, p_in_sighandler))) diff --git a/linuxthreads/semaphore.c b/linuxthreads/semaphore.c index e5afce43eb..bb681b3621 100644 --- a/linuxthreads/semaphore.c +++ b/linuxthreads/semaphore.c @@ -85,7 +85,7 @@ int __new_sem_wait(sem_t * sem) if (already_canceled) { __pthread_set_own_extricate_if(self, 0); - pthread_exit(PTHREAD_CANCELED); + __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME); } /* Wait for sem_post or cancellation, or fall through if already canceled */ @@ -111,7 +111,7 @@ int __new_sem_wait(sem_t * sem) 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_exit(PTHREAD_CANCELED); + __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME); } /* We got the semaphore */ return 0; @@ -245,7 +245,7 @@ int sem_timedwait(sem_t *sem, const struct timespec *abstime) if (already_canceled) { __pthread_set_own_extricate_if(self, 0); - pthread_exit(PTHREAD_CANCELED); + __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME); } spurious_wakeup_count = 0; @@ -289,7 +289,7 @@ int sem_timedwait(sem_t *sem, const struct timespec *abstime) 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_exit(PTHREAD_CANCELED); + __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME); } /* We got the semaphore */ return 0; diff --git a/linuxthreads/tst-cancel.c b/linuxthreads/tst-cancel.c new file mode 100644 index 0000000000..da70d1278f --- /dev/null +++ b/linuxthreads/tst-cancel.c @@ -0,0 +1,153 @@ +/* Tests for cancelation handling. */ + +#include +#include +#include +#include +#include +#include + +int fd; + +pthread_barrier_t bar; + + +static void +cleanup (void *arg) +{ + int nr = (int) (long int) arg; + char s[30]; + char *cp = stpcpy (s, "cleanup "); + *cp++ = '0' + nr; + *cp++ = '\n'; + __libc_write (fd, s, cp - s); +} + + +static void * +t1 (void *arg) +{ + pthread_cleanup_push (cleanup, (void *) (long int) 1); + return NULL; + pthread_cleanup_pop (0); +} + + +static void +inner (int a) +{ + pthread_cleanup_push (cleanup, (void *) (long int) a); + if (a) + return; + pthread_cleanup_pop (0); +} + + +static void * +t2 (void *arg) +{ + pthread_cleanup_push (cleanup, (void *) (long int) 2); + inner ((int) (long int) arg); + return NULL; + pthread_cleanup_pop (0); +} + + +static void * +t3 (void *arg) +{ + pthread_cleanup_push (cleanup, (void *) (long int) 4); + inner ((int) (long int) arg); + pthread_exit (NULL); + pthread_cleanup_pop (0); +} + + +int +main (void) +{ + pthread_t td; + int err; + char tmp[] = "thtstXXXXXX"; + struct stat64 st; + int result = 0; + + fd = mkstemp (tmp); + if (fd == -1) + { + printf ("cannot create temporary file: %m"); + exit (1); + } + unlink (tmp); + + err = pthread_barrier_init (&bar, NULL, 2); + if (err != 0 ) + { + printf ("cannot create barrier: %s\n", strerror (err)); + exit (1); + } + + err = pthread_create (&td, NULL, t1, NULL); + if (err != 0) + { + printf ("cannot create thread t1: %s\n", strerror (err)); + exit (1); + } + + err = pthread_join (td, NULL); + if (err != 0) + { + printf ("cannot join thread: %s\n", strerror (err)); + exit (1); + } + + err = pthread_create (&td, NULL, t2, (void *) 3); + if (err != 0) + { + printf ("cannot create thread t2: %s\n", strerror (err)); + exit (1); + } + + err = pthread_join (td, NULL); + if (err != 0) + { + printf ("cannot join thread: %s\n", strerror (err)); + exit (1); + } + + err = pthread_create (&td, NULL, t3, (void *) 5); + if (err != 0) + { + printf ("cannot create thread t3: %s\n", strerror (err)); + exit (1); + } + + err = pthread_join (td, NULL); + if (err != 0) + { + printf ("cannot join thread: %s\n", strerror (err)); + exit (1); + } + + if (fstat64 (fd, &st) < 0) + { + printf ("cannot stat temporary file: %m\n"); + result = 1; + } + else if (st.st_size != 0) + { + char buf[512]; + puts ("some cleanup handlers ran:"); + fflush (stdout); + while (1) + { + ssize_t n = read (fd, buf, sizeof buf); + if (n < 0) + break; + write (STDOUT_FILENO, buf, n); + } + result = 1; + } + + return result; +} diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/getcontext.S b/sysdeps/unix/sysv/linux/s390/s390-32/getcontext.S index e64cc8588c..b3cae1c7fe 100644 --- a/sysdeps/unix/sysv/linux/s390/s390-32/getcontext.S +++ b/sysdeps/unix/sysv/linux/s390/s390-32/getcontext.S @@ -25,9 +25,7 @@ /* __getcontext (const ucontext_t *ucp) Saves the machine context in UCP such that when it is activated, - it appears as if __getcontext() returned again. The only difference - is that on a first return, %r2 contains 1 and on a subsequent - return, it contains 0. + it appears as if __getcontext() returned again. This implementation is intended to be used for *synchronous* context switches only. Therefore, it does not have to save anything @@ -61,11 +59,13 @@ ENTRY(__getcontext) std %f14,SC_FPRS+112(%r5) std %f15,SC_FPRS+120(%r5) + /* Set __getcontext return value to 0. */ + slr %r2,%r2 + /* Store general purpose registers. */ stm %r0,%r15,SC_GPRS(%r5) - /* Return 0. */ - slr %r2,%r2 + /* Return. */ br %r14 END(__getcontext) diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/setcontext.S b/sysdeps/unix/sysv/linux/s390/s390-32/setcontext.S index 40adc8574a..2bed31847a 100644 --- a/sysdeps/unix/sysv/linux/s390/s390-32/setcontext.S +++ b/sysdeps/unix/sysv/linux/s390/s390-32/setcontext.S @@ -62,8 +62,7 @@ ENTRY(__setcontext) /* Load general purpose registers. */ lm %r0,%r15,SC_GPRS(%r5) - /* Return 1. */ - la %r2,1 + /* Return. */ br %r14 END(__setcontext) diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/getcontext.S b/sysdeps/unix/sysv/linux/s390/s390-64/getcontext.S index 2fc21e2b60..9eba838edf 100644 --- a/sysdeps/unix/sysv/linux/s390/s390-64/getcontext.S +++ b/sysdeps/unix/sysv/linux/s390/s390-64/getcontext.S @@ -25,9 +25,7 @@ /* __getcontext (const ucontext_t *ucp) Saves the machine context in UCP such that when it is activated, - it appears as if __getcontext() returned again. The only difference - is that on a first return, %r2 contains 1 and on a subsequent - return, it contains 0. + it appears as if __getcontext() returned again. This implementation is intended to be used for *synchronous* context switches only. Therefore, it does not have to save anything @@ -61,11 +59,13 @@ ENTRY(__getcontext) std %f14,SC_FPRS+112(%r5) std %f15,SC_FPRS+120(%r5) + /* Set __getcontext return value to 0. */ + slr %r2,%r2 + /* Store general purpose registers. */ stmg %r0,%r15,SC_GPRS(%r5) - /* Return 0. */ - slgr %r2,%r2 + /* Return. */ br %r14 END(__getcontext) diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/setcontext.S b/sysdeps/unix/sysv/linux/s390/s390-64/setcontext.S index b3a9fea405..a652ad3173 100644 --- a/sysdeps/unix/sysv/linux/s390/s390-64/setcontext.S +++ b/sysdeps/unix/sysv/linux/s390/s390-64/setcontext.S @@ -62,8 +62,7 @@ ENTRY(__setcontext) /* Load general purpose registers. */ lmg %r0,%r15,SC_GPRS(%r5) - /* Return 1. */ - la %r2,1 + /* Return. */ br %r14 END(__setcontext) diff --git a/sysdeps/unix/sysv/linux/s390/swapcontext.c b/sysdeps/unix/sysv/linux/s390/swapcontext.c index 9f1be058db..2b34b63ce4 100644 --- a/sysdeps/unix/sysv/linux/s390/swapcontext.c +++ b/sysdeps/unix/sysv/linux/s390/swapcontext.c @@ -25,8 +25,14 @@ extern int __setcontext (__const ucontext_t *__ucp) __THROW; int __swapcontext (ucontext_t *oucp, const ucontext_t *ucp) { - if (__getcontext (oucp) == 0) - __setcontext (ucp); + /* Save the current machine context to oucp. */ + __getcontext (oucp); + /* Modify oucp to skip the __setcontext call on reactivation. */ + oucp->uc_mcontext.gregs[14] = &&fake_return; + /* Restore the machine context in ucp. */ + __setcontext (ucp); + +fake_return: return 0; }