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