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