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.
This commit is contained in:
Ulrich Drepper 2003-11-06 04:29:42 +00:00
parent c28422b575
commit 44e941492d
6 changed files with 390 additions and 10 deletions

View file

@ -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

View file

@ -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

198
nptl/tst-cleanup4.c Normal file
View file

@ -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"

121
nptl/tst-cleanup4aux.c Normal file
View file

@ -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);
}

1
nptl/tst-cleanupx4.c Normal file
View file

@ -0,0 +1 @@
#include "tst-cleanup4.c"

View file

@ -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