nptl: Add test nptl/tst-pthread-perthread-inherit

Message ID 87woh1af8h.fsf@oldenburg2.str.redhat.com
State New, archived
Headers

Commit Message

Florian Weimer July 1, 2019, 4:31 p.m. UTC
  I had to fix a race condition related to the use of pthread_getattr_np.
I believe the patch below should address your concerns.

Thanks,
Florian

nptl: Add test nptl/tst-pthread-perthread-inherit

This test checks the inheritance behavior of the per-thread flags.

It is also a regular (non-xtest) for pthread_attr_setperthreadids_np
and pthread_attr_getperthreadids_np.

2019-06-28  Florian Weimer  <fweimer@redhat.com>

	* nptl/Makefile (tests): Add tst-pthread-perthread-inherit.
	* nptl/tst-pthread-perthread-inherit.c: New file.
  

Comments

Carlos O'Donell July 4, 2019, 4:07 a.m. UTC | #1
On 7/1/19 12:31 PM, Florian Weimer wrote:
> I had to fix a race condition related to the use of pthread_getattr_np.
> I believe the patch below should address your concerns.
> 
> Thanks,
> Florian
> 
> nptl: Add test nptl/tst-pthread-perthread-inherit
> 
> This test checks the inheritance behavior of the per-thread flags.
> 
> It is also a regular (non-xtest) for pthread_attr_setperthreadids_np
> and pthread_attr_getperthreadids_np.

LGTM

Reviewed-by: Carlos O'Donell <carlos@redhat.com>

> 2019-06-28  Florian Weimer  <fweimer@redhat.com>
> 
> 	* nptl/Makefile (tests): Add tst-pthread-perthread-inherit.
> 	* nptl/tst-pthread-perthread-inherit.c: New file.
> 
> diff --git a/nptl/Makefile b/nptl/Makefile
> index 52913cc2f0..56f1578860 100644
> --- a/nptl/Makefile
> +++ b/nptl/Makefile
> @@ -324,7 +324,8 @@ tests = tst-attr1 tst-attr2 tst-attr3 tst-default-attr \
>   	tst-rwlock-pwn \
>   	tst-rwlock-tryrdlock-stall tst-rwlock-trywrlock-stall \
>   	tst-unwind-thread \
> -	tst-pthread-perthreadfs tst-pthread-perthreadfs-chroot
> +	tst-pthread-perthreadfs tst-pthread-perthreadfs-chroot \
> +	tst-pthread-perthread-inherit
>   
>   tests-internal := tst-rwlock19 tst-rwlock20 \
>   		  tst-sem11 tst-sem12 tst-sem13 \
> diff --git a/nptl/tst-pthread-perthread-inherit.c b/nptl/tst-pthread-perthread-inherit.c
> new file mode 100644
> index 0000000000..99feb01dc9
> --- /dev/null
> +++ b/nptl/tst-pthread-perthread-inherit.c
> @@ -0,0 +1,220 @@
> +/* Test inheritance of per-thread attributes.
> +   Copyright (C) 2019 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 <errno.h>
> +#include <stdbool.h>
> +#include <stdlib.h>
> +#include <support/check.h>
> +#include <support/support.h>
> +#include <support/xthread.h>
> +
> +/* Controls how an attribute is applied. */
> +enum kind { null_attribute, default_attribute, attribute_per_process,
> +            attribute_per_thread };
> +
> +/* Determine the combined per-thread/per-process status from the two
> +   requested attributes.  */
> +static int
> +combine_kind (enum kind a, enum kind b)
> +{
> +  if (a == attribute_per_thread || b == attribute_per_thread)
> +    return PTHREAD_PER_THREAD_NP;
> +  else
> +    return PTHREAD_PER_PROCESS_NP;
> +}
> +
> +/* Convert one kind to the corresponding PTHREAD_PER_PROCESS_NP,
> +   PTHREAD_PER_THREAD_NP flag.  */
> +static int
> +convert_kind (enum kind k)
> +{
> +  if (k == attribute_per_thread)
> +    return PTHREAD_PER_THREAD_NP;
> +  else
> +    return PTHREAD_PER_PROCESS_NP;
> +}
> +
> +/* Return either PTHREAD_PER_PROCESS_NP or PTHREAD_PER_THREAD_NP.  */
> +static int
> +scope (pthread_t thr, int (*getter) (const pthread_attr_t *, int *))
> +{
> +  pthread_attr_t attr;
> +  int ret = pthread_getattr_np (thr, &attr);
> +  if (ret != 0)
> +    {
> +      errno = ret;
> +      FAIL_EXIT1 ("pthread_getattr_np: %m");
> +    }
> +  int flag = -1;
> +  TEST_COMPARE (getter (&attr, &flag), 0);
> +  if (flag != PTHREAD_PER_THREAD_NP)
> +    TEST_COMPARE (flag, PTHREAD_PER_PROCESS_NP);
> +  xpthread_attr_destroy (&attr);
> +  return flag;
> +}
> +
> +/* The loop in do_test iterates through the Cartesian product of all
> +   possible values.  */
> +static enum kind outer_perthreadfs;
> +static enum kind inner_perthreadfs;
> +static enum kind outer_perthreadids;
> +static enum kind inner_perthreadids;
> +
> +/* Used to prevent premature thread exit, so that we can safely call
> +   pthread_getattr_np.  */
> +static pthread_barrier_t barrier;
> +
> +static void *
> +inner_thread (void *closure)
> +{
> +  TEST_COMPARE (scope (pthread_self (),
> +                       pthread_attr_getperthreadfs_np),
> +                combine_kind (outer_perthreadfs, inner_perthreadfs));
> +  TEST_COMPARE (scope (pthread_self (),
> +                       pthread_attr_getperthreadids_np),
> +                combine_kind (outer_perthreadids, inner_perthreadids));
> +  /* Delay exit of the thread until the outer thread has read the
> +     attributes.  */
> +  xpthread_barrier_wait (&barrier);
> +  return NULL;
> +}
> +
> +static void *
> +outer_thread (void *attr)
> +{
> +  TEST_COMPARE (scope (pthread_self (),
> +                       pthread_attr_getperthreadfs_np),
> +                convert_kind (outer_perthreadfs));
> +  TEST_COMPARE (scope (pthread_self (),
> +                       pthread_attr_getperthreadids_np),
> +                convert_kind (outer_perthreadids));
> +  pthread_t thr = xpthread_create (attr, inner_thread, NULL);
> +  TEST_COMPARE (scope (thr, pthread_attr_getperthreadfs_np),
> +                combine_kind (outer_perthreadfs, inner_perthreadfs));
> +  TEST_COMPARE (scope (thr, pthread_attr_getperthreadids_np),
> +                combine_kind (outer_perthreadids, inner_perthreadids));
> +  /* Delay exit of the thread until the main thread has read the
> +     attributes.  */
> +  xpthread_barrier_wait (&barrier);
> +  return xpthread_join (thr);
> +}
> +
> +/* Apply the attribute KIND according to SETTER to ATTR.  */
> +static void
> +attribute_apply (pthread_attr_t *attr, enum kind kind,
> +                 int (*setter) (pthread_attr_t *attr, int))
> +{
> +  switch (kind)
> +    {
> +    case default_attribute:
> +      return;
> +
> +    case attribute_per_process:
> +    case attribute_per_thread:
> +      TEST_COMPARE (setter (attr, convert_kind (kind)), 0);
> +      return;
> +
> +    case null_attribute:
> +      /* Report failure below.  */
> +      ;
> +    }
> +
> +  FAIL_EXIT1 ("invalid kind: %d", (int) kind);
> +}
> +
> +/* Create a new attribute according to both kinds.  */
> +static pthread_attr_t *
> +make_attribute (enum kind perthreadfs, enum kind perthreadids)
> +{
> +  if (perthreadfs == null_attribute)
> +    {
> +      TEST_COMPARE (perthreadids, null_attribute);
> +      return NULL;
> +    }
> +  pthread_attr_t *result = xmalloc (sizeof (*result));
> +  xpthread_attr_init (result);
> +  attribute_apply (result, perthreadfs, pthread_attr_setperthreadfs_np);
> +  attribute_apply (result, perthreadids, pthread_attr_setperthreadids_np);
> +  return result;
> +}
> +
> +/* Deallocate the attribute pointer returned from make_attribute.  */
> +static void
> +free_attribute (pthread_attr_t *attr)
> +{
> +  if (attr != NULL)
> +    {
> +      xpthread_attr_destroy (attr);
> +      free (attr);
> +    }
> +}
> +
> +static int
> +do_test (void)
> +{
> +  /* Used to delay thread exit, so that pthread_getattr_np works
> +     reliably.  Inner thread, outer thread, and main thread are
> +     waiting.  */
> +  xpthread_barrier_init (&barrier, NULL, 3);
> +
> +  for (outer_perthreadfs = null_attribute;
> +       outer_perthreadfs <= attribute_per_thread;
> +       ++outer_perthreadfs)
> +    for (inner_perthreadfs = null_attribute;
> +         inner_perthreadfs <= attribute_per_thread;
> +         ++inner_perthreadfs)
> +      for (outer_perthreadids = null_attribute;
> +           outer_perthreadids <= attribute_per_thread;
> +           ++outer_perthreadids)
> +        for (inner_perthreadids = null_attribute;
> +             inner_perthreadids <= attribute_per_thread;
> +             ++inner_perthreadids)
> +          {
> +            /* Some combinations are impossible.  If we must pass a
> +               NULL attribute at one level, then that is true for both
> +               kinds of properties.  */
> +            if ((outer_perthreadfs == null_attribute)
> +                != (outer_perthreadids == null_attribute))
> +              continue;
> +            if ((inner_perthreadfs == null_attribute)
> +                != (inner_perthreadids == null_attribute))
> +              continue;
> +
> +            pthread_attr_t *outer_attr
> +              = make_attribute (outer_perthreadfs, outer_perthreadids);
> +            pthread_attr_t *inner_attr
> +              = make_attribute (inner_perthreadfs, inner_perthreadids);
> +            pthread_t thr = xpthread_create (outer_attr,
> +                                             outer_thread, inner_attr);
> +            TEST_COMPARE (scope (thr, pthread_attr_getperthreadfs_np),
> +                          convert_kind (outer_perthreadfs));
> +            TEST_COMPARE (scope (thr, pthread_attr_getperthreadids_np),
> +                          convert_kind (outer_perthreadids));
> +            /* The threads may now exit, after we have checked the
> +               attributes.  */
> +            xpthread_barrier_wait (&barrier);
> +            xpthread_join (thr);
> +            free_attribute (inner_attr);
> +            free_attribute (outer_attr);
> +          }
> +
> +  xpthread_barrier_destroy (&barrier);
> +  return 0;
> +}
> +
> +#include <support/test-driver.c>
>
  

Patch

diff --git a/nptl/Makefile b/nptl/Makefile
index 52913cc2f0..56f1578860 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -324,7 +324,8 @@  tests = tst-attr1 tst-attr2 tst-attr3 tst-default-attr \
 	tst-rwlock-pwn \
 	tst-rwlock-tryrdlock-stall tst-rwlock-trywrlock-stall \
 	tst-unwind-thread \
-	tst-pthread-perthreadfs tst-pthread-perthreadfs-chroot
+	tst-pthread-perthreadfs tst-pthread-perthreadfs-chroot \
+	tst-pthread-perthread-inherit
 
 tests-internal := tst-rwlock19 tst-rwlock20 \
 		  tst-sem11 tst-sem12 tst-sem13 \
diff --git a/nptl/tst-pthread-perthread-inherit.c b/nptl/tst-pthread-perthread-inherit.c
new file mode 100644
index 0000000000..99feb01dc9
--- /dev/null
+++ b/nptl/tst-pthread-perthread-inherit.c
@@ -0,0 +1,220 @@ 
+/* Test inheritance of per-thread attributes.
+   Copyright (C) 2019 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 <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/xthread.h>
+
+/* Controls how an attribute is applied. */
+enum kind { null_attribute, default_attribute, attribute_per_process,
+            attribute_per_thread };
+
+/* Determine the combined per-thread/per-process status from the two
+   requested attributes.  */
+static int
+combine_kind (enum kind a, enum kind b)
+{
+  if (a == attribute_per_thread || b == attribute_per_thread)
+    return PTHREAD_PER_THREAD_NP;
+  else
+    return PTHREAD_PER_PROCESS_NP;
+}
+
+/* Convert one kind to the corresponding PTHREAD_PER_PROCESS_NP,
+   PTHREAD_PER_THREAD_NP flag.  */
+static int
+convert_kind (enum kind k)
+{
+  if (k == attribute_per_thread)
+    return PTHREAD_PER_THREAD_NP;
+  else
+    return PTHREAD_PER_PROCESS_NP;
+}
+
+/* Return either PTHREAD_PER_PROCESS_NP or PTHREAD_PER_THREAD_NP.  */
+static int
+scope (pthread_t thr, int (*getter) (const pthread_attr_t *, int *))
+{
+  pthread_attr_t attr;
+  int ret = pthread_getattr_np (thr, &attr);
+  if (ret != 0)
+    {
+      errno = ret;
+      FAIL_EXIT1 ("pthread_getattr_np: %m");
+    }
+  int flag = -1;
+  TEST_COMPARE (getter (&attr, &flag), 0);
+  if (flag != PTHREAD_PER_THREAD_NP)
+    TEST_COMPARE (flag, PTHREAD_PER_PROCESS_NP);
+  xpthread_attr_destroy (&attr);
+  return flag;
+}
+
+/* The loop in do_test iterates through the Cartesian product of all
+   possible values.  */
+static enum kind outer_perthreadfs;
+static enum kind inner_perthreadfs;
+static enum kind outer_perthreadids;
+static enum kind inner_perthreadids;
+
+/* Used to prevent premature thread exit, so that we can safely call
+   pthread_getattr_np.  */
+static pthread_barrier_t barrier;
+
+static void *
+inner_thread (void *closure)
+{
+  TEST_COMPARE (scope (pthread_self (),
+                       pthread_attr_getperthreadfs_np),
+                combine_kind (outer_perthreadfs, inner_perthreadfs));
+  TEST_COMPARE (scope (pthread_self (),
+                       pthread_attr_getperthreadids_np),
+                combine_kind (outer_perthreadids, inner_perthreadids));
+  /* Delay exit of the thread until the outer thread has read the
+     attributes.  */
+  xpthread_barrier_wait (&barrier);
+  return NULL;
+}
+
+static void *
+outer_thread (void *attr)
+{
+  TEST_COMPARE (scope (pthread_self (),
+                       pthread_attr_getperthreadfs_np),
+                convert_kind (outer_perthreadfs));
+  TEST_COMPARE (scope (pthread_self (),
+                       pthread_attr_getperthreadids_np),
+                convert_kind (outer_perthreadids));
+  pthread_t thr = xpthread_create (attr, inner_thread, NULL);
+  TEST_COMPARE (scope (thr, pthread_attr_getperthreadfs_np),
+                combine_kind (outer_perthreadfs, inner_perthreadfs));
+  TEST_COMPARE (scope (thr, pthread_attr_getperthreadids_np),
+                combine_kind (outer_perthreadids, inner_perthreadids));
+  /* Delay exit of the thread until the main thread has read the
+     attributes.  */
+  xpthread_barrier_wait (&barrier);
+  return xpthread_join (thr);
+}
+
+/* Apply the attribute KIND according to SETTER to ATTR.  */
+static void
+attribute_apply (pthread_attr_t *attr, enum kind kind,
+                 int (*setter) (pthread_attr_t *attr, int))
+{
+  switch (kind)
+    {
+    case default_attribute:
+      return;
+
+    case attribute_per_process:
+    case attribute_per_thread:
+      TEST_COMPARE (setter (attr, convert_kind (kind)), 0);
+      return;
+
+    case null_attribute:
+      /* Report failure below.  */
+      ;
+    }
+
+  FAIL_EXIT1 ("invalid kind: %d", (int) kind);
+}
+
+/* Create a new attribute according to both kinds.  */
+static pthread_attr_t *
+make_attribute (enum kind perthreadfs, enum kind perthreadids)
+{
+  if (perthreadfs == null_attribute)
+    {
+      TEST_COMPARE (perthreadids, null_attribute);
+      return NULL;
+    }
+  pthread_attr_t *result = xmalloc (sizeof (*result));
+  xpthread_attr_init (result);
+  attribute_apply (result, perthreadfs, pthread_attr_setperthreadfs_np);
+  attribute_apply (result, perthreadids, pthread_attr_setperthreadids_np);
+  return result;
+}
+
+/* Deallocate the attribute pointer returned from make_attribute.  */
+static void
+free_attribute (pthread_attr_t *attr)
+{
+  if (attr != NULL)
+    {
+      xpthread_attr_destroy (attr);
+      free (attr);
+    }
+}
+
+static int
+do_test (void)
+{
+  /* Used to delay thread exit, so that pthread_getattr_np works
+     reliably.  Inner thread, outer thread, and main thread are
+     waiting.  */
+  xpthread_barrier_init (&barrier, NULL, 3);
+
+  for (outer_perthreadfs = null_attribute;
+       outer_perthreadfs <= attribute_per_thread;
+       ++outer_perthreadfs)
+    for (inner_perthreadfs = null_attribute;
+         inner_perthreadfs <= attribute_per_thread;
+         ++inner_perthreadfs)
+      for (outer_perthreadids = null_attribute;
+           outer_perthreadids <= attribute_per_thread;
+           ++outer_perthreadids)
+        for (inner_perthreadids = null_attribute;
+             inner_perthreadids <= attribute_per_thread;
+             ++inner_perthreadids)
+          {
+            /* Some combinations are impossible.  If we must pass a
+               NULL attribute at one level, then that is true for both
+               kinds of properties.  */
+            if ((outer_perthreadfs == null_attribute)
+                != (outer_perthreadids == null_attribute))
+              continue;
+            if ((inner_perthreadfs == null_attribute)
+                != (inner_perthreadids == null_attribute))
+              continue;
+
+            pthread_attr_t *outer_attr
+              = make_attribute (outer_perthreadfs, outer_perthreadids);
+            pthread_attr_t *inner_attr
+              = make_attribute (inner_perthreadfs, inner_perthreadids);
+            pthread_t thr = xpthread_create (outer_attr,
+                                             outer_thread, inner_attr);
+            TEST_COMPARE (scope (thr, pthread_attr_getperthreadfs_np),
+                          convert_kind (outer_perthreadfs));
+            TEST_COMPARE (scope (thr, pthread_attr_getperthreadids_np),
+                          convert_kind (outer_perthreadids));
+            /* The threads may now exit, after we have checked the
+               attributes.  */
+            xpthread_barrier_wait (&barrier);
+            xpthread_join (thr);
+            free_attribute (inner_attr);
+            free_attribute (outer_attr);
+          }
+
+  xpthread_barrier_destroy (&barrier);
+  return 0;
+}
+
+#include <support/test-driver.c>