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 0e05fe4..e5a42d7 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 cfc679c..bd7311f 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 07a1022..2a36d01 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 c2fabeb..60d2d22 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 68c690e..24a2471 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 249aa11..1bfca63 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 7fffb0d..459b8cf 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 8f3023c..43a6e39 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