summaryrefslogtreecommitdiff
path: root/tests/test-thread_local.c
blob: 67f2d451abe0b795d82782ef58e3aee4853f2f11 (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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
/* Test of thread-local storage in multithreaded situations.
   Copyright (C) 2005, 2008-2020 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 <https://www.gnu.org/licenses/>.  */

/* Written by Bruno Haible <bruno@clisp.org>, 2005.  */

#include <config.h>

#include <threads.h>

#ifdef thread_local

/* Whether to help the scheduler through explicit thrd_yield().
   Uncomment this to see if the operating system has a fair scheduler.  */
#define EXPLICIT_YIELD 1

/* Whether to print debugging messages.  */
#define ENABLE_DEBUGGING 0

/* Number of simultaneous threads.  */
#define THREAD_COUNT 16

/* Number of operations performed in each thread.  */
#define REPEAT_COUNT 50000

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#if HAVE_DECL_ALARM
# include <signal.h>
# include <unistd.h>
#endif

#include "macros.h"

#if ENABLE_DEBUGGING
# define dbgprintf printf
#else
# define dbgprintf if (0) printf
#endif

#if EXPLICIT_YIELD
# define yield() thrd_yield ()
#else
# define yield()
#endif

/* Returns a reference to the current thread as a pointer, for debugging.  */
#if defined __MVS__
  /* On IBM z/OS, pthread_t is a struct with an 8-byte '__' field.
     The first three bytes of this field appear to uniquely identify a
     pthread_t, though not necessarily representing a pointer.  */
# define thrd_current_pointer() (*((void **) thrd_current ().__))
#elif defined __sun
  /* On Solaris, thrd_t is merely an 'unsigned int'.  */
# define thrd_current_pointer() ((void *) (uintptr_t) thrd_current ())
#else
# define thrd_current_pointer() ((void *) thrd_current ())
#endif

static void
perhaps_yield (void)
{
  /* Call yield () only with a certain probability, otherwise the
     sequence of thread activations may be too predictable.  */
  if ((((unsigned int) rand () >> 3) % 4) == 0)
    yield ();
}


/* ----------------------- Test thread-local storage ----------------------- */

#define KEYS_COUNT 4
static unsigned int thread_local value0;
static unsigned int thread_local value1;
static unsigned int thread_local value2;
static unsigned int thread_local value3;

static int
worker_thread (void *arg)
{
  unsigned int id = (unsigned int) (uintptr_t) arg;
  int i, j, repeat;
  unsigned int *values[KEYS_COUNT] = { &value0, &value1, &value2, &value3 };

  dbgprintf ("Worker %p started\n", thrd_current_pointer ());

  /* Initialize the per-thread storage.  */
  dbgprintf ("Worker %p before first assignment\n", thrd_current_pointer ());
  for (i = 0; i < KEYS_COUNT; i++)
    {
      *values[i] = (((unsigned int) rand () >> 3) % 1000000) * THREAD_COUNT + id;
      /* Hopefully no arithmetic overflow.  */
      if ((*values[i] % THREAD_COUNT) != id)
        abort ();
    }
  dbgprintf ("Worker %p after  first assignment\n", thrd_current_pointer ());
  perhaps_yield ();

  /* Shuffle around the pointers.  */
  for (repeat = REPEAT_COUNT; repeat > 0; repeat--)
    {
      dbgprintf ("Worker %p doing value swapping\n", thrd_current_pointer ());
      i = ((unsigned int) rand () >> 3) % KEYS_COUNT;
      j = ((unsigned int) rand () >> 3) % KEYS_COUNT;
      if (i != j)
        {
          unsigned int vi = *values[i];
          unsigned int vj = *values[j];

          *values[i] = vj;
          *values[j] = vi;
        }
      perhaps_yield ();
    }

  /* Verify that all the values are from this thread.  */
  dbgprintf ("Worker %p before final verify\n", thrd_current_pointer ());
  for (i = 0; i < KEYS_COUNT; i++)
    if ((*values[i] % THREAD_COUNT) != id)
      abort ();
  dbgprintf ("Worker %p after  final verify\n", thrd_current_pointer ());
  perhaps_yield ();

  dbgprintf ("Worker %p dying.\n", thrd_current_pointer ());
  return 0;
}

static void
test_thread_local (void)
{
  int pass, i;

  for (pass = 0; pass < 2; pass++)
    {
      thrd_t threads[THREAD_COUNT];

      /* Spawn the threads.  */
      for (i = 0; i < THREAD_COUNT; i++)
        ASSERT (thrd_create (&threads[i], worker_thread, (void *) (uintptr_t) i)
                == thrd_success);

      /* Wait for the threads to terminate.  */
      for (i = 0; i < THREAD_COUNT; i++)
        ASSERT (thrd_join (threads[i], NULL) == thrd_success);
    }
}


/* -------------------------------------------------------------------------- */

int
main ()
{
#if HAVE_DECL_ALARM
  /* Declare failure if test takes too long, by using default abort
     caused by SIGALRM.  */
  int alarm_value = 600;
  signal (SIGALRM, SIG_DFL);
  alarm (alarm_value);
#endif

  printf ("Starting test_thread_local ..."); fflush (stdout);
  test_thread_local ();
  printf (" OK\n"); fflush (stdout);

  return 0;
}

#else

/* No thread-local storage support available in the compiler and linker.  */

#include <stdio.h>

int
main ()
{
  fputs ("Skipping test: thread_local not supported\n", stderr);
  return 77;
}

#endif