Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Merge branch 'master' of scm.gforge.inria.fr:/gitroot/simgrid/simgrid
[simgrid.git] / teshsuite / smpi / mpich3-test / rma / mcs-mutex.c
1 /* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil ; -*- */
2 /*
3  *  (C) 2013 by Argonne National Laboratory.
4  *      See COPYRIGHT in top-level directory.
5  */
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <assert.h>
10 #include <strings.h>
11
12 #include <mpi.h>
13 #include "mcs-mutex.h"
14
15 /* TODO: Make these mutex operations no-ops for sequential runs */
16
17 /** Create an MCS mutex.  Collective on comm.
18   *
19   * @param[out] comm communicator containing all processes that will use the
20   *                  mutex
21   * @param[out] tail_rank rank of the process in comm that holds the tail
22   *                  pointer
23   * @param[out] hdl  handle to the mutex
24   * @return          MPI status
25   */
26 int MCS_Mutex_create(int tail_rank, MPI_Comm comm, MCS_Mutex * hdl_out)
27 {
28     int rank, nproc;
29     MCS_Mutex hdl;
30
31     hdl = malloc(sizeof(struct mcs_mutex_s));
32     assert(hdl != NULL);
33
34     MPI_Comm_dup(comm, &hdl->comm);
35
36     MPI_Comm_rank(hdl->comm, &rank);
37     MPI_Comm_size(hdl->comm, &nproc);
38
39     hdl->tail_rank = tail_rank;
40
41 #ifdef USE_WIN_SHARED
42     MPI_Win_allocate_shared(2*sizeof(int), sizeof(int), MPI_INFO_NULL,
43                             hdl->comm, &hdl->base, &hdl->window);
44 #else
45     MPI_Win_allocate(2*sizeof(int), sizeof(int), MPI_INFO_NULL, hdl->comm,
46                      &hdl->base, &hdl->window);
47 #endif
48
49     MPI_Win_lock_all(0, hdl->window);
50
51     hdl->base[0] = MPI_PROC_NULL;
52     hdl->base[1] = MPI_PROC_NULL;
53
54     MPI_Win_sync(hdl->window);
55     MPI_Barrier(hdl->comm);
56
57     *hdl_out = hdl;
58     return MPI_SUCCESS;
59 }
60
61
62 /** Free an MCS mutex.  Collective on ranks in the communicator used at the
63   * time of creation.
64   *
65   * @param[in] hdl handle to the group that will be freed
66   * @return        MPI status
67   */
68 int MCS_Mutex_free(MCS_Mutex * hdl_ptr)
69 {
70     MCS_Mutex hdl = *hdl_ptr;
71
72     MPI_Win_unlock_all(hdl->window);
73
74     MPI_Win_free(&hdl->window);
75     MPI_Comm_free(&hdl->comm);
76
77     free(hdl);
78     hdl_ptr = NULL;
79
80     return MPI_SUCCESS;
81 }
82
83
84 /** Lock a mutex.
85   *
86   * @param[in] hdl   Handle to the mutex
87   * @return          MPI status
88   */
89 int MCS_Mutex_lock(MCS_Mutex hdl)
90 {
91     int rank, nproc;
92     int prev;
93
94     MPI_Comm_rank(hdl->comm, &rank);
95     MPI_Comm_size(hdl->comm, &nproc);
96
97     /* This store is safe, since it cannot happen concurrently with a remote
98      * write */
99     hdl->base[MCS_MTX_ELEM_DISP] = MPI_PROC_NULL;
100     MPI_Win_sync(hdl->window);
101
102     MPI_Fetch_and_op(&rank, &prev, MPI_INT, hdl->tail_rank, MCS_MTX_TAIL_DISP,
103                      MPI_REPLACE, hdl->window);
104     MPI_Win_flush(hdl->tail_rank, hdl->window);
105
106     /* If there was a previous tail, update their next pointer and wait for
107      * notification.  Otherwise, the mutex was successfully acquired. */
108     if (prev != MPI_PROC_NULL) {
109         /* Wait for notification */
110         MPI_Status status;
111
112         MPI_Accumulate(&rank, 1, MPI_INT, prev, MCS_MTX_ELEM_DISP, 1, MPI_INT, MPI_REPLACE, hdl->window);
113         MPI_Win_flush(prev, hdl->window);
114
115         debug_print("%2d: LOCK   - waiting for notification from %d\n", rank, prev);
116         MPI_Recv(NULL, 0, MPI_BYTE, prev, MCS_MUTEX_TAG, hdl->comm, &status);
117     }
118
119     debug_print("%2d: LOCK   - lock acquired\n", rank);
120
121     return MPI_SUCCESS;
122 }
123
124
125 /** Attempt to acquire a mutex.
126   *
127   * @param[in] hdl   Handle to the mutex
128   * @param[out] success Indicates whether the mutex was acquired
129   * @return          MPI status
130   */
131 int MCS_Mutex_trylock(MCS_Mutex hdl, int *success)
132 {
133     int rank, nproc;
134     int tail, nil = MPI_PROC_NULL;
135
136     MPI_Comm_rank(hdl->comm, &rank);
137     MPI_Comm_size(hdl->comm, &nproc);
138
139     /* This store is safe, since it cannot happen concurrently with a remote
140      * write */
141     hdl->base[MCS_MTX_ELEM_DISP] = MPI_PROC_NULL;
142     MPI_Win_sync(hdl->window);
143
144     /* Check if the lock is available and claim it if it is. */
145     MPI_Compare_and_swap(&rank, &nil, &tail, MPI_INT, hdl->tail_rank,
146                          MCS_MTX_TAIL_DISP, hdl->window);
147     MPI_Win_flush(hdl->tail_rank, hdl->window);
148
149     /* If the old tail was MPI_PROC_NULL, we have claimed the mutex */
150     *success = (tail == nil);
151
152     debug_print("%2d: TRYLOCK - %s\n", rank, (*success) ? "Success" : "Non-success");
153
154     return MPI_SUCCESS;
155 }
156
157
158 /** Unlock a mutex.
159   *
160   * @param[in] hdl   Handle to the mutex
161   * @return          MPI status
162   */
163 int MCS_Mutex_unlock(MCS_Mutex hdl)
164 {
165     int rank, nproc, next;
166
167     MPI_Comm_rank(hdl->comm, &rank);
168     MPI_Comm_size(hdl->comm, &nproc);
169
170     MPI_Win_sync(hdl->window);
171
172     /* Read my next pointer.  FOP is used since another process may write to
173      * this location concurrent with this read. */
174     MPI_Fetch_and_op(NULL, &next, MPI_INT, rank, MCS_MTX_ELEM_DISP, MPI_NO_OP,
175                      hdl->window);
176     MPI_Win_flush(rank, hdl->window);
177
178     if ( next == MPI_PROC_NULL) {
179         int tail;
180         int nil = MPI_PROC_NULL;
181
182         /* Check if we are the at the tail of the lock queue.  If so, we're
183          * done.  If not, we need to send notification. */
184         MPI_Compare_and_swap(&nil, &rank, &tail, MPI_INT, hdl->tail_rank,
185                              MCS_MTX_TAIL_DISP, hdl->window);
186         MPI_Win_flush(hdl->tail_rank, hdl->window);
187
188         if (tail != rank) {
189             debug_print("%2d: UNLOCK - waiting for next pointer (tail = %d)\n", rank, tail);
190             assert(tail >= 0 && tail < nproc);
191
192             for (;;) {
193                 int flag;
194
195                 MPI_Fetch_and_op(NULL, &next, MPI_INT, rank, MCS_MTX_ELEM_DISP,
196                                  MPI_NO_OP, hdl->window);
197
198                 MPI_Win_flush(rank, hdl->window);
199                 if (next != MPI_PROC_NULL) break;
200
201                 MPI_Iprobe(MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &flag,
202                            MPI_STATUS_IGNORE);
203             }
204         }
205     }
206
207     /* Notify the next waiting process */
208     if (next != MPI_PROC_NULL) {
209         debug_print("%2d: UNLOCK - notifying %d\n", rank, next);
210         MPI_Send(NULL, 0, MPI_BYTE, next, MCS_MUTEX_TAG, hdl->comm);
211     }
212
213     debug_print("%2d: UNLOCK - lock released\n", rank);
214
215     return MPI_SUCCESS;
216 }