about summary refs log tree commit diff
path: root/nptl
diff options
context:
space:
mode:
Diffstat (limited to 'nptl')
-rw-r--r--nptl/ChangeLog10
-rw-r--r--nptl/Makefile2
-rw-r--r--nptl/descr.h19
-rw-r--r--nptl/init.c2
-rw-r--r--nptl/pthread_cancel.c7
-rw-r--r--nptl/pthread_join.c8
-rw-r--r--nptl/tst-cancel4.c8
-rw-r--r--nptl/tst-join5.c141
8 files changed, 179 insertions, 18 deletions
diff --git a/nptl/ChangeLog b/nptl/ChangeLog
index 8e5f206156..3b02b45b88 100644
--- a/nptl/ChangeLog
+++ b/nptl/ChangeLog
@@ -1,5 +1,15 @@
 2003-02-14  Ulrich Drepper  <drepper@redhat.com>
 
+	* descr.h: Define CANCELING_BIT and CANCELING_BITMASK.  Introduce
+	after CANCELTYPE_BIT, move the other bits up.  Update CANCEL_RESTMASK.
+	* init.c (sigcancel_handler): Also set CANCELING_BITMASK bit in newval.
+	* pthread_cancel.c (pthread_cancel): Likewise.  Also set CANCELING_BIT
+	if asynchronous canceling is enabled.
+	* pthread_join.c (pthread_join): When recognizing circular joins,
+	take into account the other thread might be already canceled.
+	* Makefile (tests): Add tst-join5.
+	* tst-join5.c: New file.
+
 	* Makefile (tests): Add tst-join4.
 	* tst-join4.c: New file.
 
diff --git a/nptl/Makefile b/nptl/Makefile
index fbd78791ec..65c90c2182 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -137,7 +137,7 @@ tests = tst-attr1 tst-attr2 \
 	tst-sem1 tst-sem2 tst-sem3 tst-sem4 tst-sem5 \
 	tst-barrier1 tst-barrier2 tst-barrier3 \
 	tst-basic1 tst-basic2 \
-	tst-join1 tst-join2 tst-join3 tst-join4 \
+	tst-join1 tst-join2 tst-join3 tst-join4 tst-join5 \
 	tst-tsd1 tst-tsd2 \
 	tst-fork1 tst-fork2 tst-fork3 \
 	tst-atfork1 \
diff --git a/nptl/descr.h b/nptl/descr.h
index 3fe04ec2aa..e96f538c68 100644
--- a/nptl/descr.h
+++ b/nptl/descr.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2002 Free Software Foundation, Inc.
+/* Copyright (C) 2002, 2003 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
 
@@ -127,17 +127,20 @@ struct pthread
   /* Bit set if asynchronous cancellation mode is selected.  */
 #define CANCELTYPE_BIT		1
 #define CANCELTYPE_BITMASK	0x02
+  /* Bit set if canceling has been initiated.  */
+#define CANCELING_BIT		2
+#define CANCELING_BITMASK	0x04
   /* Bit set if canceled.  */
-#define CANCELED_BIT		2
-#define CANCELED_BITMASK	0x04
+#define CANCELED_BIT		3
+#define CANCELED_BITMASK	0x08
   /* Bit set if thread is exiting.  */
-#define EXITING_BIT		3
-#define EXITING_BITMASK		0x08
+#define EXITING_BIT		4
+#define EXITING_BITMASK		0x10
   /* Bit set if thread terminated and TCB is freed.  */
-#define TERMINATED_BIT		4
-#define TERMINATED_BITMASK	0x10
+#define TERMINATED_BIT		5
+#define TERMINATED_BITMASK	0x20
   /* Mask for the rest.  Helps the compiler to optimize.  */
-#define CANCEL_RESTMASK		0xffffffe0
+#define CANCEL_RESTMASK		0xffffffc0
 
 #define CANCEL_ENABLED_AND_CANCELED(value) \
   (((value) & (CANCELSTATE_BITMASK | CANCELED_BITMASK | EXITING_BITMASK	      \
diff --git a/nptl/init.c b/nptl/init.c
index 92afd09546..4d30084efb 100644
--- a/nptl/init.c
+++ b/nptl/init.c
@@ -130,7 +130,7 @@ sigcancel_handler (int sig __attribute ((unused)))
 	 is already set but if the signal is directly send (internally or
 	 from another process) is has to be done here.  */
       int oldval = THREAD_GETMEM (self, cancelhandling);
-      int newval = oldval | CANCELED_BITMASK;
+      int newval = oldval | CANCELING_BITMASK | CANCELED_BITMASK;
 
       if (oldval == newval || (oldval & EXITING_BITMASK) != 0)
 	/* Already canceled or exiting.  */
diff --git a/nptl/pthread_cancel.c b/nptl/pthread_cancel.c
index a189b6eac9..0dd97a27e5 100644
--- a/nptl/pthread_cancel.c
+++ b/nptl/pthread_cancel.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2002 Free Software Foundation, Inc.
+/* Copyright (C) 2002, 2003 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
 
@@ -31,7 +31,7 @@ pthread_cancel (th)
   while (1)
     {
       int oldval = pd->cancelhandling;
-      int newval = oldval | CANCELED_BITMASK;
+      int newval = oldval | CANCELING_BITMASK | CANCELED_BITMASK;
 
       /* Avoid doing unnecessary work.  The atomic operation can
 	 potentially be expensive if the bug has to be locked and
@@ -44,6 +44,9 @@ pthread_cancel (th)
 	 expensive.  */
       if (CANCEL_ENABLED_AND_CANCELED_AND_ASYNCHRONOUS (newval))
 	{
+	  /* Mark the cancellation as "in progress".  */
+	  atomic_bit_set (&pd->cancelhandling, CANCELING_BIT);
+
 	  /* The cancellation handler will take care of marking the
 	     thread as canceled.  */
 	  __pthread_kill (th, SIGCANCEL);
diff --git a/nptl/pthread_join.c b/nptl/pthread_join.c
index 4edbced2c1..5954af778d 100644
--- a/nptl/pthread_join.c
+++ b/nptl/pthread_join.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2002 Free Software Foundation, Inc.
+/* Copyright (C) 2002, 2003 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
 
@@ -50,7 +50,11 @@ pthread_join (threadid, thread_return)
     return EINVAL;
 
   self = THREAD_SELF;
-  if (pd == self || self->joinid == pd)
+  if (pd == self
+      || (self->joinid == pd
+	  && (pd->cancelhandling
+	      & (CANCELING_BITMASK | CANCELED_BITMASK | EXITING_BITMASK
+		 | TERMINATED_BITMASK)) == 0))
     /* This is a deadlock situation.  The threads are waiting for each
        other to finish.  Note that this is a "may" error.  To be 100%
        sure we catch this error we would have to lock the data
diff --git a/nptl/tst-cancel4.c b/nptl/tst-cancel4.c
index 7ab7bd60da..eb5eb9e15c 100644
--- a/nptl/tst-cancel4.c
+++ b/nptl/tst-cancel4.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2002 Free Software Foundation, Inc.
+/* Copyright (C) 2002, 2003 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
 
@@ -42,15 +42,15 @@
      mq_send()             mq_timedreceive()  mq_timedsend()
      msgrcv()              msgsnd()           msync()
                            open()             pause()
-                           pread()            pthread_cond_timedwait()
-     pthread_cond_wait()   pthread_join()     pthread_testcancel()
+                           pread()
+			   pthread_join()     pthread_testcancel()
      putmsg()              putpmsg()          pwrite()
                                               recv()
      recvfrom()            recvmsg()
      sem_timedwait()       sem_wait()         send()
      sendmsg()             sendto()           sigpause()
      sigsuspend()          sigtimedwait()     sigwait()
-     sigwaitinfo()                            system()
+     sigwaitinfo()
      tcdrain()
 
    Since STREAMS are not supported in the standard Linux kernel there
diff --git a/nptl/tst-join5.c b/nptl/tst-join5.c
new file mode 100644
index 0000000000..68c4ac6fc6
--- /dev/null
+++ b/nptl/tst-join5.c
@@ -0,0 +1,141 @@
+/* Copyright (C) 2003 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@redhat.com>, 2003.
+
+   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, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+
+static void *
+tf1 (void *arg)
+{
+  pthread_join (arg, NULL);
+
+  puts ("1st join returned");
+
+  return (void *) 1l;
+}
+
+
+static void *
+tf2 (void *arg)
+{
+  pthread_join (arg, NULL);
+
+  puts ("2nd join returned");
+
+  return (void *) 1l;
+}
+
+
+static int
+do_test (void)
+{
+  pthread_t th;
+
+  int err = pthread_join (pthread_self (), NULL);
+  if (err == 0)
+    {
+      puts ("1st circular join succeeded");
+      exit (1);
+    }
+  if (err != EDEADLK)
+    {
+      printf ("1st circular join %d, not EDEADLK\n", err);
+      exit (1);
+    }
+
+  if (pthread_create (&th, NULL, tf1, (void *) pthread_self ()) != 0)
+    {
+      puts ("1st create failed");
+      exit (1);
+    }
+
+  if (pthread_cancel (th) != 0)
+    {
+      puts ("cannot cancel 1st thread");
+      exit (1);
+    }
+
+  void *r;
+  err = pthread_join (th, &r);
+  if (err != 0)
+    {
+      printf ("cannot join 1st thread: %d\n", err);
+      exit (1);
+    }
+  if (r != PTHREAD_CANCELED)
+    {
+      puts ("1st thread not canceled");
+      exit (1);
+    }
+
+  err = pthread_join (pthread_self (), NULL);
+  if (err == 0)
+    {
+      puts ("2nd circular join succeeded");
+      exit (1);
+    }
+  if (err != EDEADLK)
+    {
+      printf ("2nd circular join %d, not EDEADLK\n", err);
+      exit (1);
+    }
+
+  if (pthread_create (&th, NULL, tf2, (void *) pthread_self ()) != 0)
+    {
+      puts ("2nd create failed");
+      exit (1);
+    }
+
+  if (pthread_cancel (th) != 0)
+    {
+      puts ("cannot cancel 2nd thread");
+      exit (1);
+    }
+
+  if (pthread_join (th, &r) != 0)
+    {
+      puts ("cannot join 2nd thread");
+      exit (1);
+    }
+  if (r != PTHREAD_CANCELED)
+    {
+      puts ("2nd thread not canceled");
+      exit (1);
+    }
+
+  err = pthread_join (pthread_self (), NULL);
+  if (err == 0)
+    {
+      puts ("2nd circular join succeeded");
+      exit (1);
+    }
+  if (err != EDEADLK)
+    {
+      printf ("2nd circular join %d, not EDEADLK\n", err);
+      exit (1);
+    }
+
+  exit (0);
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"