xref: /libCEED/interface/ceed.c (revision d7b241e67f6e33d9b297db3da3be4f167f32bbee)
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   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 /// Error handling implementation; use \ref CeedError instead.
91 int CeedErrorImpl(Ceed ceed, const char *filename, int lineno, const char *func,
92                   int ecode, const char *format, ...) {
93   va_list args;
94   va_start(args, format);
95   if (ceed) return ceed->Error(ceed, filename, lineno, func, ecode, format, args);
96   return CeedErrorAbort(ceed, filename, lineno, func, ecode, format, args);
97 }
98 
99 /// Error handler that returns without printing anything.
100 ///
101 /// Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
102 ///
103 /// @sa CeedErrorAbort
104 int CeedErrorReturn(Ceed ceed, const char *filename, int lineno,
105                     const char *func, int ecode, const char *format,
106                     va_list args) {
107   return ecode;
108 }
109 
110 /// Error handler that prints to stderr and aborts
111 ///
112 /// Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
113 ///
114 /// @sa CeedErrorReturn
115 int CeedErrorAbort(Ceed ceed, const char *filename, int lineno,
116                    const char *func, int ecode,
117                    const char *format, va_list args) {
118   fprintf(stderr, "%s:%d in %s(): ", filename, lineno, func);
119   vfprintf(stderr, format, args);
120   fprintf(stderr, "\n");
121   abort();
122   return ecode;
123 }
124 
125 /// Set error handler
126 ///
127 /// A default error handler is set in CeedInit().  Use this function to change
128 /// the error handler to CeedErrorReturn(), CeedErrorAbort(), or a user-defined
129 /// error handler.
130 int CeedSetErrorHandler(Ceed ceed,
131                         int (eh)(Ceed, const char *, int, const char *,
132                                  int, const char *, va_list)) {
133   ceed->Error = eh;
134   return 0;
135 }
136 
137 /**
138   Register a Ceed backend
139 
140   @param prefix Prefix of resources for this backend to respond to.  For
141                 example, the reference backend responds to "/cpu/self".
142   @param init   Initialization function called by CeedInit() when the backend
143                 is selected to drive the requested resource.
144   @param priority Integer priority.  Lower values are preferred in case the
145                   resource requested by CeedInit() has non-unique best prefix
146                   match.
147   @return 0 on success
148  */
149 int CeedRegister(const char *prefix,
150                  int (*init)(const char *, Ceed), unsigned int priority) {
151   if (num_backends >= sizeof(backends) / sizeof(backends[0])) {
152     return CeedError(NULL, 1, "Too many backends");
153   }
154   strncpy(backends[num_backends].prefix, prefix, CEED_MAX_RESOURCE_LEN);
155   backends[num_backends].init = init;
156   backends[num_backends].priority = priority;
157   num_backends++;
158   return 0;
159 }
160 
161 /// Allocate an array on the host; use CeedMalloc()
162 ///
163 /// Memory usage can be tracked by the library.  This ensures sufficient
164 /// alignment for vectorization and should be used for large allocations.
165 ///
166 /// @param n Number of units to allocate
167 /// @param unit Size of each unit
168 /// @param p Address of pointer to hold the result.
169 /// @sa CeedFree()
170 int CeedMallocArray(size_t n, size_t unit, void *p) {
171   int ierr = posix_memalign((void **)p, CEED_ALIGN, n*unit);
172   if (ierr)
173     return CeedError(NULL, ierr,
174                      "posix_memalign failed to allocate %zd members of size %zd\n", n, unit);
175   return 0;
176 }
177 
178 /// Allocate a cleared (zeroed) array on the host; use CeedCalloc()
179 ///
180 /// Memory usage can be tracked by the library.
181 ///
182 /// @param n Number of units to allocate
183 /// @param unit Size of each unit
184 /// @param p Address of pointer to hold the result.
185 /// @sa CeedFree()
186 int CeedCallocArray(size_t n, size_t unit, void *p) {
187   *(void **)p = calloc(n, unit);
188   if (n && unit && !*(void **)p)
189     return CeedError(NULL, 1, "calloc failed to allocate %zd members of size %zd\n",
190                      n, unit);
191   return 0;
192 }
193 
194 /// Reallocate an array on the host; use CeedRealloc()
195 ///
196 /// Memory usage can be tracked by the library.
197 ///
198 /// @param n Number of units to allocate
199 /// @param unit Size of each unit
200 /// @param p Address of pointer to hold the result.
201 /// @sa CeedFree()
202 int CeedReallocArray(size_t n, size_t unit, void *p) {
203   *(void **)p = realloc(*(void **)p, n*unit);
204   if (n && unit && !*(void **)p)
205     return CeedError(NULL, 1,
206                      "realloc failed to allocate %zd members of size %zd\n",
207                      n, unit);
208   return 0;
209 }
210 
211 /// Free memory allocated using CeedMalloc() or CeedCalloc()
212 ///
213 /// @param p address of pointer to memory.  This argument is of type void* to
214 /// avoid needing a cast, but is the address of the pointer (which is zeroed)
215 /// rather than the pointer.
216 int CeedFree(void *p) {
217   free(*(void **)p);
218   *(void **)p = NULL;
219   return 0;
220 }
221 
222 /**
223   Wait for a CeedRequest to complete.
224 
225   Calling CeedRequestWait on a NULL request is a no-op.
226 
227   @param req Address of CeedRequest to wait for; zeroed on completion.
228   @return 0 on success
229  */
230 int CeedRequestWait(CeedRequest *req) {
231   if (!*req) return 0;
232   return CeedError(NULL, 2, "CeedRequestWait not implemented");
233 }
234 
235 /// Initialize a \ref Ceed to use the specified resource.
236 ///
237 /// @param resource  Resource to use, e.g., "/cpu/self"
238 /// @param ceed The library context
239 /// @sa CeedRegister() CeedDestroy()
240 int CeedInit(const char *resource, Ceed *ceed) {
241   int ierr;
242   size_t matchlen = 0, matchidx;
243   unsigned int matchpriority = 100, priority;
244 
245   if (!resource) return CeedError(NULL, 1, "No resource provided");
246   for (size_t i=0; i<num_backends; i++) {
247     size_t n;
248     const char *prefix = backends[i].prefix;
249     for (n = 0; prefix[n] && prefix[n] == resource[n]; n++) {}
250     priority = backends[i].priority;
251     if (n > matchlen || (n == matchlen && matchpriority > priority)) {
252       matchlen = n;
253       matchpriority = priority;
254       matchidx = i;
255     }
256   }
257   if (!matchlen) return CeedError(NULL, 1, "No suitable backend");
258   ierr = CeedCalloc(1,ceed); CeedChk(ierr);
259   (*ceed)->Error = CeedErrorAbort;
260   (*ceed)->refcount = 1;
261   (*ceed)->data = NULL;
262   ierr = backends[matchidx].init(resource, *ceed); CeedChk(ierr);
263   return 0;
264 }
265 
266 /**
267   Destroy a Ceed context
268 
269   @param ceed Address of Ceed context to destroy
270   @return 0 on success
271  */
272 int CeedDestroy(Ceed *ceed) {
273   int ierr;
274 
275   if (!*ceed || --(*ceed)->refcount > 0) return 0;
276   if ((*ceed)->Destroy) {
277     ierr = (*ceed)->Destroy(*ceed); CeedChk(ierr);
278   }
279   ierr = CeedFree(ceed); CeedChk(ierr);
280   return 0;
281 }
282 
283 /// @}
284