| /* This testcase is part of GDB, the GNU debugger. |
| |
| Copyright 2020-2021 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/>. */ |
| |
| #include <pthread.h> |
| #include <sys/types.h> |
| #include <stdio.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <pthread.h> |
| |
| /* The number of threads to create. */ |
| int thread_count = 3; |
| |
| /* Counter accessed from threads to ensure that all threads have been |
| started. Is initialised to THREAD_COUNT and each thread decrements it |
| upon startup. */ |
| volatile int counter; |
| |
| /* Lock guarding COUNTER. */ |
| pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER; |
| |
| /* Is initialised with our pid, GDB will read this. */ |
| pid_t global_pid; |
| |
| /* Just somewhere to put a breakpoint. */ |
| static void |
| breakpt () |
| { |
| /* Nothing. */ |
| } |
| |
| /* Thread safe decrement of the COUNTER global. */ |
| static void |
| decrement_counter () |
| { |
| if (pthread_mutex_lock (&counter_mutex) != 0) |
| abort (); |
| --counter; |
| if (pthread_mutex_unlock (&counter_mutex) != 0) |
| abort (); |
| } |
| |
| /* Thread safe read of the COUNTER global. */ |
| static int |
| read_counter () |
| { |
| int val; |
| |
| if (pthread_mutex_lock (&counter_mutex) != 0) |
| abort (); |
| val = counter; |
| if (pthread_mutex_unlock (&counter_mutex) != 0) |
| abort (); |
| |
| return val; |
| } |
| |
| #if defined DO_EXIT_TEST |
| |
| /* Thread entry point. ARG is a pointer to a single integer, the ID for |
| this thread numbered 1 to THREAD_COUNT (a global). */ |
| static void * |
| thread_worker_exiting (void *arg) |
| { |
| int id; |
| |
| id = *((int *) arg); |
| |
| decrement_counter (); |
| |
| if (id != thread_count) |
| { |
| int i; |
| |
| /* All threads except the last one will wait here while the test is |
| carried out. Don't wait forever though, just in case the test |
| goes wrong. */ |
| for (i = 0; i < 60; ++i) |
| sleep (1); |
| } |
| else |
| { |
| /* The last thread waits here until all other threads have been |
| created. */ |
| while (read_counter () > 0) |
| sleep (1); |
| |
| /* Hit the breakpoint so GDB can stop. */ |
| breakpt (); |
| |
| /* And exit all threads. */ |
| exit (0); |
| } |
| |
| return NULL; |
| } |
| |
| #define thread_worker thread_worker_exiting |
| |
| #elif defined DO_SIGNAL_TEST |
| |
| /* Thread entry point. ARG is a pointer to a single integer, the ID for |
| this thread numbered 1 to THREAD_COUNT (a global). */ |
| static void * |
| thread_worker_signalling (void *arg) |
| { |
| int i, id; |
| |
| id = *((int *) arg); |
| |
| decrement_counter (); |
| |
| if (id == thread_count) |
| { |
| /* The last thread waits here until all other threads have been |
| created. */ |
| while (read_counter () > 0) |
| sleep (1); |
| |
| /* Hit the breakpoint so GDB can stop. */ |
| breakpt (); |
| } |
| |
| /* All threads wait here while the testsuite sends us a signal. Don't |
| block forever though, just in case the test goes wrong. */ |
| for (i = 0; i < 60; ++i) |
| sleep (1); |
| |
| return NULL; |
| } |
| |
| #define thread_worker thread_worker_signalling |
| |
| #else |
| |
| #error "Compile with DO_EXIT_TEST or DO_SIGNAL_TEST defined" |
| |
| #endif |
| |
| struct thread_info |
| { |
| pthread_t thread; |
| int id; |
| }; |
| |
| int |
| main () |
| { |
| int i, max = thread_count; |
| |
| /* Put the pid somewhere easy for GDB to read. */ |
| global_pid = getpid (); |
| |
| /* Space to hold all of the thread_info objects. */ |
| struct thread_info *info = malloc (sizeof (struct thread_info) * max); |
| if (info == NULL) |
| abort (); |
| |
| /* Initialise the counter. Don't do this under lock as we only have the |
| main thread at this point. */ |
| counter = thread_count; |
| |
| /* Create all of the threads. */ |
| for (i = 0; i < max; ++i) |
| { |
| struct thread_info *thr = &info[i]; |
| thr->id = i + 1; |
| if (pthread_create (&thr->thread, NULL, thread_worker, &thr->id) != 0) |
| abort (); |
| } |
| |
| /* Gather in all of the threads. This never completes, as the |
| final thread created will exit the process, and all of the other |
| threads block forever. Still, it gives the main thread something to |
| do. */ |
| for (i = 0; i < max; ++i) |
| { |
| struct thread_info *thr = &info[i]; |
| if (pthread_join (thr->thread, NULL) != 0) |
| abort (); |
| } |
| |
| free (info); |
| |
| /* Return non-zero. We should never get here, but if we do make sure we |
| indicate something has gone wrong. */ |
| return 1; |
| } |