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 <limits.h> 21 #include <stdarg.h> 22 #include <stddef.h> 23 #include <stdio.h> 24 #include <stdlib.h> 25 #include <string.h> 26 27 /// @cond DOXYGEN_SKIP 28 static CeedRequest ceed_request_immediate; 29 static CeedRequest ceed_request_ordered; 30 31 static struct { 32 char prefix[CEED_MAX_RESOURCE_LEN]; 33 int (*init)(const char *resource, Ceed f); 34 unsigned int priority; 35 } backends[32]; 36 static size_t num_backends; 37 38 #define CEED_FTABLE_ENTRY(class, method) \ 39 {#class #method, offsetof(struct class ##_private, method)} 40 /// @endcond 41 42 /// @file 43 /// Implementation of core components of Ceed library 44 /// 45 /// @addtogroup Ceed 46 /// @{ 47 48 /** 49 @brief Request immediate completion 50 51 This predefined constant is passed as the \ref CeedRequest argument to 52 interfaces when the caller wishes for the operation to be performed 53 immediately. The code 54 55 @code 56 CeedOperatorApply(op, ..., CEED_REQUEST_IMMEDIATE); 57 @endcode 58 59 is semantically equivalent to 60 61 @code 62 CeedRequest request; 63 CeedOperatorApply(op, ..., &request); 64 CeedRequestWait(&request); 65 @endcode 66 67 @sa CEED_REQUEST_ORDERED 68 **/ 69 CeedRequest *const CEED_REQUEST_IMMEDIATE = &ceed_request_immediate; 70 71 /** 72 @brief Request ordered completion 73 74 This predefined constant is passed as the \ref CeedRequest argument to 75 interfaces when the caller wishes for the operation to be completed in the 76 order that it is submitted to the device. It is typically used in a construct 77 such as 78 79 @code 80 CeedRequest request; 81 CeedOperatorApply(op1, ..., CEED_REQUEST_ORDERED); 82 CeedOperatorApply(op2, ..., &request); 83 // other optional work 84 CeedWait(&request); 85 @endcode 86 87 which allows the sequence to complete asynchronously but does not start 88 `op2` until `op1` has completed. 89 90 @todo The current implementation is overly strict, offering equivalent 91 semantics to CEED_REQUEST_IMMEDIATE. 92 93 @sa CEED_REQUEST_IMMEDIATE 94 */ 95 CeedRequest *const CEED_REQUEST_ORDERED = &ceed_request_ordered; 96 97 /** 98 @brief Error handling implementation; use \ref CeedError instead. 99 100 @ref Developer 101 **/ 102 int CeedErrorImpl(Ceed ceed, const char *filename, int lineno, const char *func, 103 int ecode, const char *format, ...) { 104 va_list args; 105 int retval; 106 va_start(args, format); 107 if (ceed) { 108 retval = ceed->Error(ceed, filename, lineno, func, ecode, format, args); 109 } else { 110 // This function doesn't actually return 111 retval = CeedErrorAbort(ceed, filename, lineno, func, ecode, format, args); 112 } 113 va_end(args); 114 return retval; 115 } 116 117 /** 118 @brief Error handler that returns without printing anything. 119 120 Pass this to CeedSetErrorHandler() to obtain this error handling behavior. 121 122 @ref Developer 123 **/ 124 int CeedErrorReturn(Ceed ceed, const char *filename, int lineno, 125 const char *func, int ecode, const char *format, 126 va_list args) { 127 return ecode; 128 } 129 130 /** 131 @brief Error handler that prints to stderr and aborts 132 133 Pass this to CeedSetErrorHandler() to obtain this error handling behavior. 134 135 @ref Developer 136 **/ 137 int CeedErrorAbort(Ceed ceed, const char *filename, int lineno, 138 const char *func, int ecode, 139 const char *format, va_list args) { 140 fprintf(stderr, "%s:%d in %s(): ", filename, lineno, func); 141 vfprintf(stderr, format, args); 142 fprintf(stderr, "\n"); 143 abort(); 144 return ecode; 145 } 146 147 /** 148 @brief Error handler that prints to stderr and exits 149 150 Pass this to CeedSetErrorHandler() to obtain this error handling behavior. 151 152 In contrast to CeedErrorAbort(), this exits without a signal, so atexit() 153 handlers (e.g., as used by gcov) are run. 154 155 @ref Developer 156 **/ 157 int CeedErrorExit(Ceed ceed, const char *filename, int lineno, 158 const char *func, int ecode, 159 const char *format, va_list args) { 160 fprintf(stderr, "%s:%d in %s(): ", filename, lineno, func); 161 vfprintf(stderr, format, args); 162 fprintf(stderr, "\n"); 163 exit(ecode); 164 return ecode; 165 } 166 167 /** 168 @brief Set error handler 169 170 A default error handler is set in CeedInit(). Use this function to change 171 the error handler to CeedErrorReturn(), CeedErrorAbort(), or a user-defined 172 error handler. 173 174 @ref Developer 175 **/ 176 int CeedSetErrorHandler(Ceed ceed, 177 int (eh)(Ceed, const char *, int, const char *, 178 int, const char *, va_list)) { 179 ceed->Error = eh; 180 return 0; 181 } 182 183 /** 184 @brief Register a Ceed backend 185 186 @param prefix Prefix of resources for this backend to respond to. For 187 example, the reference backend responds to "/cpu/self". 188 @param init Initialization function called by CeedInit() when the backend 189 is selected to drive the requested resource. 190 @param priority Integer priority. Lower values are preferred in case the 191 resource requested by CeedInit() has non-unique best prefix 192 match. 193 194 @return An error code: 0 - success, otherwise - failure 195 196 @ref Advanced 197 **/ 198 int CeedRegister(const char *prefix, 199 int (*init)(const char *, Ceed), unsigned int priority) { 200 if (num_backends >= sizeof(backends) / sizeof(backends[0])) 201 // LCOV_EXCL_START 202 return CeedError(NULL, 1, "Too many backends"); 203 // LCOV_EXCL_STOP 204 205 strncpy(backends[num_backends].prefix, prefix, CEED_MAX_RESOURCE_LEN); 206 backends[num_backends].prefix[CEED_MAX_RESOURCE_LEN-1] = 0; 207 backends[num_backends].init = init; 208 backends[num_backends].priority = priority; 209 num_backends++; 210 return 0; 211 } 212 213 /** 214 @brief Allocate an array on the host; use CeedMalloc() 215 216 Memory usage can be tracked by the library. This ensures sufficient 217 alignment for vectorization and should be used for large allocations. 218 219 @param n Number of units to allocate 220 @param unit Size of each unit 221 @param p Address of pointer to hold the result. 222 223 @return An error code: 0 - success, otherwise - failure 224 225 @sa CeedFree() 226 227 @ref Advanced 228 **/ 229 int CeedMallocArray(size_t n, size_t unit, void *p) { 230 int ierr = posix_memalign((void **)p, CEED_ALIGN, n*unit); 231 if (ierr) 232 // LCOV_EXCL_START 233 return CeedError(NULL, ierr, 234 "posix_memalign failed to allocate %zd members of size %zd\n", 235 n, unit); 236 // LCOV_EXCL_STOP 237 238 return 0; 239 } 240 241 /** 242 @brief Allocate a cleared (zeroed) array on the host; use CeedCalloc() 243 244 Memory usage can be tracked by the library. 245 246 @param n Number of units to allocate 247 @param unit Size of each unit 248 @param p Address of pointer to hold the result. 249 250 @return An error code: 0 - success, otherwise - failure 251 252 @sa CeedFree() 253 254 @ref Advanced 255 **/ 256 int CeedCallocArray(size_t n, size_t unit, void *p) { 257 *(void **)p = calloc(n, unit); 258 if (n && unit && !*(void **)p) 259 // LCOV_EXCL_START 260 return CeedError(NULL, 1, 261 "calloc failed to allocate %zd members of size %zd\n", 262 n, unit); 263 // LCOV_EXCL_STOP 264 265 return 0; 266 } 267 268 /** 269 @brief Reallocate an array on the host; use CeedRealloc() 270 271 Memory usage can be tracked by the library. 272 273 @param n Number of units to allocate 274 @param unit Size of each unit 275 @param p Address of pointer to hold the result. 276 277 @return An error code: 0 - success, otherwise - failure 278 279 @sa CeedFree() 280 281 @ref Advanced 282 **/ 283 int CeedReallocArray(size_t n, size_t unit, void *p) { 284 *(void **)p = realloc(*(void **)p, n*unit); 285 if (n && unit && !*(void **)p) 286 // LCOV_EXCL_START 287 return CeedError(NULL, 1, 288 "realloc failed to allocate %zd members of size %zd\n", 289 n, unit); 290 // LCOV_EXCL_STOP 291 292 return 0; 293 } 294 295 /// Free memory allocated using CeedMalloc() or CeedCalloc() 296 /// 297 /// @param p address of pointer to memory. This argument is of type void* to 298 /// avoid needing a cast, but is the address of the pointer (which is zeroed) 299 /// rather than the pointer. 300 int CeedFree(void *p) { 301 free(*(void **)p); 302 *(void **)p = NULL; 303 return 0; 304 } 305 306 /** 307 @brief Wait for a CeedRequest to complete. 308 309 Calling CeedRequestWait on a NULL request is a no-op. 310 311 @param req Address of CeedRequest to wait for; zeroed on completion. 312 313 @return An error code: 0 - success, otherwise - failure 314 315 @ref Advanced 316 **/ 317 int CeedRequestWait(CeedRequest *req) { 318 if (!*req) return 0; 319 return CeedError(NULL, 2, "CeedRequestWait not implemented"); 320 } 321 322 /** 323 @brief Initialize a \ref Ceed to use the specified resource. 324 325 @param resource Resource to use, e.g., "/cpu/self" 326 @param ceed The library context 327 @sa CeedRegister() CeedDestroy() 328 329 @return An error code: 0 - success, otherwise - failure 330 331 @ref Basic 332 **/ 333 int CeedInit(const char *resource, Ceed *ceed) { 334 int ierr; 335 size_t matchlen = 0, matchidx = UINT_MAX, matchpriority = UINT_MAX, priority; 336 337 // Find matching backend 338 if (!resource) return CeedError(NULL, 1, "No resource provided"); 339 for (size_t i=0; i<num_backends; i++) { 340 size_t n; 341 const char *prefix = backends[i].prefix; 342 for (n = 0; prefix[n] && prefix[n] == resource[n]; n++) {} 343 priority = backends[i].priority; 344 if (n > matchlen || (n == matchlen && matchpriority > priority)) { 345 matchlen = n; 346 matchpriority = priority; 347 matchidx = i; 348 } 349 } 350 if (!matchlen) return CeedError(NULL, 1, "No suitable backend"); 351 352 // Setup Ceed 353 ierr = CeedCalloc(1,ceed); CeedChk(ierr); 354 const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER"); 355 if (!ceed_error_handler) ceed_error_handler = "abort"; 356 if (!strcmp(ceed_error_handler, "exit")) 357 (*ceed)->Error = CeedErrorExit; 358 else 359 (*ceed)->Error = CeedErrorAbort; 360 (*ceed)->refcount = 1; 361 (*ceed)->data = NULL; 362 363 // Set lookup table 364 foffset foffsets[] = { 365 CEED_FTABLE_ENTRY(Ceed, Error), 366 CEED_FTABLE_ENTRY(Ceed, GetPreferredMemType), 367 CEED_FTABLE_ENTRY(Ceed, Destroy), 368 CEED_FTABLE_ENTRY(Ceed, VectorCreate), 369 CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreate), 370 CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreateBlocked), 371 CEED_FTABLE_ENTRY(Ceed, BasisCreateTensorH1), 372 CEED_FTABLE_ENTRY(Ceed, BasisCreateH1), 373 CEED_FTABLE_ENTRY(Ceed, TensorContractCreate), 374 CEED_FTABLE_ENTRY(Ceed, QFunctionCreate), 375 CEED_FTABLE_ENTRY(Ceed, OperatorCreate), 376 CEED_FTABLE_ENTRY(Ceed, CompositeOperatorCreate), 377 CEED_FTABLE_ENTRY(CeedVector, SetArray), 378 CEED_FTABLE_ENTRY(CeedVector, SetValue), 379 CEED_FTABLE_ENTRY(CeedVector, GetArray), 380 CEED_FTABLE_ENTRY(CeedVector, GetArrayRead), 381 CEED_FTABLE_ENTRY(CeedVector, RestoreArray), 382 CEED_FTABLE_ENTRY(CeedVector, RestoreArrayRead), 383 CEED_FTABLE_ENTRY(CeedVector, Destroy), 384 CEED_FTABLE_ENTRY(CeedElemRestriction, Apply), 385 CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyBlock), 386 CEED_FTABLE_ENTRY(CeedElemRestriction, Destroy), 387 CEED_FTABLE_ENTRY(CeedBasis, Apply), 388 CEED_FTABLE_ENTRY(CeedBasis, Destroy), 389 CEED_FTABLE_ENTRY(CeedTensorContract, Apply), 390 CEED_FTABLE_ENTRY(CeedTensorContract, Destroy), 391 CEED_FTABLE_ENTRY(CeedQFunction, Apply), 392 CEED_FTABLE_ENTRY(CeedQFunction, Destroy), 393 CEED_FTABLE_ENTRY(CeedOperator, Apply), 394 CEED_FTABLE_ENTRY(CeedOperator, ApplyJacobian), 395 CEED_FTABLE_ENTRY(CeedOperator, Destroy), 396 {NULL, 0} // End of lookup table - used in SetBackendFunction loop 397 }; 398 399 ierr = CeedCalloc(sizeof(foffsets), &(*ceed)->foffsets); CeedChk(ierr); 400 memcpy((*ceed)->foffsets, foffsets, sizeof(foffsets)); 401 402 // Backend specific setup 403 ierr = backends[matchidx].init(resource, *ceed); CeedChk(ierr); 404 405 return 0; 406 } 407 408 /** 409 @brief Retrieve a parent CEED 410 411 @param ceed Ceed to retrieve parent of 412 @param[out] parent Address to save the parent to 413 414 @return An error code: 0 - success, otherwise - failure 415 416 @ref Developer 417 **/ 418 int CeedGetParent(Ceed ceed, Ceed *parent) { 419 int ierr; 420 if (ceed->parent) { 421 ierr = CeedGetParent(ceed->parent, parent); CeedChk(ierr); 422 return 0; 423 } 424 *parent = ceed; 425 return 0; 426 } 427 428 /** 429 @brief Retrieve a delegate CEED 430 431 @param ceed Ceed to retrieve delegate of 432 @param[out] delegate Address to save the delegate to 433 434 @return An error code: 0 - success, otherwise - failure 435 436 @ref Developer 437 **/ 438 int CeedGetDelegate(Ceed ceed, Ceed *delegate) { 439 *delegate = ceed->delegate; 440 return 0; 441 } 442 443 /** 444 @brief Set a delegate CEED 445 446 This function allows a CEED to set a delegate CEED. All backend 447 implementations default to the delegate CEED, unless overridden. 448 449 @param ceed Ceed to set delegate of 450 @param[out] delegate Address to set the delegate to 451 452 @return An error code: 0 - success, otherwise - failure 453 454 @ref Advanced 455 **/ 456 int CeedSetDelegate(Ceed ceed, Ceed delegate) { 457 ceed->delegate = delegate; 458 delegate->parent = ceed; 459 return 0; 460 } 461 462 /** 463 @brief Retrieve a delegate CEED for a specific object type 464 465 @param ceed Ceed to retrieve delegate of 466 @param[out] delegate Address to save the delegate to 467 @param[in] objname Name of the object type to retrieve delegate for 468 469 @return An error code: 0 - success, otherwise - failure 470 471 @ref Developer 472 **/ 473 int CeedGetObjectDelegate(Ceed ceed, Ceed *delegate, const char *objname) { 474 CeedInt ierr; 475 476 // Check for object delegate 477 for (CeedInt i=0; i<ceed->objdelegatecount; i++) { 478 if (!strcmp(objname, ceed->objdelegates->objname)) { 479 *delegate = ceed->objdelegates->delegate; 480 return 0; 481 } 482 } 483 484 // Use default delegate if no object delegate 485 ierr = CeedGetDelegate(ceed, delegate); CeedChk(ierr); 486 487 return 0; 488 } 489 490 /** 491 @brief Set a delegate CEED for a specific object type 492 493 This function allows a CEED to set a delegate CEED for a given type of 494 CEED object. All backend implementations default to the delegate CEED for 495 this object. For example, 496 CeedSetObjectDelegate(ceed, refceed, "Basis") 497 uses refceed implementations for all CeedBasis backend functions. 498 499 @param ceed Ceed to set delegate of 500 @param[out] delegate Address to set the delegate to 501 @param[in] objname Name of the object type to set delegate for 502 503 @return An error code: 0 - success, otherwise - failure 504 505 @ref Advanced 506 **/ 507 int CeedSetObjectDelegate(Ceed ceed, Ceed delegate, const char *objname) { 508 CeedInt ierr; 509 CeedInt count = ceed->objdelegatecount; 510 511 // Malloc or Realloc 512 if (count) { 513 ierr = CeedRealloc(count+1, &ceed->objdelegates); 514 CeedChk(ierr); 515 } else { 516 ierr = CeedCalloc(1, &ceed->objdelegates); CeedChk(ierr); 517 } 518 ceed->objdelegatecount++; 519 520 // Set object delegate 521 ceed->objdelegates[count].delegate = delegate; 522 ierr = CeedCalloc(strlen(objname)+1, &ceed->objdelegates[count].objname); 523 CeedChk(ierr); 524 CeedInt len = strlen(ceed->objdelegates[count].objname); 525 strncpy(ceed->objdelegates[count].objname, objname, len); 526 ceed->objdelegates[count].objname[len-1] = 0; 527 528 // Set delegate parent 529 delegate->parent = ceed; 530 531 return 0; 532 } 533 534 /** 535 @brief Return Ceed preferred memory type 536 537 @param ceed Ceed to get preferred memory type of 538 @param[out] type Address to save preferred memory type to 539 540 @return An error code: 0 - success, otherwise - failure 541 542 @ref Basic 543 **/ 544 int CeedGetPreferredMemType(Ceed ceed, CeedMemType *type) { 545 int ierr; 546 547 if (ceed->GetPreferredMemType) { 548 ierr = ceed->GetPreferredMemType(type); CeedChk(ierr); 549 } else { 550 Ceed delegate; 551 ierr = CeedGetDelegate(ceed, &delegate); CeedChk(ierr); 552 553 if (delegate) { 554 ierr = CeedGetPreferredMemType(delegate, type); CeedChk(ierr); 555 } else { 556 *type = CEED_MEM_HOST; 557 } 558 } 559 560 return 0; 561 } 562 563 /** 564 @brief Set a backend function 565 566 This function is used for a backend to set the function associated with 567 the CEED objects. For example, 568 CeedSetBackendFunction(ceed, "Ceed", ceed, "VectorCreate", BackendVectorCreate) 569 sets the backend implementation of 'CeedVectorCreate' and 570 CeedSetBackendFunction(ceed, "Basis", basis, "Apply", BackendBasisApply) 571 sets the backend implementation of 'CeedBasisApply'. Note, the prefix 'Ceed' 572 is not required for the object type ("Basis" vs "CeedBasis"). 573 574 @param ceed Ceed for error handling 575 @param type Type of Ceed object to set function for 576 @param[out] object Ceed object to set function for 577 @param fname Name of function to set 578 @param f Function to set 579 580 @return An error code: 0 - success, otherwise - failure 581 582 @ref Advanced 583 **/ 584 int CeedSetBackendFunction(Ceed ceed, 585 const char *type, void *object, 586 const char *fname, int (*f)()) { 587 char lookupname[CEED_MAX_RESOURCE_LEN+1] = ""; 588 589 // Build lookup name 590 if (strcmp(type, "Ceed")) 591 strncat (lookupname, "Ceed", CEED_MAX_RESOURCE_LEN); 592 strncat(lookupname, type, CEED_MAX_RESOURCE_LEN); 593 strncat(lookupname, fname, CEED_MAX_RESOURCE_LEN); 594 595 // Find and use offset 596 for (CeedInt i = 0; ceed->foffsets[i].fname; i++) { 597 if (!strcmp(ceed->foffsets[i].fname, lookupname)) { 598 size_t offset = ceed->foffsets[i].offset; 599 int (**fpointer)(void) = (int (* *)(void))((char *)object + offset); 600 *fpointer = f; 601 return 0; 602 } 603 } 604 605 // LCOV_EXCL_START 606 return CeedError(ceed, 1, 607 "Requested function '%s' was not found for CEED object '%s'", 608 fname, type); 609 // LCOV_EXCL_STOP 610 } 611 612 /** 613 @brief Retrieve backend data for a CEED 614 615 @param ceed Ceed to retrieve data of 616 @param[out] data Address to save data to 617 618 @return An error code: 0 - success, otherwise - failure 619 620 @ref Advanced 621 **/ 622 int CeedGetData(Ceed ceed, void* *data) { 623 *data = ceed->data; 624 return 0; 625 } 626 627 /** 628 @brief Set backend data for a CEED 629 630 @param ceed Ceed to set data of 631 @param data Address of data to set 632 633 @return An error code: 0 - success, otherwise - failure 634 635 @ref Advanced 636 **/ 637 int CeedSetData(Ceed ceed, void* *data) { 638 ceed->data = *data; 639 return 0; 640 } 641 642 /** 643 @brief Destroy a Ceed context 644 645 @param ceed Address of Ceed context to destroy 646 647 @return An error code: 0 - success, otherwise - failure 648 649 @ref Basic 650 **/ 651 int CeedDestroy(Ceed *ceed) { 652 int ierr; 653 654 if (!*ceed || --(*ceed)->refcount > 0) return 0; 655 if ((*ceed)->delegate) { 656 ierr = CeedDestroy(&(*ceed)->delegate); CeedChk(ierr); 657 } 658 if ((*ceed)->objdelegatecount > 0) { 659 for (int i=0; i<(*ceed)->objdelegatecount; i++) { 660 ierr = CeedDestroy(&((*ceed)->objdelegates[i].delegate)); CeedChk(ierr); 661 ierr = CeedFree(&(*ceed)->objdelegates[i].objname); CeedChk(ierr); 662 } 663 ierr = CeedFree(&(*ceed)->objdelegates); CeedChk(ierr); 664 } 665 if ((*ceed)->Destroy) { 666 ierr = (*ceed)->Destroy(*ceed); CeedChk(ierr); 667 } 668 ierr = CeedFree(&(*ceed)->foffsets); CeedChk(ierr); 669 ierr = CeedFree(ceed); CeedChk(ierr); 670 return 0; 671 } 672 673 /// @} 674