/* Run with: GLIBC_TUNABLES=glibc.malloc.tcache_count=0 */ #include #include #include #include #include #include #include static void usage (char **argv) { fprintf (stderr, "usage: %s THREADS FREES-PER-THREAD\n", argv[0]); exit (1); } static void check (const char *function, int ret) { if (ret != 0) { errno = ret; err (1, "%s", function); } } static pthread_barrier_t barrier; static void xpthread_barrier_wait (pthread_barrier_t *barrier) { int ret = pthread_barrier_wait (barrier); if (ret != 0 && ret != PTHREAD_BARRIER_SERIAL_THREAD) { errno = ret; err (1, "pthread_barrier_wait"); } } static int per_thread; static void * thread_routine (void *closure) { char **allocations = closure; int count = per_thread; while (true) { /* Wait for the main thread to perform allocations. */ xpthread_barrier_wait (&barrier); for (int i = 0; i < count; ++i) { if (allocations[i] == NULL) errx (1, "allocation is NULL"); free (allocations[i]); } /* Signal to the main thread that new allocations can be performed. */ xpthread_barrier_wait (&barrier); } } static void make_allocations (char **allocations, int count) { for (int i = 0; i < count; ++i) { size_t size = 120; /* 128 byte cache line, 8 bytes malloc overhead. */ allocations[i] = malloc (size); if (allocations[i] == NULL) err (1, "malloc"); memset (allocations[i], 0xc0, size); } } int main (int argc, char **argv) { if (argc != 3) usage (argv); int thread_count = atoi (argv[1]); if (thread_count <= 0) usage (argv); per_thread = atoi (argv[2]); if (per_thread <= 0) usage (argv); check ("pthread_barrier_init", pthread_barrier_init (&barrier, NULL, thread_count + 1)); pthread_attr_t attr; check ("pthread_attr_init", pthread_attr_init (&attr)); check ("pthread_attr_setstacksize", pthread_attr_setstacksize (&attr, 256 * 1024)); pthread_t *threads = calloc (thread_count, sizeof (*threads)); if (threads == NULL) err (1, "calloc"); char **allocations = calloc (thread_count * per_thread, sizeof (*allocations)); if (allocations == NULL) err (1, "calloc"); char **next_allocations = calloc (thread_count * per_thread, sizeof (*allocations)); if (next_allocations == NULL) err (1, "calloc"); for (int i = 0; i < thread_count; ++i) check ("pthread_create", pthread_create (threads + i, &attr, thread_routine, allocations + i * per_thread)); make_allocations (allocations, thread_count * per_thread); while (true) { xpthread_barrier_wait (&barrier); /* Threads perform deallocations here. */ make_allocations (next_allocations, thread_count * per_thread); xpthread_barrier_wait (&barrier); memcpy (allocations, next_allocations, thread_count * per_thread * sizeof (*allocations)); } }