17494Sjkh/* This testcase is part of GDB, the GNU debugger.
27494Sjkh
37494Sjkh   Copyright 2016-2023 Free Software Foundation, Inc.
47494Sjkh
57494Sjkh   This program is free software; you can redistribute it and/or modify
67494Sjkh   it under the terms of the GNU General Public License as published by
77494Sjkh   the Free Software Foundation; either version 3 of the License, or
87494Sjkh   (at your option) any later version.
97494Sjkh
107494Sjkh   This program is distributed in the hope that it will be useful,
117494Sjkh   but WITHOUT ANY WARRANTY; without even the implied warranty of
127494Sjkh   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
137494Sjkh   GNU General Public License for more details.
147494Sjkh
157494Sjkh   You should have received a copy of the GNU General Public License
167494Sjkh   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
177494Sjkh
187494Sjkh#define _GNU_SOURCE
197494Sjkh#include <stdlib.h>
207494Sjkh#include <unistd.h>
217494Sjkh#include <sched.h>
227494Sjkh#include <pthread.h>
237494Sjkh
247494Sjkhstatic void
257494Sjkhmarker ()
267494Sjkh{}
277494Sjkh
287494Sjkh#define STACK_SIZE 0x1000
2965437Sphantom
30142665Sphantom/* These are used to signal that the threads have started correctly.  The
317494Sjkh   GLOBAL_THREAD_COUNT is set to the number of threads in main, then
327494Sjkh   decremented (under a lock) in each new thread.  */
337494Sjkhpthread_mutex_t global_lock = PTHREAD_MUTEX_INITIALIZER;
347494Sjkhint global_thread_count = 0;
3579754Sdd
3659460Sphantomstatic int
3759460Sphantomclone_fn (void *unused)
387494Sjkh{
3984306Sru  /* Signal that this thread has started correctly.  */
407494Sjkh  if (pthread_mutex_lock (&global_lock) != 0)
417494Sjkh    abort ();
427494Sjkh  global_thread_count--;
4379754Sdd  if (pthread_mutex_unlock (&global_lock) != 0)
447494Sjkh    abort ();
457494Sjkh
4679754Sdd  return 0;
477494Sjkh}
487494Sjkh
497494Sjkhint
507494Sjkhmain (void)
5179754Sdd{
5279754Sdd  int i, pid;
537494Sjkh  unsigned char *stack[6];
5479754Sdd
557494Sjkh  /* Due to bug gdb/19675 the cloned thread _might_ try to reenter main
5679754Sdd     (this depends on where the displaced instruction is placed for
5779754Sdd     execution).  However, if we do reenter main then lets ensure we fail
5865485Sache     hard rather then just silently executing the code below.  */
5965485Sache  static int started = 0;
6065485Sache  if (!started)
6165485Sache    started = 1;
627494Sjkh  else
6365485Sache    abort ();
6465485Sache
6565485Sache  for (i = 0; i < (sizeof (stack) / sizeof (stack[0])); i++)
6665485Sache    stack[i] = malloc (STACK_SIZE);
6765485Sache
6865485Sache  global_thread_count = (sizeof (stack) / sizeof (stack[0]));
6965485Sache
7065485Sache  for (i = 0; i < (sizeof (stack) / sizeof (stack[0])); i++)
7165485Sache    {
7265485Sache      pid = clone (clone_fn, stack[i] + STACK_SIZE, CLONE_FILES | CLONE_VM,
7365485Sache		   NULL);
7465485Sache    }
7565485Sache
7665485Sache  for (i = 0; i < (sizeof (stack) / sizeof (stack[0])); i++)
7765485Sache    free (stack[i]);
7865485Sache
7965485Sache  /* Set an alarm so we don't end up stuck waiting for threads that might
8065485Sache     never start correctly.  */
8165485Sache  alarm (120);
8265485Sache
8365485Sache  /* Now wait for all the threads to start up.  */
8465485Sache  while (global_thread_count != 0)
8565485Sache    {
8665485Sache      /* Force memory barrier so GLOBAL_THREAD_COUNT will be refetched.  */
8765485Sache      asm volatile ("" ::: "memory");
8865485Sache      sleep (1);
8979754Sdd    }
9065485Sache
917494Sjkh  /* Call marker, this is what GDB is waiting for.  */
9265485Sache  marker ();
9365485Sache}
9465485Sache