diff options
author | Siddhesh Poyarekar <siddhesh@redhat.com> | 2012-05-15 09:41:27 +0530 |
---|---|---|
committer | Siddhesh Poyarekar <siddhesh@redhat.com> | 2012-05-15 09:41:57 +0530 |
commit | 439bf404b8fa125cf950dc1aa37838702c5353ea (patch) | |
tree | da5913033bcfa18987da0aabf69ad99b751772a3 /nptl | |
parent | 2949684c162a4413e42249d6b2ad554cb468b5be (diff) | |
download | glibc-439bf404b8fa125cf950dc1aa37838702c5353ea.tar.gz glibc-439bf404b8fa125cf950dc1aa37838702c5353ea.tar.xz glibc-439bf404b8fa125cf950dc1aa37838702c5353ea.zip |
Allow a single-threaded program to cancel itself
There is nothing in the POSIX specification to disallow a single-threaded program from cancelling itself, so we forcibly enable multiple_threads to allow the next available cancellation point in the thread to run. Also added additional tests to cover various cancellation scenarios.
Diffstat (limited to 'nptl')
-rw-r--r-- | nptl/ChangeLog | 19 | ||||
-rw-r--r-- | nptl/Makefile | 2 | ||||
-rw-r--r-- | nptl/descr.h | 15 | ||||
-rw-r--r-- | nptl/pthreadP.h | 4 | ||||
-rw-r--r-- | nptl/pthread_cancel.c | 8 | ||||
-rw-r--r-- | nptl/sysdeps/unix/sysv/linux/libc_multiple_threads.c | 3 | ||||
-rw-r--r-- | nptl/tst-cancel-self-cancelstate.c | 65 | ||||
-rw-r--r-- | nptl/tst-cancel-self-canceltype.c | 53 | ||||
-rw-r--r-- | nptl/tst-cancel-self-cleanup.c | 23 | ||||
-rw-r--r-- | nptl/tst-cancel-self-testcancel.c | 48 | ||||
-rw-r--r-- | nptl/tst-cancel-self.c | 48 | ||||
-rw-r--r-- | nptl/vars.c | 4 |
12 files changed, 290 insertions, 2 deletions
diff --git a/nptl/ChangeLog b/nptl/ChangeLog index cfc679cbe8..bd7311fb09 100644 --- a/nptl/ChangeLog +++ b/nptl/ChangeLog @@ -1,3 +1,22 @@ +2012-05-15 Siddhesh Poyarekar <siddhesh@redhat.com> + Jakub Jelinek <jakub@redhat.com> + + [BZ #13613] + * Makefile (tests): Add test cases. + * descr.h (struct pthread): Add a comment describing multiple_threads. + * pthreadP.h (__pthread_multiple_threads): Expand comment to include + single-process case. + * pthread_cancel.c (pthread_cancel): Enable multiple_threads + before setting cancelstate of the thread. + * sysdeps/unix/sysv/linux/libc_multiple_threads.c + (__libc_multiple_threads): Add explanatory comment. + * tst-cancel-self-cancelstate.c: New test case. + * tst-cancel-self-canceltype.c: Likewise. + * tst-cancel-self-cleanup.c: Supporting file for test cases. + * tst-cancel-self-testcancel.c: New test case. + * tst-cancel-self.c: Likewise. + * vars.c: Expand comment to include single-process case. + 2012-05-14 H.J. Lu <hongjiu.lu@intel.com> * sysdeps/x86_64/tls.h: Don't include <bits/wordsize.h>. diff --git a/nptl/Makefile b/nptl/Makefile index 07a10225f1..2a36d01720 100644 --- a/nptl/Makefile +++ b/nptl/Makefile @@ -236,6 +236,8 @@ tests = tst-typesizes \ tst-cancel11 tst-cancel12 tst-cancel13 tst-cancel14 tst-cancel15 \ tst-cancel16 tst-cancel17 tst-cancel18 tst-cancel19 tst-cancel20 \ tst-cancel21 tst-cancel22 tst-cancel23 tst-cancel24 tst-cancel25 \ + tst-cancel-self tst-cancel-self-cancelstate \ + tst-cancel-self-canceltype tst-cancel-self-testcancel \ tst-cleanup0 tst-cleanup1 tst-cleanup2 tst-cleanup3 tst-cleanup4 \ tst-flock1 tst-flock2 \ tst-signal1 tst-signal2 tst-signal3 tst-signal4 tst-signal5 \ diff --git a/nptl/descr.h b/nptl/descr.h index c2fabeb1a0..60d2d22e7a 100644 --- a/nptl/descr.h +++ b/nptl/descr.h @@ -131,6 +131,21 @@ struct pthread #else struct { + /* multiple_threads is enabled either when the process has spawned at + least one thread or when a single-threaded process cancels itself. + This enables additional code to introduce locking before doing some + compare_and_exchange operations and also enable cancellation points. + The concepts of multiple threads and cancellation points ideally + should be separate, since it is not necessary for multiple threads to + have been created for cancellation points to be enabled, as is the + case is when single-threaded process cancels itself. + + Since enabling multiple_threads enables additional code in + cancellation points and compare_and_exchange operations, there is a + potential for an unneeded performance hit when it is enabled in a + single-threaded, self-canceling process. This is OK though, since a + single-threaded process will enable async cancellation only when it + looks to cancel itself and is hence going to end anyway. */ int multiple_threads; int gscope_flag; # ifndef __ASSUME_PRIVATE_FUTEX diff --git a/nptl/pthreadP.h b/nptl/pthreadP.h index 68c690e88e..24a24717fc 100644 --- a/nptl/pthreadP.h +++ b/nptl/pthreadP.h @@ -378,7 +378,9 @@ extern int *__libc_pthread_init (unsigned long int *ptr, const struct pthread_functions *functions) internal_function; -/* Variable set to a nonzero value if more than one thread runs or ran. */ +/* Variable set to a nonzero value either if more than one thread runs or ran, + or if a single-threaded process is trying to cancel itself. See + nptl/descr.h for more context on the single-threaded process case. */ extern int __pthread_multiple_threads attribute_hidden; /* Pointer to the corresponding variable in libc. */ extern int *__libc_multiple_threads_ptr attribute_hidden; diff --git a/nptl/pthread_cancel.c b/nptl/pthread_cancel.c index 249aa1109a..1bfca63581 100644 --- a/nptl/pthread_cancel.c +++ b/nptl/pthread_cancel.c @@ -95,6 +95,14 @@ pthread_cancel (th) break; } + + /* A single-threaded process should be able to kill itself, since there is + nothing in the POSIX specification that says that it cannot. So we set + multiple_threads to true so that cancellation points get executed. */ + THREAD_SETMEM (THREAD_SELF, header.multiple_threads, 1); +#ifndef TLS_MULTIPLE_THREADS_IN_TCB + __pthread_multiple_threads = *__libc_multiple_threads_ptr = 1; +#endif } /* Mark the thread as canceled. This has to be done atomically since other bits could be modified as well. */ diff --git a/nptl/sysdeps/unix/sysv/linux/libc_multiple_threads.c b/nptl/sysdeps/unix/sysv/linux/libc_multiple_threads.c index 7fffb0d808..459b8cf7c0 100644 --- a/nptl/sysdeps/unix/sysv/linux/libc_multiple_threads.c +++ b/nptl/sysdeps/unix/sysv/linux/libc_multiple_threads.c @@ -20,6 +20,9 @@ #ifndef NOT_IN_libc # ifndef TLS_MULTIPLE_THREADS_IN_TCB +/* Variable set to a nonzero value either if more than one thread runs or ran, + or if a single-threaded process is trying to cancel itself. See + nptl/descr.h for more context on the single-threaded process case. */ int __libc_multiple_threads attribute_hidden; # endif #endif diff --git a/nptl/tst-cancel-self-cancelstate.c b/nptl/tst-cancel-self-cancelstate.c new file mode 100644 index 0000000000..c82e6f3ced --- /dev/null +++ b/nptl/tst-cancel-self-cancelstate.c @@ -0,0 +1,65 @@ +/* Copyright (C) 2012 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + 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, see + <http://www.gnu.org/licenses/>. */ + +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "tst-cancel-self-cleanup.c" + + +static int +do_test (void) +{ + int ret = 0; + volatile int should_fail = 1; + + pthread_cleanup_push (cleanup, &should_fail); + + if ((ret = pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL)) != 0) + { + printf ("setcancelstate(disable) failed: %s\n", strerror (ret)); + exit (1); + } + + if ((ret = pthread_cancel (pthread_self ())) != 0) + { + printf ("cancel failed: %s\n", strerror (ret)); + exit (1); + } + + usleep (100); + should_fail = 0; + + if ((ret = pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, NULL)) != 0) + { + printf ("setcancelstate(enable) failed: %s\n", strerror (ret)); + exit (1); + } + + /* The write syscall within this printf should give us our cancellation + point. */ + printf ("Could not cancel self.\n"); + pthread_cleanup_pop (0); + + return 1; +} + + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/nptl/tst-cancel-self-canceltype.c b/nptl/tst-cancel-self-canceltype.c new file mode 100644 index 0000000000..c9bb653131 --- /dev/null +++ b/nptl/tst-cancel-self-canceltype.c @@ -0,0 +1,53 @@ +/* Copyright (C) 2012 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + 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, see + <http://www.gnu.org/licenses/>. */ + +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "tst-cancel-self-cleanup.c" + + +static int +do_test (void) +{ + int ret = 0, should_fail = 0; + + pthread_cleanup_push (cleanup, &should_fail); + + if ((ret = pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL)) != 0) + { + printf ("setcanceltype failed: %s\n", strerror (ret)); + exit (1); + } + + if ((ret = pthread_cancel (pthread_self ())) != 0) + { + printf ("cancel failed: %s\n", strerror (ret)); + exit (1); + } + + /* Wait to be canceled. Don't give any cancellation points to play with. */ + while (1); + pthread_cleanup_pop (0); + + return 1; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/nptl/tst-cancel-self-cleanup.c b/nptl/tst-cancel-self-cleanup.c new file mode 100644 index 0000000000..9b15f555dc --- /dev/null +++ b/nptl/tst-cancel-self-cleanup.c @@ -0,0 +1,23 @@ +/* Copyright (C) 2012 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + 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, see + <http://www.gnu.org/licenses/>. */ + +static void +cleanup (void *cleanup_should_fail) +{ + printf ("Main thread got cancelled and is being cleaned up now\n"); + exit (*(int *)cleanup_should_fail); +} diff --git a/nptl/tst-cancel-self-testcancel.c b/nptl/tst-cancel-self-testcancel.c new file mode 100644 index 0000000000..c9422321ce --- /dev/null +++ b/nptl/tst-cancel-self-testcancel.c @@ -0,0 +1,48 @@ +/* Copyright (C) 2012 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + 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, see + <http://www.gnu.org/licenses/>. */ + +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "tst-cancel-self-cleanup.c" + + +static int +do_test (void) +{ + int ret = 0, should_fail = 0; + + pthread_cleanup_push (cleanup, &should_fail); + if ((ret = pthread_cancel (pthread_self ())) != 0) + { + printf ("cancel failed: %s\n", strerror (ret)); + exit (1); + } + + pthread_testcancel (); + + printf ("Could not cancel self.\n"); + pthread_cleanup_pop (0); + + return 1; +} + + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/nptl/tst-cancel-self.c b/nptl/tst-cancel-self.c new file mode 100644 index 0000000000..966698ca3b --- /dev/null +++ b/nptl/tst-cancel-self.c @@ -0,0 +1,48 @@ +/* Copyright (C) 2012 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + 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, see + <http://www.gnu.org/licenses/>. */ + +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "tst-cancel-self-cleanup.c" + + +static int +do_test (void) +{ + int ret = 0, should_fail = 0; + + pthread_cleanup_push (cleanup, &should_fail); + if ((ret = pthread_cancel (pthread_self ())) != 0) + { + printf ("cancel failed: %s\n", strerror (ret)); + exit (1); + } + + /* The write syscall within this printf should give us our cancellation + point. */ + printf ("Could not cancel self.\n"); + pthread_cleanup_pop (0); + + return 1; +} + + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/nptl/vars.c b/nptl/vars.c index 8f3023cfc0..43a6e3957c 100644 --- a/nptl/vars.c +++ b/nptl/vars.c @@ -32,7 +32,9 @@ size_t __default_stacksize attribute_hidden int __is_smp attribute_hidden; #ifndef TLS_MULTIPLE_THREADS_IN_TCB -/* Variable set to a nonzero value if more than one thread runs or ran. */ +/* Variable set to a nonzero value either if more than one thread runs or ran, + or if a single-threaded process is trying to cancel itself. See + nptl/descr.h for more context on the single-threaded process case. */ int __pthread_multiple_threads attribute_hidden; #endif |