about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSiddhesh Poyarekar <siddhesh@redhat.com>2012-05-15 09:41:27 +0530
committerSiddhesh Poyarekar <siddhesh@redhat.com>2012-05-15 09:41:57 +0530
commit439bf404b8fa125cf950dc1aa37838702c5353ea (patch)
treeda5913033bcfa18987da0aabf69ad99b751772a3
parent2949684c162a4413e42249d6b2ad554cb468b5be (diff)
downloadglibc-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.
-rw-r--r--NEWS19
-rw-r--r--nptl/ChangeLog19
-rw-r--r--nptl/Makefile2
-rw-r--r--nptl/descr.h15
-rw-r--r--nptl/pthreadP.h4
-rw-r--r--nptl/pthread_cancel.c8
-rw-r--r--nptl/sysdeps/unix/sysv/linux/libc_multiple_threads.c3
-rw-r--r--nptl/tst-cancel-self-cancelstate.c65
-rw-r--r--nptl/tst-cancel-self-canceltype.c53
-rw-r--r--nptl/tst-cancel-self-cleanup.c23
-rw-r--r--nptl/tst-cancel-self-testcancel.c48
-rw-r--r--nptl/tst-cancel-self.c48
-rw-r--r--nptl/vars.c4
13 files changed, 300 insertions, 11 deletions
diff --git a/NEWS b/NEWS
index 0e05fe4366..e5a42d78ea 100644
--- a/NEWS
+++ b/NEWS
@@ -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:
 
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