xref: /libCEED/rust/libceed-sys/c-src/interface/ceed.c (revision d79b80ece67d77696fb2bb48d63a0a17c4bf9d99)
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 #include <ceed-backend.h>
20 #include <limits.h>
21 #include <stdarg.h>
22 #include <stddef.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 /// @cond DOXYGEN_SKIP
28 static CeedRequest ceed_request_immediate;
29 static CeedRequest ceed_request_ordered;
30 
31 static struct {
32   char prefix[CEED_MAX_RESOURCE_LEN];
33   int (*init)(const char *resource, Ceed f);
34   unsigned int priority;
35 } backends[32];
36 static size_t num_backends;
37 
38 #define CEED_FTABLE_ENTRY(class, method) \
39   {#class #method, offsetof(struct class ##_private, method)}
40 /// @endcond
41 
42 /// @file
43 /// Implementation of core components of Ceed library
44 ///
45 /// @addtogroup Ceed
46 /// @{
47 
48 /**
49   @brief Request immediate completion
50 
51   This predefined constant is passed as the \ref CeedRequest argument to
52   interfaces when the caller wishes for the operation to be performed
53   immediately.  The code
54 
55   @code
56     CeedOperatorApply(op, ..., CEED_REQUEST_IMMEDIATE);
57   @endcode
58 
59   is semantically equivalent to
60 
61   @code
62     CeedRequest request;
63     CeedOperatorApply(op, ..., &request);
64     CeedRequestWait(&request);
65   @endcode
66 
67   @sa CEED_REQUEST_ORDERED
68 **/
69 CeedRequest *const CEED_REQUEST_IMMEDIATE = &ceed_request_immediate;
70 
71 /**
72   @brief Request ordered completion
73 
74   This predefined constant is passed as the \ref CeedRequest argument to
75   interfaces when the caller wishes for the operation to be completed in the
76   order that it is submitted to the device.  It is typically used in a construct
77   such as
78 
79   @code
80     CeedRequest request;
81     CeedOperatorApply(op1, ..., CEED_REQUEST_ORDERED);
82     CeedOperatorApply(op2, ..., &request);
83     // other optional work
84     CeedWait(&request);
85   @endcode
86 
87   which allows the sequence to complete asynchronously but does not start
88   `op2` until `op1` has completed.
89 
90   @todo The current implementation is overly strict, offering equivalent
91   semantics to CEED_REQUEST_IMMEDIATE.
92 
93   @sa CEED_REQUEST_IMMEDIATE
94  */
95 CeedRequest *const CEED_REQUEST_ORDERED = &ceed_request_ordered;
96 
97 /**
98   @brief Error handling implementation; use \ref CeedError instead.
99 
100   @ref Developer
101 **/
102 int CeedErrorImpl(Ceed ceed, const char *filename, int lineno, const char *func,
103                   int ecode, const char *format, ...) {
104   va_list args;
105   int retval;
106   va_start(args, format);
107   if (ceed) {
108     retval = ceed->Error(ceed, filename, lineno, func, ecode, format, args);
109   } else {
110     // This function doesn't actually return
111     retval = CeedErrorAbort(ceed, filename, lineno, func, ecode, format, args);
112   }
113   va_end(args);
114   return retval;
115 }
116 
117 /**
118   @brief Error handler that returns without printing anything.
119 
120   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
121 
122   @ref Developer
123 **/
124 int CeedErrorReturn(Ceed ceed, const char *filename, int lineno,
125                     const char *func, int ecode, const char *format,
126                     va_list args) {
127   return ecode;
128 }
129 
130 /**
131   @brief Error handler that prints to stderr and aborts
132 
133   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
134 
135   @ref Developer
136 **/
137 int CeedErrorAbort(Ceed ceed, const char *filename, int lineno,
138                    const char *func, int ecode, const char *format,
139                    va_list args) {
140   fprintf(stderr, "%s:%d in %s(): ", filename, lineno, func);
141   vfprintf(stderr, format, args);
142   fprintf(stderr, "\n");
143   abort();
144   return ecode;
145 }
146 
147 /**
148   @brief Error handler that prints to stderr and exits
149 
150   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
151 
152   In contrast to CeedErrorAbort(), this exits without a signal, so atexit()
153   handlers (e.g., as used by gcov) are run.
154 
155   @ref Developer
156 **/
157 int CeedErrorExit(Ceed ceed, const char *filename, int lineno, const char *func,
158                   int ecode, const char *format, va_list args) {
159   fprintf(stderr, "%s:%d in %s(): ", filename, lineno, func);
160   vfprintf(stderr, format, args);
161   fprintf(stderr, "\n");
162   exit(ecode);
163   return ecode;
164 }
165 
166 /**
167   @brief Set error handler
168 
169   A default error handler is set in CeedInit().  Use this function to change
170   the error handler to CeedErrorReturn(), CeedErrorAbort(), or a user-defined
171   error handler.
172 
173   @ref Developer
174 **/
175 int CeedSetErrorHandler(Ceed ceed,
176                         int (eh)(Ceed, const char *, int, const char *,
177                                  int, const char *, va_list)) {
178   ceed->Error = eh;
179   return 0;
180 }
181 
182 /**
183   @brief Register a Ceed backend
184 
185   @param prefix   Prefix of resources for this backend to respond to.  For
186                     example, the reference backend responds to "/cpu/self".
187   @param init     Initialization function called by CeedInit() when the backend
188                     is selected to drive the requested resource.
189   @param priority Integer priority.  Lower values are preferred in case the
190                     resource requested by CeedInit() has non-unique best prefix
191                     match.
192 
193   @return An error code: 0 - success, otherwise - failure
194 
195   @ref Advanced
196 **/
197 int CeedRegister(const char *prefix, int (*init)(const char *, Ceed),
198                  unsigned int priority) {
199   if (num_backends >= sizeof(backends) / sizeof(backends[0]))
200     // LCOV_EXCL_START
201     return CeedError(NULL, 1, "Too many backends");
202   // LCOV_EXCL_STOP
203 
204   strncpy(backends[num_backends].prefix, prefix, CEED_MAX_RESOURCE_LEN);
205   backends[num_backends].prefix[CEED_MAX_RESOURCE_LEN-1] = 0;
206   backends[num_backends].init = init;
207   backends[num_backends].priority = priority;
208   num_backends++;
209   return 0;
210 }
211 
212 /**
213   @brief Allocate an array on the host; use CeedMalloc()
214 
215   Memory usage can be tracked by the library.  This ensures sufficient
216     alignment for vectorization and should be used for large allocations.
217 
218   @param n Number of units to allocate
219   @param unit Size of each unit
220   @param p Address of pointer to hold the result.
221 
222   @return An error code: 0 - success, otherwise - failure
223 
224   @sa CeedFree()
225 
226   @ref Advanced
227 **/
228 int CeedMallocArray(size_t n, size_t unit, void *p) {
229   int ierr = posix_memalign((void **)p, CEED_ALIGN, n*unit);
230   if (ierr)
231     // LCOV_EXCL_START
232     return CeedError(NULL, ierr, "posix_memalign failed to allocate %zd "
233                      "members of size %zd\n", n, unit);
234   // LCOV_EXCL_STOP
235 
236   return 0;
237 }
238 
239 /**
240   @brief Allocate a cleared (zeroed) array on the host; use CeedCalloc()
241 
242   Memory usage can be tracked by the library.
243 
244   @param n    Number of units to allocate
245   @param unit Size of each unit
246   @param p    Address of pointer to hold the result.
247 
248   @return An error code: 0 - success, otherwise - failure
249 
250   @sa CeedFree()
251 
252   @ref Advanced
253 **/
254 int CeedCallocArray(size_t n, size_t unit, void *p) {
255   *(void **)p = calloc(n, unit);
256   if (n && unit && !*(void **)p)
257     // LCOV_EXCL_START
258     return CeedError(NULL, 1, "calloc failed to allocate %zd members of size "
259                      "%zd\n", n, unit);
260   // LCOV_EXCL_STOP
261 
262   return 0;
263 }
264 
265 /**
266   @brief Reallocate an array on the host; use CeedRealloc()
267 
268   Memory usage can be tracked by the library.
269 
270   @param n    Number of units to allocate
271   @param unit Size of each unit
272   @param p    Address of pointer to hold the result.
273 
274   @return An error code: 0 - success, otherwise - failure
275 
276   @sa CeedFree()
277 
278   @ref Advanced
279 **/
280 int CeedReallocArray(size_t n, size_t unit, void *p) {
281   *(void **)p = realloc(*(void **)p, n*unit);
282   if (n && unit && !*(void **)p)
283     // LCOV_EXCL_START
284     return CeedError(NULL, 1, "realloc failed to allocate %zd members of size "
285                      "%zd\n", n, unit);
286   // LCOV_EXCL_STOP
287 
288   return 0;
289 }
290 
291 /// Free memory allocated using CeedMalloc() or CeedCalloc()
292 ///
293 /// @param p address of pointer to memory.  This argument is of type void* to
294 /// avoid needing a cast, but is the address of the pointer (which is zeroed)
295 /// rather than the pointer.
296 int CeedFree(void *p) {
297   free(*(void **)p);
298   *(void **)p = NULL;
299   return 0;
300 }
301 
302 /**
303   @brief Wait for a CeedRequest to complete.
304 
305   Calling CeedRequestWait on a NULL request is a no-op.
306 
307   @param req Address of CeedRequest to wait for; zeroed on completion.
308 
309   @return An error code: 0 - success, otherwise - failure
310 
311   @ref Advanced
312 **/
313 int CeedRequestWait(CeedRequest *req) {
314   if (!*req)
315     return 0;
316   return CeedError(NULL, 2, "CeedRequestWait not implemented");
317 }
318 
319 /**
320   @brief Initialize a \ref Ceed context to use the specified resource.
321 
322   @param resource  Resource to use, e.g., "/cpu/self"
323   @param ceed      The library context
324   @sa CeedRegister() CeedDestroy()
325 
326   @return An error code: 0 - success, otherwise - failure
327 
328   @ref Basic
329 **/
330 int CeedInit(const char *resource, Ceed *ceed) {
331   int ierr;
332   size_t matchlen = 0, matchidx = UINT_MAX, matchpriority = UINT_MAX, priority;
333 
334   // Find matching backend
335   if (!resource)
336     return CeedError(NULL, 1, "No resource provided");
337   for (size_t i=0; i<num_backends; i++) {
338     size_t n;
339     const char *prefix = backends[i].prefix;
340     for (n = 0; prefix[n] && prefix[n] == resource[n]; n++) {}
341     priority = backends[i].priority;
342     if (n > matchlen || (n == matchlen && matchpriority > priority)) {
343       matchlen = n;
344       matchpriority = priority;
345       matchidx = i;
346     }
347   }
348   if (!matchlen)
349     return CeedError(NULL, 1, "No suitable backend");
350 
351   // Setup Ceed
352   ierr = CeedCalloc(1,ceed); CeedChk(ierr);
353   const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER");
354   if (!ceed_error_handler)
355     ceed_error_handler = "abort";
356   if (!strcmp(ceed_error_handler, "exit"))
357     (*ceed)->Error = CeedErrorExit;
358   else
359     (*ceed)->Error = CeedErrorAbort;
360   (*ceed)->refcount = 1;
361   (*ceed)->data = NULL;
362 
363   // Set lookup table
364   foffset foffsets[] = {
365     CEED_FTABLE_ENTRY(Ceed, Error),
366     CEED_FTABLE_ENTRY(Ceed, GetPreferredMemType),
367     CEED_FTABLE_ENTRY(Ceed, Destroy),
368     CEED_FTABLE_ENTRY(Ceed, VectorCreate),
369     CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreate),
370     CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreateBlocked),
371     CEED_FTABLE_ENTRY(Ceed, BasisCreateTensorH1),
372     CEED_FTABLE_ENTRY(Ceed, BasisCreateH1),
373     CEED_FTABLE_ENTRY(Ceed, TensorContractCreate),
374     CEED_FTABLE_ENTRY(Ceed, QFunctionCreate),
375     CEED_FTABLE_ENTRY(Ceed, OperatorCreate),
376     CEED_FTABLE_ENTRY(Ceed, CompositeOperatorCreate),
377     CEED_FTABLE_ENTRY(CeedVector, SetArray),
378     CEED_FTABLE_ENTRY(CeedVector, SetValue),
379     CEED_FTABLE_ENTRY(CeedVector, GetArray),
380     CEED_FTABLE_ENTRY(CeedVector, GetArrayRead),
381     CEED_FTABLE_ENTRY(CeedVector, RestoreArray),
382     CEED_FTABLE_ENTRY(CeedVector, RestoreArrayRead),
383     CEED_FTABLE_ENTRY(CeedVector, Destroy),
384     CEED_FTABLE_ENTRY(CeedElemRestriction, Apply),
385     CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyBlock),
386     CEED_FTABLE_ENTRY(CeedElemRestriction, Destroy),
387     CEED_FTABLE_ENTRY(CeedBasis, Apply),
388     CEED_FTABLE_ENTRY(CeedBasis, Destroy),
389     CEED_FTABLE_ENTRY(CeedTensorContract, Apply),
390     CEED_FTABLE_ENTRY(CeedTensorContract, Destroy),
391     CEED_FTABLE_ENTRY(CeedQFunction, Apply),
392     CEED_FTABLE_ENTRY(CeedQFunction, Destroy),
393     CEED_FTABLE_ENTRY(CeedOperator, AssembleLinearQFunction),
394     CEED_FTABLE_ENTRY(CeedOperator, AssembleLinearDiagonal),
395     CEED_FTABLE_ENTRY(CeedOperator, Apply),
396     CEED_FTABLE_ENTRY(CeedOperator, ApplyJacobian),
397     CEED_FTABLE_ENTRY(CeedOperator, Destroy),
398     {NULL, 0} // End of lookup table - used in SetBackendFunction loop
399   };
400 
401   ierr = CeedCalloc(sizeof(foffsets), &(*ceed)->foffsets); CeedChk(ierr);
402   memcpy((*ceed)->foffsets, foffsets, sizeof(foffsets));
403 
404   // Set fallback for advanced CeedOperator functions
405   const char fallbackresource[] = "/cpu/self/ref/serial";
406   ierr = CeedSetOperatorFallbackResource(*ceed, fallbackresource);
407   CeedChk(ierr);
408 
409   // Backend specific setup
410   ierr = backends[matchidx].init(resource, *ceed); CeedChk(ierr);
411 
412   // Copy resource prefix, if backend setup sucessful
413   size_t len = strlen(backends[matchidx].prefix);
414   char *tmp;
415   ierr = CeedCalloc(len+1, &tmp); CeedChk(ierr);
416   memcpy(tmp, backends[matchidx].prefix, len+1);
417   (*ceed)->resource = tmp;
418 
419   return 0;
420 }
421 
422 /**
423   @brief Retrieve a parent Ceed context
424 
425   @param ceed        Ceed context to retrieve parent of
426   @param[out] parent Address to save the parent to
427 
428   @return An error code: 0 - success, otherwise - failure
429 
430   @ref Developer
431 **/
432 int CeedGetParent(Ceed ceed, Ceed *parent) {
433   int ierr;
434   if (ceed->parent) {
435     ierr = CeedGetParent(ceed->parent, parent); CeedChk(ierr);
436     return 0;
437   }
438   *parent = ceed;
439   return 0;
440 }
441 
442 /**
443   @brief Retrieve a delegate Ceed context
444 
445   @param ceed          Ceed context to retrieve delegate of
446   @param[out] delegate Address to save the delegate to
447 
448   @return An error code: 0 - success, otherwise - failure
449 
450   @ref Developer
451 **/
452 int CeedGetDelegate(Ceed ceed, Ceed *delegate) {
453   *delegate = ceed->delegate;
454   return 0;
455 }
456 
457 /**
458   @brief Set a delegate Ceed context
459 
460   This function allows a Ceed context to set a delegate Ceed context. All
461     backend implementations default to the delegate Ceed context, unless
462     overridden.
463 
464   @param ceed           Ceed context to set delegate of
465   @param[out] delegate  Address to set the delegate to
466 
467   @return An error code: 0 - success, otherwise - failure
468 
469   @ref Advanced
470 **/
471 int CeedSetDelegate(Ceed ceed, Ceed delegate) {
472   ceed->delegate = delegate;
473   delegate->parent = ceed;
474   return 0;
475 }
476 
477 /**
478   @brief Retrieve a delegate Ceed context for a specific object type
479 
480   @param ceed           Ceed context to retrieve delegate of
481   @param[out] delegate  Address to save the delegate to
482   @param[in] objname    Name of the object type to retrieve delegate for
483 
484   @return An error code: 0 - success, otherwise - failure
485 
486   @ref Developer
487 **/
488 int CeedGetObjectDelegate(Ceed ceed, Ceed *delegate, const char *objname) {
489   CeedInt ierr;
490 
491   // Check for object delegate
492   for (CeedInt i=0; i<ceed->objdelegatecount; i++)
493     if (!strcmp(objname, ceed->objdelegates->objname)) {
494       *delegate = ceed->objdelegates->delegate;
495       return 0;
496     }
497 
498   // Use default delegate if no object delegate
499   ierr = CeedGetDelegate(ceed, delegate); CeedChk(ierr);
500 
501   return 0;
502 }
503 
504 /**
505   @brief Set a delegate Ceed context for a specific object type
506 
507   This function allows a Ceed context to set a delegate Ceed context for a
508     given type of Ceed object. All backend implementations default to the
509     delegate Ceed context for this object. For example,
510     CeedSetObjectDelegate(ceed, refceed, "Basis")
511   uses refceed implementations for all CeedBasis backend functions.
512 
513   @param ceed          Ceed context to set delegate of
514   @param[out] delegate Address to set the delegate to
515   @param[in] objname   Name of the object type to set delegate for
516 
517   @return An error code: 0 - success, otherwise - failure
518 
519   @ref Advanced
520 **/
521 int CeedSetObjectDelegate(Ceed ceed, Ceed delegate, const char *objname) {
522   CeedInt ierr;
523   CeedInt count = ceed->objdelegatecount;
524 
525   // Malloc or Realloc
526   if (count) {
527     ierr = CeedRealloc(count+1, &ceed->objdelegates); CeedChk(ierr);
528   } else {
529     ierr = CeedCalloc(1, &ceed->objdelegates); CeedChk(ierr);
530   }
531   ceed->objdelegatecount++;
532 
533   // Set object delegate
534   ceed->objdelegates[count].delegate = delegate;
535   size_t slen = strlen(objname) + 1;
536   ierr = CeedMalloc(slen, &ceed->objdelegates[count].objname); CeedChk(ierr);
537   memcpy(ceed->objdelegates[count].objname, objname, slen);
538 
539   // Set delegate parent
540   delegate->parent = ceed;
541 
542   return 0;
543 }
544 
545 /**
546   @brief Set the fallback resource for CeedOperators. The current resource, if
547            any, is freed by calling this function. This string is freed upon the
548            destruction of the Ceed context.
549 
550   @param[out] ceed Ceed context
551   @param resource  Fallback resource to set
552 
553   @return An error code: 0 - success, otherwise - failure
554 
555   @ref Advanced
556 **/
557 
558 int CeedSetOperatorFallbackResource(Ceed ceed, const char *resource) {
559   int ierr;
560 
561   // Free old
562   ierr = CeedFree(&ceed->opfallbackresource); CeedChk(ierr);
563 
564   // Set new
565   size_t len = strlen(resource);
566   char *tmp;
567   ierr = CeedCalloc(len+1, &tmp); CeedChk(ierr);
568   memcpy(tmp, resource, len+1);
569   ceed->opfallbackresource = tmp;
570 
571   return 0;
572 }
573 
574 /**
575   @brief Get the fallback resource for CeedOperators
576 
577   @param ceed          Ceed context
578   @param[out] resource Variable to store fallback resource
579 
580   @return An error code: 0 - success, otherwise - failure
581 
582   @ref Advanced
583 **/
584 
585 int CeedGetOperatorFallbackResource(Ceed ceed, const char **resource) {
586   *resource = (const char *)ceed->opfallbackresource;
587   return 0;
588 }
589 
590 /**
591   @brief Get the parent Ceed context associated with a fallback Ceed context
592            for a CeedOperator
593 
594   @param ceed            Ceed context
595   @param[out] ceed       Variable to store parent Ceed context
596 
597   @return An error code: 0 - success, otherwise - failure
598 
599   @ref Advanced
600 **/
601 
602 int CeedGetOperatorFallbackParentCeed(Ceed ceed, Ceed *parent) {
603   *parent = ceed->opfallbackparent;
604   return 0;
605 }
606 
607 /**
608   @brief Return Ceed context preferred memory type
609 
610   @param ceed      Ceed context to get preferred memory type of
611   @param[out] type Address to save preferred memory type to
612 
613   @return An error code: 0 - success, otherwise - failure
614 
615   @ref Basic
616 **/
617 int CeedGetPreferredMemType(Ceed ceed, CeedMemType *type) {
618   int ierr;
619 
620   if (ceed->GetPreferredMemType) {
621     ierr = ceed->GetPreferredMemType(type); CeedChk(ierr);
622   } else {
623     Ceed delegate;
624     ierr = CeedGetDelegate(ceed, &delegate); CeedChk(ierr);
625 
626     if (delegate) {
627       ierr = CeedGetPreferredMemType(delegate, type); CeedChk(ierr);
628     } else {
629       *type = CEED_MEM_HOST;
630     }
631   }
632 
633   return 0;
634 }
635 
636 /**
637   @brief Set a backend function
638 
639   This function is used for a backend to set the function associated with
640   the Ceed objects. For example,
641     CeedSetBackendFunction(ceed, "Ceed", ceed, "VectorCreate", BackendVectorCreate)
642   sets the backend implementation of 'CeedVectorCreate' and
643     CeedSetBackendFunction(ceed, "Basis", basis, "Apply", BackendBasisApply)
644   sets the backend implementation of 'CeedBasisApply'. Note, the prefix 'Ceed'
645   is not required for the object type ("Basis" vs "CeedBasis").
646 
647   @param ceed           Ceed context for error handling
648   @param type           Type of Ceed object to set function for
649   @param[out] object    Ceed object to set function for
650   @param fname          Name of function to set
651   @param f              Function to set
652 
653   @return An error code: 0 - success, otherwise - failure
654 
655   @ref Advanced
656 **/
657 int CeedSetBackendFunction(Ceed ceed, const char *type, void *object,
658                            const char *fname, int (*f)()) {
659   char lookupname[CEED_MAX_RESOURCE_LEN+1] = "";
660 
661   // Build lookup name
662   if (strcmp(type, "Ceed"))
663     strncat (lookupname, "Ceed", CEED_MAX_RESOURCE_LEN);
664   strncat(lookupname, type, CEED_MAX_RESOURCE_LEN);
665   strncat(lookupname, fname, CEED_MAX_RESOURCE_LEN);
666 
667   // Find and use offset
668   for (CeedInt i = 0; ceed->foffsets[i].fname; i++)
669     if (!strcmp(ceed->foffsets[i].fname, lookupname)) {
670       size_t offset = ceed->foffsets[i].offset;
671       int (**fpointer)(void) = (int (**)(void))((char *)object + offset); // *NOPAD*
672       *fpointer = f;
673       return 0;
674     }
675 
676   // LCOV_EXCL_START
677   return CeedError(ceed, 1, "Requested function '%s' was not found for CEED "
678                    "object '%s'", fname, type);
679   // LCOV_EXCL_STOP
680 }
681 
682 /**
683   @brief Retrieve backend data for a Ceed context
684 
685   @param ceed      Ceed context to retrieve data of
686   @param[out] data Address to save data to
687 
688   @return An error code: 0 - success, otherwise - failure
689 
690   @ref Advanced
691 **/
692 int CeedGetData(Ceed ceed, void **data) {
693   *data = ceed->data;
694   return 0;
695 }
696 
697 /**
698   @brief Set backend data for a Ceed context
699 
700   @param ceed           Ceed context to set data of
701   @param data           Address of data to set
702 
703   @return An error code: 0 - success, otherwise - failure
704 
705   @ref Advanced
706 **/
707 int CeedSetData(Ceed ceed, void **data) {
708   ceed->data = *data;
709   return 0;
710 }
711 
712 /**
713   @brief Get the full resource name for a Ceed context
714 
715   @param ceed            Ceed context to get resource name of
716   @param[out] resource   Variable to store resource name
717 
718   @return An error code: 0 - success, otherwise - failure
719 
720   @ref Basic
721 **/
722 
723 int CeedGetResource(Ceed ceed, const char **resource) {
724   *resource = (const char *)ceed->resource;
725   return 0;
726 }
727 
728 /**
729   @brief Destroy a Ceed context
730 
731   @param ceed Address of Ceed context to destroy
732 
733   @return An error code: 0 - success, otherwise - failure
734 
735   @ref Basic
736 **/
737 int CeedDestroy(Ceed *ceed) {
738   int ierr;
739 
740   if (!*ceed || --(*ceed)->refcount > 0)
741     return 0;
742   if ((*ceed)->delegate) {
743     ierr = CeedDestroy(&(*ceed)->delegate); CeedChk(ierr);
744   }
745   if ((*ceed)->objdelegatecount > 0) {
746     for (int i=0; i<(*ceed)->objdelegatecount; i++) {
747       ierr = CeedDestroy(&((*ceed)->objdelegates[i].delegate)); CeedChk(ierr);
748       ierr = CeedFree(&(*ceed)->objdelegates[i].objname); CeedChk(ierr);
749     }
750     ierr = CeedFree(&(*ceed)->objdelegates); CeedChk(ierr);
751   }
752   if ((*ceed)->Destroy) {
753     ierr = (*ceed)->Destroy(*ceed); CeedChk(ierr);
754   }
755   ierr = CeedFree(&(*ceed)->foffsets); CeedChk(ierr);
756   ierr = CeedFree(&(*ceed)->resource); CeedChk(ierr);
757   ierr = CeedDestroy(&(*ceed)->opfallbackceed); CeedChk(ierr);
758   ierr = CeedFree(&(*ceed)->opfallbackresource); CeedChk(ierr);
759   ierr = CeedFree(ceed); CeedChk(ierr);
760   return 0;
761 }
762 
763 /// @}
764