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