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