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 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 /// Error handling implementation; use \ref CeedError instead. 91 int CeedErrorImpl(Ceed ceed, const char *filename, int lineno, const char *func, 92 int ecode, const char *format, ...) { 93 va_list args; 94 va_start(args, format); 95 if (ceed) return ceed->Error(ceed, filename, lineno, func, ecode, format, args); 96 return CeedErrorAbort(ceed, filename, lineno, func, ecode, format, args); 97 } 98 99 /// Error handler that returns without printing anything. 100 /// 101 /// Pass this to CeedSetErrorHandler() to obtain this error handling behavior. 102 /// 103 /// @sa CeedErrorAbort 104 int CeedErrorReturn(Ceed ceed, const char *filename, int lineno, 105 const char *func, int ecode, const char *format, 106 va_list args) { 107 return ecode; 108 } 109 110 /// Error handler that prints to stderr and aborts 111 /// 112 /// Pass this to CeedSetErrorHandler() to obtain this error handling behavior. 113 /// 114 /// @sa CeedErrorReturn 115 int CeedErrorAbort(Ceed ceed, const char *filename, int lineno, 116 const char *func, int ecode, 117 const char *format, va_list args) { 118 fprintf(stderr, "%s:%d in %s(): ", filename, lineno, func); 119 vfprintf(stderr, format, args); 120 fprintf(stderr, "\n"); 121 abort(); 122 return ecode; 123 } 124 125 /// Set error handler 126 /// 127 /// A default error handler is set in CeedInit(). Use this function to change 128 /// the error handler to CeedErrorReturn(), CeedErrorAbort(), or a user-defined 129 /// error handler. 130 int CeedSetErrorHandler(Ceed ceed, 131 int (eh)(Ceed, const char *, int, const char *, 132 int, const char *, va_list)) { 133 ceed->Error = eh; 134 return 0; 135 } 136 137 /** 138 Register a Ceed backend 139 140 @param prefix Prefix of resources for this backend to respond to. For 141 example, the reference backend responds to "/cpu/self". 142 @param init Initialization function called by CeedInit() when the backend 143 is selected to drive the requested resource. 144 @param priority Integer priority. Lower values are preferred in case the 145 resource requested by CeedInit() has non-unique best prefix 146 match. 147 @return 0 on success 148 */ 149 int CeedRegister(const char *prefix, 150 int (*init)(const char *, Ceed), unsigned int priority) { 151 if (num_backends >= sizeof(backends) / sizeof(backends[0])) { 152 return CeedError(NULL, 1, "Too many backends"); 153 } 154 strncpy(backends[num_backends].prefix, prefix, CEED_MAX_RESOURCE_LEN); 155 backends[num_backends].init = init; 156 backends[num_backends].priority = priority; 157 num_backends++; 158 return 0; 159 } 160 161 /// Allocate an array on the host; use CeedMalloc() 162 /// 163 /// Memory usage can be tracked by the library. This ensures sufficient 164 /// alignment for vectorization and should be used for large allocations. 165 /// 166 /// @param n Number of units to allocate 167 /// @param unit Size of each unit 168 /// @param p Address of pointer to hold the result. 169 /// @sa CeedFree() 170 int CeedMallocArray(size_t n, size_t unit, void *p) { 171 int ierr = posix_memalign((void **)p, CEED_ALIGN, n*unit); 172 if (ierr) 173 return CeedError(NULL, ierr, 174 "posix_memalign failed to allocate %zd members of size %zd\n", n, unit); 175 return 0; 176 } 177 178 /// Allocate a cleared (zeroed) array on the host; use CeedCalloc() 179 /// 180 /// Memory usage can be tracked by the library. 181 /// 182 /// @param n Number of units to allocate 183 /// @param unit Size of each unit 184 /// @param p Address of pointer to hold the result. 185 /// @sa CeedFree() 186 int CeedCallocArray(size_t n, size_t unit, void *p) { 187 *(void **)p = calloc(n, unit); 188 if (n && unit && !*(void **)p) 189 return CeedError(NULL, 1, "calloc failed to allocate %zd members of size %zd\n", 190 n, unit); 191 return 0; 192 } 193 194 /// Reallocate an array on the host; use CeedRealloc() 195 /// 196 /// Memory usage can be tracked by the library. 197 /// 198 /// @param n Number of units to allocate 199 /// @param unit Size of each unit 200 /// @param p Address of pointer to hold the result. 201 /// @sa CeedFree() 202 int CeedReallocArray(size_t n, size_t unit, void *p) { 203 *(void **)p = realloc(*(void **)p, n*unit); 204 if (n && unit && !*(void **)p) 205 return CeedError(NULL, 1, 206 "realloc failed to allocate %zd members of size %zd\n", 207 n, unit); 208 return 0; 209 } 210 211 /// Free memory allocated using CeedMalloc() or CeedCalloc() 212 /// 213 /// @param p address of pointer to memory. This argument is of type void* to 214 /// avoid needing a cast, but is the address of the pointer (which is zeroed) 215 /// rather than the pointer. 216 int CeedFree(void *p) { 217 free(*(void **)p); 218 *(void **)p = NULL; 219 return 0; 220 } 221 222 /** 223 Wait for a CeedRequest to complete. 224 225 Calling CeedRequestWait on a NULL request is a no-op. 226 227 @param req Address of CeedRequest to wait for; zeroed on completion. 228 @return 0 on success 229 */ 230 int CeedRequestWait(CeedRequest *req) { 231 if (!*req) return 0; 232 return CeedError(NULL, 2, "CeedRequestWait not implemented"); 233 } 234 235 /// Initialize a \ref Ceed to use the specified resource. 236 /// 237 /// @param resource Resource to use, e.g., "/cpu/self" 238 /// @param ceed The library context 239 /// @sa CeedRegister() CeedDestroy() 240 int CeedInit(const char *resource, Ceed *ceed) { 241 int ierr; 242 size_t matchlen = 0, matchidx; 243 unsigned int matchpriority = 100, priority; 244 245 if (!resource) return CeedError(NULL, 1, "No resource provided"); 246 for (size_t i=0; i<num_backends; i++) { 247 size_t n; 248 const char *prefix = backends[i].prefix; 249 for (n = 0; prefix[n] && prefix[n] == resource[n]; n++) {} 250 priority = backends[i].priority; 251 if (n > matchlen || (n == matchlen && matchpriority > priority)) { 252 matchlen = n; 253 matchpriority = priority; 254 matchidx = i; 255 } 256 } 257 if (!matchlen) return CeedError(NULL, 1, "No suitable backend"); 258 ierr = CeedCalloc(1,ceed); CeedChk(ierr); 259 (*ceed)->Error = CeedErrorAbort; 260 (*ceed)->refcount = 1; 261 (*ceed)->data = NULL; 262 ierr = backends[matchidx].init(resource, *ceed); CeedChk(ierr); 263 return 0; 264 } 265 266 /** 267 Destroy a Ceed context 268 269 @param ceed Address of Ceed context to destroy 270 @return 0 on success 271 */ 272 int CeedDestroy(Ceed *ceed) { 273 int ierr; 274 275 if (!*ceed || --(*ceed)->refcount > 0) return 0; 276 if ((*ceed)->Destroy) { 277 ierr = (*ceed)->Destroy(*ceed); CeedChk(ierr); 278 } 279 ierr = CeedFree(ceed); CeedChk(ierr); 280 return 0; 281 } 282 283 /// @} 284