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 va_start(args, format); 106 if (ceed) return ceed->Error(ceed, filename, lineno, func, ecode, format, args); 107 return CeedErrorAbort(ceed, filename, lineno, func, ecode, format, args); 108 } 109 110 /** 111 @brief Error handler that returns without printing anything. 112 113 Pass this to CeedSetErrorHandler() to obtain this error handling behavior. 114 115 @ref Developer 116 **/ 117 int CeedErrorReturn(Ceed ceed, const char *filename, int lineno, 118 const char *func, int ecode, const char *format, 119 va_list args) { 120 return ecode; 121 } 122 123 /** 124 @brief Error handler that prints to stderr and aborts 125 126 Pass this to CeedSetErrorHandler() to obtain this error handling behavior. 127 128 @ref Developer 129 **/ 130 int CeedErrorAbort(Ceed ceed, const char *filename, int lineno, 131 const char *func, int ecode, 132 const char *format, va_list args) { 133 fprintf(stderr, "%s:%d in %s(): ", filename, lineno, func); 134 vfprintf(stderr, format, args); 135 fprintf(stderr, "\n"); 136 abort(); 137 return ecode; 138 } 139 140 /** 141 @brief Error handler that prints to stderr and exits 142 143 Pass this to CeedSetErrorHandler() to obtain this error handling behavior. 144 145 In contrast to CeedErrorAbort(), this exits without a signal, so atexit() 146 handlers (e.g., as used by gcov) are run. 147 148 @ref Developer 149 **/ 150 int CeedErrorExit(Ceed ceed, const char *filename, int lineno, 151 const char *func, int ecode, 152 const char *format, va_list args) { 153 fprintf(stderr, "%s:%d in %s(): ", filename, lineno, func); 154 vfprintf(stderr, format, args); 155 fprintf(stderr, "\n"); 156 exit(ecode); 157 return ecode; 158 } 159 160 /** 161 @brief Set error handler 162 163 A default error handler is set in CeedInit(). Use this function to change 164 the error handler to CeedErrorReturn(), CeedErrorAbort(), or a user-defined 165 error handler. 166 167 @ref Developer 168 **/ 169 int CeedSetErrorHandler(Ceed ceed, 170 int (eh)(Ceed, const char *, int, const char *, 171 int, const char *, va_list)) { 172 ceed->Error = eh; 173 return 0; 174 } 175 176 /** 177 @brief Register a Ceed backend 178 179 @param prefix Prefix of resources for this backend to respond to. For 180 example, the reference backend responds to "/cpu/self". 181 @param init Initialization function called by CeedInit() when the backend 182 is selected to drive the requested resource. 183 @param priority Integer priority. Lower values are preferred in case the 184 resource requested by CeedInit() has non-unique best prefix 185 match. 186 187 @return An error code: 0 - success, otherwise - failure 188 189 @ref Advanced 190 **/ 191 int CeedRegister(const char *prefix, 192 int (*init)(const char *, Ceed), unsigned int priority) { 193 if (num_backends >= sizeof(backends) / sizeof(backends[0])) { 194 return CeedError(NULL, 1, "Too many backends"); 195 } 196 strncpy(backends[num_backends].prefix, prefix, CEED_MAX_RESOURCE_LEN); 197 backends[num_backends].init = init; 198 backends[num_backends].priority = priority; 199 num_backends++; 200 return 0; 201 } 202 203 /** 204 @brief Allocate an array on the host; use CeedMalloc() 205 206 Memory usage can be tracked by the library. This ensures sufficient 207 alignment for vectorization and should be used for large allocations. 208 209 @param n Number of units to allocate 210 @param unit Size of each unit 211 @param p Address of pointer to hold the result. 212 213 @return An error code: 0 - success, otherwise - failure 214 215 @sa CeedFree() 216 217 @ref Advanced 218 **/ 219 int CeedMallocArray(size_t n, size_t unit, void *p) { 220 int ierr = posix_memalign((void **)p, CEED_ALIGN, n*unit); 221 if (ierr) 222 return CeedError(NULL, ierr, 223 "posix_memalign failed to allocate %zd members of size %zd\n", n, unit); 224 return 0; 225 } 226 227 /** 228 @brief Allocate a cleared (zeroed) array on the host; use CeedCalloc() 229 230 Memory usage can be tracked by the library. 231 232 @param n Number of units to allocate 233 @param unit Size of each unit 234 @param p Address of pointer to hold the result. 235 236 @return An error code: 0 - success, otherwise - failure 237 238 @sa CeedFree() 239 240 @ref Advanced 241 **/ 242 int CeedCallocArray(size_t n, size_t unit, void *p) { 243 *(void **)p = calloc(n, unit); 244 if (n && unit && !*(void **)p) 245 return CeedError(NULL, 1, "calloc failed to allocate %zd members of size %zd\n", 246 n, unit); 247 return 0; 248 } 249 250 /** 251 @brief Reallocate an array on the host; use CeedRealloc() 252 253 Memory usage can be tracked by the library. 254 255 @param n Number of units to allocate 256 @param unit Size of each unit 257 @param p Address of pointer to hold the result. 258 259 @return An error code: 0 - success, otherwise - failure 260 261 @sa CeedFree() 262 263 @ref Advanced 264 **/ 265 int CeedReallocArray(size_t n, size_t unit, void *p) { 266 *(void **)p = realloc(*(void **)p, n*unit); 267 if (n && unit && !*(void **)p) 268 return CeedError(NULL, 1, 269 "realloc failed to allocate %zd members of size %zd\n", 270 n, unit); 271 return 0; 272 } 273 274 /// Free memory allocated using CeedMalloc() or CeedCalloc() 275 /// 276 /// @param p address of pointer to memory. This argument is of type void* to 277 /// avoid needing a cast, but is the address of the pointer (which is zeroed) 278 /// rather than the pointer. 279 int CeedFree(void *p) { 280 free(*(void **)p); 281 *(void **)p = NULL; 282 return 0; 283 } 284 285 /** 286 @brief Wait for a CeedRequest to complete. 287 288 Calling CeedRequestWait on a NULL request is a no-op. 289 290 @param req Address of CeedRequest to wait for; zeroed on completion. 291 292 @return An error code: 0 - success, otherwise - failure 293 294 @ref Advanced 295 **/ 296 int CeedRequestWait(CeedRequest *req) { 297 if (!*req) return 0; 298 return CeedError(NULL, 2, "CeedRequestWait not implemented"); 299 } 300 301 /** 302 @brief Initialize a \ref Ceed to use the specified resource. 303 304 @param resource Resource to use, e.g., "/cpu/self" 305 @param ceed The library context 306 @sa CeedRegister() CeedDestroy() 307 308 @return An error code: 0 - success, otherwise - failure 309 310 @ref Basic 311 **/ 312 int CeedInit(const char *resource, Ceed *ceed) { 313 int ierr; 314 size_t matchlen = 0, matchidx = UINT_MAX, matchpriority = UINT_MAX, priority; 315 316 // Find matching backend 317 if (!resource) return CeedError(NULL, 1, "No resource provided"); 318 for (size_t i=0; i<num_backends; i++) { 319 size_t n; 320 const char *prefix = backends[i].prefix; 321 for (n = 0; prefix[n] && prefix[n] == resource[n]; n++) {} 322 priority = backends[i].priority; 323 if (n > matchlen || (n == matchlen && matchpriority > priority)) { 324 matchlen = n; 325 matchpriority = priority; 326 matchidx = i; 327 } 328 } 329 if (!matchlen) return CeedError(NULL, 1, "No suitable backend"); 330 331 // Setup Ceed 332 ierr = CeedCalloc(1,ceed); CeedChk(ierr); 333 const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER"); 334 if (!ceed_error_handler) ceed_error_handler = "abort"; 335 if (!strcmp(ceed_error_handler, "exit")) 336 (*ceed)->Error = CeedErrorExit; 337 else 338 (*ceed)->Error = CeedErrorAbort; 339 (*ceed)->refcount = 1; 340 (*ceed)->data = NULL; 341 342 // Set lookup table 343 foffset foffsets[] = { 344 CEED_FTABLE_ENTRY(Ceed, Error), 345 CEED_FTABLE_ENTRY(Ceed, GetPreferredMemType), 346 CEED_FTABLE_ENTRY(Ceed, Destroy), 347 CEED_FTABLE_ENTRY(Ceed, VecCreate), 348 CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreate), 349 CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreateBlocked), 350 CEED_FTABLE_ENTRY(Ceed, BasisCreateTensorH1), 351 CEED_FTABLE_ENTRY(Ceed, BasisCreateH1), 352 CEED_FTABLE_ENTRY(Ceed, TensorContractCreate), 353 CEED_FTABLE_ENTRY(Ceed, QFunctionCreate), 354 CEED_FTABLE_ENTRY(Ceed, OperatorCreate), 355 CEED_FTABLE_ENTRY(Ceed, CompositeOperatorCreate), 356 CEED_FTABLE_ENTRY(CeedVector, SetArray), 357 CEED_FTABLE_ENTRY(CeedVector, SetValue), 358 CEED_FTABLE_ENTRY(CeedVector, GetArray), 359 CEED_FTABLE_ENTRY(CeedVector, GetArrayRead), 360 CEED_FTABLE_ENTRY(CeedVector, RestoreArray), 361 CEED_FTABLE_ENTRY(CeedVector, RestoreArrayRead), 362 CEED_FTABLE_ENTRY(CeedVector, Destroy), 363 CEED_FTABLE_ENTRY(CeedElemRestriction, Apply), 364 CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyBlock), 365 CEED_FTABLE_ENTRY(CeedElemRestriction, Destroy), 366 CEED_FTABLE_ENTRY(CeedBasis, Apply), 367 CEED_FTABLE_ENTRY(CeedBasis, Destroy), 368 CEED_FTABLE_ENTRY(CeedTensorContract, Apply), 369 CEED_FTABLE_ENTRY(CeedTensorContract, Destroy), 370 CEED_FTABLE_ENTRY(CeedQFunction, Apply), 371 CEED_FTABLE_ENTRY(CeedQFunction, Destroy), 372 CEED_FTABLE_ENTRY(CeedOperator, Apply), 373 CEED_FTABLE_ENTRY(CeedOperator, ApplyJacobian), 374 CEED_FTABLE_ENTRY(CeedOperator, Destroy), 375 {NULL, 0} // End of lookup table - used in SetBackendFunction loop 376 }; 377 378 ierr = CeedCalloc(sizeof(foffsets), &(*ceed)->foffsets); CeedChk(ierr); 379 memcpy((*ceed)->foffsets, foffsets, sizeof(foffsets)); 380 381 // Backend specific setup 382 ierr = backends[matchidx].init(resource, *ceed); CeedChk(ierr); 383 384 return 0; 385 } 386 387 /** 388 @brief Retrieve a parent CEED 389 390 @param ceed Ceed to retrieve parent of 391 @param[out] parent Address to save the parent to 392 393 @return An error code: 0 - success, otherwise - failure 394 395 @ref Developer 396 **/ 397 int CeedGetParent(Ceed ceed, Ceed *parent) { 398 int ierr; 399 if (ceed->parent) { 400 ierr = CeedGetParent(ceed->parent, parent); CeedChk(ierr); 401 return 0; 402 } 403 *parent = ceed; 404 return 0; 405 } 406 407 /** 408 @brief Retrieve a delegate CEED 409 410 @param ceed Ceed to retrieve delegate of 411 @param[out] delegate Address to save the delegate to 412 413 @return An error code: 0 - success, otherwise - failure 414 415 @ref Developer 416 **/ 417 int CeedGetDelegate(Ceed ceed, Ceed *delegate) { 418 *delegate = ceed->delegate; 419 return 0; 420 } 421 422 /** 423 @brief Set a delegate CEED 424 425 @param ceed Ceed to set delegate of 426 @param[out] delegate Address to set the delegate to 427 428 @return An error code: 0 - success, otherwise - failure 429 430 @ref Advanced 431 **/ 432 int CeedSetDelegate(Ceed ceed, Ceed *delegate) { 433 ceed->delegate = *delegate; 434 (*delegate)->parent = ceed; 435 return 0; 436 } 437 438 /** 439 @brief Return Ceed perferred memory type 440 441 @param ceed Ceed to get preferred memory type of 442 @param[out] delegate Address to save preferred memory type to 443 444 @return An error code: 0 - success, otherwise - failure 445 446 @ref Basic 447 **/ 448 int CeedGetPreferredMemType(Ceed ceed, CeedMemType *type) { 449 int ierr; 450 if (ceed->GetPreferredMemType) { 451 ierr = ceed->GetPreferredMemType(type); CeedChk(ierr); 452 } else { 453 *type = CEED_MEM_HOST; 454 } 455 456 return 0; 457 } 458 459 /** 460 @brief Set a backend function 461 462 @param ceed Ceed for error handling 463 @param type Type of Ceed object to set function for 464 @param[out] object Ceed object to set function for 465 @param fname Name of function to set 466 @param f Function to set 467 468 @return An error code: 0 - success, otherwise - failure 469 470 @ref Advanced 471 **/ 472 int CeedSetBackendFunction(Ceed ceed, 473 const char *type, void *object, 474 const char *fname, int (*f)()) { 475 char lookupname[CEED_MAX_RESOURCE_LEN+1] = ""; 476 477 // Build lookup name 478 if (strcmp(type, "Ceed")) 479 strncat (lookupname, "Ceed", CEED_MAX_RESOURCE_LEN); 480 strncat(lookupname, type, CEED_MAX_RESOURCE_LEN); 481 strncat(lookupname, fname, CEED_MAX_RESOURCE_LEN); 482 483 // Find and use offset 484 for (CeedInt i = 0; ceed->foffsets[i].fname; i++) { 485 if (!strcmp(ceed->foffsets[i].fname, lookupname)) { 486 size_t offset = ceed->foffsets[i].offset; 487 int (**fpointer)(void) = (int (**)(void))((char*)object + offset); 488 *fpointer = f; 489 return 0; 490 } 491 } 492 493 return CeedError(ceed, 1, 494 "Requested function '%s' was not found for CEED object '%s'", fname, type); 495 } 496 497 /** 498 @brief Retrieve backend data for a CEED 499 500 @param ceed Ceed to retrieve data of 501 @param[out] data Address to save data to 502 503 @return An error code: 0 - success, otherwise - failure 504 505 @ref Advanced 506 **/ 507 int CeedGetData(Ceed ceed, void* *data) { 508 *data = ceed->data; 509 return 0; 510 } 511 512 /** 513 @brief Set backend data for a CEED 514 515 @param ceed Ceed to set data of 516 @param data Address of data to set 517 518 @return An error code: 0 - success, otherwise - failure 519 520 @ref Advanced 521 **/ 522 int CeedSetData(Ceed ceed, void* *data) { 523 ceed->data = *data; 524 return 0; 525 } 526 527 /** 528 @brief Destroy a Ceed context 529 530 @param ceed Address of Ceed context to destroy 531 532 @return An error code: 0 - success, otherwise - failure 533 534 @ref Basic 535 **/ 536 int CeedDestroy(Ceed *ceed) { 537 int ierr; 538 539 if (!*ceed || --(*ceed)->refcount > 0) return 0; 540 if ((*ceed)->delegate) { 541 ierr = CeedDestroy(&(*ceed)->delegate); CeedChk(ierr); 542 } 543 if ((*ceed)->Destroy) { 544 ierr = (*ceed)->Destroy(*ceed); CeedChk(ierr); 545 } 546 ierr = CeedFree(&(*ceed)->foffsets); CeedChk(ierr); 547 ierr = CeedFree(ceed); CeedChk(ierr); 548 return 0; 549 } 550 551 /// @} 552