+2001-04-10 Martin Schwidefsky <schwidefsky@de.ibm.com>
+
+ * 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 <drepper@redhat.com>
* sysdeps/alpha/stackinfo.h: New file.
+2001-04-10 Ulrich Drepper <drepper@redhat.com>
+
+ * 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 <hp@axis.com>
* sysdeps/cris/pt-machine.h: New file.
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
#include "internals.h"
#include "spinlock.h"
#include "restart.h"
+#include <stackinfo.h>
+
+#include <stdio.h>
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;
}
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;
}
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,
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)
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);
&& 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. */
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);
&& 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. */
/* 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, ...);
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;
struct pthread_request request;
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);
if (already_canceled) {
__pthread_set_own_extricate_if(self, 0);
- pthread_exit(PTHREAD_CANCELED);
+ __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
}
suspend(self);
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);
}
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;
}
}
}
}
- pthread_exit(PTHREAD_CANCELED);
+ __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
}
}
}
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);
#include <setjmp.h>
#include "pthread.h"
#include "internals.h"
+#include <stackinfo.h>
/* These functions are not declared anywhere since they shouldn't be
used at another place but here. */
{
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)))
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 */
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;
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;
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;
--- /dev/null
+/* Tests for cancelation handling. */
+
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+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;
+}
/* __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
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)
/* Load general purpose registers. */
lm %r0,%r15,SC_GPRS(%r5)
- /* Return 1. */
- la %r2,1
+ /* Return. */
br %r14
END(__setcontext)
/* __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
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)
/* Load general purpose registers. */
lmg %r0,%r15,SC_GPRS(%r5)
- /* Return 1. */
- la %r2,1
+ /* Return. */
br %r14
END(__setcontext)
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;
}