Merge branch 'release-4-6'
[alexxy/gromacs.git] / src / gromacs / gmxlib / thread_mpi / pthreads.c
1 /*
2    This source code file is part of thread_mpi.
3    Written by Sander Pronk, Erik Lindahl, and possibly others.
4
5    Copyright (c) 2009, Sander Pronk, Erik Lindahl.
6    All rights reserved.
7
8    Redistribution and use in source and binary forms, with or without
9    modification, are permitted provided that the following conditions are met:
10    1) Redistributions of source code must retain the above copyright
11    notice, this list of conditions and the following disclaimer.
12    2) Redistributions in binary form must reproduce the above copyright
13    notice, this list of conditions and the following disclaimer in the
14    documentation and/or other materials provided with the distribution.
15    3) Neither the name of the copyright holders nor the
16    names of its contributors may be used to endorse or promote products
17    derived from this software without specific prior written permission.
18
19    THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
20    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22    DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
23    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30    If you want to redistribute modifications, please consider that
31    scientific software is very special. Version control is crucial -
32    bugs must be traceable. We will be happy to consider code for
33    inclusion in the official distribution, but derived work should not
34    be called official thread_mpi. Details are found in the README & COPYING
35    files.
36  */
37
38
39
40 /* Include the defines that determine which thread library to use.
41  * We do not use HAVE_PTHREAD_H directly, since we might want to
42  * turn off thread support explicity (e.g. for debugging).
43  */
44 #ifdef HAVE_TMPI_CONFIG_H
45 #include "tmpi_config.h"
46 #endif
47
48 #ifdef HAVE_CONFIG_H
49 #include "config.h"
50 #endif
51
52
53 #ifdef THREAD_PTHREADS
54
55 #ifdef HAVE_PTHREAD_SETAFFINITY
56 #define _GNU_SOURCE
57 #endif
58
59 /* pthread.h must be the first header, apart from the defines in config.h */
60 #include <pthread.h>
61
62
63 #ifdef HAVE_UNISTD_H
64 #include <unistd.h>
65 #endif
66
67 #include <errno.h>
68 #include <stdlib.h>
69 #include <stdio.h>
70 #include <stdarg.h>
71
72
73 #include "thread_mpi/atomic.h"
74 #include "thread_mpi/threads.h"
75 #include "impl.h"
76
77
78 #include "pthreads.h"
79
80
81 /* mutex for initializing mutexes */
82 static pthread_mutex_t mutex_init = PTHREAD_MUTEX_INITIALIZER;
83 /* mutex for initializing barriers */
84 static pthread_mutex_t once_init = PTHREAD_MUTEX_INITIALIZER;
85 /* mutex for initializing thread_conds */
86 static pthread_mutex_t cond_init = PTHREAD_MUTEX_INITIALIZER;
87 /* mutex for initializing barriers */
88 static pthread_mutex_t barrier_init = PTHREAD_MUTEX_INITIALIZER;
89
90 /* mutex for managing  thread IDs */
91 static pthread_mutex_t thread_id_mutex = PTHREAD_MUTEX_INITIALIZER;
92 static pthread_key_t   thread_id_key;
93 static int             thread_id_key_initialized = 0;
94
95
96
97
98 enum tMPI_Thread_support tMPI_Thread_support(void)
99 {
100     return TMPI_THREAD_SUPPORT_YES;
101 }
102
103
104 int tMPI_Thread_get_hw_number(void)
105 {
106     int ret = 0;
107 #ifdef HAVE_SYSCONF
108 #if defined(_SC_NPROCESSORS_ONLN)
109     ret = sysconf(_SC_NPROCESSORS_ONLN);
110 #elif defined(_SC_NPROC_ONLN)
111     ret = sysconf(_SC_NPROC_ONLN);
112 #elif defined(_SC_NPROCESSORS_CONF)
113     ret = sysconf(_SC_NPROCESSORS_CONF);
114 #elif defined(_SC_NPROC_CONF)
115     ret = sysconf(_SC_NPROC_CONF);
116 #endif
117 #endif
118
119     return ret;
120 }
121
122 /* destructor for thread ids */
123 static void tMPI_Destroy_thread_id(void* thread_id)
124 {
125     struct tMPI_Thread *thread = (struct tMPI_Thread*)thread_id;
126     if (!thread->started_by_tmpi)
127     {
128         /* if the thread is started by tMPI, it must be freed in the join()
129            call. */
130         free(thread_id);
131     }
132 }
133
134 /* initialize the thread id vars if not already initialized */
135 static int tMPI_Init_thread_ids(void)
136 {
137     int ret;
138     ret = pthread_mutex_lock( &thread_id_mutex );
139     if (ret != 0)
140     {
141         return ret;
142     }
143
144     if (!thread_id_key_initialized)
145     {
146         /* initialize and set the thread id thread-specific variable */
147         struct tMPI_Thread *main_thread;
148
149         thread_id_key_initialized = 1;
150         ret = pthread_key_create(&thread_id_key, tMPI_Destroy_thread_id);
151         if (ret != 0)
152         {
153             goto err;
154         }
155         main_thread = (struct tMPI_Thread*)malloc(sizeof(struct tMPI_Thread)*1);
156         if (main_thread == NULL)
157         {
158             ret = ENOMEM;
159             goto err;
160         }
161         main_thread->th              = pthread_self();
162         main_thread->started_by_tmpi = 0;
163         ret = pthread_setspecific(thread_id_key, main_thread);
164         if (ret != 0)
165         {
166             goto err;
167         }
168     }
169
170     ret = pthread_mutex_unlock( &thread_id_mutex );
171     return ret;
172 err:
173     pthread_mutex_unlock( &thread_id_mutex );
174     return ret;
175 }
176
177 /* structure to hold the arguments for the thread_starter function */
178 struct tMPI_Thread_starter
179 {
180     struct tMPI_Thread *thread;
181     void               *(*start_routine)(void*);
182     void               *arg;
183 };
184
185 /* the thread_starter function that sets the thread id */
186 static void *tMPI_Thread_starter(void *arg)
187 {
188     struct tMPI_Thread_starter *starter = (struct tMPI_Thread_starter *)arg;
189     void *(*start_routine)(void*);
190     void *parg;
191     int   ret;
192
193     ret = pthread_setspecific(thread_id_key, starter->thread);
194     if (ret != 0)
195     {
196         return NULL;
197     }
198     start_routine = starter->start_routine;
199     parg          = starter->arg;
200
201     free(starter);
202     return (*start_routine)(parg);
203 }
204
205 int tMPI_Thread_create(tMPI_Thread_t *thread, void *(*start_routine)(void *),
206                        void *arg)
207 {
208     int ret;
209     struct tMPI_Thread_starter *starter;
210
211     if (thread == NULL)
212     {
213         return EINVAL;
214     }
215     tMPI_Init_thread_ids();
216
217     *thread = (struct tMPI_Thread*)malloc(sizeof(struct tMPI_Thread)*1);
218     if (*thread == NULL)
219     {
220         return ENOMEM;
221     }
222     (*thread)->started_by_tmpi = 1;
223     starter                    = (struct tMPI_Thread_starter*)
224         malloc(sizeof(struct tMPI_Thread_starter)*1);
225     if (starter == NULL)
226     {
227         return ENOMEM;
228     }
229     /* fill the starter structure */
230     starter->thread        = *thread;
231     starter->start_routine = start_routine;
232     starter->arg           = arg;
233
234     ret = pthread_create(&((*thread)->th), NULL, tMPI_Thread_starter,
235                          (void*)starter);
236
237     return ret;
238 }
239
240
241
242 int tMPI_Thread_join(tMPI_Thread_t thread, void **value_ptr)
243 {
244     int       ret;
245     pthread_t th = thread->th;
246
247     ret = pthread_join( th, value_ptr );
248     if (ret != 0)
249     {
250         return ret;
251     }
252     free(thread);
253     return 0;
254 }
255
256
257 tMPI_Thread_t tMPI_Thread_self(void)
258 {
259     tMPI_Thread_t th;
260     int           ret;
261
262     /* make sure the key var is set */
263     ret = tMPI_Init_thread_ids();
264     if (ret != 0)
265     {
266         return NULL;
267     }
268
269     th = pthread_getspecific(thread_id_key);
270
271     /* check if it is already in our list */
272     if (th == NULL)
273     {
274         /* if not, create an ID, set it and return it */
275         th = (struct tMPI_Thread*)malloc(sizeof(struct tMPI_Thread)*1);
276         if (th == NULL)
277         {
278             return NULL;
279         }
280         th->th              = pthread_self();
281         th->started_by_tmpi = 0;
282         /* we ignore errors here because they're not important -
283            the next iteration will do the same thing. */
284         pthread_setspecific(thread_id_key, th);
285     }
286     return th;
287 }
288
289 int tMPI_Thread_equal(tMPI_Thread_t t1, tMPI_Thread_t t2)
290 {
291     return pthread_equal(t1->th, t2->th);
292 }
293
294
295 enum tMPI_Thread_setaffinity_support tMPI_Thread_setaffinity_support(void)
296 {
297 #ifdef HAVE_PTHREAD_SETAFFINITY
298     cpu_set_t set;
299     int       ret;
300
301     /* run getaffinity to check whether we get back ENOSYS */
302     ret = pthread_getaffinity_np(pthread_self(), sizeof(set), &set);
303     if (ret == 0)
304     {
305         return TMPI_SETAFFINITY_SUPPORT_YES;
306     }
307     else
308     {
309         return TMPI_SETAFFINITY_SUPPORT_NO;
310     }
311 #else
312     return TMPI_SETAFFINITY_SUPPORT_NO;
313 #endif
314 }
315
316
317 /* set thread's own affinity to a processor number n */
318 int tMPI_Thread_setaffinity_single(tMPI_Thread_t thread, unsigned int nr)
319 {
320 #ifdef HAVE_PTHREAD_SETAFFINITY
321     int       nt = tMPI_Thread_get_hw_number();
322     cpu_set_t set;
323
324     if (nt < nr)
325     {
326         return TMPI_ERR_PROCNR;
327     }
328
329     CPU_ZERO(&set);
330     CPU_SET(nr, &set);
331     return pthread_setaffinity_np(thread->th, sizeof(set), &set);
332 #endif
333     return 0;
334 }
335
336
337
338
339 int tMPI_Thread_mutex_init(tMPI_Thread_mutex_t *mtx)
340 {
341     int ret;
342
343     if (mtx == NULL)
344     {
345         return EINVAL;
346     }
347
348     mtx->mutex = (struct tMPI_Mutex*)malloc(sizeof(struct tMPI_Mutex)*1);
349     if (mtx->mutex == NULL)
350     {
351         return ENOMEM;
352     }
353     ret = pthread_mutex_init(&(mtx->mutex->mtx), NULL);
354     if (ret != 0)
355     {
356         return ret;
357     }
358
359 #ifndef TMPI_NO_ATOMICS
360     tMPI_Atomic_set(&(mtx->initialized), 1);
361 #else
362     mtx->initialized.value = 1;
363 #endif
364     return 0;
365 }
366
367 static inline int tMPI_Thread_mutex_init_once(tMPI_Thread_mutex_t *mtx)
368 {
369     int ret = 0;
370
371 #ifndef TMPI_NO_ATOMICS
372     /* check whether the mutex is initialized */
373     if (tMPI_Atomic_get( &(mtx->initialized)  ) == 0)
374 #endif
375     {
376         /* we're relying on the memory barrier semantics of mutex_lock/unlock
377            for the check preceding this function call to have worked */
378         ret = pthread_mutex_lock( &(mutex_init) );
379         if (ret != 0)
380         {
381             return ret;
382         }
383
384         if (mtx->mutex == NULL)
385         {
386             mtx->mutex = (struct tMPI_Mutex*)malloc(sizeof(struct tMPI_Mutex));
387             if (mtx->mutex == NULL)
388             {
389                 ret = ENOMEM;
390                 goto err;
391             }
392             ret = pthread_mutex_init( &(mtx->mutex->mtx), NULL);
393             if (ret != 0)
394             {
395                 goto err;
396             }
397         }
398     }
399     ret = pthread_mutex_unlock( &(mutex_init) );
400     return ret;
401 err:
402     pthread_mutex_unlock( &(mutex_init) );
403     return ret;
404 }
405
406
407 int tMPI_Thread_mutex_destroy(tMPI_Thread_mutex_t *mtx)
408 {
409     int ret;
410
411     if (mtx == NULL)
412     {
413         return EINVAL;
414     }
415
416     ret = pthread_mutex_destroy( &(mtx->mutex->mtx) );
417     if (ret != 0)
418     {
419         return ret;
420     }
421     free(mtx->mutex);
422     return ret;
423 }
424
425
426
427 int tMPI_Thread_mutex_lock(tMPI_Thread_mutex_t *mtx)
428 {
429     int ret;
430
431     /* check whether the mutex is initialized */
432     ret = tMPI_Thread_mutex_init_once(mtx);
433     if (ret != 0)
434     {
435         return ret;
436     }
437
438     ret = pthread_mutex_lock(&(mtx->mutex->mtx));
439     return ret;
440 }
441
442
443
444
445 int tMPI_Thread_mutex_trylock(tMPI_Thread_mutex_t *mtx)
446 {
447     int ret;
448
449     /* check whether the mutex is initialized */
450     ret = tMPI_Thread_mutex_init_once(mtx);
451     if (ret != 0)
452     {
453         return ret;
454     }
455
456     ret = pthread_mutex_trylock(&(mtx->mutex->mtx));
457     return ret;
458 }
459
460
461
462 int tMPI_Thread_mutex_unlock(tMPI_Thread_mutex_t *mtx)
463 {
464     int ret;
465
466     /* check whether the mutex is initialized */
467     ret = tMPI_Thread_mutex_init_once(mtx);
468     if (ret != 0)
469     {
470         return ret;
471     }
472
473     ret = pthread_mutex_unlock(&(mtx->mutex->mtx));
474     return ret;
475 }
476
477
478
479 int tMPI_Thread_key_create(tMPI_Thread_key_t *key, void (*destructor)(void *))
480 {
481     int ret;
482
483     if (key == NULL)
484     {
485         return EINVAL;
486     }
487
488
489     key->key = (struct tMPI_Thread_key*)malloc(sizeof(struct
490                                                       tMPI_Thread_key)*1);
491     if (key->key == NULL)
492     {
493         return ENOMEM;
494     }
495     ret = pthread_key_create(&((key)->key->pkey), destructor);
496     if (ret != 0)
497     {
498         return ret;
499     }
500
501     tMPI_Atomic_set(&(key->initialized), 1);
502     return 0;
503 }
504
505
506 int tMPI_Thread_key_delete(tMPI_Thread_key_t key)
507 {
508     int ret;
509
510     ret = pthread_key_delete((key.key->pkey));
511     if (ret != 0)
512     {
513         return ret;
514     }
515     free(key.key);
516
517     return 0;
518 }
519
520
521
522 void * tMPI_Thread_getspecific(tMPI_Thread_key_t key)
523 {
524     void *p = NULL;
525
526     p = pthread_getspecific((key.key->pkey));
527
528     return p;
529 }
530
531
532 int tMPI_Thread_setspecific(tMPI_Thread_key_t key, void *value)
533 {
534     int ret;
535
536     ret = pthread_setspecific((key.key->pkey), value);
537
538     return ret;
539 }
540
541
542 int tMPI_Thread_once(tMPI_Thread_once_t *once_control,
543                      void                (*init_routine)(void))
544 {
545     int ret;
546     if (!once_control || !init_routine)
547     {
548         return EINVAL;
549     }
550
551     /* really ugly hack - and it's slow... */
552     ret = pthread_mutex_lock( &once_init );
553     if (ret != 0)
554     {
555         return ret;
556     }
557     if (tMPI_Atomic_get(&(once_control->once)) == 0)
558     {
559         (*init_routine)();
560         tMPI_Atomic_set(&(once_control->once), 1);
561     }
562     ret = pthread_mutex_unlock( &once_init );
563
564     return ret;
565 }
566
567
568
569
570 int tMPI_Thread_cond_init(tMPI_Thread_cond_t *cond)
571 {
572     int ret;
573
574     if (cond == NULL)
575     {
576         return EINVAL;
577     }
578
579     cond->condp = (struct tMPI_Thread_cond*)malloc(
580                 sizeof(struct tMPI_Thread_cond));
581     if (cond->condp == NULL)
582     {
583         return ENOMEM;
584     }
585
586     ret = pthread_cond_init(&(cond->condp->cond), NULL);
587     if (ret != 0)
588     {
589         return ret;
590     }
591     tMPI_Atomic_set(&(cond->initialized), 1);
592     tMPI_Atomic_memory_barrier();
593
594     return 0;
595 }
596
597
598 static int tMPI_Thread_cond_init_once(tMPI_Thread_cond_t *cond)
599 {
600     int ret = 0;
601
602     /* we're relying on the memory barrier semantics of mutex_lock/unlock
603        for the check preceding this function call to have worked */
604     ret = pthread_mutex_lock( &(cond_init) );
605     if (ret != 0)
606     {
607         return ret;
608     }
609     if (cond->condp == NULL)
610     {
611         cond->condp = (struct tMPI_Thread_cond*)
612             malloc(sizeof(struct tMPI_Thread_cond)*1);
613         if (cond->condp == NULL)
614         {
615             ret = ENOMEM;
616             goto err;
617         }
618         ret = pthread_cond_init( &(cond->condp->cond), NULL);
619         if (ret != 0)
620         {
621             goto err;
622         }
623     }
624     ret = pthread_mutex_unlock( &(cond_init) );
625     return ret;
626 err:
627     /* try to unlock anyway */
628     pthread_mutex_unlock( &(cond_init) );
629     return ret;
630 }
631
632
633
634 int tMPI_Thread_cond_destroy(tMPI_Thread_cond_t *cond)
635 {
636     int ret;
637
638     if (cond == NULL)
639     {
640         return EINVAL;
641     }
642
643     ret = pthread_cond_destroy(&(cond->condp->cond));
644     if (ret != 0)
645     {
646         return ret;
647     }
648     free(cond->condp);
649
650     return 0;
651 }
652
653
654 int tMPI_Thread_cond_wait(tMPI_Thread_cond_t *cond, tMPI_Thread_mutex_t *mtx)
655 {
656     int ret;
657
658     /* check whether the condition is initialized */
659     if (tMPI_Atomic_get( &(cond->initialized)  ) == 0)
660     {
661         ret = tMPI_Thread_cond_init_once(cond);
662         if (ret != 0)
663         {
664             return ret;
665         }
666     }
667     /* the mutex must have been initialized because it should be locked here */
668
669     ret = pthread_cond_wait( &(cond->condp->cond), &(mtx->mutex->mtx) );
670
671     return ret;
672 }
673
674
675
676
677 int tMPI_Thread_cond_signal(tMPI_Thread_cond_t *cond)
678 {
679     int ret;
680
681     /* check whether the condition is initialized */
682     if (tMPI_Atomic_get( &(cond->initialized)  ) == 0)
683     {
684         ret = tMPI_Thread_cond_init_once(cond);
685         if (ret != 0)
686         {
687             return ret;
688         }
689     }
690
691     ret = pthread_cond_signal( &(cond->condp->cond) );
692
693     return ret;
694 }
695
696
697
698 int tMPI_Thread_cond_broadcast(tMPI_Thread_cond_t *cond)
699 {
700     int ret;
701
702     /* check whether the condition is initialized */
703     if (tMPI_Atomic_get( &(cond->initialized)  ) == 0)
704     {
705         ret = tMPI_Thread_cond_init_once(cond);
706         if (ret != 0)
707         {
708             return ret;
709         }
710     }
711
712     ret = pthread_cond_broadcast( &(cond->condp->cond) );
713
714     return ret;
715 }
716
717
718
719
720 void tMPI_Thread_exit(void *value_ptr)
721 {
722     pthread_exit(value_ptr);
723 }
724
725
726 int tMPI_Thread_cancel(tMPI_Thread_t thread)
727 {
728     return pthread_cancel(thread->th);
729 }
730
731
732
733
734 int tMPI_Thread_barrier_init(tMPI_Thread_barrier_t *barrier, int n)
735 {
736     int ret;
737     /*tMPI_Thread_pthread_barrier_t *p;*/
738
739     if (barrier == NULL)
740     {
741         return EINVAL;
742     }
743
744     barrier->barrierp = (struct tMPI_Thread_barrier*)
745         malloc(sizeof(struct tMPI_Thread_barrier)*1);
746     if (barrier->barrierp == NULL)
747     {
748         return ENOMEM;
749     }
750
751     ret = pthread_mutex_init(&(barrier->barrierp->mutex), NULL);
752     if (ret != 0)
753     {
754         return ret;
755     }
756
757     ret = pthread_cond_init(&(barrier->barrierp->cv), NULL);
758     if (ret != 0)
759     {
760         return ret;
761     }
762
763     barrier->threshold = n;
764     barrier->count     = n;
765     barrier->cycle     = 0;
766
767     tMPI_Atomic_set(&(barrier->initialized), 1);
768     return 0;
769 }
770
771 static int tMPI_Thread_barrier_init_once(tMPI_Thread_barrier_t *barrier)
772 {
773     int ret = 0;
774
775     /* we're relying on the memory barrier semantics of mutex_lock/unlock
776        for the check preceding this function call to have worked */
777     ret = pthread_mutex_lock( &(barrier_init) );
778     if (ret != 0)
779     {
780         return ret;
781     }
782
783     if (barrier->barrierp == NULL)
784     {
785         barrier->barrierp = (struct tMPI_Thread_barrier*)
786             malloc(sizeof(struct tMPI_Thread_barrier)*1);
787         if (barrier->barrierp == NULL)
788         {
789             ret = ENOMEM;
790             goto err;
791         }
792
793         ret = pthread_mutex_init(&(barrier->barrierp->mutex), NULL);
794
795         if (ret != 0)
796         {
797             goto err;
798         }
799
800         ret = pthread_cond_init(&(barrier->barrierp->cv), NULL);
801
802         if (ret != 0)
803         {
804             goto err;
805         }
806     }
807     ret = pthread_mutex_unlock( &(barrier_init) );
808     return ret;
809 err:
810     pthread_mutex_unlock( &(barrier_init) );
811     return ret;
812 }
813
814
815
816
817 int tMPI_Thread_barrier_destroy(tMPI_Thread_barrier_t *barrier)
818 {
819     int ret;
820
821     if (barrier == NULL)
822     {
823         return EINVAL;
824     }
825
826     ret = pthread_mutex_destroy(&(barrier->barrierp->mutex));
827     if (ret != 0)
828     {
829         return ret;
830     }
831     ret = pthread_cond_destroy(&(barrier->barrierp->cv));
832     if (ret != 0)
833     {
834         return ret;
835     }
836
837     free(barrier->barrierp);
838
839     return 0;
840 }
841
842
843 int tMPI_Thread_barrier_wait(tMPI_Thread_barrier_t * barrier)
844 {
845     int cycle;
846     int ret;
847
848     /* check whether the barrier is initialized */
849     if (tMPI_Atomic_get( &(barrier->initialized)  ) == 0)
850     {
851         tMPI_Thread_barrier_init_once(barrier);
852     }
853
854
855     ret = pthread_mutex_lock(&barrier->barrierp->mutex);
856     if (ret != 0)
857     {
858         return ret;
859     }
860
861     cycle = barrier->cycle;
862
863     /* Decrement the count atomically and check if it is zero.
864      * This will only be true for the last thread calling us.
865      */
866     if (--barrier->count <= 0)
867     {
868         barrier->cycle = !barrier->cycle;
869         barrier->count = barrier->threshold;
870         ret            = pthread_cond_broadcast(&barrier->barrierp->cv);
871
872         if (ret == 0)
873         {
874             goto err;
875         }
876     }
877     else
878     {
879         while (cycle == barrier->cycle)
880         {
881             ret = pthread_cond_wait(&barrier->barrierp->cv,
882                                     &barrier->barrierp->mutex);
883             if (ret != 0)
884             {
885                 goto err;
886             }
887         }
888     }
889
890     ret = pthread_mutex_unlock(&barrier->barrierp->mutex);
891     return ret;
892 err:
893     pthread_mutex_unlock(&barrier->barrierp->mutex);
894     return ret;
895
896 }
897
898 #else
899
900 /* just to have some symbols */
901 int tMPI_Thread_pthreads = 0;
902
903 #endif /* THREAD_PTHREADS */