xref: /libCEED/interface/ceed.c (revision 3091a1ca78f200bc31ad6896b5e7e73163ef6550)
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
295              zeroed) rather than the pointer.
296 **/
297 int CeedFree(void *p) {
298   free(*(void **)p);
299   *(void **)p = NULL;
300   return 0;
301 }
302 
303 /**
304   @brief Wait for a CeedRequest to complete.
305 
306   Calling CeedRequestWait on a NULL request is a no-op.
307 
308   @param req Address of CeedRequest to wait for; zeroed on completion.
309 
310   @return An error code: 0 - success, otherwise - failure
311 
312   @ref Advanced
313 **/
314 int CeedRequestWait(CeedRequest *req) {
315   if (!*req)
316     return 0;
317   return CeedError(NULL, 2, "CeedRequestWait not implemented");
318 }
319 
320 /**
321   @brief Initialize a \ref Ceed context to use the specified resource.
322 
323   @param resource  Resource to use, e.g., "/cpu/self"
324   @param ceed      The library context
325   @sa CeedRegister() CeedDestroy()
326 
327   @return An error code: 0 - success, otherwise - failure
328 
329   @ref Basic
330 **/
331 int CeedInit(const char *resource, Ceed *ceed) {
332   int ierr;
333   size_t matchlen = 0, matchidx = UINT_MAX, matchpriority = UINT_MAX, priority;
334 
335   // Find matching backend
336   if (!resource)
337     return CeedError(NULL, 1, "No resource provided");
338   for (size_t i=0; i<num_backends; i++) {
339     size_t n;
340     const char *prefix = backends[i].prefix;
341     for (n = 0; prefix[n] && prefix[n] == resource[n]; n++) {}
342     priority = backends[i].priority;
343     if (n > matchlen || (n == matchlen && matchpriority > priority)) {
344       matchlen = n;
345       matchpriority = priority;
346       matchidx = i;
347     }
348   }
349   if (!matchlen)
350     return CeedError(NULL, 1, "No suitable backend");
351 
352   // Setup Ceed
353   ierr = CeedCalloc(1,ceed); CeedChk(ierr);
354   const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER");
355   if (!ceed_error_handler)
356     ceed_error_handler = "abort";
357   if (!strcmp(ceed_error_handler, "exit"))
358     (*ceed)->Error = CeedErrorExit;
359   else
360     (*ceed)->Error = CeedErrorAbort;
361   (*ceed)->refcount = 1;
362   (*ceed)->data = NULL;
363 
364   // Set lookup table
365   foffset foffsets[] = {
366     CEED_FTABLE_ENTRY(Ceed, Error),
367     CEED_FTABLE_ENTRY(Ceed, GetPreferredMemType),
368     CEED_FTABLE_ENTRY(Ceed, Destroy),
369     CEED_FTABLE_ENTRY(Ceed, VectorCreate),
370     CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreate),
371     CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreateBlocked),
372     CEED_FTABLE_ENTRY(Ceed, BasisCreateTensorH1),
373     CEED_FTABLE_ENTRY(Ceed, BasisCreateH1),
374     CEED_FTABLE_ENTRY(Ceed, TensorContractCreate),
375     CEED_FTABLE_ENTRY(Ceed, QFunctionCreate),
376     CEED_FTABLE_ENTRY(Ceed, OperatorCreate),
377     CEED_FTABLE_ENTRY(Ceed, CompositeOperatorCreate),
378     CEED_FTABLE_ENTRY(CeedVector, SetArray),
379     CEED_FTABLE_ENTRY(CeedVector, SetValue),
380     CEED_FTABLE_ENTRY(CeedVector, GetArray),
381     CEED_FTABLE_ENTRY(CeedVector, GetArrayRead),
382     CEED_FTABLE_ENTRY(CeedVector, RestoreArray),
383     CEED_FTABLE_ENTRY(CeedVector, RestoreArrayRead),
384     CEED_FTABLE_ENTRY(CeedVector, Destroy),
385     CEED_FTABLE_ENTRY(CeedElemRestriction, Apply),
386     CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyBlock),
387     CEED_FTABLE_ENTRY(CeedElemRestriction, Destroy),
388     CEED_FTABLE_ENTRY(CeedBasis, Apply),
389     CEED_FTABLE_ENTRY(CeedBasis, Destroy),
390     CEED_FTABLE_ENTRY(CeedTensorContract, Apply),
391     CEED_FTABLE_ENTRY(CeedTensorContract, Destroy),
392     CEED_FTABLE_ENTRY(CeedQFunction, Apply),
393     CEED_FTABLE_ENTRY(CeedQFunction, Destroy),
394     CEED_FTABLE_ENTRY(CeedOperator, AssembleLinearQFunction),
395     CEED_FTABLE_ENTRY(CeedOperator, AssembleLinearDiagonal),
396     CEED_FTABLE_ENTRY(CeedOperator, Apply),
397     CEED_FTABLE_ENTRY(CeedOperator, ApplyJacobian),
398     CEED_FTABLE_ENTRY(CeedOperator, Destroy),
399     {NULL, 0} // End of lookup table - used in SetBackendFunction loop
400   };
401 
402   ierr = CeedCalloc(sizeof(foffsets), &(*ceed)->foffsets); CeedChk(ierr);
403   memcpy((*ceed)->foffsets, foffsets, sizeof(foffsets));
404 
405   // Set fallback for advanced CeedOperator functions
406   const char fallbackresource[] = "/cpu/self/ref/serial";
407   ierr = CeedSetOperatorFallbackResource(*ceed, fallbackresource);
408   CeedChk(ierr);
409 
410   // Backend specific setup
411   ierr = backends[matchidx].init(resource, *ceed); CeedChk(ierr);
412 
413   // Copy resource prefix, if backend setup sucessful
414   size_t len = strlen(backends[matchidx].prefix);
415   char *tmp;
416   ierr = CeedCalloc(len+1, &tmp); CeedChk(ierr);
417   memcpy(tmp, backends[matchidx].prefix, len+1);
418   (*ceed)->resource = tmp;
419 
420   return 0;
421 }
422 
423 /**
424   @brief Retrieve a parent Ceed context
425 
426   @param ceed        Ceed context to retrieve parent of
427   @param[out] parent Address to save the parent to
428 
429   @return An error code: 0 - success, otherwise - failure
430 
431   @ref Developer
432 **/
433 int CeedGetParent(Ceed ceed, Ceed *parent) {
434   int ierr;
435   if (ceed->parent) {
436     ierr = CeedGetParent(ceed->parent, parent); CeedChk(ierr);
437     return 0;
438   }
439   *parent = ceed;
440   return 0;
441 }
442 
443 /**
444   @brief Retrieve a delegate Ceed context
445 
446   @param ceed          Ceed context to retrieve delegate of
447   @param[out] delegate Address to save the delegate to
448 
449   @return An error code: 0 - success, otherwise - failure
450 
451   @ref Developer
452 **/
453 int CeedGetDelegate(Ceed ceed, Ceed *delegate) {
454   *delegate = ceed->delegate;
455   return 0;
456 }
457 
458 /**
459   @brief Set a delegate Ceed context
460 
461   This function allows a Ceed context to set a delegate Ceed context. All
462     backend implementations default to the delegate Ceed context, unless
463     overridden.
464 
465   @param ceed           Ceed context to set delegate of
466   @param[out] delegate  Address to set the delegate to
467 
468   @return An error code: 0 - success, otherwise - failure
469 
470   @ref Advanced
471 **/
472 int CeedSetDelegate(Ceed ceed, Ceed delegate) {
473   ceed->delegate = delegate;
474   delegate->parent = ceed;
475   return 0;
476 }
477 
478 /**
479   @brief Retrieve a delegate Ceed context for a specific object type
480 
481   @param ceed           Ceed context to retrieve delegate of
482   @param[out] delegate  Address to save the delegate to
483   @param[in] objname    Name of the object type to retrieve delegate for
484 
485   @return An error code: 0 - success, otherwise - failure
486 
487   @ref Developer
488 **/
489 int CeedGetObjectDelegate(Ceed ceed, Ceed *delegate, const char *objname) {
490   CeedInt ierr;
491 
492   // Check for object delegate
493   for (CeedInt i=0; i<ceed->objdelegatecount; i++)
494     if (!strcmp(objname, ceed->objdelegates->objname)) {
495       *delegate = ceed->objdelegates->delegate;
496       return 0;
497     }
498 
499   // Use default delegate if no object delegate
500   ierr = CeedGetDelegate(ceed, delegate); CeedChk(ierr);
501 
502   return 0;
503 }
504 
505 /**
506   @brief Set a delegate Ceed context for a specific object type
507 
508   This function allows a Ceed context to set a delegate Ceed context for a
509     given type of Ceed object. All backend implementations default to the
510     delegate Ceed context for this object. For example,
511     CeedSetObjectDelegate(ceed, refceed, "Basis")
512   uses refceed implementations for all CeedBasis backend functions.
513 
514   @param ceed          Ceed context to set delegate of
515   @param[out] delegate Address to set the delegate to
516   @param[in] objname   Name of the object type to set delegate for
517 
518   @return An error code: 0 - success, otherwise - failure
519 
520   @ref Advanced
521 **/
522 int CeedSetObjectDelegate(Ceed ceed, Ceed delegate, const char *objname) {
523   CeedInt ierr;
524   CeedInt count = ceed->objdelegatecount;
525 
526   // Malloc or Realloc
527   if (count) {
528     ierr = CeedRealloc(count+1, &ceed->objdelegates); CeedChk(ierr);
529   } else {
530     ierr = CeedCalloc(1, &ceed->objdelegates); CeedChk(ierr);
531   }
532   ceed->objdelegatecount++;
533 
534   // Set object delegate
535   ceed->objdelegates[count].delegate = delegate;
536   size_t slen = strlen(objname) + 1;
537   ierr = CeedMalloc(slen, &ceed->objdelegates[count].objname); CeedChk(ierr);
538   memcpy(ceed->objdelegates[count].objname, objname, slen);
539 
540   // Set delegate parent
541   delegate->parent = ceed;
542 
543   return 0;
544 }
545 
546 /**
547   @brief Set the fallback resource for CeedOperators. The current resource, if
548            any, is freed by calling this function. This string is freed upon the
549            destruction of the Ceed context.
550 
551   @param[out] ceed Ceed context
552   @param resource  Fallback resource to set
553 
554   @return An error code: 0 - success, otherwise - failure
555 
556   @ref Advanced
557 **/
558 
559 int CeedSetOperatorFallbackResource(Ceed ceed, const char *resource) {
560   int ierr;
561 
562   // Free old
563   ierr = CeedFree(&ceed->opfallbackresource); CeedChk(ierr);
564 
565   // Set new
566   size_t len = strlen(resource);
567   char *tmp;
568   ierr = CeedCalloc(len+1, &tmp); CeedChk(ierr);
569   memcpy(tmp, resource, len+1);
570   ceed->opfallbackresource = tmp;
571 
572   return 0;
573 }
574 
575 /**
576   @brief Get the fallback resource for CeedOperators
577 
578   @param ceed          Ceed context
579   @param[out] resource Variable to store fallback resource
580 
581   @return An error code: 0 - success, otherwise - failure
582 
583   @ref Advanced
584 **/
585 
586 int CeedGetOperatorFallbackResource(Ceed ceed, const char **resource) {
587   *resource = (const char *)ceed->opfallbackresource;
588   return 0;
589 }
590 
591 /**
592   @brief Get the parent Ceed context associated with a fallback Ceed context
593            for a CeedOperator
594 
595   @param ceed            Ceed context
596   @param[out] ceed       Variable to store parent Ceed context
597 
598   @return An error code: 0 - success, otherwise - failure
599 
600   @ref Advanced
601 **/
602 
603 int CeedGetOperatorFallbackParentCeed(Ceed ceed, Ceed *parent) {
604   *parent = ceed->opfallbackparent;
605   return 0;
606 }
607 
608 /**
609   @brief Return Ceed context preferred memory type
610 
611   @param ceed      Ceed context to get preferred memory type of
612   @param[out] type Address to save preferred memory type to
613 
614   @return An error code: 0 - success, otherwise - failure
615 
616   @ref Basic
617 **/
618 int CeedGetPreferredMemType(Ceed ceed, CeedMemType *type) {
619   int ierr;
620 
621   if (ceed->GetPreferredMemType) {
622     ierr = ceed->GetPreferredMemType(type); CeedChk(ierr);
623   } else {
624     Ceed delegate;
625     ierr = CeedGetDelegate(ceed, &delegate); CeedChk(ierr);
626 
627     if (delegate) {
628       ierr = CeedGetPreferredMemType(delegate, type); CeedChk(ierr);
629     } else {
630       *type = CEED_MEM_HOST;
631     }
632   }
633 
634   return 0;
635 }
636 
637 /**
638   @brief Set a backend function
639 
640   This function is used for a backend to set the function associated with
641   the Ceed objects. For example,
642     CeedSetBackendFunction(ceed, "Ceed", ceed, "VectorCreate", BackendVectorCreate)
643   sets the backend implementation of 'CeedVectorCreate' and
644     CeedSetBackendFunction(ceed, "Basis", basis, "Apply", BackendBasisApply)
645   sets the backend implementation of 'CeedBasisApply'. Note, the prefix 'Ceed'
646   is not required for the object type ("Basis" vs "CeedBasis").
647 
648   @param ceed           Ceed context for error handling
649   @param type           Type of Ceed object to set function for
650   @param[out] object    Ceed object to set function for
651   @param fname          Name of function to set
652   @param f              Function to set
653 
654   @return An error code: 0 - success, otherwise - failure
655 
656   @ref Advanced
657 **/
658 int CeedSetBackendFunction(Ceed ceed, const char *type, void *object,
659                            const char *fname, int (*f)()) {
660   char lookupname[CEED_MAX_RESOURCE_LEN+1] = "";
661 
662   // Build lookup name
663   if (strcmp(type, "Ceed"))
664     strncat (lookupname, "Ceed", CEED_MAX_RESOURCE_LEN);
665   strncat(lookupname, type, CEED_MAX_RESOURCE_LEN);
666   strncat(lookupname, fname, CEED_MAX_RESOURCE_LEN);
667 
668   // Find and use offset
669   for (CeedInt i = 0; ceed->foffsets[i].fname; i++)
670     if (!strcmp(ceed->foffsets[i].fname, lookupname)) {
671       size_t offset = ceed->foffsets[i].offset;
672       int (**fpointer)(void) = (int (**)(void))((char *)object + offset); // *NOPAD*
673       *fpointer = f;
674       return 0;
675     }
676 
677   // LCOV_EXCL_START
678   return CeedError(ceed, 1, "Requested function '%s' was not found for CEED "
679                    "object '%s'", fname, type);
680   // LCOV_EXCL_STOP
681 }
682 
683 /**
684   @brief Retrieve backend data for a Ceed context
685 
686   @param ceed      Ceed context to retrieve data of
687   @param[out] data Address to save data to
688 
689   @return An error code: 0 - success, otherwise - failure
690 
691   @ref Advanced
692 **/
693 int CeedGetData(Ceed ceed, void **data) {
694   *data = ceed->data;
695   return 0;
696 }
697 
698 /**
699   @brief Set backend data for a Ceed context
700 
701   @param ceed           Ceed context to set data of
702   @param data           Address of data to set
703 
704   @return An error code: 0 - success, otherwise - failure
705 
706   @ref Advanced
707 **/
708 int CeedSetData(Ceed ceed, void **data) {
709   ceed->data = *data;
710   return 0;
711 }
712 
713 /**
714   @brief Get the full resource name for a Ceed context
715 
716   @param ceed            Ceed context to get resource name of
717   @param[out] resource   Variable to store resource name
718 
719   @return An error code: 0 - success, otherwise - failure
720 
721   @ref Basic
722 **/
723 
724 int CeedGetResource(Ceed ceed, const char **resource) {
725   *resource = (const char *)ceed->resource;
726   return 0;
727 }
728 
729 /**
730   @brief Destroy a Ceed context
731 
732   @param ceed Address of Ceed context to destroy
733 
734   @return An error code: 0 - success, otherwise - failure
735 
736   @ref Basic
737 **/
738 int CeedDestroy(Ceed *ceed) {
739   int ierr;
740 
741   if (!*ceed || --(*ceed)->refcount > 0)
742     return 0;
743   if ((*ceed)->delegate) {
744     ierr = CeedDestroy(&(*ceed)->delegate); CeedChk(ierr);
745   }
746   if ((*ceed)->objdelegatecount > 0) {
747     for (int i=0; i<(*ceed)->objdelegatecount; i++) {
748       ierr = CeedDestroy(&((*ceed)->objdelegates[i].delegate)); CeedChk(ierr);
749       ierr = CeedFree(&(*ceed)->objdelegates[i].objname); CeedChk(ierr);
750     }
751     ierr = CeedFree(&(*ceed)->objdelegates); CeedChk(ierr);
752   }
753   if ((*ceed)->Destroy) {
754     ierr = (*ceed)->Destroy(*ceed); CeedChk(ierr);
755   }
756   ierr = CeedFree(&(*ceed)->foffsets); CeedChk(ierr);
757   ierr = CeedFree(&(*ceed)->resource); CeedChk(ierr);
758   ierr = CeedDestroy(&(*ceed)->opfallbackceed); CeedChk(ierr);
759   ierr = CeedFree(&(*ceed)->opfallbackresource); CeedChk(ierr);
760   ierr = CeedFree(ceed); CeedChk(ierr);
761   return 0;
762 }
763 
764 /// @}
765