From ffdf4f3f121f35834131396c52049745cca21a22 Mon Sep 17 00:00:00 2001
From: Yang Yanchao <yangyanchao6@huawei.com>
Date: Mon, 18 Apr 2022 11:10:46 +0800
Subject: [PATCH] nptl 2.17 adapt for bug 29029

upstream bug 29029 has revert some struct and function,
this commit is to adapt the change.

---
 nptl_2_17/cancellation.c        | 91 +++++++++++++++++++++++++++++++++
 nptl_2_17/cancellation_2_17.c   | 60 ++++++++++++++++------
 nptl_2_17/compat_pthread_2_17.h | 21 --------
 3 files changed, 136 insertions(+), 36 deletions(-)
 create mode 100644 nptl_2_17/cancellation.c

diff --git a/nptl_2_17/cancellation.c b/nptl_2_17/cancellation.c
new file mode 100644
index 00000000..f4b08902
--- /dev/null
+++ b/nptl_2_17/cancellation.c
@@ -0,0 +1,91 @@
+/* Copyright (C) 2002-2022 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <setjmp.h>
+#include <stdlib.h>
+#include "pthreadP.h"
+#include <futex-internal.h>
+
+
+/* The next two functions are similar to pthread_setcanceltype() but
+   more specialized for the use in the cancelable functions like write().
+   They do not need to check parameters etc.  These functions must be
+   AS-safe, with the exception of the actual cancellation, because they
+   are called by wrappers around AS-safe functions like write().*/
+int
+__pthread_enable_asynccancel (void)
+{
+  struct pthread *self = THREAD_SELF;
+  int oldval = atomic_load_relaxed (&self->cancelhandling);
+
+  while (1)
+    {
+      int newval = oldval | CANCELTYPE_BITMASK;
+
+      if (newval == oldval)
+	break;
+
+      if (atomic_compare_exchange_weak_acquire (&self->cancelhandling,
+						&oldval, newval))
+	{
+	  if (cancel_enabled_and_canceled_and_async (newval))
+	    {
+	      self->result = PTHREAD_CANCELED;
+	      __do_cancel ();
+	    }
+
+	  break;
+	}
+    }
+
+  return oldval;
+}
+libc_hidden_def (__pthread_enable_asynccancel)
+
+/* See the comment for __pthread_enable_asynccancel regarding
+   the AS-safety of this function.  */
+void
+__pthread_disable_asynccancel (int oldtype)
+{
+  /* If asynchronous cancellation was enabled before we do not have
+     anything to do.  */
+  if (oldtype & CANCELTYPE_BITMASK)
+    return;
+
+  struct pthread *self = THREAD_SELF;
+  int newval;
+  int oldval = atomic_load_relaxed (&self->cancelhandling);
+  do
+    {
+      newval = oldval & ~CANCELTYPE_BITMASK;
+    }
+  while (!atomic_compare_exchange_weak_acquire (&self->cancelhandling,
+						&oldval, newval));
+
+  /* We cannot return when we are being canceled.  Upon return the
+     thread might be things which would have to be undone.  The
+     following loop should loop until the cancellation signal is
+     delivered.  */
+  while (__glibc_unlikely ((newval & (CANCELING_BITMASK | CANCELED_BITMASK))
+			   == CANCELING_BITMASK))
+    {
+      futex_wait_simple ((unsigned int *) &self->cancelhandling, newval,
+			 FUTEX_PRIVATE);
+      newval = atomic_load_relaxed (&self->cancelhandling);
+    }
+}
+libc_hidden_def (__pthread_disable_asynccancel)
diff --git a/nptl_2_17/cancellation_2_17.c b/nptl_2_17/cancellation_2_17.c
index 5c9ce572..9bc199e7 100644
--- a/nptl_2_17/cancellation_2_17.c
+++ b/nptl_2_17/cancellation_2_17.c
@@ -1,6 +1,5 @@
-/* Copyright (C) 2002-2018 Free Software Foundation, Inc.
+/* Copyright (C) 2002-2022 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
-   Contributed by Ulrich Drepper <drepper@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
@@ -14,30 +13,42 @@
 
    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/>.  */
+   <https://www.gnu.org/licenses/>.  */
 
 #include "pthreadP_2_17.h"
 #include <setjmp.h>
 #include <stdlib.h>
 #include <futex-internal.h>
 
+/* The next two functions are similar to pthread_setcanceltype() but
+   more specialized for the use in the cancelable functions like write().
+   They do not need to check parameters etc.  These functions must be
+   AS-safe, with the exception of the actual cancellation, because they
+   are called by wrappers around AS-safe functions like write().*/
 int
 __pthread_enable_asynccancel (void)
 {
   struct pthread *self = THREAD_SELF;
+  int oldval = atomic_load_relaxed (&self->cancelhandling);
 
-  int oldval = THREAD_GETMEM (self, canceltype);
-  THREAD_SETMEM (self, canceltype, PTHREAD_CANCEL_ASYNCHRONOUS);
+  while (1)
+    {
+      int newval = oldval | CANCELTYPE_BITMASK;
 
-  int ch = THREAD_GETMEM (self, cancelhandling);
+      if (newval == oldval)
+	break;
 
-  if (self->cancelstate == PTHREAD_CANCEL_ENABLE
-      && (ch & CANCELED_BITMASK)
-      && !(ch & EXITING_BITMASK)
-      && !(ch & TERMINATED_BITMASK))
-    {
-      THREAD_SETMEM (self, result, PTHREAD_CANCELED);
-      __do_cancel ();
+      if (atomic_compare_exchange_weak_acquire (&self->cancelhandling,
+						&oldval, newval))
+	{
+	  if (cancel_enabled_and_canceled_and_async (newval))
+	    {
+	      self->result = PTHREAD_CANCELED;
+	      __do_cancel ();
+	    }
+
+	  break;
+	}
     }
 
   return oldval;
@@ -51,10 +62,29 @@ __pthread_disable_asynccancel (int oldtype)
 {
   /* If asynchronous cancellation was enabled before we do not have
      anything to do.  */
-  if (oldtype == PTHREAD_CANCEL_ASYNCHRONOUS)
+  if (oldtype & CANCELTYPE_BITMASK)
     return;
 
   struct pthread *self = THREAD_SELF;
-  self->canceltype = PTHREAD_CANCEL_DEFERRED;
+  int newval;
+  int oldval = atomic_load_relaxed (&self->cancelhandling);
+  do
+    {
+      newval = oldval & ~CANCELTYPE_BITMASK;
+    }
+  while (!atomic_compare_exchange_weak_acquire (&self->cancelhandling,
+						&oldval, newval));
+
+  /* We cannot return when we are being canceled.  Upon return the
+     thread might be things which would have to be undone.  The
+     following loop should loop until the cancellation signal is
+     delivered.  */
+  while (__glibc_unlikely ((newval & (CANCELING_BITMASK | CANCELED_BITMASK))
+			   == CANCELING_BITMASK))
+    {
+      futex_wait_simple ((unsigned int *) &self->cancelhandling, newval,
+			 FUTEX_PRIVATE);
+      newval = atomic_load_relaxed (&self->cancelhandling);
+    }
 }
 libc_hidden_def (__pthread_disable_asynccancel)
diff --git a/nptl_2_17/compat_pthread_2_17.h b/nptl_2_17/compat_pthread_2_17.h
index d13051ba..5109ebc3 100644
--- a/nptl_2_17/compat_pthread_2_17.h
+++ b/nptl_2_17/compat_pthread_2_17.h
@@ -19,30 +19,9 @@
 #define __PTHREAD_MUTEX_USE_UNION          0
 #endif
 
-#define CANCELSTATE_BIT         0
-#define CANCELSTATE_BITMASK     (0x01 << CANCELSTATE_BIT)
-  /* Bit set if asynchronous cancellation mode is selected.  */
-#define CANCELTYPE_BIT          1
-#define CANCELTYPE_BITMASK      (0x01 << CANCELTYPE_BIT)
-  /* Bit set if canceling has been initiated.  */
-#define CANCELING_BIT           2
-#define CANCELING_BITMASK       (0x01 << CANCELING_BIT)
-  /* Bit set if canceled.  */
-#define CANCELED_BIT            3
-#define CANCELED_BITMASK        (0x01 << CANCELED_BIT)
-  /* Bit set if thread is exiting.  */
-#define EXITING_BIT             4
-#define EXITING_BITMASK         (0x01 << EXITING_BIT)
-  /* Bit set if thread terminated and TCB is freed.  */
-#define TERMINATED_BIT          5
-#define TERMINATED_BITMASK      (0x01 << TERMINATED_BIT)
-  /* Bit set if thread is supposed to change XID.  */
-#define SETXID_BIT              6
-#define SETXID_BITMASK          (0x01 << SETXID_BIT)
   /* Mask for the rest.  Helps the compiler to optimize.  */
 #define CANCEL_RESTMASK         0xffffff80
 
-
 #define CANCEL_ENABLED_AND_CANCELED_AND_ASYNCHRONOUS(value) \
   (((value) & (CANCELSTATE_BITMASK | CANCELTYPE_BITMASK | CANCELED_BITMASK    \
                | EXITING_BITMASK | CANCEL_RESTMASK | TERMINATED_BITMASK))     \
-- 
2.33.0

