aboutsummaryrefslogtreecommitdiff
path: root/gdb/testsuite/gdb.threads/access-mem-running-thread-exit.c
blob: e22bf12df752961e5a8758f226a38ed61919f5fb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/* This testcase is part of GDB, the GNU debugger.

   Copyright 2021-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 <http://www.gnu.org/licenses/>.  */

#define _GNU_SOURCE
#include <assert.h>
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

#define THREADS 20

static volatile unsigned int global_var = 123;

/* Wrapper around pthread_create.   */

static void
create_thread (pthread_t *child,
	       void *(*start_routine) (void *), void *arg)
{
  int rc;

  while ((rc = pthread_create (child, NULL, start_routine, arg)) != 0)
    {
      fprintf (stderr, "unexpected error from pthread_create: %s (%d)\n",
	       strerror (rc), rc);
      sleep (1);
    }
}

/* Data passed to threads on creation.  This is allocated on the heap
   and ownership transferred from parent to child.  */

struct thread_arg
{
  /* The thread's parent.  */
  pthread_t parent;

  /* Whether to call pthread_join on the parent.  */
  int join_parent;
};

/* Entry point for threads.  */

static void *
thread_fn (void *arg)
{
  struct thread_arg *p = arg;

  /* Passing no argument makes the thread exit immediately.  */
  if (p == NULL)
    return NULL;

  if (p->join_parent)
    assert (pthread_join (p->parent, NULL) == 0);

  /* Spawn a number of threads that exit immediately, and then join
     them.  The idea is to maximize the time window when we mostly
     have threads exiting.  */
  {
    pthread_t child[THREADS];
    int i;

    /* Passing no argument makes the thread exit immediately.  */
    for (i = 0; i < THREADS; i++)
      create_thread (&child[i], thread_fn, NULL);

    for (i = 0; i < THREADS; i++)
      pthread_join (child[i], NULL);
  }

  /* Spawn a new thread that joins us, and exit.  The idea here is to
     not have any thread that stays around forever.  */
  {
    pthread_t child;

    p->parent = pthread_self ();
    p->join_parent = 1;
    create_thread (&child, thread_fn, p);
  }

  return NULL;
}

static void
setup_done (void)
{
}

int
main (void)
{
  int i;

  global_var++;

  setup_done ();

  for (i = 0; i < 4; i++)
    {
      struct thread_arg *p;
      pthread_t child;

      p = malloc (sizeof *p);
      p->parent = pthread_self ();
      /* Only join the parent once.  */
      if (i == 0)
	p->join_parent = 1;
      else
	p->join_parent = 0;
      create_thread (&child, thread_fn, p);
    }

  /* Exit the leader to make sure that we can access memory with the
     leader gone.  */
  pthread_exit (NULL);
}