/* 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;
}