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 #include <ceed/ceed.h> 18 #include <ceed/backend.h> 19 #include <ceed-impl.h> 20 #include <stdint.h> 21 #include <stdio.h> 22 #include <string.h> 23 24 /// @file 25 /// Implementation of public CeedQFunctionContext interfaces 26 27 /// ---------------------------------------------------------------------------- 28 /// CeedQFunctionContext Library Internal Functions 29 /// ---------------------------------------------------------------------------- 30 /// @addtogroup CeedQFunctionDeveloper 31 /// @{ 32 33 /** 34 @brief Get index for QFunctionContext field 35 36 @param ctx CeedQFunctionContext 37 @param field_name Name of field 38 @param field_index Index of field, or -1 if field is not registered 39 40 @return An error code: 0 - success, otherwise - failure 41 42 @ref Developer 43 **/ 44 int CeedQFunctionContextGetFieldIndex(CeedQFunctionContext ctx, 45 const char *field_name, CeedInt *field_index) { 46 *field_index = -1; 47 for (CeedInt i=0; i<ctx->num_fields; i++) 48 if (!strcmp(ctx->field_descriptions[i].name, field_name)) 49 *field_index = i; 50 return CEED_ERROR_SUCCESS; 51 } 52 53 /** 54 @brief Common function for registering QFunctionContext fields 55 56 @param ctx CeedQFunctionContext 57 @param field_name Name of field to register 58 @param field_offset Offset of field to register 59 @param field_description Description of field, or NULL for none 60 @param field_type Field data type, such as double or int32 61 @param field_size Size of field, in bytes 62 63 @return An error code: 0 - success, otherwise - failure 64 65 @ref Developer 66 **/ 67 int CeedQFunctionContextRegisterGeneric(CeedQFunctionContext ctx, 68 const char *field_name, size_t field_offset, 69 const char *field_description, 70 CeedContextFieldType field_type, 71 size_t field_size) { 72 int ierr; 73 74 // Check for duplicate 75 CeedInt field_index = -1; 76 ierr = CeedQFunctionContextGetFieldIndex(ctx, field_name, &field_index); 77 CeedChk(ierr); 78 if (field_index != -1) 79 // LCOV_EXCL_START 80 return CeedError(ctx->ceed, CEED_ERROR_UNSUPPORTED, 81 "QFunctionContext field with name \"%s\" already registered", 82 field_name); 83 // LCOV_EXCL_STOP 84 85 // Allocate space for field data 86 if (ctx->num_fields == 0) { 87 ierr = CeedCalloc(1, &ctx->field_descriptions); CeedChk(ierr); 88 ctx->max_fields = 1; 89 } else if (ctx->num_fields == ctx->max_fields) { 90 ierr = CeedRealloc(2*ctx->max_fields, &ctx->field_descriptions); 91 CeedChk(ierr); 92 ctx->max_fields *= 2; 93 } 94 95 // Copy field data 96 ierr = CeedStringAllocCopy(field_name, 97 (char **)&ctx->field_descriptions[ctx->num_fields].name); 98 CeedChk(ierr); 99 ierr = CeedStringAllocCopy(field_description, 100 (char **)&ctx->field_descriptions[ctx->num_fields].description); 101 CeedChk(ierr); 102 ctx->field_descriptions[ctx->num_fields].type = field_type; 103 ctx->field_descriptions[ctx->num_fields].offset = field_offset; 104 ctx->field_descriptions[ctx->num_fields].size = field_size; 105 ctx->num_fields++; 106 return CEED_ERROR_SUCCESS; 107 } 108 109 /** 110 @brief Set QFunctionContext field holding a double precision value 111 112 @param ctx CeedQFunctionContext 113 @param field_name Name of field to set 114 @param field_type Type of field to set 115 @param value Value to set 116 117 @return An error code: 0 - success, otherwise - failure 118 119 @ref User 120 **/ 121 int CeedQFunctionContextSetGeneric(CeedQFunctionContext ctx, 122 const char *field_name, 123 CeedContextFieldType field_type, void *value) { 124 int ierr; 125 126 // Check field index 127 CeedInt field_index = -1; 128 ierr = CeedQFunctionContextGetFieldIndex(ctx, field_name, &field_index); 129 CeedChk(ierr); 130 if (field_index == -1) 131 // LCOV_EXCL_START 132 return CeedError(ctx->ceed, CEED_ERROR_UNSUPPORTED, 133 "QFunctionContext field with name \"%s\" not registered", 134 field_name); 135 // LCOV_EXCL_STOP 136 137 if (ctx->field_descriptions[field_index].type != field_type) 138 // LCOV_EXCL_START 139 return CeedError(ctx->ceed, CEED_ERROR_UNSUPPORTED, 140 "QFunctionContext field with name \"%s\" registered as %s, " 141 "not registered as %s", field_name, 142 CeedContextFieldTypes[ctx->field_descriptions[field_index].type], 143 CeedContextFieldTypes[field_type]); 144 // LCOV_EXCL_STOP 145 146 char *data; 147 ierr = CeedQFunctionContextGetData(ctx, CEED_MEM_HOST, &data); CeedChk(ierr); 148 memcpy(&data[ctx->field_descriptions[field_index].offset], value, 149 ctx->field_descriptions[field_index].size); 150 ierr = CeedQFunctionContextRestoreData(ctx, &data); CeedChk(ierr); 151 return CEED_ERROR_SUCCESS; 152 } 153 154 /// @} 155 156 /// ---------------------------------------------------------------------------- 157 /// CeedQFunctionContext Backend API 158 /// ---------------------------------------------------------------------------- 159 /// @addtogroup CeedQFunctionBackend 160 /// @{ 161 162 /** 163 @brief Get the Ceed associated with a CeedQFunctionContext 164 165 @param ctx CeedQFunctionContext 166 @param[out] ceed Variable to store Ceed 167 168 @return An error code: 0 - success, otherwise - failure 169 170 @ref Backend 171 **/ 172 int CeedQFunctionContextGetCeed(CeedQFunctionContext ctx, Ceed *ceed) { 173 *ceed = ctx->ceed; 174 return CEED_ERROR_SUCCESS; 175 } 176 177 /** 178 @brief Check for valid data in a CeedQFunctionContext 179 180 @param ctx CeedQFunctionContext to check validity 181 @param[out] has_valid_data Variable to store validity 182 183 @return An error code: 0 - success, otherwise - failure 184 185 @ref Backend 186 **/ 187 int CeedQFunctionContextHasValidData(CeedQFunctionContext ctx, 188 bool *has_valid_data) { 189 int ierr; 190 191 if (!ctx->HasValidData) 192 // LCOV_EXCL_START 193 return CeedError(ctx->ceed, CEED_ERROR_UNSUPPORTED, 194 "Backend does not support HasValidData"); 195 // LCOV_EXCL_STOP 196 197 ierr = ctx->HasValidData(ctx, has_valid_data); CeedChk(ierr); 198 199 return CEED_ERROR_SUCCESS; 200 } 201 202 /** 203 @brief Check for borrowed data of a specific CeedMemType in a 204 CeedQFunctionContext 205 206 @param ctx CeedQFunctionContext to check 207 @param mem_type Memory type to check 208 @param[out] has_borrowed_data_of_type Variable to store result 209 210 @return An error code: 0 - success, otherwise - failure 211 212 @ref Backend 213 **/ 214 int CeedQFunctionContextHasBorrowedDataOfType(CeedQFunctionContext ctx, 215 CeedMemType mem_type, bool *has_borrowed_data_of_type) { 216 int ierr; 217 218 if (!ctx->HasBorrowedDataOfType) 219 // LCOV_EXCL_START 220 return CeedError(ctx->ceed, CEED_ERROR_UNSUPPORTED, 221 "Backend does not support HasBorrowedDataOfType"); 222 // LCOV_EXCL_STOP 223 224 ierr = ctx->HasBorrowedDataOfType(ctx, mem_type, has_borrowed_data_of_type); 225 CeedChk(ierr); 226 227 return CEED_ERROR_SUCCESS; 228 } 229 230 /** 231 @brief Get the state of a CeedQFunctionContext 232 233 @param ctx CeedQFunctionContext to retrieve state 234 @param[out] state Variable to store state 235 236 @return An error code: 0 - success, otherwise - failure 237 238 @ref Backend 239 **/ 240 int CeedQFunctionContextGetState(CeedQFunctionContext ctx, uint64_t *state) { 241 *state = ctx->state; 242 return CEED_ERROR_SUCCESS; 243 } 244 245 /** 246 @brief Get backend data of a CeedQFunctionContext 247 248 @param ctx CeedQFunctionContext 249 @param[out] data Variable to store data 250 251 @return An error code: 0 - success, otherwise - failure 252 253 @ref Backend 254 **/ 255 int CeedQFunctionContextGetBackendData(CeedQFunctionContext ctx, void *data) { 256 *(void **)data = ctx->data; 257 return CEED_ERROR_SUCCESS; 258 } 259 260 /** 261 @brief Set backend data of a CeedQFunctionContext 262 263 @param[out] ctx CeedQFunctionContext 264 @param data Data to set 265 266 @return An error code: 0 - success, otherwise - failure 267 268 @ref Backend 269 **/ 270 int CeedQFunctionContextSetBackendData(CeedQFunctionContext ctx, void *data) { 271 ctx->data = data; 272 return CEED_ERROR_SUCCESS; 273 } 274 275 /** 276 @brief Increment the reference counter for a CeedQFunctionContext 277 278 @param ctx CeedQFunctionContext to increment the reference counter 279 280 @return An error code: 0 - success, otherwise - failure 281 282 @ref Backend 283 **/ 284 int CeedQFunctionContextReference(CeedQFunctionContext ctx) { 285 ctx->ref_count++; 286 return CEED_ERROR_SUCCESS; 287 } 288 289 /// @} 290 291 /// ---------------------------------------------------------------------------- 292 /// CeedQFunctionContext Public API 293 /// ---------------------------------------------------------------------------- 294 /// @addtogroup CeedQFunctionUser 295 /// @{ 296 297 /** 298 @brief Create a CeedQFunctionContext for storing CeedQFunction user context data 299 300 @param ceed A Ceed object where the CeedQFunctionContext will be created 301 @param[out] ctx Address of the variable where the newly created 302 CeedQFunctionContext will be stored 303 304 @return An error code: 0 - success, otherwise - failure 305 306 @ref User 307 **/ 308 int CeedQFunctionContextCreate(Ceed ceed, CeedQFunctionContext *ctx) { 309 int ierr; 310 311 if (!ceed->QFunctionContextCreate) { 312 Ceed delegate; 313 ierr = CeedGetObjectDelegate(ceed, &delegate, "Context"); CeedChk(ierr); 314 315 if (!delegate) 316 // LCOV_EXCL_START 317 return CeedError(ceed, CEED_ERROR_UNSUPPORTED, 318 "Backend does not support ContextCreate"); 319 // LCOV_EXCL_STOP 320 321 ierr = CeedQFunctionContextCreate(delegate, ctx); CeedChk(ierr); 322 return CEED_ERROR_SUCCESS; 323 } 324 325 ierr = CeedCalloc(1, ctx); CeedChk(ierr); 326 (*ctx)->ceed = ceed; 327 ierr = CeedReference(ceed); CeedChk(ierr); 328 (*ctx)->ref_count = 1; 329 ierr = ceed->QFunctionContextCreate(*ctx); CeedChk(ierr); 330 return CEED_ERROR_SUCCESS; 331 } 332 333 /** 334 @brief Copy the pointer to a CeedQFunctionContext. Both pointers should 335 be destroyed with `CeedQFunctionContextDestroy()`; 336 Note: If `*ctx_copy` is non-NULL, then it is assumed that 337 `*ctx_copy` is a pointer to a CeedQFunctionContext. This 338 CeedQFunctionContext will be destroyed if `*ctx_copy` is the 339 only reference to this CeedQFunctionContext. 340 341 @param ctx CeedQFunctionContext to copy reference to 342 @param[out] ctx_copy Variable to store copied reference 343 344 @return An error code: 0 - success, otherwise - failure 345 346 @ref User 347 **/ 348 int CeedQFunctionContextReferenceCopy(CeedQFunctionContext ctx, 349 CeedQFunctionContext *ctx_copy) { 350 int ierr; 351 352 ierr = CeedQFunctionContextReference(ctx); CeedChk(ierr); 353 ierr = CeedQFunctionContextDestroy(ctx_copy); CeedChk(ierr); 354 *ctx_copy = ctx; 355 return CEED_ERROR_SUCCESS; 356 } 357 358 /** 359 @brief Set the data used by a CeedQFunctionContext, freeing any previously allocated 360 data if applicable. The backend may copy values to a different 361 memtype, such as during @ref CeedQFunctionApply(). 362 See also @ref CeedQFunctionContextTakeData(). 363 364 @param ctx CeedQFunctionContext 365 @param mem_type Memory type of the data being passed 366 @param copy_mode Copy mode for the data 367 @param size Size of data, in bytes 368 @param data Data to be used 369 370 @return An error code: 0 - success, otherwise - failure 371 372 @ref User 373 **/ 374 int CeedQFunctionContextSetData(CeedQFunctionContext ctx, CeedMemType mem_type, 375 CeedCopyMode copy_mode, 376 size_t size, void *data) { 377 int ierr; 378 379 if (!ctx->SetData) 380 // LCOV_EXCL_START 381 return CeedError(ctx->ceed, CEED_ERROR_UNSUPPORTED, 382 "Backend does not support ContextSetData"); 383 // LCOV_EXCL_STOP 384 385 if (ctx->state % 2 == 1) 386 // LCOV_EXCL_START 387 return CeedError(ctx->ceed, 1, 388 "Cannot grant CeedQFunctionContext data access, the " 389 "access lock is already in use"); 390 // LCOV_EXCL_STOP 391 392 ctx->ctx_size = size; 393 ierr = ctx->SetData(ctx, mem_type, copy_mode, data); CeedChk(ierr); 394 ctx->state += 2; 395 return CEED_ERROR_SUCCESS; 396 } 397 398 /** 399 @brief Take ownership of the data in a CeedQFunctionContext via the specified memory type. 400 The caller is responsible for managing and freeing the memory. 401 402 @param ctx CeedQFunctionContext to access 403 @param mem_type Memory type on which to access the data. If the backend 404 uses a different memory type, this will perform a copy. 405 @param[out] data Data on memory type mem_type 406 407 @return An error code: 0 - success, otherwise - failure 408 409 @ref User 410 **/ 411 int CeedQFunctionContextTakeData(CeedQFunctionContext ctx, CeedMemType mem_type, 412 void *data) { 413 int ierr; 414 415 bool has_valid_data = true; 416 ierr = CeedQFunctionContextHasValidData(ctx, &has_valid_data); CeedChk(ierr); 417 if (!has_valid_data) 418 // LCOV_EXCL_START 419 return CeedError(ctx->ceed, CEED_ERROR_BACKEND, 420 "CeedQFunctionContext has no valid data to take, must set data"); 421 // LCOV_EXCL_STOP 422 423 if (!ctx->TakeData) 424 // LCOV_EXCL_START 425 return CeedError(ctx->ceed, CEED_ERROR_UNSUPPORTED, 426 "Backend does not support TakeData"); 427 // LCOV_EXCL_STOP 428 429 if (ctx->state % 2 == 1) 430 // LCOV_EXCL_START 431 return CeedError(ctx->ceed, 1, 432 "Cannot grant CeedQFunctionContext data access, the " 433 "access lock is already in use"); 434 // LCOV_EXCL_STOP 435 436 bool has_borrowed_data_of_type = true; 437 ierr = CeedQFunctionContextHasBorrowedDataOfType(ctx, mem_type, 438 &has_borrowed_data_of_type); CeedChk(ierr); 439 if (!has_borrowed_data_of_type) 440 // LCOV_EXCL_START 441 return CeedError(ctx->ceed, CEED_ERROR_BACKEND, 442 "CeedQFunctionContext has no borowed %s data, " 443 "must set data with CeedQFunctionContextSetData", 444 CeedMemTypes[mem_type]); 445 // LCOV_EXCL_STOP 446 447 void *temp_data = NULL; 448 ierr = ctx->TakeData(ctx, mem_type, &temp_data); CeedChk(ierr); 449 if (data) (*(void **)data) = temp_data; 450 return CEED_ERROR_SUCCESS; 451 } 452 453 /** 454 @brief Get read/write access to a CeedQFunctionContext via the specified memory type. 455 Restore access with @ref CeedQFunctionContextRestoreData(). 456 457 @param ctx CeedQFunctionContext to access 458 @param mem_type Memory type on which to access the data. If the backend 459 uses a different memory type, this will perform a copy. 460 @param[out] data Data on memory type mem_type 461 462 @note The CeedQFunctionContextGetData() and @ref CeedQFunctionContextRestoreData() functions 463 provide access to array pointers in the desired memory space. Pairing 464 get/restore allows the Context to track access. 465 466 @return An error code: 0 - success, otherwise - failure 467 468 @ref User 469 **/ 470 int CeedQFunctionContextGetData(CeedQFunctionContext ctx, CeedMemType mem_type, 471 void *data) { 472 int ierr; 473 474 if (!ctx->GetData) 475 // LCOV_EXCL_START 476 return CeedError(ctx->ceed, CEED_ERROR_UNSUPPORTED, 477 "Backend does not support GetData"); 478 // LCOV_EXCL_STOP 479 480 if (ctx->state % 2 == 1) 481 // LCOV_EXCL_START 482 return CeedError(ctx->ceed, 1, 483 "Cannot grant CeedQFunctionContext data access, the " 484 "access lock is already in use"); 485 // LCOV_EXCL_STOP 486 487 bool has_valid_data = true; 488 ierr = CeedQFunctionContextHasValidData(ctx, &has_valid_data); CeedChk(ierr); 489 if (!has_valid_data) 490 // LCOV_EXCL_START 491 return CeedError(ctx->ceed, CEED_ERROR_BACKEND, 492 "CeedQFunctionContext has no valid data to get, must set data"); 493 // LCOV_EXCL_STOP 494 495 ierr = ctx->GetData(ctx, mem_type, data); CeedChk(ierr); 496 ctx->state += 1; 497 return CEED_ERROR_SUCCESS; 498 } 499 500 /** 501 @brief Restore data obtained using @ref CeedQFunctionContextGetData() 502 503 @param ctx CeedQFunctionContext to restore 504 @param data Data to restore 505 506 @return An error code: 0 - success, otherwise - failure 507 508 @ref User 509 **/ 510 int CeedQFunctionContextRestoreData(CeedQFunctionContext ctx, void *data) { 511 int ierr; 512 513 if (!ctx->RestoreData) 514 // LCOV_EXCL_START 515 return CeedError(ctx->ceed, CEED_ERROR_UNSUPPORTED, 516 "Backend does not support RestoreData"); 517 // LCOV_EXCL_STOP 518 519 if (ctx->state % 2 != 1) 520 // LCOV_EXCL_START 521 return CeedError(ctx->ceed, 1, 522 "Cannot restore CeedQFunctionContext array access, " 523 "access was not granted"); 524 // LCOV_EXCL_STOP 525 526 ierr = ctx->RestoreData(ctx); CeedChk(ierr); 527 *(void **)data = NULL; 528 ctx->state += 1; 529 return CEED_ERROR_SUCCESS; 530 } 531 532 /** 533 @brief Register QFunctionContext a field holding a double precision value 534 535 @param ctx CeedQFunctionContext 536 @param field_name Name of field to register 537 @param field_offset Offset of field to register 538 @param field_description Description of field, or NULL for none 539 540 @return An error code: 0 - success, otherwise - failure 541 542 @ref User 543 **/ 544 int CeedQFunctionContextRegisterDouble(CeedQFunctionContext ctx, 545 const char *field_name, size_t field_offset, 546 const char *field_description) { 547 return CeedQFunctionContextRegisterGeneric(ctx, field_name, field_offset, 548 field_description, CEED_CONTEXT_FIELD_DOUBLE, sizeof(double)); 549 } 550 551 /** 552 @brief Register QFunctionContext a field holding a int32 value 553 554 @param ctx CeedQFunctionContext 555 @param field_name Name of field to register 556 @param field_offset Offset of field to register 557 @param field_description Description of field, or NULL for none 558 559 @return An error code: 0 - success, otherwise - failure 560 561 @ref User 562 **/ 563 int CeedQFunctionContextRegisterInt32(CeedQFunctionContext ctx, 564 const char *field_name, size_t field_offset, 565 const char *field_description) { 566 return CeedQFunctionContextRegisterGeneric(ctx, field_name, field_offset, 567 field_description, CEED_CONTEXT_FIELD_INT32, sizeof(int)); 568 } 569 570 /** 571 @brief Get descriptions for registered QFunctionContext fields 572 573 @param ctx CeedQFunctionContext 574 @param[out] field_descriptions Variable to hold array of field descriptions 575 @param[out] num_fields Length of field descriptions array 576 577 @return An error code: 0 - success, otherwise - failure 578 579 @ref User 580 **/ 581 int CeedQFunctionContextGetFieldDescriptions(CeedQFunctionContext ctx, 582 const CeedQFunctionContextFieldDescription **field_descriptions, 583 CeedInt *num_fields) { 584 *field_descriptions = ctx->field_descriptions; 585 *num_fields = ctx->num_fields; 586 return CEED_ERROR_SUCCESS; 587 } 588 589 /** 590 @brief Set QFunctionContext field holding a double precision value 591 592 @param ctx CeedQFunctionContext 593 @param field_name Name of field to register 594 @param value Value to set 595 596 @return An error code: 0 - success, otherwise - failure 597 598 @ref User 599 **/ 600 int CeedQFunctionContextSetDouble(CeedQFunctionContext ctx, 601 const char *field_name, double value) { 602 return CeedQFunctionContextSetGeneric(ctx, field_name, 603 CEED_CONTEXT_FIELD_DOUBLE, 604 &value); 605 } 606 607 /** 608 @brief Set QFunctionContext field holding a int32 value 609 610 @param ctx CeedQFunctionContext 611 @param field_name Name of field to set 612 @param value Value to set 613 614 @return An error code: 0 - success, otherwise - failure 615 616 @ref User 617 **/ 618 int CeedQFunctionContextSetInt32(CeedQFunctionContext ctx, 619 const char *field_name, int value) { 620 return CeedQFunctionContextSetGeneric(ctx, field_name, CEED_CONTEXT_FIELD_INT32, 621 &value); 622 } 623 624 /** 625 @brief Get data size for a Context 626 627 @param ctx CeedQFunctionContext 628 @param[out] ctx_size Variable to store size of context data values 629 630 @return An error code: 0 - success, otherwise - failure 631 632 @ref User 633 **/ 634 int CeedQFunctionContextGetContextSize(CeedQFunctionContext ctx, 635 size_t *ctx_size) { 636 *ctx_size = ctx->ctx_size; 637 return CEED_ERROR_SUCCESS; 638 } 639 640 641 /** 642 @brief View a CeedQFunctionContext 643 644 @param[in] ctx CeedQFunctionContext to view 645 @param[in] stream Filestream to write to 646 647 @return An error code: 0 - success, otherwise - failure 648 649 @ref User 650 **/ 651 int CeedQFunctionContextView(CeedQFunctionContext ctx, FILE *stream) { 652 fprintf(stream, "CeedQFunctionContext\n"); 653 fprintf(stream, " Context Data Size: %ld\n", ctx->ctx_size); 654 return CEED_ERROR_SUCCESS; 655 } 656 657 /** 658 @brief Destroy a CeedQFunctionContext 659 660 @param ctx CeedQFunctionContext to destroy 661 662 @return An error code: 0 - success, otherwise - failure 663 664 @ref User 665 **/ 666 int CeedQFunctionContextDestroy(CeedQFunctionContext *ctx) { 667 int ierr; 668 669 if (!*ctx || --(*ctx)->ref_count > 0) 670 return CEED_ERROR_SUCCESS; 671 672 if ((*ctx) && ((*ctx)->state % 2) == 1) 673 // LCOV_EXCL_START 674 return CeedError((*ctx)->ceed, 1, 675 "Cannot destroy CeedQFunctionContext, the access " 676 "lock is in use"); 677 // LCOV_EXCL_STOP 678 679 if ((*ctx)->Destroy) { 680 ierr = (*ctx)->Destroy(*ctx); CeedChk(ierr); 681 } 682 for (CeedInt i=0; i<(*ctx)->num_fields; i++) { 683 ierr = CeedFree(&(*ctx)->field_descriptions[i].name); CeedChk(ierr); 684 ierr = CeedFree(&(*ctx)->field_descriptions[i].description); CeedChk(ierr); 685 } 686 ierr = CeedFree(&(*ctx)->field_descriptions); CeedChk(ierr); 687 ierr = CeedDestroy(&(*ctx)->ceed); CeedChk(ierr); 688 ierr = CeedFree(ctx); CeedChk(ierr); 689 690 return CEED_ERROR_SUCCESS; 691 } 692 693 /// @} 694