xref: /libCEED/rust/libceed-sys/c-src/interface/ceed.c (revision d7b241e67f6e33d9b297db3da3be4f167f32bbee)
1*d7b241e6Sjeremylt // Copyright (c) 2017, Lawrence Livermore National Security, LLC. Produced at
2*d7b241e6Sjeremylt // the Lawrence Livermore National Laboratory. LLNL-CODE-734707. All Rights
3*d7b241e6Sjeremylt // reserved. See files LICENSE and NOTICE for details.
4*d7b241e6Sjeremylt //
5*d7b241e6Sjeremylt // This file is part of CEED, a collection of benchmarks, miniapps, software
6*d7b241e6Sjeremylt // libraries and APIs for efficient high-order finite element and spectral
7*d7b241e6Sjeremylt // element discretizations for exascale applications. For more information and
8*d7b241e6Sjeremylt // source code availability see http://github.com/ceed.
9*d7b241e6Sjeremylt //
10*d7b241e6Sjeremylt // The CEED research is supported by the Exascale Computing Project 17-SC-20-SC,
11*d7b241e6Sjeremylt // a collaborative effort of two U.S. Department of Energy organizations (Office
12*d7b241e6Sjeremylt // of Science and the National Nuclear Security Administration) responsible for
13*d7b241e6Sjeremylt // the planning and preparation of a capable exascale ecosystem, including
14*d7b241e6Sjeremylt // software, applications, hardware, advanced system engineering and early
15*d7b241e6Sjeremylt // testbed platforms, in support of the nation's exascale computing imperative.
16*d7b241e6Sjeremylt 
17*d7b241e6Sjeremylt #define _POSIX_C_SOURCE 200112
18*d7b241e6Sjeremylt #include <ceed-impl.h>
19*d7b241e6Sjeremylt 
20*d7b241e6Sjeremylt #include <stdarg.h>
21*d7b241e6Sjeremylt #include <stdio.h>
22*d7b241e6Sjeremylt #include <stdlib.h>
23*d7b241e6Sjeremylt #include <string.h>
24*d7b241e6Sjeremylt 
25*d7b241e6Sjeremylt /// @cond DOXYGEN_SKIP
26*d7b241e6Sjeremylt static CeedRequest ceed_request_immediate;
27*d7b241e6Sjeremylt static CeedRequest ceed_request_ordered;
28*d7b241e6Sjeremylt 
29*d7b241e6Sjeremylt static struct {
30*d7b241e6Sjeremylt   char prefix[CEED_MAX_RESOURCE_LEN];
31*d7b241e6Sjeremylt   int (*init)(const char *resource, Ceed f);
32*d7b241e6Sjeremylt   unsigned int priority;
33*d7b241e6Sjeremylt } backends[32];
34*d7b241e6Sjeremylt static size_t num_backends;
35*d7b241e6Sjeremylt /// @endcond
36*d7b241e6Sjeremylt 
37*d7b241e6Sjeremylt /// @file
38*d7b241e6Sjeremylt /// Implementation of core components of Ceed library
39*d7b241e6Sjeremylt ///
40*d7b241e6Sjeremylt /// @defgroup Ceed Ceed: core components
41*d7b241e6Sjeremylt /// @{
42*d7b241e6Sjeremylt 
43*d7b241e6Sjeremylt /// Request immediate completion
44*d7b241e6Sjeremylt ///
45*d7b241e6Sjeremylt /// This predefined constant is passed as the \ref CeedRequest argument to
46*d7b241e6Sjeremylt /// interfaces when the caller wishes for the operation to be performed
47*d7b241e6Sjeremylt /// immediately.  The code
48*d7b241e6Sjeremylt ///
49*d7b241e6Sjeremylt /// @code
50*d7b241e6Sjeremylt ///   CeedOperatorApply(op, ..., CEED_REQUEST_IMMEDIATE);
51*d7b241e6Sjeremylt /// @endcode
52*d7b241e6Sjeremylt ///
53*d7b241e6Sjeremylt /// is semantically equivalent to
54*d7b241e6Sjeremylt ///
55*d7b241e6Sjeremylt /// @code
56*d7b241e6Sjeremylt ///   CeedRequest request;
57*d7b241e6Sjeremylt ///   CeedOperatorApply(op, ..., &request);
58*d7b241e6Sjeremylt ///   CeedRequestWait(&request);
59*d7b241e6Sjeremylt /// @endcode
60*d7b241e6Sjeremylt ///
61*d7b241e6Sjeremylt /// @sa CEED_REQUEST_ORDERED
62*d7b241e6Sjeremylt CeedRequest *const CEED_REQUEST_IMMEDIATE = &ceed_request_immediate;
63*d7b241e6Sjeremylt 
64*d7b241e6Sjeremylt /**
65*d7b241e6Sjeremylt   Request ordered completion
66*d7b241e6Sjeremylt 
67*d7b241e6Sjeremylt   This predefined constant is passed as the \ref CeedRequest argument to
68*d7b241e6Sjeremylt   interfaces when the caller wishes for the operation to be completed in the
69*d7b241e6Sjeremylt   order that it is submitted to the device.  It is typically used in a construct
70*d7b241e6Sjeremylt   such as
71*d7b241e6Sjeremylt 
72*d7b241e6Sjeremylt   @code
73*d7b241e6Sjeremylt     CeedRequest request;
74*d7b241e6Sjeremylt     CeedOperatorApply(op1, ..., CEED_REQUEST_ORDERED);
75*d7b241e6Sjeremylt     CeedOperatorApply(op2, ..., &request);
76*d7b241e6Sjeremylt     // other optional work
77*d7b241e6Sjeremylt     CeedWait(&request);
78*d7b241e6Sjeremylt   @endcode
79*d7b241e6Sjeremylt 
80*d7b241e6Sjeremylt   which allows the sequence to complete asynchronously but does not start
81*d7b241e6Sjeremylt   `op2` until `op1` has completed.
82*d7b241e6Sjeremylt 
83*d7b241e6Sjeremylt   @fixme The current implementation is overly strict, offering equivalent
84*d7b241e6Sjeremylt   semantics to CEED_REQUEST_IMMEDIATE.
85*d7b241e6Sjeremylt 
86*d7b241e6Sjeremylt   @sa CEED_REQUEST_IMMEDIATE
87*d7b241e6Sjeremylt  */
88*d7b241e6Sjeremylt CeedRequest *const CEED_REQUEST_ORDERED = &ceed_request_ordered;
89*d7b241e6Sjeremylt 
90*d7b241e6Sjeremylt /// Error handling implementation; use \ref CeedError instead.
91*d7b241e6Sjeremylt int CeedErrorImpl(Ceed ceed, const char *filename, int lineno, const char *func,
92*d7b241e6Sjeremylt                   int ecode, const char *format, ...) {
93*d7b241e6Sjeremylt   va_list args;
94*d7b241e6Sjeremylt   va_start(args, format);
95*d7b241e6Sjeremylt   if (ceed) return ceed->Error(ceed, filename, lineno, func, ecode, format, args);
96*d7b241e6Sjeremylt   return CeedErrorAbort(ceed, filename, lineno, func, ecode, format, args);
97*d7b241e6Sjeremylt }
98*d7b241e6Sjeremylt 
99*d7b241e6Sjeremylt /// Error handler that returns without printing anything.
100*d7b241e6Sjeremylt ///
101*d7b241e6Sjeremylt /// Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
102*d7b241e6Sjeremylt ///
103*d7b241e6Sjeremylt /// @sa CeedErrorAbort
104*d7b241e6Sjeremylt int CeedErrorReturn(Ceed ceed, const char *filename, int lineno,
105*d7b241e6Sjeremylt                     const char *func, int ecode, const char *format,
106*d7b241e6Sjeremylt                     va_list args) {
107*d7b241e6Sjeremylt   return ecode;
108*d7b241e6Sjeremylt }
109*d7b241e6Sjeremylt 
110*d7b241e6Sjeremylt /// Error handler that prints to stderr and aborts
111*d7b241e6Sjeremylt ///
112*d7b241e6Sjeremylt /// Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
113*d7b241e6Sjeremylt ///
114*d7b241e6Sjeremylt /// @sa CeedErrorReturn
115*d7b241e6Sjeremylt int CeedErrorAbort(Ceed ceed, const char *filename, int lineno,
116*d7b241e6Sjeremylt                    const char *func, int ecode,
117*d7b241e6Sjeremylt                    const char *format, va_list args) {
118*d7b241e6Sjeremylt   fprintf(stderr, "%s:%d in %s(): ", filename, lineno, func);
119*d7b241e6Sjeremylt   vfprintf(stderr, format, args);
120*d7b241e6Sjeremylt   fprintf(stderr, "\n");
121*d7b241e6Sjeremylt   abort();
122*d7b241e6Sjeremylt   return ecode;
123*d7b241e6Sjeremylt }
124*d7b241e6Sjeremylt 
125*d7b241e6Sjeremylt /// Set error handler
126*d7b241e6Sjeremylt ///
127*d7b241e6Sjeremylt /// A default error handler is set in CeedInit().  Use this function to change
128*d7b241e6Sjeremylt /// the error handler to CeedErrorReturn(), CeedErrorAbort(), or a user-defined
129*d7b241e6Sjeremylt /// error handler.
130*d7b241e6Sjeremylt int CeedSetErrorHandler(Ceed ceed,
131*d7b241e6Sjeremylt                         int (eh)(Ceed, const char *, int, const char *,
132*d7b241e6Sjeremylt                                  int, const char *, va_list)) {
133*d7b241e6Sjeremylt   ceed->Error = eh;
134*d7b241e6Sjeremylt   return 0;
135*d7b241e6Sjeremylt }
136*d7b241e6Sjeremylt 
137*d7b241e6Sjeremylt /**
138*d7b241e6Sjeremylt   Register a Ceed backend
139*d7b241e6Sjeremylt 
140*d7b241e6Sjeremylt   @param prefix Prefix of resources for this backend to respond to.  For
141*d7b241e6Sjeremylt                 example, the reference backend responds to "/cpu/self".
142*d7b241e6Sjeremylt   @param init   Initialization function called by CeedInit() when the backend
143*d7b241e6Sjeremylt                 is selected to drive the requested resource.
144*d7b241e6Sjeremylt   @param priority Integer priority.  Lower values are preferred in case the
145*d7b241e6Sjeremylt                   resource requested by CeedInit() has non-unique best prefix
146*d7b241e6Sjeremylt                   match.
147*d7b241e6Sjeremylt   @return 0 on success
148*d7b241e6Sjeremylt  */
149*d7b241e6Sjeremylt int CeedRegister(const char *prefix,
150*d7b241e6Sjeremylt                  int (*init)(const char *, Ceed), unsigned int priority) {
151*d7b241e6Sjeremylt   if (num_backends >= sizeof(backends) / sizeof(backends[0])) {
152*d7b241e6Sjeremylt     return CeedError(NULL, 1, "Too many backends");
153*d7b241e6Sjeremylt   }
154*d7b241e6Sjeremylt   strncpy(backends[num_backends].prefix, prefix, CEED_MAX_RESOURCE_LEN);
155*d7b241e6Sjeremylt   backends[num_backends].init = init;
156*d7b241e6Sjeremylt   backends[num_backends].priority = priority;
157*d7b241e6Sjeremylt   num_backends++;
158*d7b241e6Sjeremylt   return 0;
159*d7b241e6Sjeremylt }
160*d7b241e6Sjeremylt 
161*d7b241e6Sjeremylt /// Allocate an array on the host; use CeedMalloc()
162*d7b241e6Sjeremylt ///
163*d7b241e6Sjeremylt /// Memory usage can be tracked by the library.  This ensures sufficient
164*d7b241e6Sjeremylt /// alignment for vectorization and should be used for large allocations.
165*d7b241e6Sjeremylt ///
166*d7b241e6Sjeremylt /// @param n Number of units to allocate
167*d7b241e6Sjeremylt /// @param unit Size of each unit
168*d7b241e6Sjeremylt /// @param p Address of pointer to hold the result.
169*d7b241e6Sjeremylt /// @sa CeedFree()
170*d7b241e6Sjeremylt int CeedMallocArray(size_t n, size_t unit, void *p) {
171*d7b241e6Sjeremylt   int ierr = posix_memalign((void **)p, CEED_ALIGN, n*unit);
172*d7b241e6Sjeremylt   if (ierr)
173*d7b241e6Sjeremylt     return CeedError(NULL, ierr,
174*d7b241e6Sjeremylt                      "posix_memalign failed to allocate %zd members of size %zd\n", n, unit);
175*d7b241e6Sjeremylt   return 0;
176*d7b241e6Sjeremylt }
177*d7b241e6Sjeremylt 
178*d7b241e6Sjeremylt /// Allocate a cleared (zeroed) array on the host; use CeedCalloc()
179*d7b241e6Sjeremylt ///
180*d7b241e6Sjeremylt /// Memory usage can be tracked by the library.
181*d7b241e6Sjeremylt ///
182*d7b241e6Sjeremylt /// @param n Number of units to allocate
183*d7b241e6Sjeremylt /// @param unit Size of each unit
184*d7b241e6Sjeremylt /// @param p Address of pointer to hold the result.
185*d7b241e6Sjeremylt /// @sa CeedFree()
186*d7b241e6Sjeremylt int CeedCallocArray(size_t n, size_t unit, void *p) {
187*d7b241e6Sjeremylt   *(void **)p = calloc(n, unit);
188*d7b241e6Sjeremylt   if (n && unit && !*(void **)p)
189*d7b241e6Sjeremylt     return CeedError(NULL, 1, "calloc failed to allocate %zd members of size %zd\n",
190*d7b241e6Sjeremylt                      n, unit);
191*d7b241e6Sjeremylt   return 0;
192*d7b241e6Sjeremylt }
193*d7b241e6Sjeremylt 
194*d7b241e6Sjeremylt /// Reallocate an array on the host; use CeedRealloc()
195*d7b241e6Sjeremylt ///
196*d7b241e6Sjeremylt /// Memory usage can be tracked by the library.
197*d7b241e6Sjeremylt ///
198*d7b241e6Sjeremylt /// @param n Number of units to allocate
199*d7b241e6Sjeremylt /// @param unit Size of each unit
200*d7b241e6Sjeremylt /// @param p Address of pointer to hold the result.
201*d7b241e6Sjeremylt /// @sa CeedFree()
202*d7b241e6Sjeremylt int CeedReallocArray(size_t n, size_t unit, void *p) {
203*d7b241e6Sjeremylt   *(void **)p = realloc(*(void **)p, n*unit);
204*d7b241e6Sjeremylt   if (n && unit && !*(void **)p)
205*d7b241e6Sjeremylt     return CeedError(NULL, 1,
206*d7b241e6Sjeremylt                      "realloc failed to allocate %zd members of size %zd\n",
207*d7b241e6Sjeremylt                      n, unit);
208*d7b241e6Sjeremylt   return 0;
209*d7b241e6Sjeremylt }
210*d7b241e6Sjeremylt 
211*d7b241e6Sjeremylt /// Free memory allocated using CeedMalloc() or CeedCalloc()
212*d7b241e6Sjeremylt ///
213*d7b241e6Sjeremylt /// @param p address of pointer to memory.  This argument is of type void* to
214*d7b241e6Sjeremylt /// avoid needing a cast, but is the address of the pointer (which is zeroed)
215*d7b241e6Sjeremylt /// rather than the pointer.
216*d7b241e6Sjeremylt int CeedFree(void *p) {
217*d7b241e6Sjeremylt   free(*(void **)p);
218*d7b241e6Sjeremylt   *(void **)p = NULL;
219*d7b241e6Sjeremylt   return 0;
220*d7b241e6Sjeremylt }
221*d7b241e6Sjeremylt 
222*d7b241e6Sjeremylt /**
223*d7b241e6Sjeremylt   Wait for a CeedRequest to complete.
224*d7b241e6Sjeremylt 
225*d7b241e6Sjeremylt   Calling CeedRequestWait on a NULL request is a no-op.
226*d7b241e6Sjeremylt 
227*d7b241e6Sjeremylt   @param req Address of CeedRequest to wait for; zeroed on completion.
228*d7b241e6Sjeremylt   @return 0 on success
229*d7b241e6Sjeremylt  */
230*d7b241e6Sjeremylt int CeedRequestWait(CeedRequest *req) {
231*d7b241e6Sjeremylt   if (!*req) return 0;
232*d7b241e6Sjeremylt   return CeedError(NULL, 2, "CeedRequestWait not implemented");
233*d7b241e6Sjeremylt }
234*d7b241e6Sjeremylt 
235*d7b241e6Sjeremylt /// Initialize a \ref Ceed to use the specified resource.
236*d7b241e6Sjeremylt ///
237*d7b241e6Sjeremylt /// @param resource  Resource to use, e.g., "/cpu/self"
238*d7b241e6Sjeremylt /// @param ceed The library context
239*d7b241e6Sjeremylt /// @sa CeedRegister() CeedDestroy()
240*d7b241e6Sjeremylt int CeedInit(const char *resource, Ceed *ceed) {
241*d7b241e6Sjeremylt   int ierr;
242*d7b241e6Sjeremylt   size_t matchlen = 0, matchidx;
243*d7b241e6Sjeremylt   unsigned int matchpriority = 100, priority;
244*d7b241e6Sjeremylt 
245*d7b241e6Sjeremylt   if (!resource) return CeedError(NULL, 1, "No resource provided");
246*d7b241e6Sjeremylt   for (size_t i=0; i<num_backends; i++) {
247*d7b241e6Sjeremylt     size_t n;
248*d7b241e6Sjeremylt     const char *prefix = backends[i].prefix;
249*d7b241e6Sjeremylt     for (n = 0; prefix[n] && prefix[n] == resource[n]; n++) {}
250*d7b241e6Sjeremylt     priority = backends[i].priority;
251*d7b241e6Sjeremylt     if (n > matchlen || (n == matchlen && matchpriority > priority)) {
252*d7b241e6Sjeremylt       matchlen = n;
253*d7b241e6Sjeremylt       matchpriority = priority;
254*d7b241e6Sjeremylt       matchidx = i;
255*d7b241e6Sjeremylt     }
256*d7b241e6Sjeremylt   }
257*d7b241e6Sjeremylt   if (!matchlen) return CeedError(NULL, 1, "No suitable backend");
258*d7b241e6Sjeremylt   ierr = CeedCalloc(1,ceed); CeedChk(ierr);
259*d7b241e6Sjeremylt   (*ceed)->Error = CeedErrorAbort;
260*d7b241e6Sjeremylt   (*ceed)->refcount = 1;
261*d7b241e6Sjeremylt   (*ceed)->data = NULL;
262*d7b241e6Sjeremylt   ierr = backends[matchidx].init(resource, *ceed); CeedChk(ierr);
263*d7b241e6Sjeremylt   return 0;
264*d7b241e6Sjeremylt }
265*d7b241e6Sjeremylt 
266*d7b241e6Sjeremylt /**
267*d7b241e6Sjeremylt   Destroy a Ceed context
268*d7b241e6Sjeremylt 
269*d7b241e6Sjeremylt   @param ceed Address of Ceed context to destroy
270*d7b241e6Sjeremylt   @return 0 on success
271*d7b241e6Sjeremylt  */
272*d7b241e6Sjeremylt int CeedDestroy(Ceed *ceed) {
273*d7b241e6Sjeremylt   int ierr;
274*d7b241e6Sjeremylt 
275*d7b241e6Sjeremylt   if (!*ceed || --(*ceed)->refcount > 0) return 0;
276*d7b241e6Sjeremylt   if ((*ceed)->Destroy) {
277*d7b241e6Sjeremylt     ierr = (*ceed)->Destroy(*ceed); CeedChk(ierr);
278*d7b241e6Sjeremylt   }
279*d7b241e6Sjeremylt   ierr = CeedFree(ceed); CeedChk(ierr);
280*d7b241e6Sjeremylt   return 0;
281*d7b241e6Sjeremylt }
282*d7b241e6Sjeremylt 
283*d7b241e6Sjeremylt /// @}
284