1 // Copyright (c) 2017, Lawrence Livermore National Security, LLC. Produced at 2 // the Lawrence Livermore National Laboratory. LLNL-CODE-734707. All Rights 3 // reserved. See files LICENSE and NOTICE for details. 4 // 5 // This file is part of CEED, a collection of benchmarks, miniapps, software 6 // libraries and APIs for efficient high-order finite element and spectral 7 // element discretizations for exascale applications. For more information and 8 // source code availability see http://github.com/ceed. 9 // 10 // The CEED research is supported by the Exascale Computing Project 17-SC-20-SC, 11 // a collaborative effort of two U.S. Department of Energy organizations (Office 12 // of Science and the National Nuclear Security Administration) responsible for 13 // the planning and preparation of a capable exascale ecosystem, including 14 // software, applications, hardware, advanced system engineering and early 15 // testbed platforms, in support of the nation's exascale computing imperative. 16 17 #define _POSIX_C_SOURCE 200112 18 #include <ceed-impl.h> 19 #include <ceed-backend.h> 20 #include <stdarg.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 25 /// @cond DOXYGEN_SKIP 26 static CeedRequest ceed_request_immediate; 27 static CeedRequest ceed_request_ordered; 28 29 static struct { 30 char prefix[CEED_MAX_RESOURCE_LEN]; 31 int (*init)(const char *resource, Ceed f); 32 unsigned int priority; 33 } backends[32]; 34 static size_t num_backends; 35 36 #define ceedoffsetof(st, m) \ 37 ((size_t) ( (char *)&((st)(0))->m - (char *)0 )) 38 /// @endcond 39 40 /// @file 41 /// Implementation of core components of Ceed library 42 /// 43 /// @addtogroup Ceed 44 /// @{ 45 46 /** 47 @brief Request immediate completion 48 49 This predefined constant is passed as the \ref CeedRequest argument to 50 interfaces when the caller wishes for the operation to be performed 51 immediately. The code 52 53 @code 54 CeedOperatorApply(op, ..., CEED_REQUEST_IMMEDIATE); 55 @endcode 56 57 is semantically equivalent to 58 59 @code 60 CeedRequest request; 61 CeedOperatorApply(op, ..., &request); 62 CeedRequestWait(&request); 63 @endcode 64 65 @sa CEED_REQUEST_ORDERED 66 **/ 67 CeedRequest *const CEED_REQUEST_IMMEDIATE = &ceed_request_immediate; 68 69 /** 70 @brief Request ordered completion 71 72 This predefined constant is passed as the \ref CeedRequest argument to 73 interfaces when the caller wishes for the operation to be completed in the 74 order that it is submitted to the device. It is typically used in a construct 75 such as 76 77 @code 78 CeedRequest request; 79 CeedOperatorApply(op1, ..., CEED_REQUEST_ORDERED); 80 CeedOperatorApply(op2, ..., &request); 81 // other optional work 82 CeedWait(&request); 83 @endcode 84 85 which allows the sequence to complete asynchronously but does not start 86 `op2` until `op1` has completed. 87 88 @fixme The current implementation is overly strict, offering equivalent 89 semantics to CEED_REQUEST_IMMEDIATE. 90 91 @sa CEED_REQUEST_IMMEDIATE 92 */ 93 CeedRequest *const CEED_REQUEST_ORDERED = &ceed_request_ordered; 94 95 /** 96 @brief Error handling implementation; use \ref CeedError instead. 97 98 @ref Developer 99 **/ 100 int CeedErrorImpl(Ceed ceed, const char *filename, int lineno, const char *func, 101 int ecode, const char *format, ...) { 102 va_list args; 103 va_start(args, format); 104 if (ceed) return ceed->Error(ceed, filename, lineno, func, ecode, format, args); 105 return CeedErrorAbort(ceed, filename, lineno, func, ecode, format, args); 106 } 107 108 /** 109 @brief Error handler that returns without printing anything. 110 111 Pass this to CeedSetErrorHandler() to obtain this error handling behavior. 112 113 @ref Developer 114 **/ 115 int CeedErrorReturn(Ceed ceed, const char *filename, int lineno, 116 const char *func, int ecode, const char *format, 117 va_list args) { 118 return ecode; 119 } 120 121 /** 122 @brief Error handler that prints to stderr and aborts 123 124 Pass this to CeedSetErrorHandler() to obtain this error handling behavior. 125 126 @ref Developer 127 **/ 128 int CeedErrorAbort(Ceed ceed, const char *filename, int lineno, 129 const char *func, int ecode, 130 const char *format, va_list args) { 131 fprintf(stderr, "%s:%d in %s(): ", filename, lineno, func); 132 vfprintf(stderr, format, args); 133 fprintf(stderr, "\n"); 134 abort(); 135 return ecode; 136 } 137 138 /** 139 @brief Error handler that prints to stderr and exits 140 141 Pass this to CeedSetErrorHandler() to obtain this error handling behavior. 142 143 In contrast to CeedErrorAbort(), this exits without a signal, so atexit() 144 handlers (e.g., as used by gcov) are run. 145 146 @ref Developer 147 **/ 148 int CeedErrorExit(Ceed ceed, const char *filename, int lineno, 149 const char *func, int ecode, 150 const char *format, va_list args) { 151 fprintf(stderr, "%s:%d in %s(): ", filename, lineno, func); 152 vfprintf(stderr, format, args); 153 fprintf(stderr, "\n"); 154 exit(ecode); 155 return ecode; 156 } 157 158 /** 159 @brief Set error handler 160 161 A default error handler is set in CeedInit(). Use this function to change 162 the error handler to CeedErrorReturn(), CeedErrorAbort(), or a user-defined 163 error handler. 164 165 @ref Developer 166 **/ 167 int CeedSetErrorHandler(Ceed ceed, 168 int (eh)(Ceed, const char *, int, const char *, 169 int, const char *, va_list)) { 170 ceed->Error = eh; 171 return 0; 172 } 173 174 /** 175 @brief Register a Ceed backend 176 177 @param prefix Prefix of resources for this backend to respond to. For 178 example, the reference backend responds to "/cpu/self". 179 @param init Initialization function called by CeedInit() when the backend 180 is selected to drive the requested resource. 181 @param priority Integer priority. Lower values are preferred in case the 182 resource requested by CeedInit() has non-unique best prefix 183 match. 184 185 @return An error code: 0 - success, otherwise - failure 186 187 @ref Advanced 188 **/ 189 int CeedRegister(const char *prefix, 190 int (*init)(const char *, Ceed), unsigned int priority) { 191 if (num_backends >= sizeof(backends) / sizeof(backends[0])) { 192 return CeedError(NULL, 1, "Too many backends"); 193 } 194 strncpy(backends[num_backends].prefix, prefix, CEED_MAX_RESOURCE_LEN); 195 backends[num_backends].init = init; 196 backends[num_backends].priority = priority; 197 num_backends++; 198 return 0; 199 } 200 201 /** 202 @brief Allocate an array on the host; use CeedMalloc() 203 204 Memory usage can be tracked by the library. This ensures sufficient 205 alignment for vectorization and should be used for large allocations. 206 207 @param n Number of units to allocate 208 @param unit Size of each unit 209 @param p Address of pointer to hold the result. 210 211 @return An error code: 0 - success, otherwise - failure 212 213 @sa CeedFree() 214 215 @ref Advanced 216 **/ 217 int CeedMallocArray(size_t n, size_t unit, void *p) { 218 int ierr = posix_memalign((void **)p, CEED_ALIGN, n*unit); 219 if (ierr) 220 return CeedError(NULL, ierr, 221 "posix_memalign failed to allocate %zd members of size %zd\n", n, unit); 222 return 0; 223 } 224 225 /** 226 @brief Allocate a cleared (zeroed) array on the host; use CeedCalloc() 227 228 Memory usage can be tracked by the library. 229 230 @param n Number of units to allocate 231 @param unit Size of each unit 232 @param p Address of pointer to hold the result. 233 234 @return An error code: 0 - success, otherwise - failure 235 236 @sa CeedFree() 237 238 @ref Advanced 239 **/ 240 int CeedCallocArray(size_t n, size_t unit, void *p) { 241 *(void **)p = calloc(n, unit); 242 if (n && unit && !*(void **)p) 243 return CeedError(NULL, 1, "calloc failed to allocate %zd members of size %zd\n", 244 n, unit); 245 return 0; 246 } 247 248 /** 249 @brief Reallocate an array on the host; use CeedRealloc() 250 251 Memory usage can be tracked by the library. 252 253 @param n Number of units to allocate 254 @param unit Size of each unit 255 @param p Address of pointer to hold the result. 256 257 @return An error code: 0 - success, otherwise - failure 258 259 @sa CeedFree() 260 261 @ref Advanced 262 **/ 263 int CeedReallocArray(size_t n, size_t unit, void *p) { 264 *(void **)p = realloc(*(void **)p, n*unit); 265 if (n && unit && !*(void **)p) 266 return CeedError(NULL, 1, 267 "realloc failed to allocate %zd members of size %zd\n", 268 n, unit); 269 return 0; 270 } 271 272 /// Free memory allocated using CeedMalloc() or CeedCalloc() 273 /// 274 /// @param p address of pointer to memory. This argument is of type void* to 275 /// avoid needing a cast, but is the address of the pointer (which is zeroed) 276 /// rather than the pointer. 277 int CeedFree(void *p) { 278 free(*(void **)p); 279 *(void **)p = NULL; 280 return 0; 281 } 282 283 /** 284 @brief Wait for a CeedRequest to complete. 285 286 Calling CeedRequestWait on a NULL request is a no-op. 287 288 @param req Address of CeedRequest to wait for; zeroed on completion. 289 290 @return An error code: 0 - success, otherwise - failure 291 292 @ref Advanced 293 **/ 294 int CeedRequestWait(CeedRequest *req) { 295 if (!*req) return 0; 296 return CeedError(NULL, 2, "CeedRequestWait not implemented"); 297 } 298 299 /** 300 @brief Initialize a \ref Ceed to use the specified resource. 301 302 @param resource Resource to use, e.g., "/cpu/self" 303 @param ceed The library context 304 @sa CeedRegister() CeedDestroy() 305 306 @return An error code: 0 - success, otherwise - failure 307 308 @ref Basic 309 **/ 310 int CeedInit(const char *resource, Ceed *ceed) { 311 int ierr; 312 size_t matchlen = 0, matchidx; 313 unsigned int matchpriority = 100, priority; 314 315 // Find matching backend 316 if (!resource) return CeedError(NULL, 1, "No resource provided"); 317 for (size_t i=0; i<num_backends; i++) { 318 size_t n; 319 const char *prefix = backends[i].prefix; 320 for (n = 0; prefix[n] && prefix[n] == resource[n]; n++) {} 321 priority = backends[i].priority; 322 if (n > matchlen || (n == matchlen && matchpriority > priority)) { 323 matchlen = n; 324 matchpriority = priority; 325 matchidx = i; 326 } 327 } 328 if (!matchlen) return CeedError(NULL, 1, "No suitable backend"); 329 330 // Setup Ceed 331 ierr = CeedCalloc(1,ceed); CeedChk(ierr); 332 const char * ceed_error_handler = getenv("CEED_ERROR_HANDLER"); 333 if (!ceed_error_handler) ceed_error_handler = "abort"; 334 if (!strcmp(ceed_error_handler, "exit")) 335 (*ceed)->Error = CeedErrorExit; 336 else 337 (*ceed)->Error = CeedErrorAbort; 338 (*ceed)->refcount = 1; 339 (*ceed)->data = NULL; 340 341 // Set lookup table 342 foffset foffsets[CEED_NUM_BACKEND_FUNCTIONS] = { 343 {"CeedError", ceedoffsetof(Ceed, Error)}, 344 {"CeedDestroy", ceedoffsetof(Ceed, Destroy)}, 345 {"CeedVecCreate", ceedoffsetof(Ceed, VecCreate)}, 346 {"CeedElemRestrictionCreate", ceedoffsetof(Ceed, ElemRestrictionCreate)}, 347 { 348 "CeedElemRestrictionCreateBlocked", 349 ceedoffsetof(Ceed, ElemRestrictionCreateBlocked) 350 }, 351 {"CeedBasisCreateTensorH1", ceedoffsetof(Ceed, BasisCreateTensorH1)}, 352 {"CeedBasisCreateH1", ceedoffsetof(Ceed, BasisCreateH1)}, 353 {"CeedQFunctionCreate", ceedoffsetof(Ceed, QFunctionCreate)}, 354 {"CeedOperatorCreate", ceedoffsetof(Ceed, OperatorCreate)}, 355 {"VectorSetArray", ceedoffsetof(CeedVector, SetArray)}, 356 {"VectorSetValue", ceedoffsetof(CeedVector, SetValue)}, 357 {"VectorGetArray", ceedoffsetof(CeedVector, GetArray)}, 358 {"VectorGetArrayRead", ceedoffsetof(CeedVector, GetArrayRead)}, 359 {"VectorRestoreArray", ceedoffsetof(CeedVector, RestoreArray)}, 360 {"VectorRestoreArrayRead", ceedoffsetof(CeedVector, RestoreArrayRead)}, 361 {"VectorDestroy", ceedoffsetof(CeedVector, Destroy)}, 362 {"ElemRestrictionApply", ceedoffsetof(CeedElemRestriction, Apply)}, 363 {"ElemRestrictionDestroy", ceedoffsetof(CeedElemRestriction, Destroy)}, 364 {"BasisApply", ceedoffsetof(CeedBasis, Apply)}, 365 {"BasisDestroy", ceedoffsetof(CeedBasis, Destroy)}, 366 {"QFunctionApply", ceedoffsetof(CeedQFunction, Apply)}, 367 {"QFunctionDestroy", ceedoffsetof(CeedQFunction, Destroy)}, 368 {"OperatorApply", ceedoffsetof(CeedOperator, Apply)}, 369 {"OperatorApplyJacobian", ceedoffsetof(CeedOperator, ApplyJacobian)}, 370 {"OperatorDestroy", ceedoffsetof(CeedOperator, Destroy)} 371 }; 372 373 memcpy((*ceed)->foffsets, foffsets, 374 CEED_NUM_BACKEND_FUNCTIONS*sizeof(foffset)); 375 376 // Backend specific setup 377 ierr = backends[matchidx].init(resource, *ceed); CeedChk(ierr); 378 379 return 0; 380 } 381 382 /** 383 @brief Retrieve a delegate CEED 384 385 @param ceed Ceed to retrieve delegate of 386 @param[out] delegate Address to save the delegate to 387 388 @return An error code: 0 - success, otherwise - failure 389 390 @ref Developer 391 **/ 392 int CeedGetDelegate(Ceed ceed, Ceed *delegate) { 393 *delegate = ceed->delegate; 394 return 0; 395 } 396 397 /** 398 @brief Set a delegate CEED 399 400 @param ceed Ceed to set delegate of 401 @param[out] delegate Address to set the delegate to 402 403 @return An error code: 0 - success, otherwise - failure 404 405 @ref Advanced 406 **/ 407 int CeedSetDelegate(Ceed ceed, Ceed *delegate) { 408 ceed->delegate = *delegate; 409 return 0; 410 } 411 412 /** 413 @brief Set a backend function 414 415 @param ceed Ceed for error handling 416 @param type Type of Ceed object to set function for 417 @param[out] object Ceed object to set function for 418 @param fname Name of function to set 419 @param f Function to set 420 421 @return An error code: 0 - success, otherwise - failure 422 423 @ref Advanced 424 **/ 425 int CeedSetBackendFunction(Ceed ceed, 426 const char *type, void *object, 427 const char *fname, int (*f)()) { 428 char lookupname[100]; 429 strcpy(lookupname, ""); 430 431 // Build lookup name 432 strcat(strcat(lookupname, type), fname); 433 434 // Find and use offset 435 for (CeedInt i = 0; i < CEED_NUM_BACKEND_FUNCTIONS; i++) { 436 if (!strcmp(ceed->foffsets[i].fname, lookupname)) { 437 size_t offset = ceed->foffsets[i].offset; 438 size_t *fpointer; 439 fpointer = (size_t *)(object + offset); 440 *fpointer = (size_t) f; 441 return 0; 442 } 443 } 444 445 return CeedError(ceed, 1, 446 "Requested function '%s' was not found for CEED object '%s'", fname, type); 447 } 448 449 /** 450 @brief Retrieve backend data for a CEED 451 452 @param ceed Ceed to retrieve data of 453 @param[out] data Address to save data to 454 455 @return An error code: 0 - success, otherwise - failure 456 457 @ref Advanced 458 **/ 459 int CeedGetData(Ceed ceed, void* *data) { 460 *data = ceed->data; 461 return 0; 462 } 463 464 /** 465 @brief Set backend data for a CEED 466 467 @param ceed Ceed to set data of 468 @param data Address of data to set 469 470 @return An error code: 0 - success, otherwise - failure 471 472 @ref Advanced 473 **/ 474 int CeedSetData(Ceed ceed, void* *data) { 475 ceed->data = *data; 476 return 0; 477 } 478 479 /** 480 @brief Destroy a Ceed context 481 482 @param ceed Address of Ceed context to destroy 483 484 @return An error code: 0 - success, otherwise - failure 485 486 @ref Basic 487 **/ 488 int CeedDestroy(Ceed *ceed) { 489 int ierr; 490 491 if (!*ceed || --(*ceed)->refcount > 0) return 0; 492 if ((*ceed)->delegate) { 493 ierr = CeedDestroy(&(*ceed)->delegate); CeedChk(ierr); 494 } 495 if ((*ceed)->Destroy) { 496 ierr = (*ceed)->Destroy(*ceed); CeedChk(ierr); 497 } 498 ierr = CeedFree(ceed); CeedChk(ierr); 499 return 0; 500 } 501 502 /// @} 503