167 lines
4.2 KiB
C
167 lines
4.2 KiB
C
|
/* This testcase is part of GDB, the GNU debugger.
|
||
|
|
||
|
Copyright 2015-2022 Free Software Foundation, Inc.
|
||
|
|
||
|
This program is free software; you can redistribute it and/or modify
|
||
|
it under the terms of the GNU General Public License as published by
|
||
|
the Free Software Foundation; either version 3 of the License, or
|
||
|
(at your option) any later version.
|
||
|
|
||
|
This program 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 General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||
|
|
||
|
#define _GNU_SOURCE
|
||
|
#include <assert.h>
|
||
|
#include <pthread.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <unistd.h>
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <limits.h>
|
||
|
|
||
|
/* How many threads fit in the target's thread number space. */
|
||
|
long tid_max = -1;
|
||
|
|
||
|
/* Number of threads spawned. */
|
||
|
unsigned long thread_counter;
|
||
|
|
||
|
/* How long it takes to spawn as many threads as fits in the thread
|
||
|
number space. On systems where thread IDs are just monotonically
|
||
|
incremented, this is enough for the tid numbers to wrap around. On
|
||
|
targets that randomize thread IDs, this is enough time to give each
|
||
|
number in the thread number space some chance of reuse. It'll be
|
||
|
capped to a lower value if we can't compute it. REUSE_TIME_CAP
|
||
|
is the max value, and the default value if ever the program
|
||
|
has problem to compute it. */
|
||
|
#define REUSE_TIME_CAP 60
|
||
|
unsigned int reuse_time = REUSE_TIME_CAP;
|
||
|
|
||
|
void *
|
||
|
do_nothing_thread_func (void *arg)
|
||
|
{
|
||
|
usleep (1);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
check_rc (int rc, const char *what)
|
||
|
{
|
||
|
if (rc != 0)
|
||
|
{
|
||
|
fprintf (stderr, "unexpected error from %s: %s (%d)\n",
|
||
|
what, strerror (rc), rc);
|
||
|
assert (0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void *
|
||
|
spawner_thread_func (void *arg)
|
||
|
{
|
||
|
while (1)
|
||
|
{
|
||
|
pthread_t child;
|
||
|
int rc;
|
||
|
|
||
|
thread_counter++;
|
||
|
|
||
|
rc = pthread_create (&child, NULL, do_nothing_thread_func, NULL);
|
||
|
check_rc (rc, "pthread_create");
|
||
|
|
||
|
rc = pthread_join (child, NULL);
|
||
|
check_rc (rc, "pthread_join");
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/* Called after the program is done counting number of spawned threads
|
||
|
for a period, to compute REUSE_TIME. */
|
||
|
|
||
|
void
|
||
|
after_count (void)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
/* Called after enough time has passed for TID reuse to occur. */
|
||
|
|
||
|
void
|
||
|
after_reuse_time (void)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
#ifdef __linux__
|
||
|
|
||
|
/* Get the running system's configured pid_max. */
|
||
|
|
||
|
static int
|
||
|
linux_proc_get_pid_max (void)
|
||
|
{
|
||
|
static const char filename[] ="/proc/sys/kernel/pid_max";
|
||
|
FILE *file;
|
||
|
char buf[100];
|
||
|
int retval = -1;
|
||
|
|
||
|
file = fopen (filename, "r");
|
||
|
if (file == NULL)
|
||
|
{
|
||
|
fprintf (stderr, "unable to open %s\n", filename);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (fgets (buf, sizeof (buf), file) != NULL)
|
||
|
retval = strtol (buf, NULL, 10);
|
||
|
|
||
|
fclose (file);
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
int
|
||
|
main (int argc, char *argv[])
|
||
|
{
|
||
|
pthread_t child;
|
||
|
int rc;
|
||
|
unsigned int reuse_time_raw = 0;
|
||
|
|
||
|
rc = pthread_create (&child, NULL, spawner_thread_func, NULL);
|
||
|
check_rc (rc, "pthread_create spawner_thread");
|
||
|
|
||
|
#define COUNT_TIME 2
|
||
|
sleep (COUNT_TIME);
|
||
|
|
||
|
#ifdef __linux__
|
||
|
tid_max = linux_proc_get_pid_max ();
|
||
|
#endif
|
||
|
/* If we don't know how many threads it would take to use the whole
|
||
|
number space on this system, just run the test for a bit. */
|
||
|
if (tid_max > 0)
|
||
|
{
|
||
|
reuse_time_raw = tid_max / ((float) thread_counter / COUNT_TIME) + 0.5;
|
||
|
|
||
|
/* Give it a bit more, just in case. */
|
||
|
reuse_time = reuse_time_raw + 3;
|
||
|
}
|
||
|
|
||
|
/* 4 seconds were sufficient on the machine this was first observed,
|
||
|
an Intel i7-2620M @ 2.70GHz running Linux 3.18.7, with
|
||
|
pid_max=32768. Going forward, as machines get faster, this will
|
||
|
need less time, unless pid_max is set to a very high number. To
|
||
|
avoid unreasonably long test time, cap to an upper bound. */
|
||
|
if (reuse_time > REUSE_TIME_CAP)
|
||
|
reuse_time = REUSE_TIME_CAP;
|
||
|
printf ("thread_counter=%lu, tid_max = %ld, reuse_time_raw=%u, reuse_time=%u\n",
|
||
|
thread_counter, tid_max, reuse_time_raw, reuse_time);
|
||
|
after_count ();
|
||
|
|
||
|
sleep (reuse_time);
|
||
|
|
||
|
after_reuse_time ();
|
||
|
return 0;
|
||
|
}
|