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