[02/13] nptl: Fix testcases for new pthread cancellation mechanism

Message ID 1444234995-9542-3-git-send-email-adhemerval.zanella@linaro.com
State Dropped
Headers

Commit Message

Adhemerval Zanella Oct. 7, 2015, 4:23 p.m. UTC
  With upcoming fix for BZ#12683, pthread cancellation does not act for:

1. If syscall is blocked but with some side effects already having taken
   place (e.g. a partial read or write)
2. After the syscall has returned.

It is because program need to act on such cases (for instance, to avoid
leak of allocated resources our handling partial read/write).

This patches fixes the NPTL testcase that assumes the old behavior and
also remove the tst-cancel-wrappers.sh test (which checks for symbols
that does not exist anymore).

It also add two more testcase to check both the returned and the errno
value on case of a cancelled syscall.

Tested on i686, x86_64, x32, powerpc64le, and aarch64.

	* nptl/Makefile [$(run-built-tests) = yes] (tests-special): Remove
	tst-cancel-wrappers.sh.
	* nptl/tst-cancel-wrappers.sh: Remove file.
	* nptl/tst-cancel2.c (tf): Add pthread_testcancel after cancellable
	syscall.
	* nptl/tst-cancel20.c (sh_body): Likewise.
	* nptl/tst-cancel21.c (sh_body): Likewise.
	(tf_body): Likewise.
	* nptl/tst-cancel4.c (tf_Write): Likewise.
	(tf_send): Likewise.
	(tf_open64): New test: check for open64 cancellable syscall.
	(tf_pread64): New test: check for pread64 cancellable syscall.
	(tf_pwrite64): New test: check for pwrite64 cancellable syscall.
---
 nptl/Makefile               |  18 ++-----
 nptl/tst-cancel-wrappers.sh |  92 -------------------------------
 nptl/tst-cancel2.c          |   3 ++
 nptl/tst-cancel20.c         |   6 ++-
 nptl/tst-cancel21.c         |   5 ++
 nptl/tst-cancel4.c          | 128 +++++++++++++++++++++++++++++++++++++++++++-
 6 files changed, 142 insertions(+), 110 deletions(-)
 delete mode 100644 nptl/tst-cancel-wrappers.sh
  

Comments

Roland McGrath Oct. 7, 2015, 8:59 p.m. UTC | #1
> diff --git a/nptl/tst-cancel2.c b/nptl/tst-cancel2.c
> index 1a74992..3d34169 100644
> --- a/nptl/tst-cancel2.c
> +++ b/nptl/tst-cancel2.c
> @@ -33,6 +33,9 @@ tf (void *arg)
>    char buf[100000];
>  
>    while (write (fd[1], buf, sizeof (buf)) > 0);
> +  /* The write can return a value higher than 0 (meaning partial write)
> +     due the SIGCANCEL, but the thread may still pending cancellation.  */
> +  pthread_testcancel ();

"due to the"
"may still be pending"

The same comment with the same errors is repeated in several tests.

> --- a/nptl/tst-cancel4.c
> +++ b/nptl/tst-cancel4.c
> @@ -38,8 +38,10 @@
>  #include <sys/un.h>
>  #include <sys/wait.h>
>  
> -#include "pthreadP.h"
> -
> +/* The signal used for asynchronous cancelation.  */
> +#ifndef SIGCANCEL
> +# define SIGCANCEL       __SIGRTMIN
> +#endif

What's the rationale for this change?  If the test needs SIGCANCEL then it
is relying on implementation internals.  If that is necessary for this
test, then it is better to use an internal header than to duplicate part of
its content.  If some other part of pthreadP.h interferes with this code,
or if you just want to avoid unnecessary internals, then you can just
#include <nptl-signals.h> instead.

> +  if (arg == NULL)
> +    // XXX If somebody can provide a portable test case in which open()
> +    // blocks we can enable this test to run in both rounds.
> +    abort ();

You can create such a scenario in POSIX systems using mkfifo.
  
Adhemerval Zanella Oct. 8, 2015, 8:06 p.m. UTC | #2
Thanks for the review.

On 07-10-2015 17:59, Roland McGrath wrote:
>> diff --git a/nptl/tst-cancel2.c b/nptl/tst-cancel2.c
>> index 1a74992..3d34169 100644
>> --- a/nptl/tst-cancel2.c
>> +++ b/nptl/tst-cancel2.c
>> @@ -33,6 +33,9 @@ tf (void *arg)
>>    char buf[100000];
>>  
>>    while (write (fd[1], buf, sizeof (buf)) > 0);
>> +  /* The write can return a value higher than 0 (meaning partial write)
>> +     due the SIGCANCEL, but the thread may still pending cancellation.  */
>> +  pthread_testcancel ();
> 
> "due to the"
> "may still be pending"
> 
> The same comment with the same errors is repeated in several tests.

I will fix that.

> 
>> --- a/nptl/tst-cancel4.c
>> +++ b/nptl/tst-cancel4.c
>> @@ -38,8 +38,10 @@
>>  #include <sys/un.h>
>>  #include <sys/wait.h>
>>  
>> -#include "pthreadP.h"
>> -
>> +/* The signal used for asynchronous cancelation.  */
>> +#ifndef SIGCANCEL
>> +# define SIGCANCEL       __SIGRTMIN
>> +#endif
> 
> What's the rationale for this change?  If the test needs SIGCANCEL then it
> is relying on implementation internals.  If that is necessary for this
> test, then it is better to use an internal header than to duplicate part of
> its content.  If some other part of pthreadP.h interferes with this code,
> or if you just want to avoid unnecessary internals, then you can just
> #include <nptl-signals.h> instead.

The rationale is to not tied the test with implementation internals.  However
the sigpause test itself does not really require to block SIGCANCEL, we can
rather use another signal.  I will change to 'sigpause (sigmask (SIGINT))'
and remote both the inclusion of 'pthreadP.h' and the SIGCANCEL definitions.

> 
>> +  if (arg == NULL)
>> +    // XXX If somebody can provide a portable test case in which open()
>> +    // blocks we can enable this test to run in both rounds.
>> +    abort ();
> 
> You can create such a scenario in POSIX systems using mkfifo.
> 

Indeed (I just copies the block from previous tests), I will change that.
  

Patch

diff --git a/nptl/Makefile b/nptl/Makefile
index f3de49b..b7ff3f1 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -259,6 +259,7 @@  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-cancel26 tst-cancel27 \
 	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 \
@@ -397,8 +398,7 @@  tests-reverse += tst-cancel5 tst-cancel23 tst-vfork1x tst-vfork2x
 ifeq ($(run-built-tests),yes)
 tests-special += $(objpfx)tst-stack3-mem.out $(objpfx)tst-oddstacklimit.out
 ifeq ($(build-shared),yes)
-tests-special += $(objpfx)tst-tls6.out $(objpfx)tst-cleanup0-cmp.out \
-		 $(objpfx)tst-cancel-wrappers.out
+tests-special += $(objpfx)tst-tls6.out $(objpfx)tst-cleanup0-cmp.out
 endif
 endif
 
@@ -614,7 +614,7 @@  $(objpfx)$(multidir)/crtn.o: $(objpfx)crtn.o $(objpfx)$(multidir)/
 endif
 
 generated += libpthread_nonshared.a \
-	     multidir.mk tst-atfork2.mtrace tst-cancel-wrappers.out \
+	     multidir.mk tst-atfork2.mtrace \
 	     tst-tls6.out
 
 generated += $(objpfx)tst-atfork2.mtrace \
@@ -631,18 +631,6 @@  LDFLAGS-pthread.so += -e __nptl_main
 $(objpfx)pt-interp.os: $(common-objpfx)runtime-linker.h
 endif
 
-ifeq ($(run-built-tests),yes)
-ifeq (yes,$(build-shared))
-$(objpfx)tst-cancel-wrappers.out: tst-cancel-wrappers.sh
-	$(SHELL) $< '$(NM)' \
-		    $(common-objpfx)libc_pic.a \
-		    $(common-objpfx)libc.a \
-		    $(objpfx)libpthread_pic.a \
-		    $(objpfx)libpthread.a > $@; \
-	$(evaluate-test)
-endif
-endif
-
 tst-exec4-ARGS = $(host-test-program-cmd)
 
 $(objpfx)tst-execstack: $(libdl)
diff --git a/nptl/tst-cancel-wrappers.sh b/nptl/tst-cancel-wrappers.sh
deleted file mode 100644
index d492a54..0000000
--- a/nptl/tst-cancel-wrappers.sh
+++ /dev/null
@@ -1,92 +0,0 @@ 
-#! /bin/sh
-# Test whether all cancelable functions are cancelable.
-# Copyright (C) 2002-2015 Free Software Foundation, Inc.
-# This file is part of the GNU C Library.
-# Contributed by Jakub Jelinek <jakub@redhat.com>, 2002.
-
-# 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/>.
-
-NM="$1"; shift
-while [ $# -gt 0 ]; do
-  ( $NM -P $1; echo 'end[end]:' ) | gawk ' BEGIN {
-C["accept"]=1
-C["close"]=1
-C["connect"]=1
-C["creat"]=1
-C["fcntl"]=1
-C["fdatasync"]=1
-C["fsync"]=1
-C["msgrcv"]=1
-C["msgsnd"]=1
-C["msync"]=1
-C["nanosleep"]=1
-C["open"]=1
-C["open64"]=1
-C["pause"]=1
-C["poll"]=1
-C["pread"]=1
-C["pread64"]=1
-C["pselect"]=1
-C["pwrite"]=1
-C["pwrite64"]=1
-C["read"]=1
-C["readv"]=1
-C["recv"]=1
-C["recvfrom"]=1
-C["recvmsg"]=1
-C["select"]=1
-C["send"]=1
-C["sendmsg"]=1
-C["sendto"]=1
-C["sigpause"]=1
-C["sigsuspend"]=1
-C["sigwait"]=1
-C["sigwaitinfo"]=1
-C["tcdrain"]=1
-C["wait"]=1
-C["waitid"]=1
-C["waitpid"]=1
-C["write"]=1
-C["writev"]=1
-C["__xpg_sigpause"]=1
-}
-/:$/ {
-  if (seen)
-    {
-      if (!seen_enable || !seen_disable)
-	{
-	  printf "in '$1'(%s) %s'\''s cancellation missing\n", object, seen
-	  ret = 1
-	}
-    }
-  seen=""
-  seen_enable=""
-  seen_disable=""
-  object=gensub(/^.*\[(.*)\]:$/, "\\1", 1, $0)
-  next
-}
-{
-  if (C[$1] && $2 ~ /^[TW]$/)
-    seen=$1
-  else if ($1 ~ /^([.]|)__(libc|pthread)_enable_asynccancel$/ && $2 == "U")
-    seen_enable=1
-  else if ($1 ~ /^([.]|)__(libc|pthread)_disable_asynccancel$/ && $2 == "U")
-    seen_disable=1
-}
-END {
-  exit ret
-}' || exit
-  shift
-done
diff --git a/nptl/tst-cancel2.c b/nptl/tst-cancel2.c
index 1a74992..3d34169 100644
--- a/nptl/tst-cancel2.c
+++ b/nptl/tst-cancel2.c
@@ -33,6 +33,9 @@  tf (void *arg)
   char buf[100000];
 
   while (write (fd[1], buf, sizeof (buf)) > 0);
+  /* The write can return a value higher than 0 (meaning partial write)
+     due the SIGCANCEL, but the thread may still pending cancellation.  */
+  pthread_testcancel ();
 
   return (void *) 42l;
 }
diff --git a/nptl/tst-cancel20.c b/nptl/tst-cancel20.c
index 51b558e..614708a 100644
--- a/nptl/tst-cancel20.c
+++ b/nptl/tst-cancel20.c
@@ -49,6 +49,9 @@  sh_body (void)
       puts ("read succeeded");
       exit (1);
     }
+  /* The read can return a value higher than 0 (meaning partial reads)
+     due the SIGCANCEL, but the thread may still pending cancellation.  */
+  pthread_testcancel ();
 
   pthread_cleanup_pop (0);
 }
@@ -84,7 +87,8 @@  tf_body (void)
       puts ("read succeeded");
       exit (1);
     }
-
+  /* Check for partial read.  */
+  pthread_testcancel ();
   read (fd[0], &c, 1);
 
   pthread_cleanup_pop (0);
diff --git a/nptl/tst-cancel21.c b/nptl/tst-cancel21.c
index b54f236..b1370e1 100644
--- a/nptl/tst-cancel21.c
+++ b/nptl/tst-cancel21.c
@@ -50,6 +50,9 @@  sh_body (void)
       puts ("read succeeded");
       exit (1);
     }
+  /* The write can return a value higher than 0 (meaning partial write)
+     due the SIGCANCEL, but the thread may still pending cancellation.  */
+  pthread_testcancel ();
 
   pthread_cleanup_pop (0);
 }
@@ -85,6 +88,8 @@  tf_body (void)
       puts ("read succeeded");
       exit (1);
     }
+  /* Check partial read.  */
+  pthread_testcancel ();
 
   read (fd[0], &c, 1);
 
diff --git a/nptl/tst-cancel4.c b/nptl/tst-cancel4.c
index e50afd7..0a41d95 100644
--- a/nptl/tst-cancel4.c
+++ b/nptl/tst-cancel4.c
@@ -38,8 +38,10 @@ 
 #include <sys/un.h>
 #include <sys/wait.h>
 
-#include "pthreadP.h"
-
+/* The signal used for asynchronous cancelation.  */
+#ifndef SIGCANCEL
+# define SIGCANCEL       __SIGRTMIN
+#endif
 
 /* Since STREAMS are not supported in the standard Linux kernel and
    there we don't advertise STREAMS as supported is no need to test
@@ -247,6 +249,9 @@  tf_write  (void *arg)
   char buf[WRITE_BUFFER_SIZE];
   memset (buf, '\0', sizeof (buf));
   s = write (fd, buf, sizeof (buf));
+  /* The write can return a value higher than 0 (meaning partial write)
+     due the SIGCANCEL, but the thread may still pending cancellation.  */
+  pthread_testcancel ();
 
   pthread_cleanup_pop (0);
 
@@ -1143,6 +1148,8 @@  tf_send (void *arg)
   char mem[700000];
 
   send (tempfd2, mem, arg == NULL ? sizeof (mem) : 1, 0);
+  /* Check for partial send.  */
+  pthread_testcancel ();
 
   pthread_cleanup_pop (0);
 
@@ -1425,6 +1432,38 @@  tf_open (void *arg)
   exit (1);
 }
 
+static void *
+tf_open64 (void *arg)
+{
+  if (arg == NULL)
+    // XXX If somebody can provide a portable test case in which open()
+    // blocks we can enable this test to run in both rounds.
+    abort ();
+
+  int r = pthread_barrier_wait (&b2);
+  if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
+    {
+      printf ("%s: barrier_wait failed\n", __FUNCTION__);
+      exit (1);
+    }
+
+  r = pthread_barrier_wait (&b2);
+  if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
+    {
+      printf ("%s: 2nd barrier_wait failed\n", __FUNCTION__);
+      exit (1);
+    }
+
+  pthread_cleanup_push (cl, NULL);
+
+  open64 ("Makefile", O_RDONLY);
+
+  pthread_cleanup_pop (0);
+
+  printf ("%s: open returned\n", __FUNCTION__);
+
+  exit (1);
+}
 
 static void *
 tf_close (void *arg)
@@ -1510,6 +1549,46 @@  tf_pread (void *arg)
   exit (1);
 }
 
+static void *
+tf_pread64 (void *arg)
+{
+  if (arg == NULL)
+    // XXX If somebody can provide a portable test case in which pread()
+    // blocks we can enable this test to run in both rounds.
+    abort ();
+
+  tempfd = open64 ("Makefile", O_RDONLY);
+  if (tempfd == -1)
+    {
+      printf ("%s: cannot open64 Makefile\n", __FUNCTION__);
+      exit (1);
+    }
+
+  int r = pthread_barrier_wait (&b2);
+  if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
+    {
+      printf ("%s: barrier_wait failed\n", __FUNCTION__);
+      exit (1);
+    }
+
+  r = pthread_barrier_wait (&b2);
+  if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
+    {
+      printf ("%s: 2nd barrier_wait failed\n", __FUNCTION__);
+      exit (1);
+    }
+
+  pthread_cleanup_push (cl, NULL);
+
+  char mem[10];
+  pread64 (tempfd, mem, sizeof (mem), 0);
+
+  pthread_cleanup_pop (0);
+
+  printf ("%s: pread64 returned\n", __FUNCTION__);
+
+  exit (1);
+}
 
 static void *
 tf_pwrite (void *arg)
@@ -1554,6 +1633,48 @@  tf_pwrite (void *arg)
   exit (1);
 }
 
+static void *
+tf_pwrite64 (void *arg)
+{
+  if (arg == NULL)
+    // XXX If somebody can provide a portable test case in which pwrite()
+    // blocks we can enable this test to run in both rounds.
+    abort ();
+
+  char fname[] = "/tmp/tst-cancel4-fd-XXXXXX";
+  tempfd = mkstemp (fname);
+  if (tempfd == -1)
+    {
+      printf ("%s: mkstemp failed\n", __FUNCTION__);
+      exit (1);
+    }
+  unlink (fname);
+
+  int r = pthread_barrier_wait (&b2);
+  if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
+    {
+      printf ("%s: barrier_wait failed\n", __FUNCTION__);
+      exit (1);
+    }
+
+  r = pthread_barrier_wait (&b2);
+  if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD)
+    {
+      printf ("%s: 2nd barrier_wait failed\n", __FUNCTION__);
+      exit (1);
+    }
+
+  pthread_cleanup_push (cl, NULL);
+
+  char mem[10];
+  pwrite64 (tempfd, mem, sizeof (mem), 0);
+
+  pthread_cleanup_pop (0);
+
+  printf ("%s: pwrite64 returned\n", __FUNCTION__);
+
+  exit (1);
+}
 
 static void *
 tf_fsync (void *arg)
@@ -2141,9 +2262,12 @@  static struct
   ADD_TEST (recvfrom, 2, 0),
   ADD_TEST (recvmsg, 2, 0),
   ADD_TEST (open, 2, 1),
+  ADD_TEST (open64, 2, 1),
   ADD_TEST (close, 2, 1),
   ADD_TEST (pread, 2, 1),
+  ADD_TEST (pread64, 2, 1),
   ADD_TEST (pwrite, 2, 1),
+  ADD_TEST (pwrite64, 2, 1),
   ADD_TEST (fsync, 2, 1),
   ADD_TEST (fdatasync, 2, 1),
   ADD_TEST (msync, 2, 1),