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, const char *format, 139 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, "posix_memalign failed to allocate %zd " 234 "members of size %zd\n", n, unit); 235 // LCOV_EXCL_STOP 236 237 return 0; 238 } 239 240 /** 241 @brief Allocate a cleared (zeroed) array on the host; use CeedCalloc() 242 243 Memory usage can be tracked by the library. 244 245 @param n Number of units to allocate 246 @param unit Size of each unit 247 @param p Address of pointer to hold the result. 248 249 @return An error code: 0 - success, otherwise - failure 250 251 @sa CeedFree() 252 253 @ref Advanced 254 **/ 255 int CeedCallocArray(size_t n, size_t unit, void *p) { 256 *(void **)p = calloc(n, unit); 257 if (n && unit && !*(void **)p) 258 // LCOV_EXCL_START 259 return CeedError(NULL, 1, "calloc failed to allocate %zd members of size " 260 "%zd\n", n, unit); 261 // LCOV_EXCL_STOP 262 263 return 0; 264 } 265 266 /** 267 @brief Reallocate an array on the host; use CeedRealloc() 268 269 Memory usage can be tracked by the library. 270 271 @param n Number of units to allocate 272 @param unit Size of each unit 273 @param p Address of pointer to hold the result. 274 275 @return An error code: 0 - success, otherwise - failure 276 277 @sa CeedFree() 278 279 @ref Advanced 280 **/ 281 int CeedReallocArray(size_t n, size_t unit, void *p) { 282 *(void **)p = realloc(*(void **)p, n*unit); 283 if (n && unit && !*(void **)p) 284 // LCOV_EXCL_START 285 return CeedError(NULL, 1, "realloc failed to allocate %zd members of size " 286 "%zd\n", n, unit); 287 // LCOV_EXCL_STOP 288 289 return 0; 290 } 291 292 /// Free memory allocated using CeedMalloc() or CeedCalloc() 293 /// 294 /// @param p address of pointer to memory. This argument is of type void* to 295 /// avoid needing a cast, but is the address of the pointer (which is zeroed) 296 /// rather than the pointer. 297 int CeedFree(void *p) { 298 free(*(void **)p); 299 *(void **)p = NULL; 300 return 0; 301 } 302 303 /** 304 @brief Wait for a CeedRequest to complete. 305 306 Calling CeedRequestWait on a NULL request is a no-op. 307 308 @param req Address of CeedRequest to wait for; zeroed on completion. 309 310 @return An error code: 0 - success, otherwise - failure 311 312 @ref Advanced 313 **/ 314 int CeedRequestWait(CeedRequest *req) { 315 if (!*req) 316 return 0; 317 return CeedError(NULL, 2, "CeedRequestWait not implemented"); 318 } 319 320 /** 321 @brief Initialize a \ref Ceed to use the specified resource. 322 323 @param resource Resource to use, e.g., "/cpu/self" 324 @param ceed The library context 325 @sa CeedRegister() CeedDestroy() 326 327 @return An error code: 0 - success, otherwise - failure 328 329 @ref Basic 330 **/ 331 int CeedInit(const char *resource, Ceed *ceed) { 332 int ierr; 333 size_t matchlen = 0, matchidx = UINT_MAX, matchpriority = UINT_MAX, priority; 334 335 // Find matching backend 336 if (!resource) 337 return CeedError(NULL, 1, "No resource provided"); 338 for (size_t i=0; i<num_backends; i++) { 339 size_t n; 340 const char *prefix = backends[i].prefix; 341 for (n = 0; prefix[n] && prefix[n] == resource[n]; n++) {} 342 priority = backends[i].priority; 343 if (n > matchlen || (n == matchlen && matchpriority > priority)) { 344 matchlen = n; 345 matchpriority = priority; 346 matchidx = i; 347 } 348 } 349 if (!matchlen) 350 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) 356 ceed_error_handler = "abort"; 357 if (!strcmp(ceed_error_handler, "exit")) 358 (*ceed)->Error = CeedErrorExit; 359 else 360 (*ceed)->Error = CeedErrorAbort; 361 (*ceed)->refcount = 1; 362 (*ceed)->data = NULL; 363 364 // Set lookup table 365 foffset foffsets[] = { 366 CEED_FTABLE_ENTRY(Ceed, Error), 367 CEED_FTABLE_ENTRY(Ceed, GetPreferredMemType), 368 CEED_FTABLE_ENTRY(Ceed, Destroy), 369 CEED_FTABLE_ENTRY(Ceed, VectorCreate), 370 CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreate), 371 CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreateBlocked), 372 CEED_FTABLE_ENTRY(Ceed, BasisCreateTensorH1), 373 CEED_FTABLE_ENTRY(Ceed, BasisCreateH1), 374 CEED_FTABLE_ENTRY(Ceed, TensorContractCreate), 375 CEED_FTABLE_ENTRY(Ceed, QFunctionCreate), 376 CEED_FTABLE_ENTRY(Ceed, OperatorCreate), 377 CEED_FTABLE_ENTRY(Ceed, CompositeOperatorCreate), 378 CEED_FTABLE_ENTRY(CeedVector, SetArray), 379 CEED_FTABLE_ENTRY(CeedVector, SetValue), 380 CEED_FTABLE_ENTRY(CeedVector, GetArray), 381 CEED_FTABLE_ENTRY(CeedVector, GetArrayRead), 382 CEED_FTABLE_ENTRY(CeedVector, RestoreArray), 383 CEED_FTABLE_ENTRY(CeedVector, RestoreArrayRead), 384 CEED_FTABLE_ENTRY(CeedVector, Destroy), 385 CEED_FTABLE_ENTRY(CeedElemRestriction, Apply), 386 CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyBlock), 387 CEED_FTABLE_ENTRY(CeedElemRestriction, Destroy), 388 CEED_FTABLE_ENTRY(CeedBasis, Apply), 389 CEED_FTABLE_ENTRY(CeedBasis, Destroy), 390 CEED_FTABLE_ENTRY(CeedTensorContract, Apply), 391 CEED_FTABLE_ENTRY(CeedTensorContract, Destroy), 392 CEED_FTABLE_ENTRY(CeedQFunction, Apply), 393 CEED_FTABLE_ENTRY(CeedQFunction, Destroy), 394 CEED_FTABLE_ENTRY(CeedOperator, AssembleLinearQFunction), 395 CEED_FTABLE_ENTRY(CeedOperator, AssembleLinearDiagonal), 396 CEED_FTABLE_ENTRY(CeedOperator, Apply), 397 CEED_FTABLE_ENTRY(CeedOperator, ApplyJacobian), 398 CEED_FTABLE_ENTRY(CeedOperator, Destroy), 399 {NULL, 0} // End of lookup table - used in SetBackendFunction loop 400 }; 401 402 ierr = CeedCalloc(sizeof(foffsets), &(*ceed)->foffsets); CeedChk(ierr); 403 memcpy((*ceed)->foffsets, foffsets, sizeof(foffsets)); 404 405 // Backend specific setup 406 ierr = backends[matchidx].init(resource, *ceed); CeedChk(ierr); 407 408 // Copy resource prefix, if backend setup sucessful 409 size_t len = strlen(backends[matchidx].prefix); 410 char *tmp; 411 ierr = CeedCalloc(len+1, &tmp); CeedChk(ierr); 412 memcpy(tmp, backends[matchidx].prefix, len+1); 413 (*ceed)->resource = tmp; 414 415 return 0; 416 } 417 418 /** 419 @brief Retrieve a parent CEED 420 421 @param ceed Ceed to retrieve parent of 422 @param[out] parent Address to save the parent to 423 424 @return An error code: 0 - success, otherwise - failure 425 426 @ref Developer 427 **/ 428 int CeedGetParent(Ceed ceed, Ceed *parent) { 429 int ierr; 430 if (ceed->parent) { 431 ierr = CeedGetParent(ceed->parent, parent); CeedChk(ierr); 432 return 0; 433 } 434 *parent = ceed; 435 return 0; 436 } 437 438 /** 439 @brief Retrieve a delegate CEED 440 441 @param ceed Ceed to retrieve delegate of 442 @param[out] delegate Address to save the delegate to 443 444 @return An error code: 0 - success, otherwise - failure 445 446 @ref Developer 447 **/ 448 int CeedGetDelegate(Ceed ceed, Ceed *delegate) { 449 *delegate = ceed->delegate; 450 return 0; 451 } 452 453 /** 454 @brief Set a delegate CEED 455 456 This function allows a CEED to set a delegate CEED. All backend 457 implementations default to the delegate CEED, unless overridden. 458 459 @param ceed Ceed to set delegate of 460 @param[out] delegate Address to set the delegate to 461 462 @return An error code: 0 - success, otherwise - failure 463 464 @ref Advanced 465 **/ 466 int CeedSetDelegate(Ceed ceed, Ceed delegate) { 467 ceed->delegate = delegate; 468 delegate->parent = ceed; 469 return 0; 470 } 471 472 /** 473 @brief Retrieve a delegate CEED for a specific object type 474 475 @param ceed Ceed to retrieve delegate of 476 @param[out] delegate Address to save the delegate to 477 @param[in] objname Name of the object type to retrieve delegate for 478 479 @return An error code: 0 - success, otherwise - failure 480 481 @ref Developer 482 **/ 483 int CeedGetObjectDelegate(Ceed ceed, Ceed *delegate, const char *objname) { 484 CeedInt ierr; 485 486 // Check for object delegate 487 for (CeedInt i=0; i<ceed->objdelegatecount; i++) 488 if (!strcmp(objname, ceed->objdelegates->objname)) { 489 *delegate = ceed->objdelegates->delegate; 490 return 0; 491 } 492 493 // Use default delegate if no object delegate 494 ierr = CeedGetDelegate(ceed, delegate); CeedChk(ierr); 495 496 return 0; 497 } 498 499 /** 500 @brief Set a delegate CEED for a specific object type 501 502 This function allows a CEED to set a delegate CEED for a given type of 503 CEED object. All backend implementations default to the delegate CEED for 504 this object. For example, 505 CeedSetObjectDelegate(ceed, refceed, "Basis") 506 uses refceed implementations for all CeedBasis backend functions. 507 508 @param ceed Ceed to set delegate of 509 @param[out] delegate Address to set the delegate to 510 @param[in] objname Name of the object type to set delegate for 511 512 @return An error code: 0 - success, otherwise - failure 513 514 @ref Advanced 515 **/ 516 int CeedSetObjectDelegate(Ceed ceed, Ceed delegate, const char *objname) { 517 CeedInt ierr; 518 CeedInt count = ceed->objdelegatecount; 519 520 // Malloc or Realloc 521 if (count) { 522 ierr = CeedRealloc(count+1, &ceed->objdelegates); CeedChk(ierr); 523 } else { 524 ierr = CeedCalloc(1, &ceed->objdelegates); CeedChk(ierr); 525 } 526 ceed->objdelegatecount++; 527 528 // Set object delegate 529 ceed->objdelegates[count].delegate = delegate; 530 size_t slen = strlen(objname) + 1; 531 ierr = CeedMalloc(slen, &ceed->objdelegates[count].objname); CeedChk(ierr); 532 memcpy(ceed->objdelegates[count].objname, objname, slen); 533 534 // Set delegate parent 535 delegate->parent = ceed; 536 537 return 0; 538 } 539 540 /** 541 @brief Return Ceed preferred memory type 542 543 @param ceed Ceed to get preferred memory type of 544 @param[out] type Address to save preferred memory type to 545 546 @return An error code: 0 - success, otherwise - failure 547 548 @ref Basic 549 **/ 550 int CeedGetPreferredMemType(Ceed ceed, CeedMemType *type) { 551 int ierr; 552 553 if (ceed->GetPreferredMemType) { 554 ierr = ceed->GetPreferredMemType(type); CeedChk(ierr); 555 } else { 556 Ceed delegate; 557 ierr = CeedGetDelegate(ceed, &delegate); CeedChk(ierr); 558 559 if (delegate) { 560 ierr = CeedGetPreferredMemType(delegate, type); CeedChk(ierr); 561 } else { 562 *type = CEED_MEM_HOST; 563 } 564 } 565 566 return 0; 567 } 568 569 /** 570 @brief Set a backend function 571 572 This function is used for a backend to set the function associated with 573 the CEED objects. For example, 574 CeedSetBackendFunction(ceed, "Ceed", ceed, "VectorCreate", BackendVectorCreate) 575 sets the backend implementation of 'CeedVectorCreate' and 576 CeedSetBackendFunction(ceed, "Basis", basis, "Apply", BackendBasisApply) 577 sets the backend implementation of 'CeedBasisApply'. Note, the prefix 'Ceed' 578 is not required for the object type ("Basis" vs "CeedBasis"). 579 580 @param ceed Ceed for error handling 581 @param type Type of Ceed object to set function for 582 @param[out] object Ceed object to set function for 583 @param fname Name of function to set 584 @param f Function to set 585 586 @return An error code: 0 - success, otherwise - failure 587 588 @ref Advanced 589 **/ 590 int CeedSetBackendFunction(Ceed ceed, 591 const char *type, void *object, 592 const char *fname, int (*f)()) { 593 char lookupname[CEED_MAX_RESOURCE_LEN+1] = ""; 594 595 // Build lookup name 596 if (strcmp(type, "Ceed")) 597 strncat (lookupname, "Ceed", CEED_MAX_RESOURCE_LEN); 598 strncat(lookupname, type, CEED_MAX_RESOURCE_LEN); 599 strncat(lookupname, fname, CEED_MAX_RESOURCE_LEN); 600 601 // Find and use offset 602 for (CeedInt i = 0; ceed->foffsets[i].fname; i++) 603 if (!strcmp(ceed->foffsets[i].fname, lookupname)) { 604 size_t offset = ceed->foffsets[i].offset; 605 int (**fpointer)(void) = (int (* *)(void))((char *)object + offset); 606 *fpointer = f; 607 return 0; 608 } 609 610 // LCOV_EXCL_START 611 return CeedError(ceed, 1, "Requested function '%s' was not found for CEED " 612 "object '%s'", fname, type); 613 // LCOV_EXCL_STOP 614 } 615 616 /** 617 @brief Retrieve backend data for a CEED 618 619 @param ceed Ceed to retrieve data of 620 @param[out] data Address to save data to 621 622 @return An error code: 0 - success, otherwise - failure 623 624 @ref Advanced 625 **/ 626 int CeedGetData(Ceed ceed, void* *data) { 627 *data = ceed->data; 628 return 0; 629 } 630 631 /** 632 @brief Set backend data for a CEED 633 634 @param ceed Ceed to set data of 635 @param data Address of data to set 636 637 @return An error code: 0 - success, otherwise - failure 638 639 @ref Advanced 640 **/ 641 int CeedSetData(Ceed ceed, void* *data) { 642 ceed->data = *data; 643 return 0; 644 } 645 646 /** 647 @brief Get the full resource name for a CEED 648 649 @param ceed Ceed to get resource name of 650 @param[out] resource Variable to store resource name 651 652 @return An error code: 0 - success, otherwise - failure 653 654 @ref Basic 655 **/ 656 657 int CeedGetResource(Ceed ceed, const char **resource) { 658 *resource = (const char *)ceed->resource; 659 return 0; 660 } 661 662 /** 663 @brief Destroy a Ceed context 664 665 @param ceed Address of Ceed context to destroy 666 667 @return An error code: 0 - success, otherwise - failure 668 669 @ref Basic 670 **/ 671 int CeedDestroy(Ceed *ceed) { 672 int ierr; 673 674 if (!*ceed || --(*ceed)->refcount > 0) 675 return 0; 676 if ((*ceed)->delegate) { 677 ierr = CeedDestroy(&(*ceed)->delegate); CeedChk(ierr); 678 } 679 if ((*ceed)->objdelegatecount > 0) { 680 for (int i=0; i<(*ceed)->objdelegatecount; i++) { 681 ierr = CeedDestroy(&((*ceed)->objdelegates[i].delegate)); CeedChk(ierr); 682 ierr = CeedFree(&(*ceed)->objdelegates[i].objname); CeedChk(ierr); 683 } 684 ierr = CeedFree(&(*ceed)->objdelegates); CeedChk(ierr); 685 } 686 if ((*ceed)->Destroy) { 687 ierr = (*ceed)->Destroy(*ceed); CeedChk(ierr); 688 } 689 ierr = CeedFree(&(*ceed)->foffsets); CeedChk(ierr); 690 ierr = CeedFree(&(*ceed)->resource); CeedChk(ierr); 691 ierr = CeedFree(ceed); CeedChk(ierr); 692 return 0; 693 } 694 695 /// @} 696