7 /* This is really a test of semaphore handling
8 (sem_{init,destroy,post,wait}). Using semaphores a barrier
9 function is created. Helgrind-3.3 (p.k.a Thrcheck) does understand
10 the barrier semantics implied by the barrier, as pieced together
11 from happens-before relationships obtained from the component
12 semaphores. However, it does falsely report one race. Ah well.
13 Helgrind-3.4 is pure h-b and so reports no races (yay!). */
14 /* This code is derived from
15 gcc-4.3-20071012/libgomp/config/posix/bar.c, which is
17 Copyright (C) 2005 Free Software Foundation, Inc.
18 Contributed by Richard Henderson <rth@redhat.com>.
20 and available under version 2.1 or later of the GNU Lesser General
23 Relative to the libgomp sources, the gomp_barrier_t type here has
24 an extra semaphore field, xxx. This is not functionally useful,
25 but it is used to create enough extra inter-thread dependencies
26 that the barrier-like behaviour of gomp_barrier_t is evident to
27 Thrcheck. There is no other purpose for the .xxx field. */
28 static sem_t
* my_sem_init(char*, int, unsigned);
29 static int my_sem_destroy(sem_t
*);
30 static int my_sem_wait(sem_t
*); static int my_sem_post(sem_t
*);
33 pthread_mutex_t mutex1
;
34 pthread_mutex_t mutex2
;
45 gomp_barrier_init (gomp_barrier_t
*bar
, unsigned count
)
47 pthread_mutex_init (&bar
->mutex1
, NULL
);
48 pthread_mutex_init (&bar
->mutex2
, NULL
);
49 bar
->sem1
= my_sem_init ("sem1", 0, 0);
50 bar
->sem2
= my_sem_init ("sem2", 0, 0);
51 bar
->xxx
= my_sem_init ("xxx", 0, 0);
57 gomp_barrier_destroy (gomp_barrier_t
*bar
)
59 /* Before destroying, make sure all threads have left the barrier. */
60 pthread_mutex_lock (&bar
->mutex1
);
61 pthread_mutex_unlock (&bar
->mutex1
);
63 pthread_mutex_destroy (&bar
->mutex1
);
64 pthread_mutex_destroy (&bar
->mutex2
);
65 my_sem_destroy(bar
->sem1
);
66 my_sem_destroy(bar
->sem2
);
67 my_sem_destroy(bar
->xxx
);
71 gomp_barrier_reinit (gomp_barrier_t
*bar
, unsigned count
)
73 pthread_mutex_lock (&bar
->mutex1
);
75 pthread_mutex_unlock (&bar
->mutex1
);
79 gomp_barrier_wait (gomp_barrier_t
*bar
)
82 pthread_mutex_lock (&bar
->mutex1
);
86 if (bar
->arrived
== bar
->total
)
93 for (i
= 0; i
< n
; i
++)
94 my_sem_wait(bar
->xxx
); // acquire an obvious dependency from
95 // all other threads arriving at the barrier
97 // 1 up n times, 2 down once
98 // now let all the other threads past the barrier, giving them
99 // an obvious dependency with this thread.
101 my_sem_post (bar
->sem1
); // 1 up
103 // and wait till the last thread has left
104 my_sem_wait (bar
->sem2
); // 2 down
106 pthread_mutex_unlock (&bar
->mutex1
);
107 /* "Resultats professionnels!" First we made this thread have an
108 obvious (Thrcheck-visible) dependency on all other threads
109 calling gomp_barrier_wait. Then, we released them all again,
110 so they all have a (visible) dependency on this thread.
111 Transitively, the result is that all threads leaving the
112 barrier have a a Thrcheck-visible dependency on all threads
113 arriving at the barrier. As required. */
117 pthread_mutex_unlock (&bar
->mutex1
);
118 my_sem_post(bar
->xxx
);
119 // first N-1 threads wind up waiting here
120 my_sem_wait (bar
->sem1
); // 1 down
122 pthread_mutex_lock (&bar
->mutex2
);
123 n
= --bar
->arrived
; /* XXX see below */
124 pthread_mutex_unlock (&bar
->mutex2
);
127 my_sem_post (bar
->sem2
); // 2 up
132 /* re XXX, thrcheck reports a race at this point. It doesn't
133 understand that bar->arrived is protected by mutex1 whilst threads
134 are arriving at the barrier and by mutex2 whilst they are leaving,
135 but not consistently by either of them. Oh well. */
137 static gomp_barrier_t bar
;
139 /* What's with the volatile here? It stops gcc compiling
140 "if (myid == 4) { unprotected = 99; }" and
141 "if (myid == 3) { unprotected = 88; }" into a conditional
142 load followed by a store. The cmov/store sequence reads and
143 writes memory in all threads and cause Thrcheck to (correctly)
144 report a race, the underlying cause of which is that gcc is
145 generating non threadsafe code.
147 (The lack of) thread safe code generation by gcc is currently a
148 hot topic. See the following discussions:
149 http://gcc.gnu.org/ml/gcc/2007-10/msg00266.html
150 http://lkml.org/lkml/2007/10/24/673
151 and this is interesting background:
152 www.hpl.hp.com/techreports/2004/HPL-2004-209.pdf
154 static volatile long unprotected
= 0;
156 void* child ( void* argV
)
158 long myid
= (long)argV
;
159 // assert(myid >= 2 && myid <= 5);
161 /* First, we all wait to get to this point. */
162 gomp_barrier_wait( &bar
);
164 /* Now, thread #4 writes to 'unprotected' and so becomes its
170 /* Now we all wait again. */
171 gomp_barrier_wait( &bar
);
173 /* This time, thread #3 writes to 'unprotected'. If all goes well,
174 Thrcheck sees the dependency through the barrier back to thread
175 #4 before it, and so thread #3 becomes the exclusive owner of
181 /* And just to be on the safe side ... */
182 gomp_barrier_wait( &bar
);
187 int main (int argc
, char *argv
[])
191 fprintf(stderr
, "starting\n");
193 gomp_barrier_init( &bar
, 4 );
195 for (i
= 0; i
< 4; i
++) {
196 res
= pthread_create( &thr
[i
], NULL
, child
, (void*)(i
+2) );
200 for (i
= 0; i
< 4; i
++) {
201 res
= pthread_join( thr
[i
], NULL
);
205 gomp_barrier_destroy( &bar
);
207 /* And finally here, the root thread can get exclusive ownership
208 back from thread #4, because #4 has exited by this point and so
209 we have a dependency edge back to the write it did. */
210 fprintf(stderr
, "done, result is %ld, should be 88\n", unprotected
);
221 static sem_t
* my_sem_init (char* identity
, int pshared
, unsigned count
)
225 #if defined(VGO_linux) || defined(VGO_solaris)
226 s
= malloc(sizeof(*s
));
228 if (sem_init(s
, pshared
, count
) < 0) {
234 #elif defined(VGO_darwin)
236 sprintf(name
, "anonsem_%s_pid%d", identity
, (int)getpid());
237 name
[ sizeof(name
)-1 ] = 0;
238 if (0) printf("name = %s\n", name
);
239 s
= sem_open(name
, O_CREAT
| O_EXCL
, 0600, count
);
240 if (s
== SEM_FAILED
) {
245 # error "Unsupported OS"
251 static int my_sem_destroy ( sem_t
* s
)
253 return sem_destroy(s
);
256 static int my_sem_wait(sem_t
* s
)
261 static int my_sem_post(sem_t
* s
)