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