xref: /netbsd-src/external/gpl3/gdb.old/dist/gdb/testsuite/gdb.server/exit-multiple-threads.c (revision f4748aaa01faf324805f9747191535eb6600f82c)
1 /* This testcase is part of GDB, the GNU debugger.
2 
3    Copyright 2020 Free Software Foundation, Inc.
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 3 of the License, or
8    (at your option) any later version.
9 
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17 
18 #include <pthread.h>
19 #include <sys/types.h>
20 #include <stdio.h>
21 #include <unistd.h>
22 #include <stdlib.h>
23 #include <pthread.h>
24 
25 /* The number of threads to create.  */
26 int thread_count = 3;
27 
28 /* Counter accessed from threads to ensure that all threads have been
29    started.  Is initialised to THREAD_COUNT and each thread decrements it
30    upon startup.  */
31 volatile int counter;
32 
33 /* Lock guarding COUNTER. */
34 pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER;
35 
36 /* Is initialised with our pid, GDB will read this.  */
37 pid_t global_pid;
38 
39 /* Just somewhere to put a breakpoint.  */
40 static void
41 breakpt ()
42 {
43   /* Nothing.  */
44 }
45 
46 /* Thread safe decrement of the COUNTER global.  */
47 static void
48 decrement_counter ()
49 {
50   if (pthread_mutex_lock (&counter_mutex) != 0)
51     abort ();
52   --counter;
53   if (pthread_mutex_unlock (&counter_mutex) != 0)
54     abort ();
55 }
56 
57 /* Thread safe read of the COUNTER global.  */
58 static int
59 read_counter ()
60 {
61   int val;
62 
63   if (pthread_mutex_lock (&counter_mutex) != 0)
64     abort ();
65   val = counter;
66   if (pthread_mutex_unlock (&counter_mutex) != 0)
67     abort ();
68 
69   return val;
70 }
71 
72 #if defined DO_EXIT_TEST
73 
74 /* Thread entry point.  ARG is a pointer to a single integer, the ID for
75    this thread numbered 1 to THREAD_COUNT (a global).  */
76 static void *
77 thread_worker_exiting (void *arg)
78 {
79   int id;
80 
81   id = *((int *) arg);
82 
83   decrement_counter ();
84 
85   if (id != thread_count)
86     {
87       int i;
88 
89       /* All threads except the last one will wait here while the test is
90 	 carried out.  Don't wait forever though, just in case the test
91 	 goes wrong.  */
92       for (i = 0; i < 60; ++i)
93 	sleep (1);
94     }
95   else
96     {
97       /* The last thread waits here until all other threads have been
98 	 created.  */
99       while (read_counter () > 0)
100 	sleep (1);
101 
102       /* Hit the breakpoint so GDB can stop.  */
103       breakpt ();
104 
105       /* And exit all threads.  */
106       exit (0);
107     }
108 
109   return NULL;
110 }
111 
112 #define thread_worker thread_worker_exiting
113 
114 #elif defined DO_SIGNAL_TEST
115 
116 /* Thread entry point.  ARG is a pointer to a single integer, the ID for
117    this thread numbered 1 to THREAD_COUNT (a global).  */
118 static void *
119 thread_worker_signalling (void *arg)
120 {
121   int i, id;
122 
123   id = *((int *) arg);
124 
125   decrement_counter ();
126 
127   if (id == thread_count)
128     {
129       /* The last thread waits here until all other threads have been
130 	 created.  */
131       while (read_counter () > 0)
132 	sleep (1);
133 
134       /* Hit the breakpoint so GDB can stop.  */
135       breakpt ();
136     }
137 
138   /* All threads wait here while the testsuite sends us a signal.  Don't
139      block forever though, just in case the test goes wrong.  */
140   for (i = 0; i < 60; ++i)
141     sleep (1);
142 
143   return NULL;
144 }
145 
146 #define thread_worker thread_worker_signalling
147 
148 #else
149 
150 #error "Compile with DO_EXIT_TEST or DO_SIGNAL_TEST defined"
151 
152 #endif
153 
154 struct thread_info
155 {
156   pthread_t thread;
157   int id;
158 };
159 
160 int
161 main ()
162 {
163   int i, max = thread_count;
164 
165   /* Put the pid somewhere easy for GDB to read.  */
166   global_pid = getpid ();
167 
168   /* Space to hold all of the thread_info objects.  */
169   struct thread_info *info = malloc (sizeof (struct thread_info) * max);
170   if (info == NULL)
171     abort ();
172 
173   /* Initialise the counter.  Don't do this under lock as we only have the
174      main thread at this point.  */
175   counter = thread_count;
176 
177   /* Create all of the threads.  */
178   for (i = 0; i < max; ++i)
179     {
180       struct thread_info *thr = &info[i];
181       thr->id = i + 1;
182       if (pthread_create (&thr->thread, NULL, thread_worker, &thr->id) != 0)
183 	abort ();
184     }
185 
186   /* Gather in all of the threads.  This never completes, as the
187      final thread created will exit the process, and all of the other
188      threads block forever.  Still, it gives the main thread something to
189      do.  */
190   for (i = 0; i < max; ++i)
191     {
192       struct thread_info *thr = &info[i];
193       if (pthread_join (thr->thread, NULL) != 0)
194 	abort ();
195     }
196 
197   free (info);
198 
199   /* Return non-zero.  We should never get here, but if we do make sure we
200      indicate something has gone wrong.  */
201   return 1;
202 }
203