Commit | Line | Data |
---|---|---|
cada5fc9 AB |
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 | } |