7 #if defined(VGO_freebsd)
8 # include <sys/fcntl.h>
10 /* This is really a test of semaphore handling
11 (sem_{init,destroy,post,wait}). Using semaphores a barrier
12 function is created. Helgrind-3.3 (p.k.a Thrcheck) does understand
13 the barrier semantics implied by the barrier, as pieced together
14 from happens-before relationships obtained from the component
15 semaphores. However, it does falsely report one race. Ah well.
16 Helgrind-3.4 is pure h-b and so reports no races (yay!). */
17 /* This code is derived from
18 gcc-4.3-20071012/libgomp/config/posix/bar.c, which is
20 Copyright (C) 2005 Free Software Foundation, Inc.
21 Contributed by Richard Henderson <rth@redhat.com>.
23 and available under version 2.1 or later of the GNU Lesser General
26 Relative to the libgomp sources, the gomp_barrier_t type here has
27 an extra semaphore field, xxx. This is not functionally useful,
28 but it is used to create enough extra inter-thread dependencies
29 that the barrier-like behaviour of gomp_barrier_t is evident to
30 Thrcheck. There is no other purpose for the .xxx field. */
31 static sem_t
* my_sem_init(char*, int, unsigned);
32 static int my_sem_destroy(sem_t
*);
33 static int my_sem_wait(sem_t
*); static int my_sem_post(sem_t
*);
36 pthread_mutex_t mutex1
;
37 pthread_mutex_t mutex2
;
48 gomp_barrier_init (gomp_barrier_t
*bar
, unsigned count
)
50 pthread_mutex_init (&bar
->mutex1
, NULL
);
51 pthread_mutex_init (&bar
->mutex2
, NULL
);
52 bar
->sem1
= my_sem_init ("sem1", 0, 0);
53 bar
->sem2
= my_sem_init ("sem2", 0, 0);
54 bar
->xxx
= my_sem_init ("xxx", 0, 0);
60 gomp_barrier_destroy (gomp_barrier_t
*bar
)
62 /* Before destroying, make sure all threads have left the barrier. */
63 pthread_mutex_lock (&bar
->mutex1
);
64 pthread_mutex_unlock (&bar
->mutex1
);
66 pthread_mutex_destroy (&bar
->mutex1
);
67 pthread_mutex_destroy (&bar
->mutex2
);
68 my_sem_destroy(bar
->sem1
);
69 my_sem_destroy(bar
->sem2
);
70 my_sem_destroy(bar
->xxx
);
74 gomp_barrier_reinit (gomp_barrier_t
*bar
, unsigned count
)
76 pthread_mutex_lock (&bar
->mutex1
);
78 pthread_mutex_unlock (&bar
->mutex1
);
82 gomp_barrier_wait (gomp_barrier_t
*bar
)
85 pthread_mutex_lock (&bar
->mutex1
);
89 if (bar
->arrived
== bar
->total
)
96 for (i
= 0; i
< n
; i
++)
97 my_sem_wait(bar
->xxx
); // acquire an obvious dependency from
98 // all other threads arriving at the barrier
100 // 1 up n times, 2 down once
101 // now let all the other threads past the barrier, giving them
102 // an obvious dependency with this thread.
104 my_sem_post (bar
->sem1
); // 1 up
106 // and wait till the last thread has left
107 my_sem_wait (bar
->sem2
); // 2 down
109 pthread_mutex_unlock (&bar
->mutex1
);
110 /* "Resultats professionnels!" First we made this thread have an
111 obvious (Thrcheck-visible) dependency on all other threads
112 calling gomp_barrier_wait. Then, we released them all again,
113 so they all have a (visible) dependency on this thread.
114 Transitively, the result is that all threads leaving the
115 barrier have a a Thrcheck-visible dependency on all threads
116 arriving at the barrier. As required. */
120 pthread_mutex_unlock (&bar
->mutex1
);
121 my_sem_post(bar
->xxx
);
122 // first N-1 threads wind up waiting here
123 my_sem_wait (bar
->sem1
); // 1 down
125 pthread_mutex_lock (&bar
->mutex2
);
126 n
= --bar
->arrived
; /* XXX see below */
127 pthread_mutex_unlock (&bar
->mutex2
);
130 my_sem_post (bar
->sem2
); // 2 up
135 /* re XXX, thrcheck reports a race at this point. It doesn't
136 understand that bar->arrived is protected by mutex1 whilst threads
137 are arriving at the barrier and by mutex2 whilst they are leaving,
138 but not consistently by either of them. Oh well. */
140 static gomp_barrier_t bar
;
142 /* What's with the volatile here? It stops gcc compiling
143 "if (myid == 4) { unprotected = 99; }" and
144 "if (myid == 3) { unprotected = 88; }" into a conditional
145 load followed by a store. The cmov/store sequence reads and
146 writes memory in all threads and cause Thrcheck to (correctly)
147 report a race, the underlying cause of which is that gcc is
148 generating non threadsafe code.
150 (The lack of) thread safe code generation by gcc is currently a
151 hot topic. See the following discussions:
152 http://gcc.gnu.org/ml/gcc/2007-10/msg00266.html
153 http://lkml.org/lkml/2007/10/24/673
154 and this is interesting background:
155 www.hpl.hp.com/techreports/2004/HPL-2004-209.pdf
157 static volatile long unprotected
= 0;
159 void* child ( void* argV
)
161 long myid
= (long)argV
;
162 // assert(myid >= 2 && myid <= 5);
164 /* First, we all wait to get to this point. */
165 gomp_barrier_wait( &bar
);
167 /* Now, thread #4 writes to 'unprotected' and so becomes its
173 /* Now we all wait again. */
174 gomp_barrier_wait( &bar
);
176 /* This time, thread #3 writes to 'unprotected'. If all goes well,
177 Thrcheck sees the dependency through the barrier back to thread
178 #4 before it, and so thread #3 becomes the exclusive owner of
184 /* And just to be on the safe side ... */
185 gomp_barrier_wait( &bar
);
190 int main (int argc
, char *argv
[])
194 fprintf(stderr
, "starting\n");
196 gomp_barrier_init( &bar
, 4 );
198 for (i
= 0; i
< 4; i
++) {
199 res
= pthread_create( &thr
[i
], NULL
, child
, (void*)(i
+2) );
203 for (i
= 0; i
< 4; i
++) {
204 res
= pthread_join( thr
[i
], NULL
);
208 gomp_barrier_destroy( &bar
);
210 /* And finally here, the root thread can get exclusive ownership
211 back from thread #4, because #4 has exited by this point and so
212 we have a dependency edge back to the write it did. */
213 fprintf(stderr
, "done, result is %ld, should be 88\n", unprotected
);
224 static sem_t
* my_sem_init (char* identity
, int pshared
, unsigned count
)
228 #if defined(VGO_linux) || defined(VGO_solaris) || defined(VGO_freebsd)
229 s
= malloc(sizeof(*s
));
231 if (sem_init(s
, pshared
, count
) < 0) {
237 #elif defined(VGO_darwin)
239 sprintf(name
, "anonsem_%s_pid%d", identity
, (int)getpid());
240 name
[ sizeof(name
)-1 ] = 0;
241 if (0) printf("name = %s\n", name
);
242 s
= sem_open(name
, O_CREAT
| O_EXCL
, 0600, count
);
243 if (s
== SEM_FAILED
) {
248 # error "Unsupported OS"
254 static int my_sem_destroy ( sem_t
* s
)
256 return sem_destroy(s
);
259 static int my_sem_wait(sem_t
* s
)
264 static int my_sem_post(sem_t
* s
)