]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
Allow a single-threaded program to cancel itself
authorSiddhesh Poyarekar <siddhesh@redhat.com>
Tue, 15 May 2012 04:11:27 +0000 (09:41 +0530)
committerSiddhesh Poyarekar <siddhesh@redhat.com>
Tue, 15 May 2012 04:11:57 +0000 (09:41 +0530)
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.

13 files changed:
NEWS
nptl/ChangeLog
nptl/Makefile
nptl/descr.h
nptl/pthreadP.h
nptl/pthread_cancel.c
nptl/sysdeps/unix/sysv/linux/libc_multiple_threads.c
nptl/tst-cancel-self-cancelstate.c [new file with mode: 0644]
nptl/tst-cancel-self-canceltype.c [new file with mode: 0644]
nptl/tst-cancel-self-cleanup.c [new file with mode: 0644]
nptl/tst-cancel-self-testcancel.c [new file with mode: 0644]
nptl/tst-cancel-self.c [new file with mode: 0644]
nptl/vars.c

diff --git a/NEWS b/NEWS
index 0e05fe4366919764d9b153208716b439a8a0bfdc..e5a42d78ea1bdfe208a6c5cb4135cff2db09a6aa 100644 (file)
--- 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:
 
index cfc679cbe8c426aad61baab2d7b40b0dbd643a20..bd7311fb096b25cc5d869c677e82356740121ea3 100644 (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>.
index 07a10225f114dd6c38b669cb3caef6d608692d72..2a36d0172037ce42fcc77e434b3abcbe56056837 100644 (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 \
index c2fabeb1a0fc956968a063bd0868c31fd24efcb4..60d2d22e7a4bb6e5828ac7696d269da902a5373a 100644 (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
index 68c690e88e9f5c350ac3778e9795f777f633e43b..24a24717fcc37716bbe03cdd039463026c05ba76 100644 (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;
index 249aa1109a0847a61904f02ec4e50a6661dd5001..1bfca6358152153bf6c75c7defdbcc604d363fce 100644 (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.  */
index 7fffb0d808f0da41ef9a1292afdaa017e9c5ecb4..459b8cf7c01d187e11d7d8badcc32d2878bc4db8 100644 (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
diff --git a/nptl/tst-cancel-self-cancelstate.c b/nptl/tst-cancel-self-cancelstate.c
new file mode 100644 (file)
index 0000000..c82e6f3
--- /dev/null
@@ -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 (file)
index 0000000..c9bb653
--- /dev/null
@@ -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 (file)
index 0000000..9b15f55
--- /dev/null
@@ -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 (file)
index 0000000..c942232
--- /dev/null
@@ -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 (file)
index 0000000..966698c
--- /dev/null
@@ -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"
index 8f3023cfc01d1f3a64502541fc2513385e8b4e6d..43a6e3957c23c87d3af147e8d4750eae53c1d622 100644 (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