2014-12-17 20:40:05 +00:00
|
|
|
/* This testcase is part of GDB, the GNU debugger.
|
|
|
|
|
|
|
|
Copyright 2014-2015 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 <stdio.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
pthread_t main_thread;
|
|
|
|
pthread_attr_t detached_attr;
|
|
|
|
pthread_attr_t joinable_attr;
|
|
|
|
|
|
|
|
/* Number of threads we'll create of each variant
|
|
|
|
(joinable/detached). */
|
|
|
|
int n_threads = 50;
|
|
|
|
|
|
|
|
/* Mutex used to hold creating detached threads. */
|
|
|
|
pthread_mutex_t dthrds_create_mutex;
|
|
|
|
|
|
|
|
/* Wrapper for pthread_create. */
|
|
|
|
|
|
|
|
void
|
|
|
|
create_thread (pthread_attr_t *attr,
|
|
|
|
void *(*start_routine) (void *), void *arg)
|
|
|
|
{
|
|
|
|
pthread_t child;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
while ((rc = pthread_create (&child, attr, start_routine, arg)) != 0)
|
|
|
|
{
|
|
|
|
fprintf (stderr, "unexpected error from pthread_create: %s (%d)\n",
|
|
|
|
strerror (rc), rc);
|
|
|
|
sleep (1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
break_fn (void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Data passed to joinable threads on creation. This is allocated on
|
|
|
|
the heap and ownership transferred from parent to child. (We do
|
|
|
|
this because it's not portable to cast pthread_t to pointer.) */
|
|
|
|
|
|
|
|
struct thread_arg
|
|
|
|
{
|
|
|
|
pthread_t parent;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Entry point for joinable threads. These threads first join their
|
|
|
|
parent before spawning a new child (and exiting). The parent's tid
|
|
|
|
is passed as pthread_create argument, encapsulated in a struct
|
|
|
|
thread_arg object. */
|
|
|
|
|
|
|
|
void *
|
|
|
|
joinable_fn (void *arg)
|
|
|
|
{
|
|
|
|
struct thread_arg *p = arg;
|
|
|
|
|
|
|
|
pthread_setname_np (pthread_self (), "joinable");
|
|
|
|
|
|
|
|
if (p->parent != main_thread)
|
|
|
|
assert (pthread_join (p->parent, NULL) == 0);
|
|
|
|
|
|
|
|
p->parent = pthread_self ();
|
|
|
|
|
|
|
|
create_thread (&joinable_attr, joinable_fn, p);
|
|
|
|
|
|
|
|
break_fn ();
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Entry point for detached threads. */
|
|
|
|
|
|
|
|
void *
|
|
|
|
detached_fn (void *arg)
|
|
|
|
{
|
|
|
|
pthread_setname_np (pthread_self (), "detached");
|
|
|
|
|
|
|
|
/* This should throttle threads a bit in case we manage to spawn
|
|
|
|
threads faster than they exit. */
|
|
|
|
pthread_mutex_lock (&dthrds_create_mutex);
|
|
|
|
|
|
|
|
create_thread (&detached_attr, detached_fn, NULL);
|
|
|
|
|
|
|
|
/* Note this is called before the mutex is unlocked otherwise in
|
|
|
|
non-stop mode, when the breakpoint is hit we'd keep spawning more
|
|
|
|
threads forever while the old threads stay alive (stopped in the
|
|
|
|
breakpoint). */
|
|
|
|
break_fn ();
|
|
|
|
|
|
|
|
pthread_mutex_unlock (&dthrds_create_mutex);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-02-06 12:24:32 +00:00
|
|
|
/* Allow for as much timeout as DejaGnu wants, plus a bit of
|
|
|
|
slack. */
|
|
|
|
#define SECONDS (TIMEOUT + 20)
|
|
|
|
|
|
|
|
/* We'll exit after this many seconds. */
|
|
|
|
unsigned int seconds_left = SECONDS;
|
|
|
|
|
|
|
|
/* GDB sets this whenever it's about to start a new detach/attach
|
|
|
|
sequence. We react by resetting the seconds left counter. */
|
|
|
|
volatile int again = 0;
|
|
|
|
|
2014-12-17 20:40:05 +00:00
|
|
|
int
|
|
|
|
main (int argc, char *argv[])
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (argc > 1)
|
|
|
|
n_threads = atoi (argv[1]);
|
|
|
|
|
|
|
|
pthread_mutex_init (&dthrds_create_mutex, NULL);
|
|
|
|
|
|
|
|
pthread_attr_init (&detached_attr);
|
|
|
|
pthread_attr_setdetachstate (&detached_attr, PTHREAD_CREATE_DETACHED);
|
|
|
|
pthread_attr_init (&joinable_attr);
|
|
|
|
pthread_attr_setdetachstate (&joinable_attr, PTHREAD_CREATE_JOINABLE);
|
|
|
|
|
|
|
|
main_thread = pthread_self ();
|
|
|
|
|
|
|
|
/* Spawn the initial set of test threads. Some threads are
|
|
|
|
joinable, others are detached. This exercises different code
|
|
|
|
paths in the runtime. */
|
|
|
|
for (i = 0; i < n_threads; ++i)
|
|
|
|
{
|
|
|
|
struct thread_arg *p;
|
|
|
|
|
|
|
|
p = malloc (sizeof *p);
|
|
|
|
p->parent = main_thread;
|
|
|
|
create_thread (&joinable_attr, joinable_fn, p);
|
|
|
|
|
|
|
|
create_thread (&detached_attr, detached_fn, NULL);
|
|
|
|
}
|
|
|
|
|
2015-02-06 12:24:32 +00:00
|
|
|
/* Exit after a while if GDB is gone/crashes. But wait long enough
|
|
|
|
for one attach/detach sequence done by the .exp file. */
|
|
|
|
while (--seconds_left > 0)
|
|
|
|
{
|
|
|
|
sleep (1);
|
|
|
|
|
|
|
|
if (again)
|
|
|
|
{
|
|
|
|
/* GDB should be reattaching soon. Restart the timer. */
|
|
|
|
again = 0;
|
|
|
|
seconds_left = SECONDS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
printf ("timeout, exiting\n");
|
2014-12-17 20:40:05 +00:00
|
|
|
return 0;
|
|
|
|
}
|