diff mbox series

[v3,2/2] malloc: add multi-threaded tests for aligned_alloc/calloc/malloc

Message ID 38d564025c76128664911cfaf0f5a5cdd02442f1.1721142537.git.mmartinv@redhat.com
State New
Headers show
Series malloc: add multi-threaded tests for aligned_alloc/calloc/malloc | expand

Commit Message

Miguel Martín July 16, 2024, 3:14 p.m. UTC
Improve aligned_alloc/calloc/malloc test coverage by adding
multi-threaded tests with random memory allocations and with/without
cross-thread memory deallocations.

Perform a number of memory allocation calls with random sizes limited
to 0xffff.

Use the existing DSO ('malloc/tst-aligned_alloc-lib.c') to randomize
allocator selection.

The multi-threaded allocation/deallocation is staged as described below:

- Stage 1: Half of the threads will be allocating memory and the
  other half will be waiting for them to finish the allocation.
- Stage 2: Half of the threads will be allocating memory and the
  other half will be deallocating memory.
- Stage 3: Half of the threads will be deallocating memory and the
  second half waiting on them to finish.

Add 'malloc/tst-aligned-alloc-random-thread.c' where each thread will
deallocate only the memory that was previously allocated by itself.

Add 'malloc/tst-aligned-alloc-random-thread-cross.c' where each thread
will deallocate memory that was previously allocated by another thread.

The intention is to be able to utilize existing malloc testing to ensure
that similar allocation APIs are also exposed to the same rigors.
---
 malloc/Makefile                               |   8 +
 .../tst-aligned-alloc-random-thread-cross.c   |  19 +++
 malloc/tst-aligned-alloc-random-thread.c      | 145 ++++++++++++++++++
 3 files changed, 172 insertions(+)
 create mode 100644 malloc/tst-aligned-alloc-random-thread-cross.c
 create mode 100644 malloc/tst-aligned-alloc-random-thread.c

Comments

Arjun Shankar July 17, 2024, 3:42 p.m. UTC | #1
> Improve aligned_alloc/calloc/malloc test coverage by adding
> multi-threaded tests with random memory allocations and with/without
> cross-thread memory deallocations.
>
> Perform a number of memory allocation calls with random sizes limited
> to 0xffff.
>
> Use the existing DSO ('malloc/tst-aligned_alloc-lib.c') to randomize
> allocator selection.
>
> The multi-threaded allocation/deallocation is staged as described below:
>
> - Stage 1: Half of the threads will be allocating memory and the
>   other half will be waiting for them to finish the allocation.
> - Stage 2: Half of the threads will be allocating memory and the
>   other half will be deallocating memory.
> - Stage 3: Half of the threads will be deallocating memory and the
>   second half waiting on them to finish.
>
> Add 'malloc/tst-aligned-alloc-random-thread.c' where each thread will
> deallocate only the memory that was previously allocated by itself.
>
> Add 'malloc/tst-aligned-alloc-random-thread-cross.c' where each thread
> will deallocate memory that was previously allocated by another thread.
>
> The intention is to be able to utilize existing malloc testing to ensure
> that similar allocation APIs are also exposed to the same rigors.

Thanks. The changes since the last version look good.

Reviewed-by: Arjun Shankar <arjun@redhat.com>

> ---
>  malloc/Makefile                               |   8 +
>  .../tst-aligned-alloc-random-thread-cross.c   |  19 +++
>  malloc/tst-aligned-alloc-random-thread.c      | 145 ++++++++++++++++++
>  3 files changed, 172 insertions(+)
>  create mode 100644 malloc/tst-aligned-alloc-random-thread-cross.c
>  create mode 100644 malloc/tst-aligned-alloc-random-thread.c
>
> diff --git a/malloc/Makefile b/malloc/Makefile
> index 02aff1bd1d..98d507a6eb 100644
> --- a/malloc/Makefile
> +++ b/malloc/Makefile
> @@ -28,6 +28,8 @@ tests := \
>    mallocbug \
>    tst-aligned-alloc \
>    tst-aligned-alloc-random \
> +  tst-aligned-alloc-random-thread \
> +  tst-aligned-alloc-random-thread-cross \
>    tst-alloc_buffer \
>    tst-calloc \
>    tst-free-errno \
> @@ -151,6 +153,8 @@ ifeq ($(have-GLIBC_2.23)$(build-shared),yesyes)
>  # the tests expect specific internal behavior that is changed due to linking to
>  # libmcheck.a.
>  tests-exclude-mcheck = \
> +  tst-aligned-alloc-random-thread \
> +  tst-aligned-alloc-random-thread-cross \
>    tst-compathooks-off \
>    tst-compathooks-on \
>    tst-malloc-backtrace \
> @@ -415,7 +419,11 @@ $(objpfx)tst-mallocstate: $(objpfx)libc_malloc_debug.so
>  $(objpfx)tst-mallocstate-malloc-check: $(objpfx)libc_malloc_debug.so
>
>  $(objpfx)tst-aligned-alloc-random.out: $(objpfx)tst-aligned_alloc-lib.so
> +$(objpfx)tst-aligned-alloc-random-thread.out: $(objpfx)tst-aligned_alloc-lib.so
> +$(objpfx)tst-aligned-alloc-random-thread-cross.out: $(objpfx)tst-aligned_alloc-lib.so
>  $(objpfx)tst-malloc-random.out: $(objpfx)tst-aligned_alloc-lib.so
>
>  tst-aligned-alloc-random-ENV = LD_PRELOAD=$(objpfx)tst-aligned_alloc-lib.so
> +tst-aligned-alloc-random-thread-ENV = LD_PRELOAD=$(objpfx)tst-aligned_alloc-lib.so
> +tst-aligned-alloc-random-thread-cross-ENV = LD_PRELOAD=$(objpfx)tst-aligned_alloc-lib.so
>  tst-malloc-random-ENV = LD_PRELOAD=$(objpfx)tst-aligned_alloc-lib.so
> diff --git a/malloc/tst-aligned-alloc-random-thread-cross.c b/malloc/tst-aligned-alloc-random-thread-cross.c
> new file mode 100644
> index 0000000000..360ecc56ee
> --- /dev/null
> +++ b/malloc/tst-aligned-alloc-random-thread-cross.c
> @@ -0,0 +1,19 @@
> +/* multi-threaded memory allocation and cross-thread deallocation test.
> +   Copyright (C) 2024 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; see the file COPYING.LIB.  If
> +   not, see <https://www.gnu.org/licenses/>.  */
> +#define CROSS_THREAD_DEALLOC
> +#include "tst-aligned-alloc-random-thread.c"
> diff --git a/malloc/tst-aligned-alloc-random-thread.c b/malloc/tst-aligned-alloc-random-thread.c
> new file mode 100644
> index 0000000000..e95f79250a
> --- /dev/null
> +++ b/malloc/tst-aligned-alloc-random-thread.c
> @@ -0,0 +1,145 @@
> +/* multi-threaded memory allocation/deallocation test.
> +   Copyright (C) 2024 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; see the file COPYING.LIB.  If
> +   not, see <https://www.gnu.org/licenses/>.  */
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <support/check.h>
> +#include <support/support.h>
> +#include <support/xthread.h>
> +#include <support/test-driver.h>
> +#include <sys/sysinfo.h>
> +#include <unistd.h>
> +
> +#ifndef ITERATIONS
> +#  define ITERATIONS 16
> +#endif
> +
> +#ifndef NUM_THREADS
> +#  define NUM_THREADS 8
> +#endif
> +
> +#ifndef NUM_ALLOCATIONS
> +#  define NUM_ALLOCATIONS 2048
> +#endif
> +
> +static pthread_barrier_t barrier;
> +
> +__thread unsigned int seed;
> +
> +typedef struct
> +{
> +  int id;
> +  pthread_t thread;
> +} thread;
> +
> +thread threads[NUM_THREADS];
> +
> +void *allocations[NUM_THREADS][NUM_ALLOCATIONS];
> +
> +void
> +run_thread_dealloc (int id)
> +{
> +  for (int i = 0; i < NUM_ALLOCATIONS; i++)
> +    {
> +      free (allocations[id][i]);
> +      allocations[id][i] = NULL;
> +    }
> +}
> +
> +void
> +run_thread_alloc (int id)
> +{
> +  size_t msb, size;
> +  for (int i = 0; i < NUM_ALLOCATIONS; i++)
> +    {
> +      msb = 1 << rand_r (&seed) % 16;
> +      size = msb + rand_r (&seed) % msb;
> +      allocations[id][i] = malloc (size);

OK. Changed size distribution for allocations.

> +      TEST_VERIFY_EXIT (allocations[id][i] != NULL);
> +    }
> +}
> +
> +void *
> +run_allocations (void *arg)
> +{
> +  int id = *((int *) arg);
> +  seed = time (NULL) + id;
> +
> +  /* Stage 1: First half o the threads allocating memory and the second
> +   * half waiting for them to finish
> +   */
> +  if (id < NUM_THREADS / 2)
> +    run_thread_alloc (id);
> +
> +  xpthread_barrier_wait (&barrier);
> +
> +  /* Stage 2: Half of the threads allocationg memory and the other
> +   * half deallocating:
> +   * - In the non cross-thread dealloc scenario the first half will be
> +   *   deallocating the memory allocated by themselves in stage 1 and the
> +   *   second half will be allocating memory.
> +   * - In the cross-thread dealloc scenario the first half will continue
> +   *   to allocate memory and the second half will deallocate the memory
> +   *   allocated by the first half in stage 1.
> +   */
> +  if (id < NUM_THREADS / 2)
> +#ifndef CROSS_THREAD_DEALLOC
> +    run_thread_dealloc (id);
> +#else
> +    run_thread_alloc (id + NUM_THREADS / 2);
> +#endif
> +  else
> +#ifndef CROSS_THREAD_DEALLOC
> +    run_thread_alloc (id);
> +#else
> +    run_thread_dealloc (id - NUM_THREADS / 2);
> +#endif
> +
> +  xpthread_barrier_wait (&barrier);
> +
> +  // Stage 3: Second half of the threads deallocating and the first half
> +  // waiting for them to finish.
> +  if (id >= NUM_THREADS / 2)
> +    run_thread_dealloc (id);
> +
> +  return NULL;
> +}
> +
> +static int
> +do_test (void)
> +{
> +  xpthread_barrier_init (&barrier, NULL, NUM_THREADS);
> +
> +  for (int i = 0; i < ITERATIONS; i++)
> +    {
> +      for (int t = 0; t < NUM_THREADS; t++)
> +       {
> +         threads[t].id = t;
> +         threads[t].thread
> +             = xpthread_create (NULL, run_allocations, &threads[t].id);
> +       }
> +
> +      for (int t = 0; t < NUM_THREADS; t++)
> +       xpthread_join (threads[t].thread);

OK. Dropped check for deallocations.

> +    }
> +
> +  return 0;
> +}
> +
> +#include <support/test-driver.c>
> --
> Miguel Martín
> mmartinv@redhat.com
>
diff mbox series

Patch

diff --git a/malloc/Makefile b/malloc/Makefile
index 02aff1bd1d..98d507a6eb 100644
--- a/malloc/Makefile
+++ b/malloc/Makefile
@@ -28,6 +28,8 @@  tests := \
   mallocbug \
   tst-aligned-alloc \
   tst-aligned-alloc-random \
+  tst-aligned-alloc-random-thread \
+  tst-aligned-alloc-random-thread-cross \
   tst-alloc_buffer \
   tst-calloc \
   tst-free-errno \
@@ -151,6 +153,8 @@  ifeq ($(have-GLIBC_2.23)$(build-shared),yesyes)
 # the tests expect specific internal behavior that is changed due to linking to
 # libmcheck.a.
 tests-exclude-mcheck = \
+  tst-aligned-alloc-random-thread \
+  tst-aligned-alloc-random-thread-cross \
   tst-compathooks-off \
   tst-compathooks-on \
   tst-malloc-backtrace \
@@ -415,7 +419,11 @@  $(objpfx)tst-mallocstate: $(objpfx)libc_malloc_debug.so
 $(objpfx)tst-mallocstate-malloc-check: $(objpfx)libc_malloc_debug.so
 
 $(objpfx)tst-aligned-alloc-random.out: $(objpfx)tst-aligned_alloc-lib.so
+$(objpfx)tst-aligned-alloc-random-thread.out: $(objpfx)tst-aligned_alloc-lib.so
+$(objpfx)tst-aligned-alloc-random-thread-cross.out: $(objpfx)tst-aligned_alloc-lib.so
 $(objpfx)tst-malloc-random.out: $(objpfx)tst-aligned_alloc-lib.so
 
 tst-aligned-alloc-random-ENV = LD_PRELOAD=$(objpfx)tst-aligned_alloc-lib.so
+tst-aligned-alloc-random-thread-ENV = LD_PRELOAD=$(objpfx)tst-aligned_alloc-lib.so
+tst-aligned-alloc-random-thread-cross-ENV = LD_PRELOAD=$(objpfx)tst-aligned_alloc-lib.so
 tst-malloc-random-ENV = LD_PRELOAD=$(objpfx)tst-aligned_alloc-lib.so
diff --git a/malloc/tst-aligned-alloc-random-thread-cross.c b/malloc/tst-aligned-alloc-random-thread-cross.c
new file mode 100644
index 0000000000..360ecc56ee
--- /dev/null
+++ b/malloc/tst-aligned-alloc-random-thread-cross.c
@@ -0,0 +1,19 @@ 
+/* multi-threaded memory allocation and cross-thread deallocation test.
+   Copyright (C) 2024 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; see the file COPYING.LIB.  If
+   not, see <https://www.gnu.org/licenses/>.  */
+#define CROSS_THREAD_DEALLOC
+#include "tst-aligned-alloc-random-thread.c"
diff --git a/malloc/tst-aligned-alloc-random-thread.c b/malloc/tst-aligned-alloc-random-thread.c
new file mode 100644
index 0000000000..e95f79250a
--- /dev/null
+++ b/malloc/tst-aligned-alloc-random-thread.c
@@ -0,0 +1,145 @@ 
+/* multi-threaded memory allocation/deallocation test.
+   Copyright (C) 2024 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; see the file COPYING.LIB.  If
+   not, see <https://www.gnu.org/licenses/>.  */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/xthread.h>
+#include <support/test-driver.h>
+#include <sys/sysinfo.h>
+#include <unistd.h>
+
+#ifndef ITERATIONS
+#  define ITERATIONS 16
+#endif
+
+#ifndef NUM_THREADS
+#  define NUM_THREADS 8
+#endif
+
+#ifndef NUM_ALLOCATIONS
+#  define NUM_ALLOCATIONS 2048
+#endif
+
+static pthread_barrier_t barrier;
+
+__thread unsigned int seed;
+
+typedef struct
+{
+  int id;
+  pthread_t thread;
+} thread;
+
+thread threads[NUM_THREADS];
+
+void *allocations[NUM_THREADS][NUM_ALLOCATIONS];
+
+void
+run_thread_dealloc (int id)
+{
+  for (int i = 0; i < NUM_ALLOCATIONS; i++)
+    {
+      free (allocations[id][i]);
+      allocations[id][i] = NULL;
+    }
+}
+
+void
+run_thread_alloc (int id)
+{
+  size_t msb, size;
+  for (int i = 0; i < NUM_ALLOCATIONS; i++)
+    {
+      msb = 1 << rand_r (&seed) % 16;
+      size = msb + rand_r (&seed) % msb;
+      allocations[id][i] = malloc (size);
+      TEST_VERIFY_EXIT (allocations[id][i] != NULL);
+    }
+}
+
+void *
+run_allocations (void *arg)
+{
+  int id = *((int *) arg);
+  seed = time (NULL) + id;
+
+  /* Stage 1: First half o the threads allocating memory and the second
+   * half waiting for them to finish
+   */
+  if (id < NUM_THREADS / 2)
+    run_thread_alloc (id);
+
+  xpthread_barrier_wait (&barrier);
+
+  /* Stage 2: Half of the threads allocationg memory and the other
+   * half deallocating:
+   * - In the non cross-thread dealloc scenario the first half will be
+   *   deallocating the memory allocated by themselves in stage 1 and the
+   *   second half will be allocating memory.
+   * - In the cross-thread dealloc scenario the first half will continue
+   *   to allocate memory and the second half will deallocate the memory
+   *   allocated by the first half in stage 1.
+   */
+  if (id < NUM_THREADS / 2)
+#ifndef CROSS_THREAD_DEALLOC
+    run_thread_dealloc (id);
+#else
+    run_thread_alloc (id + NUM_THREADS / 2);
+#endif
+  else
+#ifndef CROSS_THREAD_DEALLOC
+    run_thread_alloc (id);
+#else
+    run_thread_dealloc (id - NUM_THREADS / 2);
+#endif
+
+  xpthread_barrier_wait (&barrier);
+
+  // Stage 3: Second half of the threads deallocating and the first half
+  // waiting for them to finish.
+  if (id >= NUM_THREADS / 2)
+    run_thread_dealloc (id);
+
+  return NULL;
+}
+
+static int
+do_test (void)
+{
+  xpthread_barrier_init (&barrier, NULL, NUM_THREADS);
+
+  for (int i = 0; i < ITERATIONS; i++)
+    {
+      for (int t = 0; t < NUM_THREADS; t++)
+	{
+	  threads[t].id = t;
+	  threads[t].thread
+	      = xpthread_create (NULL, run_allocations, &threads[t].id);
+	}
+
+      for (int t = 0; t < NUM_THREADS; t++)
+	xpthread_join (threads[t].thread);
+    }
+
+  return 0;
+}
+
+#include <support/test-driver.c>