1/* This testcase is part of GDB, the GNU debugger.
2
3   Copyright 2023-2024 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 <assert.h>
19#include <pthread.h>
20
21/* Test can create at most this number of extra threads.  */
22#define MAX_THREADS 3
23
24/* For convenience.  */
25#define FALSE 0
26#define TRUE (!FALSE)
27
28/* Controls a thread created by this test.  */
29struct thread_descriptor
30{
31  /* The pthread handle.  Not valid unless STARTED is true.  */
32  pthread_t thr;
33
34  /* This field is set to TRUE when a thread has been created, otherwise,
35     is false.  */
36  int started;
37
38  /* A condition variable and mutex, used for synchronising between the
39     worker thread and the main thread.  */
40  pthread_cond_t cond;
41  pthread_mutex_t mutex;
42};
43
44/* Keep track of worker threads.  */
45struct thread_descriptor threads[MAX_THREADS];
46
47/* Worker thread function.  Doesn't do much.  Synchronise with the main
48   thread, mark the thread as started, and then block waiting for the main
49   thread.  Once the main thread wakes us, this thread exits.
50
51   ARG is a thread_descriptor shared with the main thread.  */
52
53void *
54thread_function (void *arg)
55{
56  int res;
57  struct thread_descriptor *thread = (struct thread_descriptor *) arg;
58
59  /* Acquire the thread's lock.  Initially the main thread holds this lock,
60     but releases it when the main thread enters a pthread_cond_wait.  */
61  res = pthread_mutex_lock (&thread->mutex);
62  assert (res == 0);
63
64  /* Mark the thread as started.  */
65  thread->started = TRUE;
66
67  /* Signal the main thread to tell it we are started.  The main thread
68     will still be blocked though as we hold the thread's lock.  */
69  res = pthread_cond_signal (&thread->cond);
70  assert (res == 0);
71
72  /* Now wait until the main thread tells us to exit.  By entering this
73     pthread_cond_wait we release the lock, which allows the main thread to
74     resume.  */
75  res = pthread_cond_wait (&thread->cond, &thread->mutex);
76  assert (res == 0);
77
78  /* The main thread woke us up.  We reacquired the thread lock as we left
79     the pthread_cond_wait, so release the lock now.  */
80  res = pthread_mutex_unlock (&thread->mutex);
81  assert (res == 0);
82
83  return NULL;
84}
85
86/* Start a new thread within the global THREADS array.  Return true if a
87   new thread was started, otherwise return false.  */
88
89int
90start_thread ()
91{
92  int idx, res;
93
94  for (idx = 0; idx < MAX_THREADS; ++idx)
95    if (!threads[idx].started)
96      break;
97
98  if (idx == MAX_THREADS)
99    return FALSE;
100
101  /* Acquire the thread lock before starting the new thread.  */
102  res = pthread_mutex_lock (&threads[idx].mutex);
103  assert (res == 0);
104
105  /* Start the new thread.  */
106  res = pthread_create (&threads[idx].thr, NULL,
107			thread_function, &threads[idx]);
108  assert (res == 0);
109
110  /* Unlock and wait.  The thread signals us once it is ready.  */
111  res = pthread_cond_wait (&threads[idx].cond, &threads[idx].mutex);
112  assert (res == 0);
113
114  /* The worker thread is now blocked in a pthread_cond_wait and we
115     reacquired the lock as we left our own pthread_cond_wait above.  */
116  res = pthread_mutex_unlock (&threads[idx].mutex);
117  assert (res == 0);
118
119  return TRUE;
120}
121
122/* Stop a thread from within the global THREADS array.  Return true if a
123   thread was stopped, otherwise return false.  */
124int
125stop_thread ()
126{
127  /* Look for a thread that is started.  */
128  for (int idx = 0; idx < MAX_THREADS; ++idx)
129    if (threads[idx].started)
130      {
131	int res;
132
133	/* Grab the thread lock.  */
134	res = pthread_mutex_lock (&threads[idx].mutex);
135	assert (res == 0);
136
137	/* Signal the worker thread, this wakes it up, but it can't exit
138	   until it acquires the thread lock, which we currently hold.  */
139	res = pthread_cond_signal (&threads[idx].cond);
140	assert (res == 0);
141
142	/* Release the thread lock, this allows the worker thread to exit.  */
143	res = pthread_mutex_unlock (&threads[idx].mutex);
144	assert (res == 0);
145
146	/* Now wait for the thread to exit.  */
147	void *retval;
148	res = pthread_join (threads[idx].thr, &retval);
149	assert (res == 0);
150	assert (retval == NULL);
151
152	/* Now the thread has exited, mark it as no longer started.  */
153	assert (threads[idx].started);
154	threads[idx].started = FALSE;
155
156	return TRUE;
157      }
158
159  return FALSE;
160}
161
162void
163init_descriptor_array ()
164{
165  for (int i = 0; i < MAX_THREADS; ++i)
166    {
167      int res;
168
169      threads[i].started = FALSE;
170      res = pthread_cond_init (&threads[i].cond, NULL);
171      assert (res == 0);
172      res = pthread_mutex_init (&threads[i].mutex, NULL);
173      assert (res == 0);
174    }
175}
176
177void
178breakpt ()
179{
180  /* Nothing.  */
181}
182
183int
184main ()
185{
186  init_descriptor_array ();
187  breakpt ();
188  start_thread ();
189  stop_thread ();
190  breakpt ();
191  return 0;
192}
193