1 /*
2 * Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana
3 * University Research and Technology
4 * Corporation. All rights reserved.
5 * Copyright (c) 2004-2016 The University of Tennessee and The University
6 * of Tennessee Research Foundation. All rights
7 * reserved.
8 * Copyright (c) 2004-2005 High Performance Computing Center Stuttgart,
9 * University of Stuttgart. All rights reserved.
10 * Copyright (c) 2004-2005 The Regents of the University of California.
11 * All rights reserved.
12 * Copyright (c) 2006-2012 Cisco Systems, Inc. All rights reserved.
13 * Copyright (c) 2009 Sun Microsystems, Inc. All rights reserved.
14 * $COPYRIGHT$
15 *
16 * Additional copyrights may follow
17 *
18 * $HEADER$
19 */
20
21 #include "ompi_config.h"
22 #include "ompi/communicator/communicator.h"
23 #include "ompi/request/grequest.h"
24 #include "ompi/mpi/fortran/base/fint_2_int.h"
25
26
27 /*
28 * See the comment in the grequest destructor for the weird semantics
29 * here. If the request has been marked complete via a call to
30 * MPI_GREQUEST_COMPLETE, actually release the object. OTherwise,
31 * just mark this object as "freed" so that a later call to
32 * MPI_GREQUEST_COMPLETE will release it (!).
33 *
34 * Note that TEST* and WAIT* will call this function when a request
35 * has been completed.
36 */
37 static int ompi_grequest_free(ompi_request_t** req)
38 {
39 OBJ_RELEASE(*req);
40 *req = MPI_REQUEST_NULL;
41 return OMPI_SUCCESS;
42 }
43
44 static int ompi_grequest_cancel(ompi_request_t* req, int flag)
45 {
46 int rc = OMPI_SUCCESS;
47 MPI_Fint ierr;
48 ompi_fortran_logical_t fflag;
49 ompi_grequest_t* greq = (ompi_grequest_t*)req;
50
51 if (greq->greq_cancel.c_cancel != NULL) {
52 if (greq->greq_funcs_are_c) {
53 rc = greq->greq_cancel.c_cancel(greq->greq_state,
54 REQUEST_COMPLETE(&greq->greq_base));
55 } else {
56 fflag = (ompi_fortran_logical_t) REQUEST_COMPLETE(&greq->greq_base);
57 greq->greq_cancel.f_cancel((MPI_Aint*)greq->greq_state, &fflag, &ierr);
58 rc = OMPI_FINT_2_INT(ierr);
59 }
60 }
61 return rc;
62 }
63
64 static void ompi_grequest_construct(ompi_grequest_t* greq)
65 {
66 greq->greq_base.req_free = ompi_grequest_free;
67 greq->greq_base.req_cancel = ompi_grequest_cancel;
68 greq->greq_base.req_type = OMPI_REQUEST_GEN;
69 greq->greq_base.req_mpi_object.comm = &(ompi_mpi_comm_world.comm);
70 /* Set the function pointers to C here; the F77 MPI API will
71 override this value if the gen request was created from
72 Fortran */
73 greq->greq_funcs_are_c = true;
74 }
75
76 /*
77 * MPI has some weird semantics with respect to generalized requests
78 * -- different than all other MPI object types. So we move some
79 * cleanup stuff here to the destructor rather than in
80 * greqeust_request_free -- mainly because the cleanup may be required
81 * in two different places.
82 *
83 * Specifically, generalized requests can be completed (and therefore
84 * released) the following ways:
85 *
86 * 1. Call to MPI_GREQUEST_COMPLETE and then a corresponding call to
87 * some flavor of MPI_TEST* or MPI_WAIT*. This will both complete the
88 * requests and destroy the coresponding MPI generalized request
89 * object.
90 *
91 * 2. Call MPI_REQUEST_FREE and then (!) -- with some other
92 * still-valid copy of the handler -- call MPI_GREQUEST_COMPLETE.
93 *
94 * 3. Reverse the order of #2 -- call MPI_GREQUEST_COMPLETE and then
95 * MPI_REQUEST_FREE.
96 *
97 * So any one of these functions may actually be the one that
98 * de-allocates the back-end request object. Hence, this is perfect
99 * for our reference counting system -- so the call to the gen request
100 * free_fn() is back here in the destructor, whenever the object is
101 * actually freed.
102 *
103 * Hence, the following must occur before a grequest is freed:
104 *
105 * - ompi_grequest_complete() (i.e., GREQUEST_COMPLETE) is invoked
106 * - ompi_grequest_free() is invoked
107 *
108 * Remember that ompi_grequest_free() is invoked by MPI_TEST* and
109 * MPI_WAIT* when the request was previously marked as complete and
110 * TEST* / WAIT* notified the user as such, and this function is also
111 * invoked by REQUEST_FREE). Hence, these two functions will *always*
112 * be invoked, but the order in which they are invoked is up to the
113 * user. So this is a perfect opprotunity for the OBJ_* reference
114 * count system. When we create an ompi_grequest_t in
115 * ompi_grequest_start(), we both OBJ_NEW and OBJ_RETAIN it so that
116 * its reference count goes to 0. Then in ompi_grequest_complete()
117 * and ompi_grequest_free(), we OBJ_RELEASE it. Hence, when both of
118 * them have RELEASEd -- regardless of the order in which the
119 * functions were invoked, then the destructor is invoked and
120 * everything is cleaned up (and we invoked the grequest free_fn).
121 */
122 static void ompi_grequest_destruct(ompi_grequest_t* greq)
123 {
124 MPI_Fint ierr;
125
126 if (greq->greq_free.c_free != NULL) {
127 if (greq->greq_funcs_are_c) {
128 greq->greq_free.c_free(greq->greq_state);
129 } else {
130 greq->greq_free.f_free((MPI_Aint*)greq->greq_state, &ierr);
131 }
132 }
133
134 OMPI_REQUEST_FINI(&greq->greq_base);
135 }
136
137
138 OBJ_CLASS_INSTANCE(
139 ompi_grequest_t,
140 ompi_request_t,
141 ompi_grequest_construct,
142 ompi_grequest_destruct);
143
144
145 int ompi_grequest_start(
146 MPI_Grequest_query_function *gquery_fn,
147 MPI_Grequest_free_function *gfree_fn,
148 MPI_Grequest_cancel_function *gcancel_fn,
149 void* gstate,
150 ompi_request_t** request)
151 {
152 ompi_grequest_t *greq = OBJ_NEW(ompi_grequest_t);
153 if(greq == NULL) {
154 return OMPI_ERR_OUT_OF_RESOURCE;
155 }
156 /* We call RETAIN here specifically to increase the refcount to 2.
157 See comment before the destructor for an explanation. */
158 OBJ_RETAIN(greq);
159
160 greq->greq_base.req_state = OMPI_REQUEST_ACTIVE;
161 greq->greq_state = gstate;
162 greq->greq_query.c_query = gquery_fn;
163 greq->greq_free.c_free = gfree_fn;
164 greq->greq_cancel.c_cancel = gcancel_fn;
165 greq->greq_base.req_status = ompi_status_empty;
166
167 *request = &greq->greq_base;
168 return OMPI_SUCCESS;
169 }
170
171
172 /*
173 * Beware the odd semantics listed in MPI-2:8.2... See the comment in
174 * the grequest destructor.
175 *
176 * First do the normal stuff to complete the request (i.e., call
177 * ompi_request_complete()). Then, if this request object was
178 * previously freed via MPI_REQUEST_FREE, release it.
179 */
180 int ompi_grequest_complete(ompi_request_t *req)
181 {
182 int rc;
183
184 rc = ompi_request_complete(req, true);
185 OBJ_RELEASE(req);
186 return rc;
187 }
188
189
190 /*
191 * Grequest queries are invoked in two places:
192 *
193 * 1. MPI_TEST* / MPI_WAIT*, when requests have completed.
194 *
195 * 2. MPI_REQUEST_GET_STATUS, when requests may or may not have
196 * completed.
197 *
198 */
199 int ompi_grequest_invoke_query(ompi_request_t *request,
200 ompi_status_public_t *status)
201 {
202 int rc = OMPI_SUCCESS;
203 ompi_grequest_t *g = (ompi_grequest_t*) request;
204
205 /* MPI-2:8.2 does not say what to do with the return value from
206 the query function (i.e., the int return value from the C
207 function or the ierr argument from the Fortran function).
208 Making the command decision here to ignore it. If the handler
209 wants to pass an error back, it should set it in the MPI_ERROR
210 field in the status (which is always kept, regardless if the
211 top-level function was invoked with MPI_STATUS[ES]_IGNORE or
212 not). */
213 if (NULL != g->greq_query.c_query) {
214 if (g->greq_funcs_are_c) {
215 rc = g->greq_query.c_query(g->greq_state, status);
216 } else {
217 MPI_Fint ierr;
218 MPI_Fint fstatus[sizeof(MPI_Status) / sizeof(int)];
219 g->greq_query.f_query((MPI_Aint*)g->greq_state, fstatus, &ierr);
220 MPI_Status_f2c(fstatus, status);
221 rc = OMPI_FINT_2_INT(ierr);
222 }
223 }
224
225 return rc;
226 }
227