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