diff options
-rw-r--r-- | nptl/ChangeLog | 9 | ||||
-rw-r--r-- | nptl/Makefile | 2 | ||||
-rw-r--r-- | nptl/libc-cancellation.c | 34 | ||||
-rw-r--r-- | nptl/tst-cancel9.c | 126 |
4 files changed, 158 insertions, 13 deletions
diff --git a/nptl/ChangeLog b/nptl/ChangeLog index ec74f80..7e61122 100644 --- a/nptl/ChangeLog +++ b/nptl/ChangeLog @@ -1,3 +1,12 @@ +2003-02-16 Ulrich Drepper <drepper@redhat.com> + + * libc-cancellation.c (__libc_enable_asynccancel): Rwrite to avoid + going into an endless loop. + * Makefile (tests): Add tst-cancel9. + * tst-cancel9.c: New file. + + * pthread_cancel.c (pthread_cancel): Use the result of __pthread_kill. + 2003-02-15 Ulrich Drepper <drepper@redhat.com> * tst-mutex5.c (do_test): Add more timedlock tests. diff --git a/nptl/Makefile b/nptl/Makefile index 756878b..b4bbe99 100644 --- a/nptl/Makefile +++ b/nptl/Makefile @@ -142,7 +142,7 @@ tests = tst-attr1 tst-attr2 \ tst-fork1 tst-fork2 tst-fork3 \ tst-atfork1 \ tst-cancel1 tst-cancel2 tst-cancel3 tst-cancel4 tst-cancel5 \ - tst-cancel6 tst-cancel7 tst-cancel8 \ + tst-cancel6 tst-cancel7 tst-cancel8 tst-cancel9 \ tst-cleanup1 tst-cleanup2 tst-cleanup3 \ tst-flock1 tst-flock2 \ tst-signal1 tst-signal2 tst-signal3 \ diff --git a/nptl/libc-cancellation.c b/nptl/libc-cancellation.c index 713ac8e..af56891 100644 --- a/nptl/libc-cancellation.c +++ b/nptl/libc-cancellation.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2002 Free Software Foundation, Inc. +/* Copyright (C) 2002, 2003 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@redhat.com>, 2002. @@ -40,20 +40,30 @@ __libc_enable_asynccancel (void) oldval = THREAD_GETMEM (self, cancelhandling); int newval = oldval | CANCELTYPE_BITMASK; - if (newval == oldval) - break; - - if (atomic_compare_and_exchange_acq (&self->cancelhandling, newval, - oldval) == 0) + if (__builtin_expect ((oldval & CANCELED_BITMASK) != 0, 0)) { - if (CANCEL_ENABLED_AND_CANCELED_AND_ASYNCHRONOUS (newval)) - { - THREAD_SETMEM (self, result, PTHREAD_CANCELED); - __do_cancel (); - } + /* If we are already exiting stop right here. */ + if ((oldval & EXITING_BITMASK) != 0) + break; + + if (atomic_compare_and_exchange_acq (&self->cancelhandling, newval, + oldval) == 0) + /* Somebody else modified the word, try again. */ + continue; + + THREAD_SETMEM (self, result, PTHREAD_CANCELED); - break; + /* The thread is exiting now. */ + atomic_bit_set (&self->cancelhandling, EXITING_BIT); + + __do_cancel (); + + /* NOTREACHED */ } + + if (atomic_compare_and_exchange_acq (&self->cancelhandling, newval, + oldval) == 0) + break; } return oldval; diff --git a/nptl/tst-cancel9.c b/nptl/tst-cancel9.c new file mode 100644 index 0000000..1d59e19 --- /dev/null +++ b/nptl/tst-cancel9.c @@ -0,0 +1,126 @@ +/* Copyright (C) 2003 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@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 <fcntl.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + + +static pthread_barrier_t b; + + +static void +cleanup (void *arg) +{ + fprintf (stderr, "in cleanup\n"); +} + + +static void * +tf (void *arg) +{ + int fd = open ("/dev/null", O_RDWR); + if (fd == -1) + { + puts ("cannot open /dev/null"); + exit (1); + } + FILE *fp = fdopen (fd, "w"); + if (fp == NULL) + { + puts ("fdopen failed"); + exit (1); + } + + pthread_cleanup_push (cleanup, NULL); + + int e = pthread_barrier_wait (&b); + if (e != 0 && e != PTHREAD_BARRIER_SERIAL_THREAD) + { + puts ("barrier_wait failed"); + exit (1); + } + + while (1) + /* fprintf() uses write() which is a cancallation point. */ + fprintf (fp, "foo"); + + pthread_cleanup_pop (0); + + return NULL; +} + + +static int +do_test (void) +{ + if (pthread_barrier_init (&b, NULL, 2) != 0) + { + puts ("barrier_init failed"); + exit (1); + } + + pthread_t th; + if (pthread_create (&th, NULL, tf, NULL) != 0) + { + puts ("create failed"); + return 1; + } + + int e = pthread_barrier_wait (&b); + if (e != 0 && e != PTHREAD_BARRIER_SERIAL_THREAD) + { + puts ("barrier_wait failed"); + exit (1); + } + + sleep (1); + + puts ("cancel now"); + + if (pthread_cancel (th) != 0) + { + puts ("cancel failed"); + exit (1); + } + + puts ("waiting for the child"); + + void *r; + if (pthread_join (th, &r) != 0) + { + puts ("join failed"); + exit (1); + } + + if (r != PTHREAD_CANCELED) + { + puts ("thread wasn't canceled"); + exit (1); + } + + return 0; +} + + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" |