/* This testcase is part of GDB, the GNU debugger. Copyright 2022-2024 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 . */ #include #include #include #include #include #include #define NUM_THREADS 5 /* Semaphores, used to track when threads have started, and to control when the threads finish. */ sem_t startup_semaphore; sem_t finish_semaphore; sem_t thread_1_semaphore; sem_t thread_2_semaphore; /* Mutex to control when the first worker thread hit a breakpoint location. */ pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; /* Global variable to poke, just so threads have something to do. */ volatile int global_var = 0; int condition_func () { /* Let thread 2 run. */ if (sem_post (&thread_2_semaphore) != 0) abort (); /* Wait for thread 2 to complete its actions. */ if (sem_wait (&thread_1_semaphore) != 0) abort (); return 1; } void do_segfault () { volatile int *p = 0; *p = 0; /* Segfault here. */ } void * worker_func (void *arg) { int tid = *((int *) arg); /* Let the main thread know that this worker has started. */ if (sem_post (&startup_semaphore) != 0) abort (); switch (tid) { case 0: /* Wait for MUTEX to become available, then pass through the conditional breakpoint location. */ if (pthread_mutex_lock (&mutex) != 0) abort (); global_var = 99; /* Conditional breakpoint here. */ if (pthread_mutex_unlock (&mutex) != 0) abort (); break; case 1: if (sem_wait (&thread_2_semaphore) != 0) abort (); do_segfault (); if (sem_post (&thread_1_semaphore) != 0) abort (); /* Fall through. */ default: /* Wait until we are allowed to finish. */ if (sem_wait (&finish_semaphore) != 0) abort (); break; } } void stop_marker () { global_var = 99; /* Stop marker. */ } /* The main program entry point. */ int main () { pthread_t threads[NUM_THREADS]; int args[NUM_THREADS]; void *retval; /* An alarm, just in case the thread deadlocks. */ alarm (300); /* Semaphore initialization. */ if (sem_init (&startup_semaphore, 0, 0) != 0) abort (); if (sem_init (&finish_semaphore, 0, 0) != 0) abort (); if (sem_init (&thread_1_semaphore, 0, 0) != 0) abort (); if (sem_init (&thread_2_semaphore, 0, 0) != 0) abort (); /* Lock MUTEX, this prevents the first worker thread from rushing ahead. */ if (pthread_mutex_lock (&mutex) != 0) abort (); /* Worker thread creation. */ for (int i = 0; i < NUM_THREADS; i++) { args[i] = i; pthread_create (&threads[i], NULL, worker_func, &args[i]); } /* Wait for every thread to start. */ for (int i = 0; i < NUM_THREADS; i++) { if (sem_wait (&startup_semaphore) != 0) abort (); } /* Unlock the first thread so it can proceed. */ if (pthread_mutex_unlock (&mutex) != 0) abort (); /* Wait for the first thread only. */ pthread_join (threads[0], &retval); /* Now post FINISH_SEMAPHORE to allow all the other threads to finish. */ for (int i = 1; i < NUM_THREADS; i++) sem_post (&finish_semaphore); /* Now wait for the remaining threads to complete. */ for (int i = 1; i < NUM_THREADS; i++) pthread_join (threads[i], &retval); /* Semaphore cleanup. */ sem_destroy (&finish_semaphore); sem_destroy (&startup_semaphore); sem_destroy (&thread_1_semaphore); sem_destroy (&thread_2_semaphore); stop_marker (); return 0; }