Move thread_mpi to src/external/
[alexxy/gromacs.git] / src / external / thread_mpi / src / 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         {
93             *was_first = TRUE;
94         }
95     }
96     return ret;
97 }
98
99 void* tMPI_Once_wait(tMPI_Comm comm, void* (*function)(void*), void *param,
100                      int *was_first)
101 {
102     int               myrank;
103     struct coll_sync *csync;
104     struct coll_env  *cev;
105     int               syncs;
106     void             *ret;
107
108
109     if (!comm)
110     {
111         tMPI_Error(TMPI_COMM_WORLD, TMPI_ERR_COMM);
112         return NULL;
113     }
114     myrank = tMPI_Comm_seek_rank(comm, tMPI_Get_current());
115
116     /* we increase our counter, and determine which coll_env we get */
117     csync = &(comm->csync[myrank]);
118     csync->syncs++;
119     cev = &(comm->cev[csync->syncs % N_COLL_ENV]);
120
121     /* now do a compare-and-swap on the current_syncc */
122     syncs = tMPI_Atomic_get( &(cev->coll.current_sync));
123     tMPI_Atomic_memory_barrier_acq();
124     if ((csync->syncs - syncs > 0) && /* check if sync was an earlier number.
125                                          If it is a later number, we can't
126                                          have been the first to arrive here.
127                                          Calculating the difference instead
128                                          of comparing directly avoids ABA
129                                          problems. */
130         tMPI_Atomic_cas(&(cev->coll.current_sync), syncs, csync->syncs))
131     {
132         /* we're the first! */
133         ret = function(param);
134         if (was_first)
135         {
136             *was_first = TRUE;
137         }
138
139         /* broadcast the output data */
140         cev->coll.res = ret;
141
142         tMPI_Atomic_memory_barrier_rel();
143         /* signal that we're done */
144         tMPI_Atomic_fetch_add(&(cev->coll.current_sync), 1);
145         /* we need to keep being in sync */
146         csync->syncs++;
147     }
148     else
149     {
150         /* we need to wait until the current_syncc gets increased again */
151         csync->syncs++;
152         do
153         {
154             /*tMPI_Atomic_memory_barrier();*/
155             syncs = tMPI_Atomic_get( &(cev->coll.current_sync) );
156         }
157         while (csync->syncs - syncs > 0);   /* difference again due to ABA
158                                                problems */
159         tMPI_Atomic_memory_barrier_acq();
160         ret = cev->coll.res;
161     }
162     return ret;
163 }
164
165
166 static void *tMPI_Shmallocator(void *prm)
167 {
168     size_t sz = *((size_t*)prm);
169     return malloc(sz);
170 }
171
172 void *tMPI_Shmalloc(tMPI_Comm comm, size_t size)
173 {
174     return tMPI_Once_wait(comm, tMPI_Shmallocator, &size, NULL);
175 }