1*d7b241e6Sjeremylt // Copyright (c) 2017, Lawrence Livermore National Security, LLC. Produced at 2*d7b241e6Sjeremylt // the Lawrence Livermore National Laboratory. LLNL-CODE-734707. All Rights 3*d7b241e6Sjeremylt // reserved. See files LICENSE and NOTICE for details. 4*d7b241e6Sjeremylt // 5*d7b241e6Sjeremylt // This file is part of CEED, a collection of benchmarks, miniapps, software 6*d7b241e6Sjeremylt // libraries and APIs for efficient high-order finite element and spectral 7*d7b241e6Sjeremylt // element discretizations for exascale applications. For more information and 8*d7b241e6Sjeremylt // source code availability see http://github.com/ceed. 9*d7b241e6Sjeremylt // 10*d7b241e6Sjeremylt // The CEED research is supported by the Exascale Computing Project 17-SC-20-SC, 11*d7b241e6Sjeremylt // a collaborative effort of two U.S. Department of Energy organizations (Office 12*d7b241e6Sjeremylt // of Science and the National Nuclear Security Administration) responsible for 13*d7b241e6Sjeremylt // the planning and preparation of a capable exascale ecosystem, including 14*d7b241e6Sjeremylt // software, applications, hardware, advanced system engineering and early 15*d7b241e6Sjeremylt // testbed platforms, in support of the nation's exascale computing imperative. 16*d7b241e6Sjeremylt 17*d7b241e6Sjeremylt #define _POSIX_C_SOURCE 200112 18*d7b241e6Sjeremylt #include <ceed-impl.h> 19*d7b241e6Sjeremylt 20*d7b241e6Sjeremylt #include <stdarg.h> 21*d7b241e6Sjeremylt #include <stdio.h> 22*d7b241e6Sjeremylt #include <stdlib.h> 23*d7b241e6Sjeremylt #include <string.h> 24*d7b241e6Sjeremylt 25*d7b241e6Sjeremylt /// @cond DOXYGEN_SKIP 26*d7b241e6Sjeremylt static CeedRequest ceed_request_immediate; 27*d7b241e6Sjeremylt static CeedRequest ceed_request_ordered; 28*d7b241e6Sjeremylt 29*d7b241e6Sjeremylt static struct { 30*d7b241e6Sjeremylt char prefix[CEED_MAX_RESOURCE_LEN]; 31*d7b241e6Sjeremylt int (*init)(const char *resource, Ceed f); 32*d7b241e6Sjeremylt unsigned int priority; 33*d7b241e6Sjeremylt } backends[32]; 34*d7b241e6Sjeremylt static size_t num_backends; 35*d7b241e6Sjeremylt /// @endcond 36*d7b241e6Sjeremylt 37*d7b241e6Sjeremylt /// @file 38*d7b241e6Sjeremylt /// Implementation of core components of Ceed library 39*d7b241e6Sjeremylt /// 40*d7b241e6Sjeremylt /// @defgroup Ceed Ceed: core components 41*d7b241e6Sjeremylt /// @{ 42*d7b241e6Sjeremylt 43*d7b241e6Sjeremylt /// Request immediate completion 44*d7b241e6Sjeremylt /// 45*d7b241e6Sjeremylt /// This predefined constant is passed as the \ref CeedRequest argument to 46*d7b241e6Sjeremylt /// interfaces when the caller wishes for the operation to be performed 47*d7b241e6Sjeremylt /// immediately. The code 48*d7b241e6Sjeremylt /// 49*d7b241e6Sjeremylt /// @code 50*d7b241e6Sjeremylt /// CeedOperatorApply(op, ..., CEED_REQUEST_IMMEDIATE); 51*d7b241e6Sjeremylt /// @endcode 52*d7b241e6Sjeremylt /// 53*d7b241e6Sjeremylt /// is semantically equivalent to 54*d7b241e6Sjeremylt /// 55*d7b241e6Sjeremylt /// @code 56*d7b241e6Sjeremylt /// CeedRequest request; 57*d7b241e6Sjeremylt /// CeedOperatorApply(op, ..., &request); 58*d7b241e6Sjeremylt /// CeedRequestWait(&request); 59*d7b241e6Sjeremylt /// @endcode 60*d7b241e6Sjeremylt /// 61*d7b241e6Sjeremylt /// @sa CEED_REQUEST_ORDERED 62*d7b241e6Sjeremylt CeedRequest *const CEED_REQUEST_IMMEDIATE = &ceed_request_immediate; 63*d7b241e6Sjeremylt 64*d7b241e6Sjeremylt /** 65*d7b241e6Sjeremylt Request ordered completion 66*d7b241e6Sjeremylt 67*d7b241e6Sjeremylt This predefined constant is passed as the \ref CeedRequest argument to 68*d7b241e6Sjeremylt interfaces when the caller wishes for the operation to be completed in the 69*d7b241e6Sjeremylt order that it is submitted to the device. It is typically used in a construct 70*d7b241e6Sjeremylt such as 71*d7b241e6Sjeremylt 72*d7b241e6Sjeremylt @code 73*d7b241e6Sjeremylt CeedRequest request; 74*d7b241e6Sjeremylt CeedOperatorApply(op1, ..., CEED_REQUEST_ORDERED); 75*d7b241e6Sjeremylt CeedOperatorApply(op2, ..., &request); 76*d7b241e6Sjeremylt // other optional work 77*d7b241e6Sjeremylt CeedWait(&request); 78*d7b241e6Sjeremylt @endcode 79*d7b241e6Sjeremylt 80*d7b241e6Sjeremylt which allows the sequence to complete asynchronously but does not start 81*d7b241e6Sjeremylt `op2` until `op1` has completed. 82*d7b241e6Sjeremylt 83*d7b241e6Sjeremylt @fixme The current implementation is overly strict, offering equivalent 84*d7b241e6Sjeremylt semantics to CEED_REQUEST_IMMEDIATE. 85*d7b241e6Sjeremylt 86*d7b241e6Sjeremylt @sa CEED_REQUEST_IMMEDIATE 87*d7b241e6Sjeremylt */ 88*d7b241e6Sjeremylt CeedRequest *const CEED_REQUEST_ORDERED = &ceed_request_ordered; 89*d7b241e6Sjeremylt 90*d7b241e6Sjeremylt /// Error handling implementation; use \ref CeedError instead. 91*d7b241e6Sjeremylt int CeedErrorImpl(Ceed ceed, const char *filename, int lineno, const char *func, 92*d7b241e6Sjeremylt int ecode, const char *format, ...) { 93*d7b241e6Sjeremylt va_list args; 94*d7b241e6Sjeremylt va_start(args, format); 95*d7b241e6Sjeremylt if (ceed) return ceed->Error(ceed, filename, lineno, func, ecode, format, args); 96*d7b241e6Sjeremylt return CeedErrorAbort(ceed, filename, lineno, func, ecode, format, args); 97*d7b241e6Sjeremylt } 98*d7b241e6Sjeremylt 99*d7b241e6Sjeremylt /// Error handler that returns without printing anything. 100*d7b241e6Sjeremylt /// 101*d7b241e6Sjeremylt /// Pass this to CeedSetErrorHandler() to obtain this error handling behavior. 102*d7b241e6Sjeremylt /// 103*d7b241e6Sjeremylt /// @sa CeedErrorAbort 104*d7b241e6Sjeremylt int CeedErrorReturn(Ceed ceed, const char *filename, int lineno, 105*d7b241e6Sjeremylt const char *func, int ecode, const char *format, 106*d7b241e6Sjeremylt va_list args) { 107*d7b241e6Sjeremylt return ecode; 108*d7b241e6Sjeremylt } 109*d7b241e6Sjeremylt 110*d7b241e6Sjeremylt /// Error handler that prints to stderr and aborts 111*d7b241e6Sjeremylt /// 112*d7b241e6Sjeremylt /// Pass this to CeedSetErrorHandler() to obtain this error handling behavior. 113*d7b241e6Sjeremylt /// 114*d7b241e6Sjeremylt /// @sa CeedErrorReturn 115*d7b241e6Sjeremylt int CeedErrorAbort(Ceed ceed, const char *filename, int lineno, 116*d7b241e6Sjeremylt const char *func, int ecode, 117*d7b241e6Sjeremylt const char *format, va_list args) { 118*d7b241e6Sjeremylt fprintf(stderr, "%s:%d in %s(): ", filename, lineno, func); 119*d7b241e6Sjeremylt vfprintf(stderr, format, args); 120*d7b241e6Sjeremylt fprintf(stderr, "\n"); 121*d7b241e6Sjeremylt abort(); 122*d7b241e6Sjeremylt return ecode; 123*d7b241e6Sjeremylt } 124*d7b241e6Sjeremylt 125*d7b241e6Sjeremylt /// Set error handler 126*d7b241e6Sjeremylt /// 127*d7b241e6Sjeremylt /// A default error handler is set in CeedInit(). Use this function to change 128*d7b241e6Sjeremylt /// the error handler to CeedErrorReturn(), CeedErrorAbort(), or a user-defined 129*d7b241e6Sjeremylt /// error handler. 130*d7b241e6Sjeremylt int CeedSetErrorHandler(Ceed ceed, 131*d7b241e6Sjeremylt int (eh)(Ceed, const char *, int, const char *, 132*d7b241e6Sjeremylt int, const char *, va_list)) { 133*d7b241e6Sjeremylt ceed->Error = eh; 134*d7b241e6Sjeremylt return 0; 135*d7b241e6Sjeremylt } 136*d7b241e6Sjeremylt 137*d7b241e6Sjeremylt /** 138*d7b241e6Sjeremylt Register a Ceed backend 139*d7b241e6Sjeremylt 140*d7b241e6Sjeremylt @param prefix Prefix of resources for this backend to respond to. For 141*d7b241e6Sjeremylt example, the reference backend responds to "/cpu/self". 142*d7b241e6Sjeremylt @param init Initialization function called by CeedInit() when the backend 143*d7b241e6Sjeremylt is selected to drive the requested resource. 144*d7b241e6Sjeremylt @param priority Integer priority. Lower values are preferred in case the 145*d7b241e6Sjeremylt resource requested by CeedInit() has non-unique best prefix 146*d7b241e6Sjeremylt match. 147*d7b241e6Sjeremylt @return 0 on success 148*d7b241e6Sjeremylt */ 149*d7b241e6Sjeremylt int CeedRegister(const char *prefix, 150*d7b241e6Sjeremylt int (*init)(const char *, Ceed), unsigned int priority) { 151*d7b241e6Sjeremylt if (num_backends >= sizeof(backends) / sizeof(backends[0])) { 152*d7b241e6Sjeremylt return CeedError(NULL, 1, "Too many backends"); 153*d7b241e6Sjeremylt } 154*d7b241e6Sjeremylt strncpy(backends[num_backends].prefix, prefix, CEED_MAX_RESOURCE_LEN); 155*d7b241e6Sjeremylt backends[num_backends].init = init; 156*d7b241e6Sjeremylt backends[num_backends].priority = priority; 157*d7b241e6Sjeremylt num_backends++; 158*d7b241e6Sjeremylt return 0; 159*d7b241e6Sjeremylt } 160*d7b241e6Sjeremylt 161*d7b241e6Sjeremylt /// Allocate an array on the host; use CeedMalloc() 162*d7b241e6Sjeremylt /// 163*d7b241e6Sjeremylt /// Memory usage can be tracked by the library. This ensures sufficient 164*d7b241e6Sjeremylt /// alignment for vectorization and should be used for large allocations. 165*d7b241e6Sjeremylt /// 166*d7b241e6Sjeremylt /// @param n Number of units to allocate 167*d7b241e6Sjeremylt /// @param unit Size of each unit 168*d7b241e6Sjeremylt /// @param p Address of pointer to hold the result. 169*d7b241e6Sjeremylt /// @sa CeedFree() 170*d7b241e6Sjeremylt int CeedMallocArray(size_t n, size_t unit, void *p) { 171*d7b241e6Sjeremylt int ierr = posix_memalign((void **)p, CEED_ALIGN, n*unit); 172*d7b241e6Sjeremylt if (ierr) 173*d7b241e6Sjeremylt return CeedError(NULL, ierr, 174*d7b241e6Sjeremylt "posix_memalign failed to allocate %zd members of size %zd\n", n, unit); 175*d7b241e6Sjeremylt return 0; 176*d7b241e6Sjeremylt } 177*d7b241e6Sjeremylt 178*d7b241e6Sjeremylt /// Allocate a cleared (zeroed) array on the host; use CeedCalloc() 179*d7b241e6Sjeremylt /// 180*d7b241e6Sjeremylt /// Memory usage can be tracked by the library. 181*d7b241e6Sjeremylt /// 182*d7b241e6Sjeremylt /// @param n Number of units to allocate 183*d7b241e6Sjeremylt /// @param unit Size of each unit 184*d7b241e6Sjeremylt /// @param p Address of pointer to hold the result. 185*d7b241e6Sjeremylt /// @sa CeedFree() 186*d7b241e6Sjeremylt int CeedCallocArray(size_t n, size_t unit, void *p) { 187*d7b241e6Sjeremylt *(void **)p = calloc(n, unit); 188*d7b241e6Sjeremylt if (n && unit && !*(void **)p) 189*d7b241e6Sjeremylt return CeedError(NULL, 1, "calloc failed to allocate %zd members of size %zd\n", 190*d7b241e6Sjeremylt n, unit); 191*d7b241e6Sjeremylt return 0; 192*d7b241e6Sjeremylt } 193*d7b241e6Sjeremylt 194*d7b241e6Sjeremylt /// Reallocate an array on the host; use CeedRealloc() 195*d7b241e6Sjeremylt /// 196*d7b241e6Sjeremylt /// Memory usage can be tracked by the library. 197*d7b241e6Sjeremylt /// 198*d7b241e6Sjeremylt /// @param n Number of units to allocate 199*d7b241e6Sjeremylt /// @param unit Size of each unit 200*d7b241e6Sjeremylt /// @param p Address of pointer to hold the result. 201*d7b241e6Sjeremylt /// @sa CeedFree() 202*d7b241e6Sjeremylt int CeedReallocArray(size_t n, size_t unit, void *p) { 203*d7b241e6Sjeremylt *(void **)p = realloc(*(void **)p, n*unit); 204*d7b241e6Sjeremylt if (n && unit && !*(void **)p) 205*d7b241e6Sjeremylt return CeedError(NULL, 1, 206*d7b241e6Sjeremylt "realloc failed to allocate %zd members of size %zd\n", 207*d7b241e6Sjeremylt n, unit); 208*d7b241e6Sjeremylt return 0; 209*d7b241e6Sjeremylt } 210*d7b241e6Sjeremylt 211*d7b241e6Sjeremylt /// Free memory allocated using CeedMalloc() or CeedCalloc() 212*d7b241e6Sjeremylt /// 213*d7b241e6Sjeremylt /// @param p address of pointer to memory. This argument is of type void* to 214*d7b241e6Sjeremylt /// avoid needing a cast, but is the address of the pointer (which is zeroed) 215*d7b241e6Sjeremylt /// rather than the pointer. 216*d7b241e6Sjeremylt int CeedFree(void *p) { 217*d7b241e6Sjeremylt free(*(void **)p); 218*d7b241e6Sjeremylt *(void **)p = NULL; 219*d7b241e6Sjeremylt return 0; 220*d7b241e6Sjeremylt } 221*d7b241e6Sjeremylt 222*d7b241e6Sjeremylt /** 223*d7b241e6Sjeremylt Wait for a CeedRequest to complete. 224*d7b241e6Sjeremylt 225*d7b241e6Sjeremylt Calling CeedRequestWait on a NULL request is a no-op. 226*d7b241e6Sjeremylt 227*d7b241e6Sjeremylt @param req Address of CeedRequest to wait for; zeroed on completion. 228*d7b241e6Sjeremylt @return 0 on success 229*d7b241e6Sjeremylt */ 230*d7b241e6Sjeremylt int CeedRequestWait(CeedRequest *req) { 231*d7b241e6Sjeremylt if (!*req) return 0; 232*d7b241e6Sjeremylt return CeedError(NULL, 2, "CeedRequestWait not implemented"); 233*d7b241e6Sjeremylt } 234*d7b241e6Sjeremylt 235*d7b241e6Sjeremylt /// Initialize a \ref Ceed to use the specified resource. 236*d7b241e6Sjeremylt /// 237*d7b241e6Sjeremylt /// @param resource Resource to use, e.g., "/cpu/self" 238*d7b241e6Sjeremylt /// @param ceed The library context 239*d7b241e6Sjeremylt /// @sa CeedRegister() CeedDestroy() 240*d7b241e6Sjeremylt int CeedInit(const char *resource, Ceed *ceed) { 241*d7b241e6Sjeremylt int ierr; 242*d7b241e6Sjeremylt size_t matchlen = 0, matchidx; 243*d7b241e6Sjeremylt unsigned int matchpriority = 100, priority; 244*d7b241e6Sjeremylt 245*d7b241e6Sjeremylt if (!resource) return CeedError(NULL, 1, "No resource provided"); 246*d7b241e6Sjeremylt for (size_t i=0; i<num_backends; i++) { 247*d7b241e6Sjeremylt size_t n; 248*d7b241e6Sjeremylt const char *prefix = backends[i].prefix; 249*d7b241e6Sjeremylt for (n = 0; prefix[n] && prefix[n] == resource[n]; n++) {} 250*d7b241e6Sjeremylt priority = backends[i].priority; 251*d7b241e6Sjeremylt if (n > matchlen || (n == matchlen && matchpriority > priority)) { 252*d7b241e6Sjeremylt matchlen = n; 253*d7b241e6Sjeremylt matchpriority = priority; 254*d7b241e6Sjeremylt matchidx = i; 255*d7b241e6Sjeremylt } 256*d7b241e6Sjeremylt } 257*d7b241e6Sjeremylt if (!matchlen) return CeedError(NULL, 1, "No suitable backend"); 258*d7b241e6Sjeremylt ierr = CeedCalloc(1,ceed); CeedChk(ierr); 259*d7b241e6Sjeremylt (*ceed)->Error = CeedErrorAbort; 260*d7b241e6Sjeremylt (*ceed)->refcount = 1; 261*d7b241e6Sjeremylt (*ceed)->data = NULL; 262*d7b241e6Sjeremylt ierr = backends[matchidx].init(resource, *ceed); CeedChk(ierr); 263*d7b241e6Sjeremylt return 0; 264*d7b241e6Sjeremylt } 265*d7b241e6Sjeremylt 266*d7b241e6Sjeremylt /** 267*d7b241e6Sjeremylt Destroy a Ceed context 268*d7b241e6Sjeremylt 269*d7b241e6Sjeremylt @param ceed Address of Ceed context to destroy 270*d7b241e6Sjeremylt @return 0 on success 271*d7b241e6Sjeremylt */ 272*d7b241e6Sjeremylt int CeedDestroy(Ceed *ceed) { 273*d7b241e6Sjeremylt int ierr; 274*d7b241e6Sjeremylt 275*d7b241e6Sjeremylt if (!*ceed || --(*ceed)->refcount > 0) return 0; 276*d7b241e6Sjeremylt if ((*ceed)->Destroy) { 277*d7b241e6Sjeremylt ierr = (*ceed)->Destroy(*ceed); CeedChk(ierr); 278*d7b241e6Sjeremylt } 279*d7b241e6Sjeremylt ierr = CeedFree(ceed); CeedChk(ierr); 280*d7b241e6Sjeremylt return 0; 281*d7b241e6Sjeremylt } 282*d7b241e6Sjeremylt 283*d7b241e6Sjeremylt /// @} 284