xref: /libCEED/interface/ceed.c (revision b11c1e72297b0dbe2250cea5be89aa8490095155)
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