about summary refs log tree commit diff
path: root/nptl/pthread_setcanceltype.c
diff options
context:
space:
mode:
Diffstat (limited to 'nptl/pthread_setcanceltype.c')
-rw-r--r--nptl/pthread_setcanceltype.c31
1 files changed, 26 insertions, 5 deletions
diff --git a/nptl/pthread_setcanceltype.c b/nptl/pthread_setcanceltype.c
index 94e56466de..1307d355c1 100644
--- a/nptl/pthread_setcanceltype.c
+++ b/nptl/pthread_setcanceltype.c
@@ -28,11 +28,32 @@ __pthread_setcanceltype (int type, int *oldtype)
 
   volatile struct pthread *self = THREAD_SELF;
 
-  if (oldtype != NULL)
-    *oldtype = self->canceltype;
-  self->canceltype = type;
-  if (type == PTHREAD_CANCEL_ASYNCHRONOUS)
-    __pthread_testcancel ();
+  int oldval = atomic_load_relaxed (&self->cancelhandling);
+  while (1)
+    {
+      int newval = (type == PTHREAD_CANCEL_ASYNCHRONOUS
+		    ? oldval | CANCELTYPE_BITMASK
+		    : oldval & ~CANCELTYPE_BITMASK);
+
+      if (oldtype != NULL)
+	*oldtype = ((oldval & CANCELTYPE_BITMASK)
+		    ? PTHREAD_CANCEL_ASYNCHRONOUS : PTHREAD_CANCEL_DEFERRED);
+
+      if (oldval == newval)
+	break;
+
+      if (atomic_compare_exchange_weak_acquire (&self->cancelhandling,
+						&oldval, newval))
+	{
+	  if (cancel_enabled_and_canceled_and_async (newval))
+	    {
+	      THREAD_SETMEM (self, result, PTHREAD_CANCELED);
+	      __do_cancel ();
+	    }
+
+	  break;
+	}
+    }
 
   return 0;
 }