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