aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog11
-rw-r--r--linuxthreads/ChangeLog33
-rw-r--r--linuxthreads/Makefile2
-rw-r--r--linuxthreads/cancel.c27
-rw-r--r--linuxthreads/condvar.c8
-rw-r--r--linuxthreads/internals.h4
-rw-r--r--linuxthreads/join.c11
-rw-r--r--linuxthreads/manager.c2
-rw-r--r--linuxthreads/oldsemaphore.c2
-rw-r--r--linuxthreads/pthread.c2
-rw-r--r--linuxthreads/ptlongjmp.c21
-rw-r--r--linuxthreads/semaphore.c8
-rw-r--r--linuxthreads/tst-cancel.c153
-rw-r--r--sysdeps/unix/sysv/linux/s390/s390-32/getcontext.S10
-rw-r--r--sysdeps/unix/sysv/linux/s390/s390-32/setcontext.S3
-rw-r--r--sysdeps/unix/sysv/linux/s390/s390-64/getcontext.S10
-rw-r--r--sysdeps/unix/sysv/linux/s390/s390-64/setcontext.S3
-rw-r--r--sysdeps/unix/sysv/linux/s390/swapcontext.c10
18 files changed, 281 insertions, 39 deletions
diff --git a/ChangeLog b/ChangeLog
index 3c3d8a9..2522b10 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+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.
diff --git a/linuxthreads/ChangeLog b/linuxthreads/ChangeLog
index de4047c..917ab71 100644
--- a/linuxthreads/ChangeLog
+++ b/linuxthreads/ChangeLog
@@ -1,3 +1,36 @@
+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.
diff --git a/linuxthreads/Makefile b/linuxthreads/Makefile
index 8ecbd22..b719ff8 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 0ae0d12..ed67a68 100644
--- a/linuxthreads/cancel.c
+++ b/linuxthreads/cancel.c
@@ -20,6 +20,9 @@
#include "internals.h"
#include "spinlock.h"
#include "restart.h"
+#include <stackinfo.h>
+
+#include <stdio.h>
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 f9c46a3..fd0db50 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 ba0c14d..998f778 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 bbcebe1..a0cdb41 100644
--- a/linuxthreads/join.c
+++ b/linuxthreads/join.c
@@ -25,6 +25,11 @@
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;
@@ -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 567ba65..431e149 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 da5272c..2099b8b 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 479531b..ff9c083 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 1c12508..68b9235 100644
--- a/linuxthreads/ptlongjmp.c
+++ b/linuxthreads/ptlongjmp.c
@@ -18,6 +18,7 @@
#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. */
@@ -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 e5afce43..bb681b3 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 0000000..da70d12
--- /dev/null
+++ b/linuxthreads/tst-cancel.c
@@ -0,0 +1,153 @@
+/* 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;
+}
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/getcontext.S b/sysdeps/unix/sysv/linux/s390/s390-32/getcontext.S
index e64cc85..b3cae1c 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 40adc85..2bed318 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 2fc21e2..9eba838 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 b3a9fea..a652ad3 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 9f1be05..2b34b63 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;
}