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