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