aboutsummaryrefslogtreecommitdiffstats
path: root/gcc-4.9/gcc/testsuite/gcc.dg/di-sync-multithread.c
blob: 493f1e272016f83f0f2cef71202445b8731f532b (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
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
/* { dg-do run } */
/* { dg-require-effective-target sync_long_long_runtime } */
/* { dg-require-effective-target pthread_h } */
/* { dg-require-effective-target pthread } */
/* { dg-options "-pthread -std=gnu99" } */
/* { dg-additional-options "-march=pentium" { target { { i?86-*-* x86_64-*-* } && ia32 } } } */

/* test of long long atomic ops performed in parallel in 3 pthreads
   david.gilbert@linaro.org */

#include <pthread.h>
#include <unistd.h>
#ifdef _WIN32
#include <windows.h>
#endif

/*#define DEBUGIT 1 */

#ifdef DEBUGIT
#include <stdio.h>

#define DOABORT(x,...) {\
	 fprintf (stderr, x, __VA_ARGS__); fflush (stderr); abort ();\
	 }

#else

#define DOABORT(x,...) abort ();

#endif

/* Passed to each thread to describe which bits it is going to work on.  */
struct threadwork {
  unsigned long long count; /* incremented each time the worker loops.  */
  unsigned int thread;    /* ID */
  unsigned int addlsb;    /* 8 bit */
  unsigned int logic1lsb; /* 5 bit */
  unsigned int logic2lsb; /* 8 bit */
};

/* The shared word where all the atomic work is done.  */
static volatile long long workspace;

/* A shared word to tell the workers to quit when non-0.  */
static long long doquit;

extern void abort (void);

/* Note this test doesn't test the return values much.  */
void*
worker (void* data)
{
  struct threadwork *tw = (struct threadwork*)data;
  long long add1bit = 1ll << tw->addlsb;
  long long logic1bit = 1ll << tw->logic1lsb;
  long long logic2bit = 1ll << tw->logic2lsb;

  /* Clear the bits we use.  */
  __sync_and_and_fetch (&workspace, ~(0xffll * add1bit));
  __sync_fetch_and_and (&workspace, ~(0x1fll * logic1bit));
  __sync_fetch_and_and (&workspace, ~(0xffll * logic2bit));

  do
    {
      long long tmp1, tmp2, tmp3;
      /* OK, lets try and do some stuff to the workspace - by the end
         of the main loop our area should be the same as it is now - i.e. 0.  */

      /* Push the arithmetic section up to 128 - one of the threads will
         case this to carry across the 32bit boundary.  */
      for (tmp2 = 0; tmp2 < 64; tmp2++)
	{
	  /* Add 2 using the two different adds.  */
	  tmp1 = __sync_add_and_fetch (&workspace, add1bit);
	  tmp3 = __sync_fetch_and_add (&workspace, add1bit);

	  /* The value should be the intermediate add value in both cases.  */
	  if ((tmp1 & (add1bit * 0xff)) != (tmp3 & (add1bit * 0xff)))
	    DOABORT ("Mismatch of add intermediates on thread %d "
			"workspace=0x%llx tmp1=0x%llx "
			"tmp2=0x%llx tmp3=0x%llx\n",
			 tw->thread, workspace, tmp1, tmp2, tmp3);
	}

      /* Set the logic bits.  */
      tmp2=__sync_or_and_fetch (&workspace,
			  0x1fll * logic1bit | 0xffll * logic2bit);

      /* Check the logic bits are set and the arithmetic value is correct.  */
      if ((tmp2 & (0x1fll * logic1bit | 0xffll * logic2bit
			| 0xffll * add1bit))
	  != (0x1fll * logic1bit | 0xffll * logic2bit | 0x80ll * add1bit))
	DOABORT ("Midloop check failed on thread %d "
			"workspace=0x%llx tmp2=0x%llx "
			"masktmp2=0x%llx expected=0x%llx\n",
		tw->thread, workspace, tmp2,
		tmp2 & (0x1fll * logic1bit | 0xffll * logic2bit |
			 0xffll * add1bit),
		(0x1fll * logic1bit | 0xffll * logic2bit | 0x80ll * add1bit));

      /* Pull the arithmetic set back down to 0 - again this should cause a
	 carry across the 32bit boundary in one thread.  */

      for (tmp2 = 0; tmp2 < 64; tmp2++)
	{
	  /* Subtract 2 using the two different subs.  */
	  tmp1=__sync_sub_and_fetch (&workspace, add1bit);
	  tmp3=__sync_fetch_and_sub (&workspace, add1bit);

	  /* The value should be the intermediate sub value in both cases.  */
	  if ((tmp1 & (add1bit * 0xff)) != (tmp3 & (add1bit * 0xff)))
	    DOABORT ("Mismatch of sub intermediates on thread %d "
			"workspace=0x%llx tmp1=0x%llx "
			"tmp2=0x%llx tmp3=0x%llx\n",
			tw->thread, workspace, tmp1, tmp2, tmp3);
	}


      /* Clear the logic bits.  */
      __sync_fetch_and_xor (&workspace, 0x1fll * logic1bit);
      tmp3=__sync_and_and_fetch (&workspace, ~(0xffll * logic2bit));

      /* The logic bits and the arithmetic bits should be zero again.  */
      if (tmp3 & (0x1fll * logic1bit | 0xffll * logic2bit | 0xffll * add1bit))
	DOABORT ("End of worker loop; bits none 0 on thread %d "
			"workspace=0x%llx tmp3=0x%llx "
			"mask=0x%llx maskedtmp3=0x%llx\n",
		tw->thread, workspace, tmp3, (0x1fll * logic1bit |
			0xffll * logic2bit | 0xffll * add1bit),
		tmp3 & (0x1fll * logic1bit | 0xffll * logic2bit | 0xffll * add1bit));

      __sync_add_and_fetch (&tw->count, 1);
    }
  while (!__sync_bool_compare_and_swap (&doquit, 1, 1));

  pthread_exit (0);
}

int
main ()
{
  /* We have 3 threads doing three sets of operations, an 8 bit
     arithmetic field, a 5 bit logic field and an 8 bit logic
     field (just to pack them all in).

  6      5       4       4       3       2       1
  3      6       8       0       2       4       6       8       0
  |...,...|...,...|...,...|...,...|...,...|...,...|...,...|...,...
  - T0   --  T1  -- T2   --T2 --  T0  -*- T2-- T1-- T1   -***- T0-
   logic2  logic2  arith   log2  arith  log1 log1  arith     log1

  */
  unsigned int t;
  long long tmp;
  int err;

  struct threadwork tw[3]={
    { 0ll, 0, 27, 0, 56 },
    { 0ll, 1,  8,16, 48 },
    { 0ll, 2, 40,21, 35 }
  };

  pthread_t threads[3];

  __sync_lock_release (&doquit);

  /* Get the work space into a known value - All 1's.  */
  __sync_lock_release (&workspace); /* Now all 0.  */
  tmp = __sync_val_compare_and_swap (&workspace, 0, -1ll);
  if (tmp!=0)
    DOABORT ("Initial __sync_val_compare_and_swap wasn't 0 workspace=0x%llx "
		"tmp=0x%llx\n", workspace,tmp);

  for (t = 0; t < 3; t++)
  {
    err=pthread_create (&threads[t], NULL , worker, &tw[t]);
    if (err) DOABORT ("pthread_create failed on thread %d with error %d\n",
	t, err);
  };

#ifdef _WIN32
  Sleep (5000);
#else
  sleep (5);
#endif

  /* Stop please.  */
  __sync_lock_test_and_set (&doquit, 1ll);

  for (t = 0; t < 3; t++)
    {
      err=pthread_join (threads[t], NULL);
      if (err)
	DOABORT ("pthread_join failed on thread %d with error %d\n", t, err);
    };

  __sync_synchronize ();

  /* OK, so all the workers have finished -
     the workers should have zero'd their workspace, the unused areas
     should still be 1.  */
  if (!__sync_bool_compare_and_swap (&workspace, 0x040000e0ll, 0))
    DOABORT ("End of run workspace mismatch, got %llx\n", workspace);

  /* All the workers should have done some work.  */
  for (t = 0; t < 3; t++)
    {
      if (tw[t].count == 0) DOABORT ("Worker %d gave 0 count\n", t);
    };

  return 0;
}