xref: /libCEED/rust/libceed-sys/c-src/interface/ceed.c (revision d863ab9ba6a0d47c58445a35d35b36efba07fc93)
1d7b241e6Sjeremylt // Copyright (c) 2017, Lawrence Livermore National Security, LLC. Produced at
2d7b241e6Sjeremylt // the Lawrence Livermore National Laboratory. LLNL-CODE-734707. All Rights
3d7b241e6Sjeremylt // reserved. See files LICENSE and NOTICE for details.
4d7b241e6Sjeremylt //
5d7b241e6Sjeremylt // This file is part of CEED, a collection of benchmarks, miniapps, software
6d7b241e6Sjeremylt // libraries and APIs for efficient high-order finite element and spectral
7d7b241e6Sjeremylt // element discretizations for exascale applications. For more information and
8d7b241e6Sjeremylt // source code availability see http://github.com/ceed.
9d7b241e6Sjeremylt //
10d7b241e6Sjeremylt // The CEED research is supported by the Exascale Computing Project 17-SC-20-SC,
11d7b241e6Sjeremylt // a collaborative effort of two U.S. Department of Energy organizations (Office
12d7b241e6Sjeremylt // of Science and the National Nuclear Security Administration) responsible for
13d7b241e6Sjeremylt // the planning and preparation of a capable exascale ecosystem, including
14d7b241e6Sjeremylt // software, applications, hardware, advanced system engineering and early
15d7b241e6Sjeremylt // testbed platforms, in support of the nation's exascale computing imperative.
16d7b241e6Sjeremylt 
17d7b241e6Sjeremylt #define _POSIX_C_SOURCE 200112
18d7b241e6Sjeremylt #include <ceed-impl.h>
19*d863ab9bSjeremylt #include <ceed-backend.h>
20d7b241e6Sjeremylt #include <stdarg.h>
21d7b241e6Sjeremylt #include <stdio.h>
22d7b241e6Sjeremylt #include <stdlib.h>
23d7b241e6Sjeremylt #include <string.h>
24d7b241e6Sjeremylt 
25d7b241e6Sjeremylt /// @cond DOXYGEN_SKIP
26d7b241e6Sjeremylt static CeedRequest ceed_request_immediate;
27d7b241e6Sjeremylt static CeedRequest ceed_request_ordered;
28d7b241e6Sjeremylt 
29d7b241e6Sjeremylt static struct {
30d7b241e6Sjeremylt   char prefix[CEED_MAX_RESOURCE_LEN];
31d7b241e6Sjeremylt   int (*init)(const char *resource, Ceed f);
32d7b241e6Sjeremylt   unsigned int priority;
33d7b241e6Sjeremylt } backends[32];
34d7b241e6Sjeremylt static size_t num_backends;
35d7b241e6Sjeremylt /// @endcond
36d7b241e6Sjeremylt 
37d7b241e6Sjeremylt /// @file
38d7b241e6Sjeremylt /// Implementation of core components of Ceed library
39d7b241e6Sjeremylt ///
40dfdf5a53Sjeremylt /// @addtogroup Ceed
41d7b241e6Sjeremylt /// @{
42d7b241e6Sjeremylt 
43dfdf5a53Sjeremylt /**
44dfdf5a53Sjeremylt   @brief Request immediate completion
45dfdf5a53Sjeremylt 
46dfdf5a53Sjeremylt   This predefined constant is passed as the \ref CeedRequest argument to
47dfdf5a53Sjeremylt   interfaces when the caller wishes for the operation to be performed
48dfdf5a53Sjeremylt   immediately.  The code
49dfdf5a53Sjeremylt 
50dfdf5a53Sjeremylt   @code
51dfdf5a53Sjeremylt     CeedOperatorApply(op, ..., CEED_REQUEST_IMMEDIATE);
52dfdf5a53Sjeremylt   @endcode
53dfdf5a53Sjeremylt 
54dfdf5a53Sjeremylt   is semantically equivalent to
55dfdf5a53Sjeremylt 
56dfdf5a53Sjeremylt   @code
57dfdf5a53Sjeremylt     CeedRequest request;
58dfdf5a53Sjeremylt     CeedOperatorApply(op, ..., &request);
59dfdf5a53Sjeremylt     CeedRequestWait(&request);
60dfdf5a53Sjeremylt   @endcode
61dfdf5a53Sjeremylt 
62dfdf5a53Sjeremylt   @sa CEED_REQUEST_ORDERED
63dfdf5a53Sjeremylt **/
64d7b241e6Sjeremylt CeedRequest *const CEED_REQUEST_IMMEDIATE = &ceed_request_immediate;
65d7b241e6Sjeremylt 
66d7b241e6Sjeremylt /**
67b11c1e72Sjeremylt   @brief Request ordered completion
68d7b241e6Sjeremylt 
69d7b241e6Sjeremylt   This predefined constant is passed as the \ref CeedRequest argument to
70d7b241e6Sjeremylt   interfaces when the caller wishes for the operation to be completed in the
71d7b241e6Sjeremylt   order that it is submitted to the device.  It is typically used in a construct
72d7b241e6Sjeremylt   such as
73d7b241e6Sjeremylt 
74d7b241e6Sjeremylt   @code
75d7b241e6Sjeremylt     CeedRequest request;
76d7b241e6Sjeremylt     CeedOperatorApply(op1, ..., CEED_REQUEST_ORDERED);
77d7b241e6Sjeremylt     CeedOperatorApply(op2, ..., &request);
78d7b241e6Sjeremylt     // other optional work
79d7b241e6Sjeremylt     CeedWait(&request);
80d7b241e6Sjeremylt   @endcode
81d7b241e6Sjeremylt 
82d7b241e6Sjeremylt   which allows the sequence to complete asynchronously but does not start
83d7b241e6Sjeremylt   `op2` until `op1` has completed.
84d7b241e6Sjeremylt 
85d7b241e6Sjeremylt   @fixme The current implementation is overly strict, offering equivalent
86d7b241e6Sjeremylt   semantics to CEED_REQUEST_IMMEDIATE.
87d7b241e6Sjeremylt 
88d7b241e6Sjeremylt   @sa CEED_REQUEST_IMMEDIATE
89d7b241e6Sjeremylt  */
90d7b241e6Sjeremylt CeedRequest *const CEED_REQUEST_ORDERED = &ceed_request_ordered;
91d7b241e6Sjeremylt 
92b11c1e72Sjeremylt /**
93b11c1e72Sjeremylt   @brief Error handling implementation; use \ref CeedError instead.
94dfdf5a53Sjeremylt 
95dfdf5a53Sjeremylt   @ref Developer
96b11c1e72Sjeremylt **/
97d7b241e6Sjeremylt int CeedErrorImpl(Ceed ceed, const char *filename, int lineno, const char *func,
98d7b241e6Sjeremylt                   int ecode, const char *format, ...) {
99d7b241e6Sjeremylt   va_list args;
100d7b241e6Sjeremylt   va_start(args, format);
101d7b241e6Sjeremylt   if (ceed) return ceed->Error(ceed, filename, lineno, func, ecode, format, args);
102d7b241e6Sjeremylt   return CeedErrorAbort(ceed, filename, lineno, func, ecode, format, args);
103d7b241e6Sjeremylt }
104d7b241e6Sjeremylt 
105b11c1e72Sjeremylt /**
106b11c1e72Sjeremylt   @brief Error handler that returns without printing anything.
107b11c1e72Sjeremylt 
108b11c1e72Sjeremylt   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
109dfdf5a53Sjeremylt 
110dfdf5a53Sjeremylt   @ref Developer
111b11c1e72Sjeremylt **/
112d7b241e6Sjeremylt int CeedErrorReturn(Ceed ceed, const char *filename, int lineno,
113d7b241e6Sjeremylt                     const char *func, int ecode, const char *format,
114d7b241e6Sjeremylt                     va_list args) {
115d7b241e6Sjeremylt   return ecode;
116d7b241e6Sjeremylt }
117d7b241e6Sjeremylt 
118b11c1e72Sjeremylt /**
119b11c1e72Sjeremylt   @brief Error handler that prints to stderr and aborts
120b11c1e72Sjeremylt 
121b11c1e72Sjeremylt   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
122dfdf5a53Sjeremylt 
123dfdf5a53Sjeremylt   @ref Developer
124b11c1e72Sjeremylt **/
125d7b241e6Sjeremylt int CeedErrorAbort(Ceed ceed, const char *filename, int lineno,
126d7b241e6Sjeremylt                    const char *func, int ecode,
127d7b241e6Sjeremylt                    const char *format, va_list args) {
128d7b241e6Sjeremylt   fprintf(stderr, "%s:%d in %s(): ", filename, lineno, func);
129d7b241e6Sjeremylt   vfprintf(stderr, format, args);
130d7b241e6Sjeremylt   fprintf(stderr, "\n");
131d7b241e6Sjeremylt   abort();
132d7b241e6Sjeremylt   return ecode;
133d7b241e6Sjeremylt }
134d7b241e6Sjeremylt 
135b11c1e72Sjeremylt /**
13656e866f4SJed Brown   @brief Error handler that prints to stderr and exits
13756e866f4SJed Brown 
13856e866f4SJed Brown   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
13956e866f4SJed Brown 
14056e866f4SJed Brown   In contrast to CeedErrorAbort(), this exits without a signal, so atexit()
14156e866f4SJed Brown   handlers (e.g., as used by gcov) are run.
14256e866f4SJed Brown 
14356e866f4SJed Brown   @ref Developer
14456e866f4SJed Brown **/
14556e866f4SJed Brown int CeedErrorExit(Ceed ceed, const char *filename, int lineno,
14656e866f4SJed Brown                   const char *func, int ecode,
14756e866f4SJed Brown                   const char *format, va_list args) {
14856e866f4SJed Brown   fprintf(stderr, "%s:%d in %s(): ", filename, lineno, func);
14956e866f4SJed Brown   vfprintf(stderr, format, args);
15056e866f4SJed Brown   fprintf(stderr, "\n");
15156e866f4SJed Brown   exit(ecode);
15256e866f4SJed Brown   return ecode;
15356e866f4SJed Brown }
15456e866f4SJed Brown 
15556e866f4SJed Brown /**
156dfdf5a53Sjeremylt   @brief Set error handler
157b11c1e72Sjeremylt 
158b11c1e72Sjeremylt   A default error handler is set in CeedInit().  Use this function to change
159b11c1e72Sjeremylt   the error handler to CeedErrorReturn(), CeedErrorAbort(), or a user-defined
160b11c1e72Sjeremylt   error handler.
161dfdf5a53Sjeremylt 
162dfdf5a53Sjeremylt   @ref Developer
163b11c1e72Sjeremylt **/
164d7b241e6Sjeremylt int CeedSetErrorHandler(Ceed ceed,
165d7b241e6Sjeremylt                         int (eh)(Ceed, const char *, int, const char *,
166d7b241e6Sjeremylt                                  int, const char *, va_list)) {
167d7b241e6Sjeremylt   ceed->Error = eh;
168d7b241e6Sjeremylt   return 0;
169d7b241e6Sjeremylt }
170d7b241e6Sjeremylt 
171d7b241e6Sjeremylt /**
172b11c1e72Sjeremylt   @brief Register a Ceed backend
173d7b241e6Sjeremylt 
174d7b241e6Sjeremylt   @param prefix   Prefix of resources for this backend to respond to.  For
175d7b241e6Sjeremylt                     example, the reference backend responds to "/cpu/self".
176d7b241e6Sjeremylt   @param init     Initialization function called by CeedInit() when the backend
177d7b241e6Sjeremylt                     is selected to drive the requested resource.
178d7b241e6Sjeremylt   @param priority Integer priority.  Lower values are preferred in case the
179d7b241e6Sjeremylt                     resource requested by CeedInit() has non-unique best prefix
180d7b241e6Sjeremylt                     match.
181b11c1e72Sjeremylt 
182b11c1e72Sjeremylt   @return An error code: 0 - success, otherwise - failure
183dfdf5a53Sjeremylt 
184dfdf5a53Sjeremylt   @ref Advanced
185b11c1e72Sjeremylt **/
186d7b241e6Sjeremylt int CeedRegister(const char *prefix,
187d7b241e6Sjeremylt                  int (*init)(const char *, Ceed), unsigned int priority) {
188d7b241e6Sjeremylt   if (num_backends >= sizeof(backends) / sizeof(backends[0])) {
189d7b241e6Sjeremylt     return CeedError(NULL, 1, "Too many backends");
190d7b241e6Sjeremylt   }
191d7b241e6Sjeremylt   strncpy(backends[num_backends].prefix, prefix, CEED_MAX_RESOURCE_LEN);
192d7b241e6Sjeremylt   backends[num_backends].init = init;
193d7b241e6Sjeremylt   backends[num_backends].priority = priority;
194d7b241e6Sjeremylt   num_backends++;
195d7b241e6Sjeremylt   return 0;
196d7b241e6Sjeremylt }
197d7b241e6Sjeremylt 
198b11c1e72Sjeremylt /**
199b11c1e72Sjeremylt   @brief Allocate an array on the host; use CeedMalloc()
200b11c1e72Sjeremylt 
201b11c1e72Sjeremylt   Memory usage can be tracked by the library.  This ensures sufficient
202b11c1e72Sjeremylt     alignment for vectorization and should be used for large allocations.
203b11c1e72Sjeremylt 
204b11c1e72Sjeremylt   @param n Number of units to allocate
205b11c1e72Sjeremylt   @param unit Size of each unit
206b11c1e72Sjeremylt   @param p Address of pointer to hold the result.
207b11c1e72Sjeremylt 
208b11c1e72Sjeremylt   @return An error code: 0 - success, otherwise - failure
209b11c1e72Sjeremylt 
210b11c1e72Sjeremylt   @sa CeedFree()
211dfdf5a53Sjeremylt 
212dfdf5a53Sjeremylt   @ref Advanced
213b11c1e72Sjeremylt **/
214d7b241e6Sjeremylt int CeedMallocArray(size_t n, size_t unit, void *p) {
215d7b241e6Sjeremylt   int ierr = posix_memalign((void **)p, CEED_ALIGN, n*unit);
216d7b241e6Sjeremylt   if (ierr)
217d7b241e6Sjeremylt     return CeedError(NULL, ierr,
218d7b241e6Sjeremylt                      "posix_memalign failed to allocate %zd members of size %zd\n", n, unit);
219d7b241e6Sjeremylt   return 0;
220d7b241e6Sjeremylt }
221d7b241e6Sjeremylt 
222b11c1e72Sjeremylt /**
223b11c1e72Sjeremylt   @brief Allocate a cleared (zeroed) array on the host; use CeedCalloc()
224b11c1e72Sjeremylt 
225b11c1e72Sjeremylt   Memory usage can be tracked by the library.
226b11c1e72Sjeremylt 
227b11c1e72Sjeremylt   @param n Number of units to allocate
228b11c1e72Sjeremylt   @param unit Size of each unit
229b11c1e72Sjeremylt   @param p Address of pointer to hold the result.
230b11c1e72Sjeremylt 
231b11c1e72Sjeremylt   @return An error code: 0 - success, otherwise - failure
232b11c1e72Sjeremylt 
233b11c1e72Sjeremylt   @sa CeedFree()
234dfdf5a53Sjeremylt 
235dfdf5a53Sjeremylt   @ref Advanced
236b11c1e72Sjeremylt **/
237d7b241e6Sjeremylt int CeedCallocArray(size_t n, size_t unit, void *p) {
238d7b241e6Sjeremylt   *(void **)p = calloc(n, unit);
239d7b241e6Sjeremylt   if (n && unit && !*(void **)p)
240d7b241e6Sjeremylt     return CeedError(NULL, 1, "calloc failed to allocate %zd members of size %zd\n",
241d7b241e6Sjeremylt                      n, unit);
242d7b241e6Sjeremylt   return 0;
243d7b241e6Sjeremylt }
244d7b241e6Sjeremylt 
245b11c1e72Sjeremylt /**
246b11c1e72Sjeremylt   @brief Reallocate an array on the host; use CeedRealloc()
247b11c1e72Sjeremylt 
248b11c1e72Sjeremylt   Memory usage can be tracked by the library.
249b11c1e72Sjeremylt 
250b11c1e72Sjeremylt   @param n Number of units to allocate
251b11c1e72Sjeremylt   @param unit Size of each unit
252b11c1e72Sjeremylt   @param p Address of pointer to hold the result.
253b11c1e72Sjeremylt 
254b11c1e72Sjeremylt   @return An error code: 0 - success, otherwise - failure
255b11c1e72Sjeremylt 
256b11c1e72Sjeremylt   @sa CeedFree()
257dfdf5a53Sjeremylt 
258dfdf5a53Sjeremylt   @ref Advanced
259b11c1e72Sjeremylt **/
260d7b241e6Sjeremylt int CeedReallocArray(size_t n, size_t unit, void *p) {
261d7b241e6Sjeremylt   *(void **)p = realloc(*(void **)p, n*unit);
262d7b241e6Sjeremylt   if (n && unit && !*(void **)p)
263d7b241e6Sjeremylt     return CeedError(NULL, 1,
264d7b241e6Sjeremylt                      "realloc failed to allocate %zd members of size %zd\n",
265d7b241e6Sjeremylt                      n, unit);
266d7b241e6Sjeremylt   return 0;
267d7b241e6Sjeremylt }
268d7b241e6Sjeremylt 
269d7b241e6Sjeremylt /// Free memory allocated using CeedMalloc() or CeedCalloc()
270d7b241e6Sjeremylt ///
271d7b241e6Sjeremylt /// @param p address of pointer to memory.  This argument is of type void* to
272d7b241e6Sjeremylt /// avoid needing a cast, but is the address of the pointer (which is zeroed)
273d7b241e6Sjeremylt /// rather than the pointer.
274d7b241e6Sjeremylt int CeedFree(void *p) {
275d7b241e6Sjeremylt   free(*(void **)p);
276d7b241e6Sjeremylt   *(void **)p = NULL;
277d7b241e6Sjeremylt   return 0;
278d7b241e6Sjeremylt }
279d7b241e6Sjeremylt 
280d7b241e6Sjeremylt /**
281b11c1e72Sjeremylt   @brief Wait for a CeedRequest to complete.
282d7b241e6Sjeremylt 
283d7b241e6Sjeremylt   Calling CeedRequestWait on a NULL request is a no-op.
284d7b241e6Sjeremylt 
285d7b241e6Sjeremylt   @param req Address of CeedRequest to wait for; zeroed on completion.
286b11c1e72Sjeremylt 
287b11c1e72Sjeremylt   @return An error code: 0 - success, otherwise - failure
288dfdf5a53Sjeremylt 
289dfdf5a53Sjeremylt   @ref Advanced
290b11c1e72Sjeremylt **/
291d7b241e6Sjeremylt int CeedRequestWait(CeedRequest *req) {
292d7b241e6Sjeremylt   if (!*req) return 0;
293d7b241e6Sjeremylt   return CeedError(NULL, 2, "CeedRequestWait not implemented");
294d7b241e6Sjeremylt }
295d7b241e6Sjeremylt 
296b11c1e72Sjeremylt /**
297b11c1e72Sjeremylt   @brief Initialize a \ref Ceed to use the specified resource.
298b11c1e72Sjeremylt 
299b11c1e72Sjeremylt   @param resource  Resource to use, e.g., "/cpu/self"
300b11c1e72Sjeremylt   @param ceed The library context
301b11c1e72Sjeremylt   @sa CeedRegister() CeedDestroy()
302b11c1e72Sjeremylt 
303b11c1e72Sjeremylt   @return An error code: 0 - success, otherwise - failure
304dfdf5a53Sjeremylt 
305dfdf5a53Sjeremylt   @ref Basic
306b11c1e72Sjeremylt **/
307d7b241e6Sjeremylt int CeedInit(const char *resource, Ceed *ceed) {
308d7b241e6Sjeremylt   int ierr;
309d7b241e6Sjeremylt   size_t matchlen = 0, matchidx;
310d7b241e6Sjeremylt   unsigned int matchpriority = 100, priority;
311d7b241e6Sjeremylt 
312d7b241e6Sjeremylt   if (!resource) return CeedError(NULL, 1, "No resource provided");
313d7b241e6Sjeremylt   for (size_t i=0; i<num_backends; i++) {
314d7b241e6Sjeremylt     size_t n;
315d7b241e6Sjeremylt     const char *prefix = backends[i].prefix;
316d7b241e6Sjeremylt     for (n = 0; prefix[n] && prefix[n] == resource[n]; n++) {}
317d7b241e6Sjeremylt     priority = backends[i].priority;
318d7b241e6Sjeremylt     if (n > matchlen || (n == matchlen && matchpriority > priority)) {
319d7b241e6Sjeremylt       matchlen = n;
320d7b241e6Sjeremylt       matchpriority = priority;
321d7b241e6Sjeremylt       matchidx = i;
322d7b241e6Sjeremylt     }
323d7b241e6Sjeremylt   }
324d7b241e6Sjeremylt   if (!matchlen) return CeedError(NULL, 1, "No suitable backend");
325d7b241e6Sjeremylt   ierr = CeedCalloc(1,ceed); CeedChk(ierr);
326bc81ce41Sjeremylt   const char * ceed_error_handler = getenv("CEED_ERROR_HANDLER");
327bc81ce41Sjeremylt   if (!ceed_error_handler) ceed_error_handler = "abort";
328bc81ce41Sjeremylt   if (!strcmp(ceed_error_handler, "exit"))
32956e866f4SJed Brown     (*ceed)->Error = CeedErrorExit;
33056e866f4SJed Brown   else
331d7b241e6Sjeremylt     (*ceed)->Error = CeedErrorAbort;
332d7b241e6Sjeremylt   (*ceed)->refcount = 1;
333d7b241e6Sjeremylt   (*ceed)->data = NULL;
334d7b241e6Sjeremylt   ierr = backends[matchidx].init(resource, *ceed); CeedChk(ierr);
335d7b241e6Sjeremylt   return 0;
336d7b241e6Sjeremylt }
337d7b241e6Sjeremylt 
338d7b241e6Sjeremylt /**
3395fe0d4faSjeremylt   @brief Retrieve a delegate CEED
3405fe0d4faSjeremylt 
3415fe0d4faSjeremylt   @param ceed           Ceed to retrieve delegate of
3425fe0d4faSjeremylt   @param[out] delegate  Address to save the delegate to
3435fe0d4faSjeremylt 
3445fe0d4faSjeremylt   @return An error code: 0 - success, otherwise - failure
3455fe0d4faSjeremylt 
3465fe0d4faSjeremylt   @ref Utility
3475fe0d4faSjeremylt **/
3485fe0d4faSjeremylt int CeedGetDelegate(Ceed ceed, Ceed *delegate) {
3494ce2993fSjeremylt   *delegate = ceed->delegate;
3504ce2993fSjeremylt   return 0;
3514ce2993fSjeremylt }
3524ce2993fSjeremylt 
3534ce2993fSjeremylt /**
3544ce2993fSjeremylt   @brief Set a delegate CEED
3554ce2993fSjeremylt 
3564ce2993fSjeremylt   @param ceed           Ceed to set delegate of
3574ce2993fSjeremylt   @param[out] delegate  Address to set the delegate to
3584ce2993fSjeremylt 
3594ce2993fSjeremylt   @return An error code: 0 - success, otherwise - failure
3604ce2993fSjeremylt 
3614ce2993fSjeremylt   @ref Utility
3624ce2993fSjeremylt **/
3634ce2993fSjeremylt int CeedSetDelegate(Ceed ceed, Ceed *delegate) {
3644ce2993fSjeremylt   ceed->delegate = *delegate;
3654ce2993fSjeremylt   return 0;
3664ce2993fSjeremylt }
3674ce2993fSjeremylt 
3684ce2993fSjeremylt /**
3694ce2993fSjeremylt   @brief Retrieve backend data for a CEED
3704ce2993fSjeremylt 
3714ce2993fSjeremylt   @param ceed           Ceed to retrieve delegate of
3724ce2993fSjeremylt   @param[out] data      Address to save data to
3734ce2993fSjeremylt 
3744ce2993fSjeremylt   @return An error code: 0 - success, otherwise - failure
3754ce2993fSjeremylt 
3764ce2993fSjeremylt   @ref Utility
3774ce2993fSjeremylt **/
3784ce2993fSjeremylt int CeedGetData(Ceed ceed, void* *data) {
3794ce2993fSjeremylt   *data = ceed->data;
3805fe0d4faSjeremylt   return 0;
3815fe0d4faSjeremylt }
3825fe0d4faSjeremylt 
3835fe0d4faSjeremylt /**
384b11c1e72Sjeremylt   @brief Destroy a Ceed context
385d7b241e6Sjeremylt 
386d7b241e6Sjeremylt   @param ceed Address of Ceed context to destroy
387b11c1e72Sjeremylt 
388b11c1e72Sjeremylt   @return An error code: 0 - success, otherwise - failure
389dfdf5a53Sjeremylt 
390dfdf5a53Sjeremylt   @ref Basic
391b11c1e72Sjeremylt **/
392d7b241e6Sjeremylt int CeedDestroy(Ceed *ceed) {
393d7b241e6Sjeremylt   int ierr;
394d7b241e6Sjeremylt 
395d7b241e6Sjeremylt   if (!*ceed || --(*ceed)->refcount > 0) return 0;
3965fe0d4faSjeremylt   if ((*ceed)->delegate) {
3975fe0d4faSjeremylt     ierr = CeedDestroy(&(*ceed)->delegate); CeedChk(ierr);
3985fe0d4faSjeremylt   }
399d7b241e6Sjeremylt   if ((*ceed)->Destroy) {
400d7b241e6Sjeremylt     ierr = (*ceed)->Destroy(*ceed); CeedChk(ierr);
401d7b241e6Sjeremylt   }
402d7b241e6Sjeremylt   ierr = CeedFree(ceed); CeedChk(ierr);
403d7b241e6Sjeremylt   return 0;
404d7b241e6Sjeremylt }
405d7b241e6Sjeremylt 
406d7b241e6Sjeremylt /// @}
407