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 Set error handler 137 138 A default error handler is set in CeedInit(). Use this function to change 139 the error handler to CeedErrorReturn(), CeedErrorAbort(), or a user-defined 140 error handler. 141 142 @ref Developer 143 **/ 144 int CeedSetErrorHandler(Ceed ceed, 145 int (eh)(Ceed, const char *, int, const char *, 146 int, const char *, va_list)) { 147 ceed->Error = eh; 148 return 0; 149 } 150 151 /** 152 @brief Register a Ceed backend 153 154 @param prefix Prefix of resources for this backend to respond to. For 155 example, the reference backend responds to "/cpu/self". 156 @param init Initialization function called by CeedInit() when the backend 157 is selected to drive the requested resource. 158 @param priority Integer priority. Lower values are preferred in case the 159 resource requested by CeedInit() has non-unique best prefix 160 match. 161 162 @return An error code: 0 - success, otherwise - failure 163 164 @ref Advanced 165 **/ 166 int CeedRegister(const char *prefix, 167 int (*init)(const char *, Ceed), unsigned int priority) { 168 if (num_backends >= sizeof(backends) / sizeof(backends[0])) { 169 return CeedError(NULL, 1, "Too many backends"); 170 } 171 strncpy(backends[num_backends].prefix, prefix, CEED_MAX_RESOURCE_LEN); 172 backends[num_backends].init = init; 173 backends[num_backends].priority = priority; 174 num_backends++; 175 return 0; 176 } 177 178 /** 179 @brief Allocate an array on the host; use CeedMalloc() 180 181 Memory usage can be tracked by the library. This ensures sufficient 182 alignment for vectorization and should be used for large allocations. 183 184 @param n Number of units to allocate 185 @param unit Size of each unit 186 @param p Address of pointer to hold the result. 187 188 @return An error code: 0 - success, otherwise - failure 189 190 @sa CeedFree() 191 192 @ref Advanced 193 **/ 194 int CeedMallocArray(size_t n, size_t unit, void *p) { 195 int ierr = posix_memalign((void **)p, CEED_ALIGN, n*unit); 196 if (ierr) 197 return CeedError(NULL, ierr, 198 "posix_memalign failed to allocate %zd members of size %zd\n", n, unit); 199 return 0; 200 } 201 202 /** 203 @brief Allocate a cleared (zeroed) array on the host; use CeedCalloc() 204 205 Memory usage can be tracked by the library. 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 CeedCallocArray(size_t n, size_t unit, void *p) { 218 *(void **)p = calloc(n, unit); 219 if (n && unit && !*(void **)p) 220 return CeedError(NULL, 1, "calloc failed to allocate %zd members of size %zd\n", 221 n, unit); 222 return 0; 223 } 224 225 /** 226 @brief Reallocate an array on the host; use CeedRealloc() 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 CeedReallocArray(size_t n, size_t unit, void *p) { 241 *(void **)p = realloc(*(void **)p, n*unit); 242 if (n && unit && !*(void **)p) 243 return CeedError(NULL, 1, 244 "realloc failed to allocate %zd members of size %zd\n", 245 n, unit); 246 return 0; 247 } 248 249 /// Free memory allocated using CeedMalloc() or CeedCalloc() 250 /// 251 /// @param p address of pointer to memory. This argument is of type void* to 252 /// avoid needing a cast, but is the address of the pointer (which is zeroed) 253 /// rather than the pointer. 254 int CeedFree(void *p) { 255 free(*(void **)p); 256 *(void **)p = NULL; 257 return 0; 258 } 259 260 /** 261 @brief Wait for a CeedRequest to complete. 262 263 Calling CeedRequestWait on a NULL request is a no-op. 264 265 @param req Address of CeedRequest to wait for; zeroed on completion. 266 267 @return An error code: 0 - success, otherwise - failure 268 269 @ref Advanced 270 **/ 271 int CeedRequestWait(CeedRequest *req) { 272 if (!*req) return 0; 273 return CeedError(NULL, 2, "CeedRequestWait not implemented"); 274 } 275 276 /** 277 @brief Initialize a \ref Ceed to use the specified resource. 278 279 @param resource Resource to use, e.g., "/cpu/self" 280 @param ceed The library context 281 @sa CeedRegister() CeedDestroy() 282 283 @return An error code: 0 - success, otherwise - failure 284 285 @ref Basic 286 **/ 287 int CeedInit(const char *resource, Ceed *ceed) { 288 int ierr; 289 size_t matchlen = 0, matchidx; 290 unsigned int matchpriority = 100, priority; 291 292 if (!resource) return CeedError(NULL, 1, "No resource provided"); 293 for (size_t i=0; i<num_backends; i++) { 294 size_t n; 295 const char *prefix = backends[i].prefix; 296 for (n = 0; prefix[n] && prefix[n] == resource[n]; n++) {} 297 priority = backends[i].priority; 298 if (n > matchlen || (n == matchlen && matchpriority > priority)) { 299 matchlen = n; 300 matchpriority = priority; 301 matchidx = i; 302 } 303 } 304 if (!matchlen) return CeedError(NULL, 1, "No suitable backend"); 305 ierr = CeedCalloc(1,ceed); CeedChk(ierr); 306 (*ceed)->Error = CeedErrorAbort; 307 (*ceed)->refcount = 1; 308 (*ceed)->data = NULL; 309 ierr = backends[matchidx].init(resource, *ceed); CeedChk(ierr); 310 return 0; 311 } 312 313 /** 314 @brief Destroy a Ceed context 315 316 @param ceed Address of Ceed context to destroy 317 318 @return An error code: 0 - success, otherwise - failure 319 320 @ref Basic 321 **/ 322 int CeedDestroy(Ceed *ceed) { 323 int ierr; 324 325 if (!*ceed || --(*ceed)->refcount > 0) return 0; 326 if ((*ceed)->Destroy) { 327 ierr = (*ceed)->Destroy(*ceed); CeedChk(ierr); 328 } 329 ierr = CeedFree(ceed); CeedChk(ierr); 330 return 0; 331 } 332 333 /// @} 334