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