1 /* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */ 2 /* 3 * Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana 4 * University Research and Technology 5 * Corporation. All rights reserved. 6 * Copyright (c) 2004-2011 The University of Tennessee and The University 7 * of Tennessee Research Foundation. All rights 8 * reserved. 9 * Copyright (c) 2004-2005 High Performance Computing Center Stuttgart, 10 * University of Stuttgart. All rights reserved. 11 * Copyright (c) 2004-2005 The Regents of the University of California. 12 * All rights reserved. 13 * Copyright (c) 2008-2018 Cisco Systems, Inc. All rights reserved 14 * Copyright (c) 2008-2009 Sun Microsystems, Inc. All rights reserved. 15 * Copyright (c) 2015-2016 Intel, Inc. All rights reserved. 16 * Copyright (c) 2016 Los Alamos National Security, LLC. All rights 17 * reserved. 18 * Copyright (c) 2016 Research Organization for Information Science 19 * and Technology (RIST). All rights reserved. 20 * $COPYRIGHT$ 21 * 22 * Additional copyrights may follow 23 * 24 * $HEADER$ 25 */ 26 /** @file **/ 27 28 #ifndef OMPI_ERRHANDLER_H 29 #define OMPI_ERRHANDLER_H 30 31 #include "ompi_config.h" 32 33 #include "mpi.h" 34 35 #include "opal/prefetch.h" 36 #include "opal/class/opal_object.h" 37 #include "opal/class/opal_pointer_array.h" 38 #include "opal/mca/pmix/pmix.h" 39 40 #include "ompi/runtime/mpiruntime.h" 41 #include "ompi/errhandler/errhandler_predefined.h" 42 #include "ompi/errhandler/errcode-internal.h" 43 44 BEGIN_C_DECLS 45 46 /* 47 * These must correspond to the fortran handle indices 48 */ 49 enum { 50 OMPI_ERRHANDLER_NULL_FORTRAN = 0, 51 OMPI_ERRORS_ARE_FATAL_FORTRAN, 52 OMPI_ERRORS_RETURN_FORTRAN 53 }; 54 55 56 /** 57 * Typedef for all fortran errhandler functions 58 */ 59 typedef void (ompi_errhandler_fortran_handler_fn_t)(MPI_Fint *, 60 MPI_Fint *, ...); 61 62 /** 63 * Typedef for generic errhandler function 64 */ 65 typedef void (ompi_errhandler_generic_handler_fn_t)(void *, int *, ...); 66 67 /** 68 * Enum to denote what language the error handler was created from 69 */ 70 enum ompi_errhandler_lang_t { 71 OMPI_ERRHANDLER_LANG_C, 72 OMPI_ERRHANDLER_LANG_CXX, 73 OMPI_ERRHANDLER_LANG_FORTRAN 74 }; 75 typedef enum ompi_errhandler_lang_t ompi_errhandler_lang_t; 76 77 78 /** 79 * Enum used to describe what kind MPI object an error handler is used for 80 */ 81 enum ompi_errhandler_type_t { 82 OMPI_ERRHANDLER_TYPE_PREDEFINED, 83 OMPI_ERRHANDLER_TYPE_COMM, 84 OMPI_ERRHANDLER_TYPE_WIN, 85 OMPI_ERRHANDLER_TYPE_FILE 86 }; 87 typedef enum ompi_errhandler_type_t ompi_errhandler_type_t; 88 89 90 /* 91 * Need to forward declare this for use in ompi_errhandle_cxx_dispatch_fn_t. 92 */ 93 struct ompi_errhandler_t; 94 95 /** 96 * C++ invocation function signature 97 */ 98 typedef void (ompi_errhandler_cxx_dispatch_fn_t)(void *handle, int *err_code, 99 const char *message, ompi_errhandler_generic_handler_fn_t *fn); 100 101 /** 102 * Back-end type for MPI_Errorhandler. 103 */ 104 struct ompi_errhandler_t { 105 opal_object_t super; 106 107 char eh_name[MPI_MAX_OBJECT_NAME]; 108 /* Type of MPI object that this handler is for */ 109 110 ompi_errhandler_type_t eh_mpi_object_type; 111 112 /* What language was the error handler created in */ 113 ompi_errhandler_lang_t eh_lang; 114 115 /* Function pointers. Note that we *have* to have all 4 types 116 (vs., for example, a union) because the predefined errhandlers 117 can be invoked on any MPI object type, so we need callbacks for 118 all of three. */ 119 MPI_Comm_errhandler_function *eh_comm_fn; 120 ompi_file_errhandler_function *eh_file_fn; 121 MPI_Win_errhandler_function *eh_win_fn; 122 ompi_errhandler_fortran_handler_fn_t *eh_fort_fn; 123 124 /* Have separate callback for C++ errhandlers. This pointer is 125 initialized to NULL and will be set explicitly by the C++ 126 bindings for Create_errhandler. This function is invoked 127 when eh_lang==OMPI_ERRHANDLER_LANG_CXX so that the user's 128 callback function can be invoked with the right language 129 semantics. */ 130 ompi_errhandler_cxx_dispatch_fn_t *eh_cxx_dispatch_fn; 131 132 /* index in Fortran <-> C translation array */ 133 int eh_f_to_c_index; 134 }; 135 typedef struct ompi_errhandler_t ompi_errhandler_t; 136 137 /** 138 * Padded struct to maintain back compatibiltiy. 139 * See ompi/communicator/communicator.h comments with struct ompi_communicator_t 140 * for full explanation why we chose the following padding construct for predefines. 141 */ 142 #define PREDEFINED_ERRHANDLER_PAD 1024 143 144 struct ompi_predefined_errhandler_t { 145 struct ompi_errhandler_t eh; 146 char padding[PREDEFINED_ERRHANDLER_PAD - sizeof(ompi_errhandler_t)]; 147 }; 148 149 typedef struct ompi_predefined_errhandler_t ompi_predefined_errhandler_t; 150 151 152 /** 153 * Global variable for MPI_ERRHANDLER_NULL (_addr flavor is for F03 bindings) 154 */ 155 OMPI_DECLSPEC extern ompi_predefined_errhandler_t ompi_mpi_errhandler_null; 156 OMPI_DECLSPEC extern ompi_predefined_errhandler_t *ompi_mpi_errhandler_null_addr; 157 158 /** 159 * Global variable for MPI_ERRORS_ARE_FATAL (_addr flavor is for F03 bindings) 160 */ 161 OMPI_DECLSPEC extern ompi_predefined_errhandler_t ompi_mpi_errors_are_fatal; 162 OMPI_DECLSPEC extern ompi_predefined_errhandler_t *ompi_mpi_errors_are_fatal_addr; 163 164 /** 165 * Global variable for MPI_ERRORS_RETURN (_addr flavor is for F03 bindings) 166 */ 167 OMPI_DECLSPEC extern ompi_predefined_errhandler_t ompi_mpi_errors_return; 168 OMPI_DECLSPEC extern ompi_predefined_errhandler_t *ompi_mpi_errors_return_addr; 169 170 /** 171 * Global variable for MPI::ERRORS_THROW_EXCEPTIONS. Will abort if 172 * MPI_INIT wasn't called as MPI::INIT (_addr flavor is for F03 bindings) 173 */ 174 OMPI_DECLSPEC extern ompi_predefined_errhandler_t ompi_mpi_errors_throw_exceptions; 175 176 /** 177 * Table for Fortran <-> C errhandler handle conversion 178 */ 179 OMPI_DECLSPEC extern opal_pointer_array_t ompi_errhandler_f_to_c_table; 180 181 182 /** 183 * Forward declaration so that we don't have to include 184 * request/request.h here. 185 */ 186 struct ompi_request_t; 187 188 189 /** 190 * This is the macro to check the state of MPI and determine whether 191 * it was properly initialized and not yet finalized. 192 * 193 * This macro directly invokes the ompi_mpi_errors_are_fatal_handler() 194 * when an error occurs because MPI_COMM_WORLD does not exist (because 195 * we're before MPI_Init() or after MPI_Finalize()). 196 * 197 * NOTE: The ompi_mpi_state variable is a volatile that is set 198 * atomically in ompi_mpi_init() and ompi_mpi_finalize(). The 199 * appropriate memory barriers are done in those 2 functions such that 200 * we do not need to do a read memory barrier here (in 201 * potentially-performance-critical code paths) before reading the 202 * variable. 203 */ 204 #define OMPI_ERR_INIT_FINALIZE(name) \ 205 { \ 206 int32_t state = ompi_mpi_state; \ 207 if (OPAL_UNLIKELY(state < OMPI_MPI_STATE_INIT_COMPLETED || \ 208 state > OMPI_MPI_STATE_FINALIZE_PAST_COMM_SELF_DESTRUCT)) { \ 209 ompi_mpi_errors_are_fatal_comm_handler(NULL, NULL, name); \ 210 } \ 211 } 212 213 /** 214 * This is the macro to invoke to directly invoke an MPI error 215 * handler. 216 * 217 * @param mpi_object The MPI object to invoke the errhandler on (a 218 * comm, win, or win) 219 * @param err_code The error code 220 * @param message Any additional message; typically the name of the 221 * MPI function that is invoking the error. 222 * 223 * This macro is used when you want to directly invoke the error 224 * handler. It is exactly equivalent to calling 225 * ompi_errhandler_invoke() directly, but is provided to have a 226 * parallel invocation to OMPI_ERRHANDLER_CHECK() and OMPI_ERRHANDLER_RETURN(). 227 */ 228 #define OMPI_ERRHANDLER_INVOKE(mpi_object, err_code, message) \ 229 ompi_errhandler_invoke((mpi_object)->error_handler, \ 230 (mpi_object), \ 231 (int)(mpi_object)->errhandler_type, \ 232 ompi_errcode_get_mpi_code(err_code), \ 233 (message)); 234 235 /** 236 * Conditionally invoke an MPI error handler. 237 * 238 * @param rc The return code to check 239 * @param mpi_object The MPI object to invoke the errhandler on (a 240 * comm, win, or win) 241 * @param err_code The error code 242 * @param message Any additional message; typically the name of the 243 * MPI function that is invoking the error. 244 * 245 * This macro will invoke the error handler if the return code is not 246 * OMPI_SUCCESS. 247 */ 248 #define OMPI_ERRHANDLER_CHECK(rc, mpi_object, err_code, message) \ 249 if( OPAL_UNLIKELY(rc != OMPI_SUCCESS) ) { \ 250 int __mpi_err_code = ompi_errcode_get_mpi_code(err_code); \ 251 OPAL_CR_EXIT_LIBRARY() \ 252 ompi_errhandler_invoke((mpi_object)->error_handler, \ 253 (mpi_object), \ 254 (int) (mpi_object)->errhandler_type, \ 255 (__mpi_err_code), \ 256 (message)); \ 257 return (__mpi_err_code); \ 258 } 259 260 /** 261 * Conditionally invoke an MPI error handler; if there is no error, 262 * return MPI_SUCCESS. 263 * 264 * @param rc The return code to check 265 * @param mpi_object The MPI object to invoke the errhandler on (a 266 * comm, win, or win) 267 * @param err_code The error code 268 * @param message Any additional message; typically the name of the 269 * MPI function that is invoking the error. 270 * 271 * This macro will invoke the error handler if the return code is not 272 * OMPI_SUCCESS. If the return code is OMPI_SUCCESS, then return 273 * MPI_SUCCESS. 274 */ 275 #define OMPI_ERRHANDLER_RETURN(rc, mpi_object, err_code, message) \ 276 OPAL_CR_EXIT_LIBRARY() \ 277 if ( OPAL_UNLIKELY(OMPI_SUCCESS != rc) ) { \ 278 int __mpi_err_code = ompi_errcode_get_mpi_code(err_code); \ 279 ompi_errhandler_invoke((mpi_object)->error_handler, \ 280 (mpi_object), \ 281 (int)(mpi_object)->errhandler_type, \ 282 (__mpi_err_code), \ 283 (message)); \ 284 return (__mpi_err_code); \ 285 } else { \ 286 return MPI_SUCCESS; \ 287 } 288 289 290 291 /** 292 * Initialize the error handler interface. 293 * 294 * @returns OMPI_SUCCESS Upon success 295 * @returns OMPI_ERROR Otherwise 296 * 297 * Invoked from ompi_mpi_init(); sets up the error handler interface, 298 * creates the predefined MPI errorhandlers, and creates the 299 * corresopnding F2C translation table. 300 */ 301 int ompi_errhandler_init(void); 302 303 /** 304 * Finalize the error handler interface. 305 * 306 * @returns OMPI_SUCCESS Always 307 * 308 * Invokes from ompi_mpi_finalize(); tears down the error handler 309 * interface, and destroys the F2C translation table. 310 */ 311 int ompi_errhandler_finalize(void); 312 313 /** 314 * \internal 315 * 316 * This function should not be invoked directly; it should only be 317 * invoked by OMPI_ERRHANDLER_INVOKE(), OMPI_ERRHANDLER_CHECK(), or 318 * OMPI_ERRHANDLER_RETURN(). 319 * 320 * @param errhandler The MPI_Errhandler to invoke 321 * @param mpi_object The MPI object to invoke the errhandler on (a 322 * comm, win, or win) 323 * @param type The type of the MPI object. Necessary, since 324 * you can not assign a single type to the predefined 325 * error handlers. This information is therefore 326 * stored on the MPI object itself. 327 * @param err_code The error code 328 * @param message Any additional message; typically the name of the 329 * MPI function that is invoking the error. 330 * 331 * @returns err_code The same value as the parameter 332 * 333 * This function invokes the MPI exception function on the error 334 * handler. If the errhandler was created from fortran, the error 335 * handler will be invoked with fortran linkage. Otherwise, it is 336 * invoked with C linkage. 337 * 338 * If this function returns, it returns the err_code. Note that it 339 * may not return (e.g., for MPI_ERRORS_ARE_FATAL). 340 */ 341 OMPI_DECLSPEC int ompi_errhandler_invoke(ompi_errhandler_t *errhandler, void *mpi_object, 342 int type, int err_code, const char *message); 343 344 345 /** 346 * Invoke an MPI exception on the first request found in the array 347 * that has a non-MPI_SUCCESS value for MPI_ERROR in its status. It 348 * is safe to invoke this function if none of the requests have an 349 * outstanding error; MPI_SUCCESS will be returned. 350 */ 351 int ompi_errhandler_request_invoke(int count, 352 struct ompi_request_t **requests, 353 const char *message); 354 355 /** 356 * Create a ompi_errhandler_t 357 * 358 * @param object_type Enum of the type of MPI object 359 * @param func Function pointer of the error handler 360 * 361 * @returns errhandler Pointer to the ompi_errorhandler_t that will be 362 * created and returned 363 * 364 * This function is called as the back-end of all the 365 * MPI_*_CREATE_ERRHANDLER functions. It creates a new 366 * ompi_errhandler_t object, initializes it to the correct object 367 * type, and sets the callback function on it. 368 * 369 * The type of the function pointer is (arbitrarily) the fortran 370 * function handler type. Since this function has to accept 4 371 * different function pointer types (lest we have 4 different 372 * functions to create errhandlers), the fortran one was picked 373 * arbitrarily. Note that (void*) is not sufficient because at 374 * least theoretically, a sizeof(void*) may not necessarily be the 375 * same as sizeof(void(*)). 376 */ 377 OMPI_DECLSPEC ompi_errhandler_t *ompi_errhandler_create(ompi_errhandler_type_t object_type, 378 ompi_errhandler_generic_handler_fn_t *func, 379 ompi_errhandler_lang_t language); 380 381 /** 382 * Callback function to alert the MPI layer of an error or notification 383 * from the internal RTE and/or the resource manager. 384 * 385 * This function is used to alert the MPI layer to a specific fault detected by the 386 * runtime layer or host RM. This could be a process failure, a lost connection, or the inability 387 * to send an OOB message. The MPI layer has the option to perform whatever actions it 388 * needs to stabilize itself and continue running, abort, etc. 389 */ 390 typedef struct { 391 volatile bool active; 392 int status; 393 } ompi_errhandler_errtrk_t; 394 395 OMPI_DECLSPEC void ompi_errhandler_callback(int status, 396 const opal_process_name_t *source, 397 opal_list_t *info, opal_list_t *results, 398 opal_pmix_notification_complete_fn_t cbfunc, 399 void *cbdata); 400 401 OMPI_DECLSPEC void ompi_errhandler_registration_callback(int status, 402 size_t errhandler_ref, 403 void *cbdata); 404 /** 405 * Check to see if an errhandler is intrinsic. 406 * 407 * @param errhandler The errhandler to check 408 * 409 * @returns true If the errhandler is intrinsic 410 * @returns false If the errhandler is not intrinsic 411 * 412 * Self-explanitory. This is needed in a few top-level MPI functions; 413 * this function is provided to hide the internal structure field 414 * names. 415 */ 416 static inline bool ompi_errhandler_is_intrinsic(ompi_errhandler_t *errhandler) 417 { 418 if ( OMPI_ERRHANDLER_TYPE_PREDEFINED == errhandler->eh_mpi_object_type ) 419 return true; 420 421 return false; 422 } 423 424 END_C_DECLS 425 426 #endif /* OMPI_ERRHANDLER_H */