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 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 /// @endcond 36 37 /// @file 38 /// Implementation of core components of Ceed library 39 /// 40 /// @addtogroup Ceed 41 /// @{ 42 43 /** 44 @brief Request immediate completion 45 46 This predefined constant is passed as the \ref CeedRequest argument to 47 interfaces when the caller wishes for the operation to be performed 48 immediately. The code 49 50 @code 51 CeedOperatorApply(op, ..., CEED_REQUEST_IMMEDIATE); 52 @endcode 53 54 is semantically equivalent to 55 56 @code 57 CeedRequest request; 58 CeedOperatorApply(op, ..., &request); 59 CeedRequestWait(&request); 60 @endcode 61 62 @sa CEED_REQUEST_ORDERED 63 **/ 64 CeedRequest *const CEED_REQUEST_IMMEDIATE = &ceed_request_immediate; 65 66 /** 67 @brief Request ordered completion 68 69 This predefined constant is passed as the \ref CeedRequest argument to 70 interfaces when the caller wishes for the operation to be completed in the 71 order that it is submitted to the device. It is typically used in a construct 72 such as 73 74 @code 75 CeedRequest request; 76 CeedOperatorApply(op1, ..., CEED_REQUEST_ORDERED); 77 CeedOperatorApply(op2, ..., &request); 78 // other optional work 79 CeedWait(&request); 80 @endcode 81 82 which allows the sequence to complete asynchronously but does not start 83 `op2` until `op1` has completed. 84 85 @fixme The current implementation is overly strict, offering equivalent 86 semantics to CEED_REQUEST_IMMEDIATE. 87 88 @sa CEED_REQUEST_IMMEDIATE 89 */ 90 CeedRequest *const CEED_REQUEST_ORDERED = &ceed_request_ordered; 91 92 /** 93 @brief Error handling implementation; use \ref CeedError instead. 94 95 @ref Developer 96 **/ 97 int CeedErrorImpl(Ceed ceed, const char *filename, int lineno, const char *func, 98 int ecode, const char *format, ...) { 99 va_list args; 100 va_start(args, format); 101 if (ceed) return ceed->Error(ceed, filename, lineno, func, ecode, format, args); 102 return CeedErrorAbort(ceed, filename, lineno, func, ecode, format, args); 103 } 104 105 /** 106 @brief Error handler that returns without printing anything. 107 108 Pass this to CeedSetErrorHandler() to obtain this error handling behavior. 109 110 @ref Developer 111 **/ 112 int CeedErrorReturn(Ceed ceed, const char *filename, int lineno, 113 const char *func, int ecode, const char *format, 114 va_list args) { 115 return ecode; 116 } 117 118 /** 119 @brief Error handler that prints to stderr and aborts 120 121 Pass this to CeedSetErrorHandler() to obtain this error handling behavior. 122 123 @ref Developer 124 **/ 125 int CeedErrorAbort(Ceed ceed, const char *filename, int lineno, 126 const char *func, int ecode, 127 const char *format, va_list args) { 128 fprintf(stderr, "%s:%d in %s(): ", filename, lineno, func); 129 vfprintf(stderr, format, args); 130 fprintf(stderr, "\n"); 131 abort(); 132 return ecode; 133 } 134 135 /** 136 @brief Error handler that prints to stderr and exits 137 138 Pass this to CeedSetErrorHandler() to obtain this error handling behavior. 139 140 In contrast to CeedErrorAbort(), this exits without a signal, so atexit() 141 handlers (e.g., as used by gcov) are run. 142 143 @ref Developer 144 **/ 145 int CeedErrorExit(Ceed ceed, const char *filename, int lineno, 146 const char *func, int ecode, 147 const char *format, va_list args) { 148 fprintf(stderr, "%s:%d in %s(): ", filename, lineno, func); 149 vfprintf(stderr, format, args); 150 fprintf(stderr, "\n"); 151 exit(ecode); 152 return ecode; 153 } 154 155 /** 156 @brief Set error handler 157 158 A default error handler is set in CeedInit(). Use this function to change 159 the error handler to CeedErrorReturn(), CeedErrorAbort(), or a user-defined 160 error handler. 161 162 @ref Developer 163 **/ 164 int CeedSetErrorHandler(Ceed ceed, 165 int (eh)(Ceed, const char *, int, const char *, 166 int, const char *, va_list)) { 167 ceed->Error = eh; 168 return 0; 169 } 170 171 /** 172 @brief Register a Ceed backend 173 174 @param prefix Prefix of resources for this backend to respond to. For 175 example, the reference backend responds to "/cpu/self". 176 @param init Initialization function called by CeedInit() when the backend 177 is selected to drive the requested resource. 178 @param priority Integer priority. Lower values are preferred in case the 179 resource requested by CeedInit() has non-unique best prefix 180 match. 181 182 @return An error code: 0 - success, otherwise - failure 183 184 @ref Advanced 185 **/ 186 int CeedRegister(const char *prefix, 187 int (*init)(const char *, Ceed), unsigned int priority) { 188 if (num_backends >= sizeof(backends) / sizeof(backends[0])) { 189 return CeedError(NULL, 1, "Too many backends"); 190 } 191 strncpy(backends[num_backends].prefix, prefix, CEED_MAX_RESOURCE_LEN); 192 backends[num_backends].init = init; 193 backends[num_backends].priority = priority; 194 num_backends++; 195 return 0; 196 } 197 198 /** 199 @brief Allocate an array on the host; use CeedMalloc() 200 201 Memory usage can be tracked by the library. This ensures sufficient 202 alignment for vectorization and should be used for large allocations. 203 204 @param n Number of units to allocate 205 @param unit Size of each unit 206 @param p Address of pointer to hold the result. 207 208 @return An error code: 0 - success, otherwise - failure 209 210 @sa CeedFree() 211 212 @ref Advanced 213 **/ 214 int CeedMallocArray(size_t n, size_t unit, void *p) { 215 int ierr = posix_memalign((void **)p, CEED_ALIGN, n*unit); 216 if (ierr) 217 return CeedError(NULL, ierr, 218 "posix_memalign failed to allocate %zd members of size %zd\n", n, unit); 219 return 0; 220 } 221 222 /** 223 @brief Allocate a cleared (zeroed) array on the host; use CeedCalloc() 224 225 Memory usage can be tracked by the library. 226 227 @param n Number of units to allocate 228 @param unit Size of each unit 229 @param p Address of pointer to hold the result. 230 231 @return An error code: 0 - success, otherwise - failure 232 233 @sa CeedFree() 234 235 @ref Advanced 236 **/ 237 int CeedCallocArray(size_t n, size_t unit, void *p) { 238 *(void **)p = calloc(n, unit); 239 if (n && unit && !*(void **)p) 240 return CeedError(NULL, 1, "calloc failed to allocate %zd members of size %zd\n", 241 n, unit); 242 return 0; 243 } 244 245 /** 246 @brief Reallocate an array on the host; use CeedRealloc() 247 248 Memory usage can be tracked by the library. 249 250 @param n Number of units to allocate 251 @param unit Size of each unit 252 @param p Address of pointer to hold the result. 253 254 @return An error code: 0 - success, otherwise - failure 255 256 @sa CeedFree() 257 258 @ref Advanced 259 **/ 260 int CeedReallocArray(size_t n, size_t unit, void *p) { 261 *(void **)p = realloc(*(void **)p, n*unit); 262 if (n && unit && !*(void **)p) 263 return CeedError(NULL, 1, 264 "realloc failed to allocate %zd members of size %zd\n", 265 n, unit); 266 return 0; 267 } 268 269 /// Free memory allocated using CeedMalloc() or CeedCalloc() 270 /// 271 /// @param p address of pointer to memory. This argument is of type void* to 272 /// avoid needing a cast, but is the address of the pointer (which is zeroed) 273 /// rather than the pointer. 274 int CeedFree(void *p) { 275 free(*(void **)p); 276 *(void **)p = NULL; 277 return 0; 278 } 279 280 /** 281 @brief Wait for a CeedRequest to complete. 282 283 Calling CeedRequestWait on a NULL request is a no-op. 284 285 @param req Address of CeedRequest to wait for; zeroed on completion. 286 287 @return An error code: 0 - success, otherwise - failure 288 289 @ref Advanced 290 **/ 291 int CeedRequestWait(CeedRequest *req) { 292 if (!*req) return 0; 293 return CeedError(NULL, 2, "CeedRequestWait not implemented"); 294 } 295 296 /** 297 @brief Initialize a \ref Ceed to use the specified resource. 298 299 @param resource Resource to use, e.g., "/cpu/self" 300 @param ceed The library context 301 @sa CeedRegister() CeedDestroy() 302 303 @return An error code: 0 - success, otherwise - failure 304 305 @ref Basic 306 **/ 307 int CeedInit(const char *resource, Ceed *ceed) { 308 int ierr; 309 size_t matchlen = 0, matchidx; 310 unsigned int matchpriority = 100, priority; 311 312 if (!resource) return CeedError(NULL, 1, "No resource provided"); 313 for (size_t i=0; i<num_backends; i++) { 314 size_t n; 315 const char *prefix = backends[i].prefix; 316 for (n = 0; prefix[n] && prefix[n] == resource[n]; n++) {} 317 priority = backends[i].priority; 318 if (n > matchlen || (n == matchlen && matchpriority > priority)) { 319 matchlen = n; 320 matchpriority = priority; 321 matchidx = i; 322 } 323 } 324 if (!matchlen) return CeedError(NULL, 1, "No suitable backend"); 325 ierr = CeedCalloc(1,ceed); CeedChk(ierr); 326 char * ceed_error_handler = getenv("CEED_ERROR_HANDLER") ; 327 if (ceed_error_handler && !strcmp(ceed_error_handler, "exit")) 328 (*ceed)->Error = CeedErrorExit; 329 else 330 (*ceed)->Error = CeedErrorAbort; 331 (*ceed)->refcount = 1; 332 (*ceed)->data = NULL; 333 ierr = backends[matchidx].init(resource, *ceed); CeedChk(ierr); 334 return 0; 335 } 336 337 /** 338 @brief Destroy a Ceed context 339 340 @param ceed Address of Ceed context to destroy 341 342 @return An error code: 0 - success, otherwise - failure 343 344 @ref Basic 345 **/ 346 int CeedDestroy(Ceed *ceed) { 347 int ierr; 348 349 if (!*ceed || --(*ceed)->refcount > 0) return 0; 350 if ((*ceed)->Destroy) { 351 ierr = (*ceed)->Destroy(*ceed); CeedChk(ierr); 352 } 353 ierr = CeedFree(ceed); CeedChk(ierr); 354 return 0; 355 } 356 357 /// @} 358