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