Merge release-4-6 into master
[alexxy/gromacs.git] / src / gromacs / gmxlib / thread_mpi / once.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 #ifdef HAVE_TMPI_CONFIG_H
39 #include "tmpi_config.h"
40 #endif
41
42 #ifdef HAVE_CONFIG_H
43 #include "config.h"
44 #endif
45
46
47 #ifdef HAVE_UNISTD_H
48 #include <unistd.h>
49 #endif
50
51 #include <errno.h>
52 #include <stdlib.h>
53 #include <stdio.h>
54 #include <stdarg.h>
55 #include <string.h>
56
57 #include "impl.h"
58
59
60 /* once */
61 int tMPI_Once(tMPI_Comm comm, void (*function)(void*), void *param, 
62                 int *was_first)
63 {
64     int myrank;
65     int ret=TMPI_SUCCESS;
66     struct coll_sync *csync;
67     struct coll_env *cev;
68     int syncs;
69
70
71     if (!comm)
72     {
73         return tMPI_Error(TMPI_COMM_WORLD, TMPI_ERR_COMM);
74     }
75     myrank=tMPI_Comm_seek_rank(comm, tMPI_Get_current());
76
77     /* we increase our counter, and determine which coll_env we get */
78     csync=&(comm->csync[myrank]);
79     csync->syncs++;
80     cev=&(comm->cev[csync->syncs % N_COLL_ENV]);
81
82     /* now do a compare-and-swap on the current_syncc */
83     syncs=tMPI_Atomic_get( &(cev->coll.current_sync));
84     if ((csync->syncs - syncs > 0) && /* check if sync was an earlier number. 
85                                          If it is a later number, we can't 
86                                          have been the first to arrive here. */
87         tMPI_Atomic_cas(&(cev->coll.current_sync), syncs, csync->syncs))
88     {
89         /* we're the first! */
90         function(param);
91         if (was_first)
92             *was_first=TRUE;
93     }
94     return ret;
95 }
96
97 void* tMPI_Once_wait(tMPI_Comm comm, void* (*function)(void*), void *param, 
98                      int *was_first)
99 {
100     int myrank;
101     struct coll_sync *csync;
102     struct coll_env *cev;
103     int syncs;
104     void *ret;
105
106
107     if (!comm)
108     {
109         tMPI_Error(TMPI_COMM_WORLD, TMPI_ERR_COMM);
110         return NULL;
111     }
112     myrank=tMPI_Comm_seek_rank(comm, tMPI_Get_current());
113
114     /* we increase our counter, and determine which coll_env we get */
115     csync=&(comm->csync[myrank]);
116     csync->syncs++;
117     cev=&(comm->cev[csync->syncs % N_COLL_ENV]);
118
119     /* now do a compare-and-swap on the current_syncc */
120     syncs=tMPI_Atomic_get( &(cev->coll.current_sync));
121     tMPI_Atomic_memory_barrier_acq();
122     if ((csync->syncs - syncs > 0) && /* check if sync was an earlier number. 
123                                          If it is a later number, we can't 
124                                          have been the first to arrive here. 
125                                          Calculating the difference instead
126                                          of comparing directly avoids ABA 
127                                          problems. */
128         tMPI_Atomic_cas(&(cev->coll.current_sync), syncs, csync->syncs))
129     {
130         /* we're the first! */
131         ret=function(param);
132         if (was_first)
133             *was_first=TRUE;
134
135         /* broadcast the output data */
136         cev->coll.res=ret;
137
138         tMPI_Atomic_memory_barrier_rel();
139         /* signal that we're done */
140         tMPI_Atomic_add_return(&(cev->coll.current_sync), 1);
141         /* we need to keep being in sync */
142         csync->syncs++;
143     }
144     else
145     {
146         /* we need to wait until the current_syncc gets increased again */
147         csync->syncs++;
148         do
149         {
150             /*tMPI_Atomic_memory_barrier();*/
151             syncs=tMPI_Atomic_get( &(cev->coll.current_sync) );
152         } while (csync->syncs - syncs > 0); /* difference again due to ABA 
153                                                problems */
154         tMPI_Atomic_memory_barrier_acq();
155         ret=cev->coll.res;
156     }
157     return ret;
158 }
159
160
161 static void *tMPI_Shmallocator(void *prm)
162 {
163     size_t sz=*((size_t*)prm);
164     return malloc(sz);
165 }
166
167 void *tMPI_Shmalloc(tMPI_Comm comm, size_t size)
168 {
169     return tMPI_Once_wait(comm, tMPI_Shmallocator, &size, NULL);
170 }
171
172
173