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.
This commit is contained in:
Siddhesh Poyarekar 2012-05-15 09:41:27 +05:30
parent 2949684c16
commit 439bf404b8
13 changed files with 300 additions and 11 deletions

19
NEWS
View File

@ -17,15 +17,16 @@ Version 2.16
10153, 10210, 10254, 10346, 10545, 10716, 11174, 11322, 11365, 11451,
11494, 11521, 11837, 11959, 12047, 12340, 13058, 13525, 13526, 13527,
13528, 13529, 13530, 13531, 13532, 13533, 13547, 13551, 13552, 13553,
13555, 13559, 13563, 13566, 13583, 13592, 13618, 13637, 13656, 13658,
13673, 13691, 13695, 13704, 13705, 13706, 13726, 13738, 13739, 13750,
13758, 13760, 13761, 13775, 13786, 13787, 13792, 13806, 13824, 13840,
13841, 13844, 13846, 13851, 13852, 13854, 13871, 13872, 13873, 13879,
13883, 13884, 13885, 13886, 13892, 13895, 13908, 13910, 13911, 13912,
13913, 13914, 13915, 13916, 13917, 13918, 13919, 13920, 13921, 13922,
13923, 13924, 13926, 13927, 13928, 13938, 13941, 13942, 13954, 13955,
13956, 13963, 13967, 13970, 13973, 13979, 13983, 14012, 14027, 14033,
14034, 14040, 14049, 14053, 14055, 14064, 14080, 14083, 14103, 14104
13555, 13559, 13563, 13566, 13583, 13592, 13613, 13618, 13637, 13656,
13658, 13673, 13691, 13695, 13704, 13705, 13706, 13726, 13738, 13739,
13750, 13758, 13760, 13761, 13775, 13786, 13787, 13792, 13806, 13824,
13840, 13841, 13844, 13846, 13851, 13852, 13854, 13871, 13872, 13873,
13879, 13883, 13884, 13885, 13886, 13892, 13895, 13908, 13910, 13911,
13912, 13913, 13914, 13915, 13916, 13917, 13918, 13919, 13920, 13921,
13922, 13923, 13924, 13926, 13927, 13928, 13938, 13941, 13942, 13954,
13955, 13956, 13963, 13967, 13970, 13973, 13979, 13983, 14012, 14027,
14033, 14034, 14040, 14049, 14053, 14055, 14064, 14080, 14083, 14103,
14104
* ISO C11 support:

View File

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

View File

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

View File

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

View File

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

View File

@ -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. */

View File

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

View File

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

View File

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

View File

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

View File

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

48
nptl/tst-cancel-self.c Normal file
View File

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

View File

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