1 /* Ergo, version 3.8, a program for linear scaling electronic structure
2  * calculations.
3  * Copyright (C) 2019 Elias Rudberg, Emanuel H. Rubensson, Pawel Salek,
4  * and Anastasia Kruchinina.
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  * Primary academic reference:
20  * Ergo: An open-source program for linear-scaling electronic structure
21  * calculations,
22  * Elias Rudberg, Emanuel H. Rubensson, Pawel Salek, and Anastasia
23  * Kruchinina,
24  * SoftwareX 7, 107 (2018),
25  * <http://dx.doi.org/10.1016/j.softx.2018.03.005>
26  *
27  * For further information about Ergo, see <http://www.ergoscf.org>.
28  */
29 
30 /** @file barrier.c implements a pthread-compatible barrier. This is
31     to be used with older pthread implementations that do not provide
32     barriers. This implementation is applicable only in simple
33     cases. Check section 7.1.1 of "Programming with POSIX threads" for
34     a full-blown implementation.  In particular, this implementation
35     does not check for some error conditions, like destroying the
36     barrier when some threads wait on it. */
37 
38 #include <errno.h>
39 #include <pthread.h>
40 
41 #include "barrier.h"
42 
43 int
ergo_barrier_init(ergo_barrier_t * barrier,const void * attr_ignored,unsigned int count)44 ergo_barrier_init(ergo_barrier_t * barrier,
45                   const void * attr_ignored,
46                   unsigned int count)
47 {
48   int r;
49 
50   /* This may be called only once for given barrier! */
51   if( (r=pthread_mutex_init(&barrier->barrierMutex, NULL)) ) return r;
52   if( (r=pthread_cond_init(&barrier->conditionVar, NULL)) )  return r;
53   barrier->currCount = barrier->initCount = count;
54   return 0;
55 }
56 
57 int
ergo_barrier_destroy(ergo_barrier_t * barrier)58 ergo_barrier_destroy(ergo_barrier_t *barrier)
59 {
60   int r;
61   if( (r=pthread_mutex_destroy(&barrier->barrierMutex)) ) return r;
62   if( (r=pthread_cond_destroy(&barrier->conditionVar)) )  return r;
63 
64   return 0;
65 }
66 
67 int
ergo_barrier_wait(ergo_barrier_t * barrier)68 ergo_barrier_wait(ergo_barrier_t *barrier)
69 {
70   int r, ret;
71 
72   if( (r=pthread_mutex_lock(&barrier->barrierMutex)) ) return r;
73 
74   if (barrier->currCount == 0)
75     return EINVAL;
76 
77   --barrier->currCount;
78 
79   if(barrier->currCount == 0) { /* I am the retarded one! :) */
80     ++barrier->cycle;
81     barrier->currCount = barrier->initCount;
82     if( (r=pthread_cond_broadcast(&barrier->conditionVar)) ) return r;
83     ret = PTHREAD_BARRIER_SERIAL_THREAD;
84   } else {
85     int curCycle = barrier->cycle;
86     while(barrier->cycle == curCycle) {
87       if( (r=pthread_cond_wait(&barrier->conditionVar,
88                                &barrier->barrierMutex)) ) return r;
89     }
90     ret = 0;
91   }
92   if( (r=pthread_mutex_unlock(&barrier->barrierMutex)) ) return r;
93   return ret;
94 }
95 
96 #ifdef TEST
thr(void * p)97 static void* thr(void *p)
98 {
99   ergo_barrier_t *b = (ergo_barrier_t*)p;
100   pthread_t a;
101   int r;
102 
103   a  = pthread_self();
104   r = 1 + (((int)a) >> 12) & 0x03; /* this will hopefully be a bit of
105                                       a random number */
106   printf("%x sleeps for %i s\n", (int)a, r);
107   sleep(r);
108   printf("%x waits on barrier\n", (int)a);
109   r = ergo_barrier_wait(b);
110   printf("%x continues with r=%d\n", (int)a, r);
111   return NULL;
112 }
main(int argc,char * argv)113 int main(int argc, char *argv)
114 {
115 #define THREAD_COUNT 10
116   pthread_t pid[THREAD_COUNT];
117   ergo_barrier_t barrier;
118   int i;
119 
120   ergo_barrier_init(&barrier, NULL, THREAD_COUNT);
121   for(i=0; i<THREAD_COUNT; i++)
122     pthread_create(&pid[i], NULL, thr, &barrier);
123 
124   for(i=0; i<THREAD_COUNT; i++) {
125     pthread_join(pid[i], NULL);
126   }
127   ergo_barrier_destroy(&barrier);
128 
129   return 0;
130 }
131 #endif /* TEST */
132