xref: /libCEED/interface/ceed.c (revision 5fe0d4fa442f339208d7656a646e69da1442c2a1)
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 Error handler that prints to stderr and exits
137 
138   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
139 
140   In contrast to CeedErrorAbort(), this exits without a signal, so atexit()
141   handlers (e.g., as used by gcov) are run.
142 
143   @ref Developer
144 **/
145 int CeedErrorExit(Ceed ceed, const char *filename, int lineno,
146                   const char *func, int ecode,
147                   const char *format, va_list args) {
148   fprintf(stderr, "%s:%d in %s(): ", filename, lineno, func);
149   vfprintf(stderr, format, args);
150   fprintf(stderr, "\n");
151   exit(ecode);
152   return ecode;
153 }
154 
155 /**
156   @brief Set error handler
157 
158   A default error handler is set in CeedInit().  Use this function to change
159   the error handler to CeedErrorReturn(), CeedErrorAbort(), or a user-defined
160   error handler.
161 
162   @ref Developer
163 **/
164 int CeedSetErrorHandler(Ceed ceed,
165                         int (eh)(Ceed, const char *, int, const char *,
166                                  int, const char *, va_list)) {
167   ceed->Error = eh;
168   return 0;
169 }
170 
171 /**
172   @brief Register a Ceed backend
173 
174   @param prefix   Prefix of resources for this backend to respond to.  For
175                     example, the reference backend responds to "/cpu/self".
176   @param init     Initialization function called by CeedInit() when the backend
177                     is selected to drive the requested resource.
178   @param priority Integer priority.  Lower values are preferred in case the
179                     resource requested by CeedInit() has non-unique best prefix
180                     match.
181 
182   @return An error code: 0 - success, otherwise - failure
183 
184   @ref Advanced
185 **/
186 int CeedRegister(const char *prefix,
187                  int (*init)(const char *, Ceed), unsigned int priority) {
188   if (num_backends >= sizeof(backends) / sizeof(backends[0])) {
189     return CeedError(NULL, 1, "Too many backends");
190   }
191   strncpy(backends[num_backends].prefix, prefix, CEED_MAX_RESOURCE_LEN);
192   backends[num_backends].init = init;
193   backends[num_backends].priority = priority;
194   num_backends++;
195   return 0;
196 }
197 
198 /**
199   @brief Allocate an array on the host; use CeedMalloc()
200 
201   Memory usage can be tracked by the library.  This ensures sufficient
202     alignment for vectorization and should be used for large allocations.
203 
204   @param n Number of units to allocate
205   @param unit Size of each unit
206   @param p Address of pointer to hold the result.
207 
208   @return An error code: 0 - success, otherwise - failure
209 
210   @sa CeedFree()
211 
212   @ref Advanced
213 **/
214 int CeedMallocArray(size_t n, size_t unit, void *p) {
215   int ierr = posix_memalign((void **)p, CEED_ALIGN, n*unit);
216   if (ierr)
217     return CeedError(NULL, ierr,
218                      "posix_memalign failed to allocate %zd members of size %zd\n", n, unit);
219   return 0;
220 }
221 
222 /**
223   @brief Allocate a cleared (zeroed) array on the host; use CeedCalloc()
224 
225   Memory usage can be tracked by the library.
226 
227   @param n Number of units to allocate
228   @param unit Size of each unit
229   @param p Address of pointer to hold the result.
230 
231   @return An error code: 0 - success, otherwise - failure
232 
233   @sa CeedFree()
234 
235   @ref Advanced
236 **/
237 int CeedCallocArray(size_t n, size_t unit, void *p) {
238   *(void **)p = calloc(n, unit);
239   if (n && unit && !*(void **)p)
240     return CeedError(NULL, 1, "calloc failed to allocate %zd members of size %zd\n",
241                      n, unit);
242   return 0;
243 }
244 
245 /**
246   @brief Reallocate an array on the host; use CeedRealloc()
247 
248   Memory usage can be tracked by the library.
249 
250   @param n Number of units to allocate
251   @param unit Size of each unit
252   @param p Address of pointer to hold the result.
253 
254   @return An error code: 0 - success, otherwise - failure
255 
256   @sa CeedFree()
257 
258   @ref Advanced
259 **/
260 int CeedReallocArray(size_t n, size_t unit, void *p) {
261   *(void **)p = realloc(*(void **)p, n*unit);
262   if (n && unit && !*(void **)p)
263     return CeedError(NULL, 1,
264                      "realloc failed to allocate %zd members of size %zd\n",
265                      n, unit);
266   return 0;
267 }
268 
269 /// Free memory allocated using CeedMalloc() or CeedCalloc()
270 ///
271 /// @param p address of pointer to memory.  This argument is of type void* to
272 /// avoid needing a cast, but is the address of the pointer (which is zeroed)
273 /// rather than the pointer.
274 int CeedFree(void *p) {
275   free(*(void **)p);
276   *(void **)p = NULL;
277   return 0;
278 }
279 
280 /**
281   @brief Wait for a CeedRequest to complete.
282 
283   Calling CeedRequestWait on a NULL request is a no-op.
284 
285   @param req Address of CeedRequest to wait for; zeroed on completion.
286 
287   @return An error code: 0 - success, otherwise - failure
288 
289   @ref Advanced
290 **/
291 int CeedRequestWait(CeedRequest *req) {
292   if (!*req) return 0;
293   return CeedError(NULL, 2, "CeedRequestWait not implemented");
294 }
295 
296 /**
297   @brief Initialize a \ref Ceed to use the specified resource.
298 
299   @param resource  Resource to use, e.g., "/cpu/self"
300   @param ceed The library context
301   @sa CeedRegister() CeedDestroy()
302 
303   @return An error code: 0 - success, otherwise - failure
304 
305   @ref Basic
306 **/
307 int CeedInit(const char *resource, Ceed *ceed) {
308   int ierr;
309   size_t matchlen = 0, matchidx;
310   unsigned int matchpriority = 100, priority;
311 
312   if (!resource) return CeedError(NULL, 1, "No resource provided");
313   for (size_t i=0; i<num_backends; i++) {
314     size_t n;
315     const char *prefix = backends[i].prefix;
316     for (n = 0; prefix[n] && prefix[n] == resource[n]; n++) {}
317     priority = backends[i].priority;
318     if (n > matchlen || (n == matchlen && matchpriority > priority)) {
319       matchlen = n;
320       matchpriority = priority;
321       matchidx = i;
322     }
323   }
324   if (!matchlen) return CeedError(NULL, 1, "No suitable backend");
325   ierr = CeedCalloc(1,ceed); CeedChk(ierr);
326   const char * ceed_error_handler = getenv("CEED_ERROR_HANDLER");
327   if (!ceed_error_handler) ceed_error_handler = "abort";
328   if (!strcmp(ceed_error_handler, "exit"))
329     (*ceed)->Error = CeedErrorExit;
330   else
331     (*ceed)->Error = CeedErrorAbort;
332   (*ceed)->refcount = 1;
333   (*ceed)->data = NULL;
334   ierr = backends[matchidx].init(resource, *ceed); CeedChk(ierr);
335   return 0;
336 }
337 
338 /**
339   @brief Retrieve a delegate CEED
340 
341   @param ceed           Ceed to retrieve delegate of
342   @param[out] delegate  Address to save the delegate to
343 
344   @return An error code: 0 - success, otherwise - failure
345 
346   @ref Utility
347 **/
348 int CeedGetDelegate(Ceed ceed, Ceed *delegate) {
349   (*delegate)=ceed->delegate;
350   return 0;
351 }
352 
353 /**
354   @brief Destroy a Ceed context
355 
356   @param ceed Address of Ceed context to destroy
357 
358   @return An error code: 0 - success, otherwise - failure
359 
360   @ref Basic
361 **/
362 int CeedDestroy(Ceed *ceed) {
363   int ierr;
364 
365   if (!*ceed || --(*ceed)->refcount > 0) return 0;
366   if ((*ceed)->delegate) {
367     ierr = CeedDestroy(&(*ceed)->delegate); CeedChk(ierr);
368   }
369   if ((*ceed)->Destroy) {
370     ierr = (*ceed)->Destroy(*ceed); CeedChk(ierr);
371   }
372   ierr = CeedFree(ceed); CeedChk(ierr);
373   return 0;
374 }
375 
376 /// @}
377