/* 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 . */ #define _GNU_SOURCE #include #include #include #include #include #include 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; } 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); } /* Long enough for all the attach/detach sequences done by the .exp file. */ sleep (180); return 0; }