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