diff options
-rw-r--r-- | nptl/ChangeLog | 14 | ||||
-rw-r--r-- | nptl/Makefile | 8 | ||||
-rw-r--r-- | nptl/tst-cleanup4.c | 198 | ||||
-rw-r--r-- | nptl/tst-cleanup4aux.c | 121 | ||||
-rw-r--r-- | nptl/tst-cleanupx4.c | 1 | ||||
-rw-r--r-- | nptl/unwind.c | 58 |
6 files changed, 390 insertions, 10 deletions
diff --git a/nptl/ChangeLog b/nptl/ChangeLog index 9e52682111..220c2e8a18 100644 --- a/nptl/ChangeLog +++ b/nptl/ChangeLog @@ -1,3 +1,17 @@ +2003-11-05 Jakub Jelinek <jakub@redhat.com> + + * unwind.c (FRAME_LEFT): Define. + (unwind_stop): Handle old style cleanups here. + (__pthread_unwind): Handle old style cleanups only if + !HAVE_FORCED_UNWIND. + * Makefile (tests): Add tst-cleanup4 and tst-cleanupx4. + (CFLAGS-tst-cleanupx4.c): Add -fexceptions. + ($(objpfx)tst-cleanup4): Depend on $(objpfx)tst-cleanup4aux.o. + ($(objpfx)tst-cleanupx4): Likewise. + * tst-cleanup4.c: New test. + * tst-cleanup4aux.c: New. + * tst-cleanupx4.c: New test. + 2003-11-04 Ulrich Drepper <drepper@redhat.com> * sysdeps/pthread/bits/stdio-lock.h: Use lll_*lock instead of diff --git a/nptl/Makefile b/nptl/Makefile index f5ca916823..8c519ff1e6 100644 --- a/nptl/Makefile +++ b/nptl/Makefile @@ -216,7 +216,7 @@ tests = tst-attr1 tst-attr2 tst-attr3 \ tst-cancel6 tst-cancel7 tst-cancel8 tst-cancel9 tst-cancel10 \ tst-cancel11 tst-cancel12 tst-cancel13 tst-cancel14 tst-cancel15 \ tst-cancel16 tst-cancel17 tst-cancel18 tst-cancel19 \ - tst-cleanup0 tst-cleanup1 tst-cleanup2 tst-cleanup3 \ + tst-cleanup0 tst-cleanup1 tst-cleanup2 tst-cleanup3 tst-cleanup4 \ tst-flock1 tst-flock2 \ tst-signal1 tst-signal2 tst-signal3 tst-signal4 tst-signal5 \ tst-signal6 \ @@ -248,7 +248,7 @@ tests += tst-cancelx2 tst-cancelx3 tst-cancelx4 tst-cancelx5 \ tst-cancelx6 tst-cancelx7 tst-cancelx8 tst-cancelx9 tst-cancelx10 \ tst-cancelx11 tst-cancelx12 tst-cancelx13 tst-cancelx14 tst-cancelx15\ tst-cancelx16 tst-cancelx17 tst-cancelx18 \ - tst-cleanupx0 tst-cleanupx1 tst-cleanupx2 tst-cleanupx3 \ + tst-cleanupx0 tst-cleanupx1 tst-cleanupx2 tst-cleanupx3 tst-cleanupx4 \ tst-oncex3 tst-oncex4 endif ifeq ($(build-shared),yes) @@ -393,6 +393,7 @@ CFLAGS-tst-cleanupx0.c += -fexceptions -fasynchronous-unwind-tables CFLAGS-tst-cleanupx1.c += -fexceptions -fasynchronous-unwind-tables CFLAGS-tst-cleanupx2.c += -fexceptions CFLAGS-tst-cleanupx3.c += -fexceptions +CFLAGS-tst-cleanupx4.c += -fexceptions CFLAGS-tst-oncex3.c += -fexceptions CFLAGS-tst-oncex4.c += -fexceptions CFLAGS-tst-align.c += $(stack-align-test-flags) @@ -405,6 +406,9 @@ LDFLAGS-tst-atfork2 = -rdynamic tst-atfork2-ENV = MALLOC_TRACE=$(objpfx)tst-atfork2.mtrace $(objpfx)tst-atfork2mod.so: $(shared-thread-library) +$(objpfx)tst-cleanup4: $(objpfx)tst-cleanup4aux.o $(shared-thread-library) +$(objpfx)tst-cleanupx4: $(objpfx)tst-cleanup4aux.o $(shared-thread-library) + $(objpfx)tst-tls3: $(libdl) $(shared-thread-library) LDFLAGS-tst-tls3 = -rdynamic $(objpfx)tst-tls3.out: $(objpfx)tst-tls3mod.so diff --git a/nptl/tst-cleanup4.c b/nptl/tst-cleanup4.c new file mode 100644 index 0000000000..1489fd31b8 --- /dev/null +++ b/nptl/tst-cleanup4.c @@ -0,0 +1,198 @@ +/* Copyright (C) 2003 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Jakub Jelinek <jakub@redhat.com>, 2003. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +/* LinuxThreads pthread_cleanup_{push,pop} helpers. */ +extern void _pthread_cleanup_push (struct _pthread_cleanup_buffer *__buffer, + void (*__routine) (void *), + void *__arg); +extern void _pthread_cleanup_pop (struct _pthread_cleanup_buffer *__buffer, + int __execute); + +static int fds[2]; +static pthread_barrier_t b2; +static int global; + +/* Defined in tst-cleanup4aux.c, never compiled with -fexceptions. */ +extern void fn5 (void); +extern void fn7 (void); +extern void fn9 (void); + +void +clh (void *arg) +{ + int val = (long int) arg; + + printf ("clh (%d)\n", val); + + global *= val; + global += val; +} + + +static __attribute__((noinline)) void +fn_read (void) +{ + int r = pthread_barrier_wait (&b2); + if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("%s: barrier_wait failed\n", __FUNCTION__); + exit (1); + } + + char c; + read (fds[0], &c, 1); +} + + +__attribute__((noinline)) void +fn0 (void) +{ + pthread_cleanup_push (clh, (void *) 1l); + + fn_read (); + + pthread_cleanup_pop (1); +} + + +__attribute__((noinline)) void +fn1 (void) +{ + /* This is the old LinuxThreads pthread_cleanup_{push,pop}. */ + struct _pthread_cleanup_buffer b; + _pthread_cleanup_push (&b, clh, (void *) 2l); + + fn0 (); + + _pthread_cleanup_pop (&b, 1); +} + + +static __attribute__((noinline)) void +fn2 (void) +{ + pthread_cleanup_push (clh, (void *) 3l); + + fn1 (); + + pthread_cleanup_pop (1); +} + + +static void * +tf (void *a) +{ + switch ((long) a) + { + case 0: + fn2 (); + break; + case 1: + fn5 (); + break; + case 2: + fn7 (); + break; + case 3: + fn9 (); + break; + } + + return NULL; +} + + +int +do_test (void) +{ + int result = 0; + + if (pipe (fds) != 0) + { + puts ("pipe failed"); + exit (1); + } + + if (pthread_barrier_init (&b2, NULL, 2) != 0) + { + puts ("b2 init failed"); + exit (1); + } + + const int expect[] = + { + 15, /* 1 2 3 */ + 276, /* 1 4 5 6 */ + 120, /* 1 7 8 */ + 460 /* 1 2 9 10 */ + }; + + long i; + for (i = 0; i < 4; ++i) + { + global = 0; + + printf ("test %ld\n", i); + + pthread_t th; + if (pthread_create (&th, NULL, tf, (void *) i) != 0) + { + puts ("create failed"); + exit (1); + } + + int e = pthread_barrier_wait (&b2); + if (e != 0 && e != PTHREAD_BARRIER_SERIAL_THREAD) + { + printf ("%s: barrier_wait failed\n", __FUNCTION__); + exit (1); + } + + pthread_cancel (th); + + void *r; + if ((e = pthread_join (th, &r)) != 0) + { + printf ("join failed: %d\n", e); + _exit (1); + } + + if (r != PTHREAD_CANCELED) + { + puts ("thread not canceled"); + exit (1); + } + + if (global != expect[i]) + { + printf ("global = %d, expected %d\n", global, expect[i]); + result = 1; + } + } + + return result; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/nptl/tst-cleanup4aux.c b/nptl/tst-cleanup4aux.c new file mode 100644 index 0000000000..0fcec5de47 --- /dev/null +++ b/nptl/tst-cleanup4aux.c @@ -0,0 +1,121 @@ +/* Copyright (C) 2003 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Jakub Jelinek <jakub@redhat.com>, 2003. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +extern void _pthread_cleanup_push (struct _pthread_cleanup_buffer *__buffer, + void (*__routine) (void *), + void *__arg); +extern void _pthread_cleanup_pop (struct _pthread_cleanup_buffer *__buffer, + int __execute); + +extern void clh (void *arg); +extern void fn0 (void); +extern void fn1 (void); +extern void fn5 (void); +extern void fn7 (void); +extern void fn9 (void); + + +static __attribute__((noinline)) void +fn3 (void) +{ + /* This is the old LinuxThreads pthread_cleanup_{push,pop}. */ + struct _pthread_cleanup_buffer b; + _pthread_cleanup_push (&b, clh, (void *) 4l); + + fn0 (); + + _pthread_cleanup_pop (&b, 1); +} + + +static __attribute__((noinline)) void +fn4 (void) +{ + pthread_cleanup_push (clh, (void *) 5l); + + fn3 (); + + pthread_cleanup_pop (1); +} + + +void +fn5 (void) +{ + /* This is the old LinuxThreads pthread_cleanup_{push,pop}. */ + struct _pthread_cleanup_buffer b; + _pthread_cleanup_push (&b, clh, (void *) 6l); + + fn4 (); + + _pthread_cleanup_pop (&b, 1); +} + + +static __attribute__((noinline)) void +fn6 (void) +{ + pthread_cleanup_push (clh, (void *) 7l); + + fn0 (); + + pthread_cleanup_pop (1); +} + + +void +fn7 (void) +{ + /* This is the old LinuxThreads pthread_cleanup_{push,pop}. */ + struct _pthread_cleanup_buffer b; + _pthread_cleanup_push (&b, clh, (void *) 8l); + + fn6 (); + + _pthread_cleanup_pop (&b, 1); +} + + +static __attribute__((noinline)) void +fn8 (void) +{ + pthread_cleanup_push (clh, (void *) 9l); + + fn1 (); + + pthread_cleanup_pop (1); +} + + +void +fn9 (void) +{ + /* This is the old LinuxThreads pthread_cleanup_{push,pop}. */ + struct _pthread_cleanup_buffer b; + _pthread_cleanup_push (&b, clh, (void *) 10l); + + fn8 (); + + _pthread_cleanup_pop (&b, 1); +} diff --git a/nptl/tst-cleanupx4.c b/nptl/tst-cleanupx4.c new file mode 100644 index 0000000000..8dea954b55 --- /dev/null +++ b/nptl/tst-cleanupx4.c @@ -0,0 +1 @@ +#include "tst-cleanup4.c" diff --git a/nptl/unwind.c b/nptl/unwind.c index a879e9297f..8013b5a829 100644 --- a/nptl/unwind.c +++ b/nptl/unwind.c @@ -27,6 +27,14 @@ #ifdef HAVE_FORCED_UNWIND +#ifdef _STACK_GROWS_DOWN +# define FRAME_LEFT(frame, other) ((char *) frame >= (char *) other) +#elif _STACK_GROWS_UP +# define FRAME_LEFT(frame, other) ((char *) frame <= (char *) other) +#else +# error "Define either _STACK_GROWS_DOWN or _STACK_GROWS_UP" +#endif + static _Unwind_Reason_Code unwind_stop (int version, _Unwind_Action actions, _Unwind_Exception_Class exc_class, @@ -34,6 +42,9 @@ unwind_stop (int version, _Unwind_Action actions, struct _Unwind_Context *context, void *stop_parameter) { struct pthread_unwind_buf *buf = stop_parameter; + struct pthread *self = THREAD_SELF; + struct _pthread_cleanup_buffer *curp = THREAD_GETMEM (self, cleanup); + int do_longjump = 0; /* Do longjmp if we're at "end of stack", aka "end of unwind data". We assume there are only C frame without unwind data in between @@ -42,6 +53,37 @@ unwind_stop (int version, _Unwind_Action actions, previous frame. */ if ((actions & _UA_END_OF_STACK) || ! _JMPBUF_CFA_UNWINDS (buf->cancel_jmp_buf[0].jmp_buf, context)) + do_longjump = 1; + + if (__builtin_expect (curp != NULL, 0)) + { + /* Handle the compatibility stuff. Execute all handlers + registered with the old method which would be unwound by this + step. */ + struct _pthread_cleanup_buffer *oldp = buf->priv.data.cleanup; + void *cfa = (void *) _Unwind_GetCFA (context); + + if (curp != oldp && (do_longjump || FRAME_LEFT (cfa, curp))) + { + do + { + /* Pointer to the next element. */ + struct _pthread_cleanup_buffer *nextp = curp->__prev; + + /* Call the handler. */ + curp->__routine (curp->__arg); + + /* To the next. */ + curp = nextp; + } + while (curp != oldp && (do_longjump || FRAME_LEFT (cfa, curp))); + + /* Mark the current element as handled. */ + THREAD_SETMEM (self, cleanup, curp); + } + } + + if (do_longjump) __libc_longjmp ((struct __jmp_buf_tag *) buf->cancel_jmp_buf, 1); return _URC_NO_REASON; @@ -70,6 +112,14 @@ __pthread_unwind (__pthread_unwind_buf_t *buf) struct pthread_unwind_buf *ibuf = (struct pthread_unwind_buf *) buf; struct pthread *self = THREAD_SELF; +#ifdef HAVE_FORCED_UNWIND + /* This is not a catchable exception, so don't provide any details about + the exception type. We do need to initialize the field though. */ + THREAD_SETMEM (self, exc.exception_class, 0); + THREAD_SETMEM (self, exc.exception_cleanup, unwind_cleanup); + + _Unwind_ForcedUnwind (&self->exc, unwind_stop, ibuf); +#else /* Handle the compatibility stuff first. Execute all handlers registered with the old method. We don't execute them in order, instead, they will run first. */ @@ -95,14 +145,6 @@ __pthread_unwind (__pthread_unwind_buf_t *buf) THREAD_SETMEM (self, cleanup, curp); } -#ifdef HAVE_FORCED_UNWIND - /* This is not a catchable exception, so don't provide any details about - the exception type. We do need to initialize the field though. */ - THREAD_SETMEM (self, exc.exception_class, 0); - THREAD_SETMEM (self, exc.exception_cleanup, unwind_cleanup); - - _Unwind_ForcedUnwind (&self->exc, unwind_stop, ibuf); -#else /* We simply jump to the registered setjmp buffer. */ __libc_longjmp ((struct __jmp_buf_tag *) ibuf->cancel_jmp_buf, 1); #endif |