1 #include "petscdevice_interface_internal.hpp" /*I <petscdevice.h> I*/ 2 3 #include <petsc/private/cpp/object_pool.hpp> 4 #include <petsc/private/cpp/utility.hpp> 5 6 #include <unordered_map> 7 #include <algorithm> // std::remove_if(), std::find_if() 8 #include <vector> 9 #include <string> 10 #include <sstream> // std::ostringstream 11 12 #if defined(__clang__) 13 #pragma clang diagnostic push 14 #pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" 15 #endif 16 17 // ========================================================================================== 18 // PetscEvent 19 // ========================================================================================== 20 21 struct PetscEventAllocator : public Petsc::AllocatorBase<PetscEvent> { 22 PETSC_NODISCARD static PetscErrorCode create(PetscEvent *event) noexcept 23 { 24 PetscFunctionBegin; 25 PetscCall(PetscNew(event)); 26 PetscFunctionReturn(0); 27 } 28 29 PETSC_NODISCARD static PetscErrorCode destroy(PetscEvent event) noexcept 30 { 31 PetscFunctionBegin; 32 PetscCall(reset(event)); 33 PetscCall(PetscFree(event)); 34 PetscFunctionReturn(0); 35 } 36 37 PETSC_NODISCARD static PetscErrorCode reset(PetscEvent event, bool zero = true) noexcept 38 { 39 PetscFunctionBegin; 40 if (zero) { 41 if (auto &destroy = event->destroy) { 42 PetscCall((*destroy)(event)); 43 destroy = nullptr; 44 } 45 event->dctx_id = 0; 46 event->dctx_state = 0; 47 PetscAssert(!event->data, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Event failed to destroy its data member: %p", event->data); 48 } 49 event->dtype = PETSC_DEVICE_DEFAULT(); 50 PetscFunctionReturn(0); 51 } 52 }; 53 54 static Petsc::ObjectPool<PetscEvent, PetscEventAllocator> event_pool; 55 56 static PetscErrorCode PetscDeviceContextCreateEvent_Private(PetscDeviceContext dctx, PetscEvent *event) 57 { 58 PetscFunctionBegin; 59 PetscValidDeviceContext(dctx, 1); 60 PetscValidPointer(event, 2); 61 PetscCall(event_pool.allocate(event)); 62 PetscCall(PetscDeviceContextGetDeviceType(dctx, &(*event)->dtype)); 63 PetscTryTypeMethod(dctx, createevent, *event); 64 PetscFunctionReturn(0); 65 } 66 67 static PetscErrorCode PetscEventDestroy_Private(PetscEvent *event) 68 { 69 PetscFunctionBegin; 70 PetscValidPointer(event, 1); 71 if (*event) PetscCall(event_pool.deallocate(Petsc::util::exchange(*event, nullptr))); 72 PetscFunctionReturn(0); 73 } 74 75 static PetscErrorCode PetscDeviceContextRecordEvent_Private(PetscDeviceContext dctx, PetscEvent event) 76 { 77 PetscObjectId id; 78 PetscObjectState state; 79 80 PetscFunctionBegin; 81 PetscValidDeviceContext(dctx, 1); 82 PetscValidPointer(event, 2); 83 id = PetscObjectCast(dctx)->id; 84 state = PetscObjectCast(dctx)->state; 85 // technically state can never be less than event->dctx_state (only equal) but we include 86 // it in the check just in case 87 if ((id == event->dctx_id) && (state <= event->dctx_state)) PetscFunctionReturn(0); 88 if (dctx->ops->recordevent) { 89 // REVIEW ME: 90 // TODO maybe move this to impls, as they can determine whether they can interoperate with 91 // other device types more readily 92 if (PetscDefined(USE_DEBUG) && (event->dtype != PETSC_DEVICE_HOST)) { 93 PetscDeviceType dtype; 94 95 PetscCall(PetscDeviceContextGetDeviceType(dctx, &dtype)); 96 PetscCheck(event->dtype == dtype, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Event type %s does not match device context type %s", PetscDeviceTypes[event->dtype], PetscDeviceTypes[dtype]); 97 } 98 PetscUseTypeMethod(dctx, recordevent, event); 99 } 100 event->dctx_id = id; 101 event->dctx_state = state; 102 PetscFunctionReturn(0); 103 } 104 105 static PetscErrorCode PetscDeviceContextWaitForEvent_Private(PetscDeviceContext dctx, PetscEvent event) 106 { 107 PetscFunctionBegin; 108 PetscValidDeviceContext(dctx, 1); 109 PetscValidPointer(event, 2); 110 // empty data implies you cannot wait on this event 111 if (!event->data) PetscFunctionReturn(0); 112 if (PetscDefined(USE_DEBUG)) { 113 const auto etype = event->dtype; 114 PetscDeviceType dtype; 115 116 PetscCall(PetscDeviceContextGetDeviceType(dctx, &dtype)); 117 PetscCheck(etype == dtype, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Event type %s does not match device context type %s", PetscDeviceTypes[etype], PetscDeviceTypes[dtype]); 118 } 119 if (PetscObjectCast(dctx)->id == event->dctx_id) PetscFunctionReturn(0); 120 PetscTryTypeMethod(dctx, waitforevent, event); 121 PetscFunctionReturn(0); 122 } 123 124 // ========================================================================================== 125 // PetscStackFrame 126 // 127 // A helper class that (when debugging is enabled) contains the stack frame from which 128 // PetscDeviceContextMakrIntentFromID(). It is intended to be derived from, since this enables 129 // empty-base-class optimization to kick in when debugging is disabled. 130 // ========================================================================================== 131 132 template <bool use_debug> 133 struct PetscStackFrame; 134 135 template <> 136 struct PetscStackFrame</* use_debug = */ true> { 137 std::string file{}; 138 std::string function{}; 139 int line{}; 140 141 PetscStackFrame() = default; 142 143 PetscStackFrame(const char *file_, const char *func_, int line_) noexcept : file(split_on_petsc_path_(file_)), function(func_), line(line_) { } 144 145 bool operator==(const PetscStackFrame &other) const noexcept { return line == other.line && file == other.file && function == other.function; } 146 147 private: 148 static std::string split_on_petsc_path_(std::string &&in) noexcept 149 { 150 auto pos = in.find("petsc/src"); 151 152 if (pos == std::string::npos) pos = in.find("petsc/include"); 153 if (pos == std::string::npos) pos = 0; 154 return in.substr(pos); 155 } 156 157 friend std::ostream &operator<<(std::ostream &os, const PetscStackFrame &frame) 158 { 159 os << '(' << frame.function << "() at " << frame.file << ':' << frame.line << ')'; 160 return os; 161 } 162 }; 163 164 template <> 165 struct PetscStackFrame</* use_debug = */ false> { 166 template <typename... T> 167 constexpr PetscStackFrame(T &&...) noexcept 168 { 169 } 170 171 constexpr bool operator==(const PetscStackFrame &) const noexcept { return true; } 172 173 friend std::ostream &operator<<(std::ostream &os, const PetscStackFrame &) noexcept 174 { 175 os << "(unknown)"; 176 return os; 177 } 178 }; 179 180 // ========================================================================================== 181 // MarkedObjectMap 182 // 183 // A mapping from a PetscObjectId to a PetscEvent and (if debugging is enabled) a 184 // PetscStackFrame containing the location where PetscDeviceContextMarkIntentFromID was called 185 // ========================================================================================== 186 187 class MarkedObjectMap : public Petsc::RegisterFinalizeable<MarkedObjectMap> { 188 public: 189 // Note we derive from PetscStackFrame so that the empty base class optimization can kick 190 // in. If it were just a member it would still take up storage in optimized builds 191 class snapshot_type : private PetscStackFrame<PetscDefined(USE_DEBUG)> { 192 public: 193 using frame_type = PetscStackFrame<PetscDefined(USE_DEBUG)>; 194 195 snapshot_type() = default; 196 snapshot_type(PetscDeviceContext, frame_type) noexcept; 197 198 ~snapshot_type() noexcept; 199 200 // movable 201 snapshot_type(snapshot_type &&) noexcept; 202 snapshot_type &operator=(snapshot_type &&) noexcept; 203 204 // not copyable 205 snapshot_type(const snapshot_type &) noexcept = delete; 206 snapshot_type &operator=(const snapshot_type &) noexcept = delete; 207 208 PETSC_NODISCARD PetscEvent event() const noexcept { return event_; } 209 PETSC_NODISCARD const frame_type &frame() const noexcept { return *this; } 210 PETSC_NODISCARD frame_type &frame() noexcept { return *this; } 211 PETSC_NODISCARD PetscObjectId dctx_id() const noexcept { return event()->dctx_id; } 212 213 private: 214 PetscEvent event_{}; // the state of device context when this snapshot was recorded 215 216 PETSC_NODISCARD static PetscEvent init_event_(PetscDeviceContext) noexcept; 217 }; 218 219 // the "value" each key maps to 220 struct mapped_type { 221 using dependency_type = std::vector<snapshot_type>; 222 223 PetscMemoryAccessMode mode = PETSC_MEMORY_ACCESS_READ; 224 snapshot_type last_write{}; 225 dependency_type dependencies{}; 226 }; 227 228 using map_type = std::unordered_map<PetscObjectId, mapped_type>; 229 230 map_type map; 231 232 private: 233 friend class RegisterFinalizeable<MarkedObjectMap>; 234 235 PETSC_NODISCARD PetscErrorCode finalize_() noexcept; 236 }; 237 238 // ========================================================================================== 239 // MarkedObejctMap Private API 240 // ========================================================================================== 241 242 inline PetscErrorCode MarkedObjectMap::finalize_() noexcept 243 { 244 PetscFunctionBegin; 245 PetscCall(PetscInfo(nullptr, "Finalizing marked object map\n")); 246 if (PetscDefined(USE_DEBUG)) { 247 std::ostringstream oss; 248 auto wrote_to_oss = false; 249 const auto end = this->map.cend(); 250 PetscMPIInt rank; 251 252 PetscCallMPI(MPI_Comm_rank(PETSC_COMM_WORLD, &rank)); 253 for (auto it = this->map.cbegin(); it != end; ++it) { 254 // need a temporary since we want to prepend "object xxx has orphaned dependencies" if 255 // any of the dependencies have orphans. but we also need to check that in the loop, so 256 // use a temporary to accumulate and then build the rest from it. 257 std::ostringstream oss_tmp; 258 auto wrote_to_oss_tmp = false; 259 //const auto &mapped = it->second; 260 //const auto mode = PetscMemoryAccessModes(mapped.mode); 261 262 // for (auto &&dep : mapped.dependencies) { 263 // // if (!dep.ctx->options.allow_orphans) { 264 // // wrote_to_oss_tmp = true; 265 // // oss_tmp<<" ["<<rank<<"] dctx "<<dep.ctx<<" (id "<<dep.dctx_id()<<", state "<<dep.dctx_state<<", intent "<<mode<<' '<<dep.frame()<<")\n"; 266 // // } 267 // } 268 // check if we wrote to it 269 if (wrote_to_oss_tmp) { 270 oss << '[' << rank << "] object " << it->first << " has orphaned dependencies:\n" << oss_tmp.str(); 271 wrote_to_oss = true; 272 } 273 } 274 if (wrote_to_oss) { 275 //PetscCall((*PetscErrorPrintf)("%s\n",oss.str().c_str())); 276 //SETERRQ(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Orphaned dependencies found, see above"); 277 } 278 } 279 // replace with new map, since clear() does not necessarily free memory 280 PetscCallCXX(this->map = map_type{}); 281 PetscFunctionReturn(0); 282 } 283 284 // ========================================================================================== 285 // MarkedObejctMap::snapshot_type Private API 286 // ========================================================================================== 287 288 inline PetscEvent MarkedObjectMap::snapshot_type::init_event_(PetscDeviceContext dctx) noexcept 289 { 290 PetscEvent event = nullptr; 291 292 PetscFunctionBegin; 293 PetscCallAbort(PETSC_COMM_SELF, PetscDeviceContextCreateEvent_Private(dctx, &event)); 294 PetscCallAbort(PETSC_COMM_SELF, PetscDeviceContextRecordEvent_Private(dctx, event)); 295 PetscFunctionReturn(event); 296 } 297 298 // ========================================================================================== 299 // MarkedObejctMap::snapshot_type Public API 300 // ========================================================================================== 301 302 MarkedObjectMap::snapshot_type::snapshot_type(PetscDeviceContext dctx, frame_type frame) noexcept : frame_type(std::move(frame)), event_(init_event_(dctx)) { } 303 304 MarkedObjectMap::snapshot_type::~snapshot_type() noexcept 305 { 306 PetscFunctionBegin; 307 PetscCallAbort(PETSC_COMM_SELF, PetscEventDestroy_Private(&event_)); 308 PetscFunctionReturnVoid(); 309 } 310 311 // movable 312 MarkedObjectMap::snapshot_type::snapshot_type(snapshot_type &&other) noexcept : frame_type(std::move(other)), event_(Petsc::util::exchange(other.event_, nullptr)) { } 313 314 MarkedObjectMap::snapshot_type &MarkedObjectMap::snapshot_type::operator=(snapshot_type &&other) noexcept 315 { 316 PetscFunctionBegin; 317 if (this != &other) { 318 frame_type::operator=(std::move(other)); 319 PetscCallAbort(PETSC_COMM_SELF, PetscEventDestroy_Private(&event_)); 320 event_ = Petsc::util::exchange(other.event_, nullptr); 321 } 322 PetscFunctionReturn(*this); 323 } 324 325 // A mapping between PetscObjectId (i.e. some PetscObject) to the list of PetscEvent's encoding 326 // the last time the PetscObject was accessed 327 static MarkedObjectMap marked_object_map; 328 329 // ========================================================================================== 330 // Utility Functions 331 // ========================================================================================== 332 333 template <typename T> 334 static PetscErrorCode PetscDeviceContextMapIterVisitor(PetscDeviceContext dctx, T &&callback) noexcept 335 { 336 const auto dctx_id = PetscObjectCast(dctx)->id; 337 auto &dctx_deps = CxxDataCast(dctx)->deps; 338 auto &object_map = marked_object_map.map; 339 340 PetscFunctionBegin; 341 for (auto &&dep : dctx_deps) { 342 const auto mapit = object_map.find(dep); 343 344 // Need this check since the final PetscDeviceContext may run through this *after* the map 345 // has been finalized (and cleared), and hence might fail to find its dependencies. This is 346 // perfectly valid since the user no longer cares about dangling dependencies after PETSc 347 // is finalized 348 if (PetscLikely(mapit != object_map.end())) { 349 auto &deps = mapit->second.dependencies; 350 const auto end = deps.end(); 351 const auto it = std::remove_if(deps.begin(), end, [&](const MarkedObjectMap::snapshot_type &obj) { return obj.dctx_id() == dctx_id; }); 352 353 PetscCall(callback(mapit, deps.cbegin(), static_cast<decltype(deps.cend())>(it))); 354 // remove ourselves 355 PetscCallCXX(deps.erase(it, end)); 356 // continue to next object, but erase this one if it has no more dependencies 357 if (deps.empty()) PetscCallCXX(object_map.erase(mapit)); 358 } 359 } 360 PetscCallCXX(dctx_deps.clear()); 361 PetscFunctionReturn(0); 362 } 363 364 PetscErrorCode PetscDeviceContextSyncClearMap_Internal(PetscDeviceContext dctx) 365 { 366 using map_iterator = MarkedObjectMap::map_type::const_iterator; 367 using dep_iterator = MarkedObjectMap::mapped_type::dependency_type::const_iterator; 368 369 PetscFunctionBegin; 370 PetscCall(PetscDeviceContextMapIterVisitor(dctx, [&](map_iterator mapit, dep_iterator it, dep_iterator end) { 371 PetscFunctionBegin; 372 if (PetscDefined(USE_DEBUG_AND_INFO)) { 373 std::ostringstream oss; 374 const auto mode = PetscMemoryAccessModeToString(mapit->second.mode); 375 376 oss << "synced dctx " << PetscObjectCast(dctx)->id << ", remaining leaves for obj " << mapit->first << ": {"; 377 while (it != end) { 378 oss << "[dctx " << it->dctx_id() << ", " << mode << ' ' << it->frame() << ']'; 379 if (++it != end) oss << ", "; 380 } 381 oss << '}'; 382 PetscCall(PetscInfo(nullptr, "%s\n", oss.str().c_str())); 383 } 384 PetscFunctionReturn(0); 385 })); 386 { 387 // the recursive sync clear map call is unbounded in case of a dependenct loop so we make a 388 // copy 389 // clang-format off 390 const std::vector<CxxData::upstream_type::value_type> upstream_copy( 391 std::make_move_iterator(CxxDataCast(dctx)->upstream.begin()), 392 std::make_move_iterator(CxxDataCast(dctx)->upstream.end()) 393 ); 394 // clang-format on 395 396 // aftermath, clear our set of parents (to avoid infinite recursion) and mark ourselves as no 397 // longer contained (while the empty graph technically *is* always contained, it is not what 398 // we mean by it) 399 PetscCall(CxxDataCast(dctx)->clear()); 400 //dctx->contained = PETSC_FALSE; 401 for (auto &&upstrm : upstream_copy) { 402 // check that this parent still points to what we originally thought it was 403 PetscCheck(upstrm.second.id == PetscObjectCast(upstrm.first)->id, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Upstream dctx %" PetscInt64_FMT " no longer exists, now has id %" PetscInt64_FMT, upstrm.second.id, PetscObjectCast(upstrm.first)->id); 404 PetscCall(PetscDeviceContextSyncClearMap_Internal(upstrm.first)); 405 } 406 } 407 PetscFunctionReturn(0); 408 } 409 410 PetscErrorCode PetscDeviceContextCheckNotOrphaned_Internal(PetscDeviceContext dctx) 411 { 412 std::ostringstream oss; 413 //const auto allow = dctx->options.allow_orphans, contained = dctx->contained; 414 const auto allow = true, contained = true; 415 auto wrote_to_oss = false; 416 using map_iterator = MarkedObjectMap::map_type::const_iterator; 417 using dep_iterator = MarkedObjectMap::mapped_type::dependency_type::const_iterator; 418 419 PetscFunctionBegin; 420 PetscCall(PetscDeviceContextMapIterVisitor(dctx, [&](map_iterator mapit, dep_iterator it, dep_iterator end) { 421 PetscFunctionBegin; 422 if (allow || contained) PetscFunctionReturn(0); 423 wrote_to_oss = true; 424 oss << "- PetscObject (id " << mapit->first << "), intent " << PetscMemoryAccessModeToString(mapit->second.mode) << ' ' << it->frame(); 425 if (std::distance(it, end) == 0) oss << " (orphaned)"; // we were the only dependency 426 oss << '\n'; 427 PetscFunctionReturn(0); 428 })); 429 PetscCheck(!wrote_to_oss, PETSC_COMM_SELF, PETSC_ERR_ORDER, "Destroying PetscDeviceContext ('%s', id %" PetscInt64_FMT ") would leave the following dangling (possibly orphaned) dependants:\n%s\nMust synchronize before destroying it, or allow it to be destroyed with orphans", 430 PetscObjectCast(dctx)->name ? PetscObjectCast(dctx)->name : "unnamed", PetscObjectCast(dctx)->id, oss.str().c_str()); 431 PetscCall(CxxDataCast(dctx)->clear()); 432 PetscFunctionReturn(0); 433 } 434 435 template <bool use_debug> 436 static PetscErrorCode PetscDeviceContextMarkIntentFromID_Private(PetscDeviceContext dctx, PetscObjectId id, PetscMemoryAccessMode mode, PetscStackFrame<use_debug> frame, const char *name) 437 { 438 #define DEBUG_INFO(mess, ...) PetscDebugInfo(dctx, "dctx %" PetscInt64_FMT " (%s) - obj %" PetscInt64_FMT " (%s): " mess, dctx_id, PetscObjectCast(dctx)->name ? PetscObjectCast(dctx)->name : "unnamed", id, name, ##__VA_ARGS__) 439 const auto dctx_id = PetscObjectCast(dctx)->id; 440 auto &marked = marked_object_map.map[id]; 441 auto &old_mode = marked.mode; 442 auto &object_dependencies = marked.dependencies; 443 444 PetscFunctionBegin; 445 if ((mode == PETSC_MEMORY_ACCESS_READ) && (old_mode == mode)) { 446 const auto end = object_dependencies.end(); 447 const auto it = std::find_if(object_dependencies.begin(), end, [&](const MarkedObjectMap::snapshot_type &obj) { return obj.dctx_id() == dctx_id; }); 448 449 PetscCall(DEBUG_INFO("new mode (%s) COMPATIBLE with %s mode (%s), no need to serialize\n", PetscMemoryAccessModeToString(mode), PetscMemoryAccessModeToString(old_mode), object_dependencies.empty() ? "default" : "old")); 450 if (it != end) { 451 // we have been here before, all we must do is update our entry then we can bail 452 PetscCall(DEBUG_INFO("found old self as dependency, updating\n")); 453 PetscAssert(CxxDataCast(dctx)->deps.find(id) != CxxDataCast(dctx)->deps.end(), PETSC_COMM_SELF, PETSC_ERR_PLIB, "PetscDeviceContext %" PetscInt64_FMT " listed as dependency for object %" PetscInt64_FMT " (%s), but does not have the object in private dependency list!", dctx_id, id, name); 454 455 it->frame() = std::move(frame); 456 PetscCall(PetscDeviceContextRecordEvent_Private(dctx, it->event())); 457 PetscFunctionReturn(0); 458 } 459 460 // we have not been here before, need to serialize with the last write event (if it exists) 461 // and add ourselves to the dependency list 462 if (const auto event = marked.last_write.event()) PetscCall(PetscDeviceContextWaitForEvent_Private(dctx, event)); 463 } else { 464 // we are incompatible with the previous mode 465 PetscCall(DEBUG_INFO("new mode (%s) NOT COMPATIBLE with %s mode (%s), serializing then clearing (%zu) %s\n", PetscMemoryAccessModeToString(mode), object_dependencies.empty() ? "default" : "old", PetscMemoryAccessModeToString(old_mode), 466 object_dependencies.size(), object_dependencies.size() == 1 ? "dependency" : "dependencies")); 467 for (const auto &dep : object_dependencies) { 468 if (dep.dctx_id() == dctx_id) { 469 PetscCall(DEBUG_INFO("found old self as dependency, skipping\n")); 470 continue; 471 } 472 PetscCall(PetscDeviceContextWaitForEvent_Private(dctx, dep.event())); 473 } 474 475 // if the previous mode wrote, bump it to the previous write spot 476 if (PetscMemoryAccessWrite(old_mode)) { 477 PetscAssert(object_dependencies.size() == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Can only have a single writer as dependency!"); 478 PetscCall(DEBUG_INFO("moving last write dependency (intent %s)\n", PetscMemoryAccessModeToString(old_mode))); 479 // note the move around object_dependencies.back() not around event(), this is to enable 480 // the rvalue event() overload 481 marked.last_write = std::move(object_dependencies.back()); 482 } 483 484 // clear out the old dependencies and update the mode, we are about to append ourselves 485 object_dependencies.clear(); 486 old_mode = mode; 487 } 488 // become the new leaf by appending ourselves 489 PetscCall(DEBUG_INFO("%s with intent %s\n", object_dependencies.empty() ? "dependency list is empty, creating new leaf" : "appending to existing leaves", PetscMemoryAccessModeToString(mode))); 490 PetscCallCXX(object_dependencies.emplace_back(dctx, std::move(frame))); 491 PetscCallCXX(CxxDataCast(dctx)->deps.emplace(id)); 492 PetscFunctionReturn(0); 493 #undef DEBUG_INFO 494 } 495 496 /*@C 497 PetscDeviceContextMarkIntentFromID - Indicate a `PetscDeviceContext`s access intent to the 498 auto-dependency system 499 500 Not Collective 501 502 Input Parameters: 503 + dctx - The `PetscDeviceContext` 504 . id - The `PetscObjectId` to mark 505 . mode - The desired access intent 506 - name - The object name (for debug purposes, ignored in optimized builds) 507 508 Notes: 509 This routine formally informs the dependency system that `dctx` will access the object 510 represented by `id` with `mode` and adds `dctx` to `id`'s list of dependencies (termed 511 "leaves"). 512 513 If the existing set of leaves have an incompatible `PetscMemoryAccessMode` to `mode`, `dctx` 514 will be serialized against them. 515 516 Level: intermediate 517 518 .seealso: `PetscDeviceContextWaitForContext()`, `PetscDeviceContextSynchronize()`, 519 `PetscObjectGetId()`, `PetscMemoryAccessMode` 520 @*/ 521 PetscErrorCode PetscDeviceContextMarkIntentFromID(PetscDeviceContext dctx, PetscObjectId id, PetscMemoryAccessMode mode, const char name[]) 522 { 523 #if PetscDefined(USE_DEBUG) 524 const auto index = petscstack.currentsize > 2 ? petscstack.currentsize - 2 : 0; 525 const auto file = petscstack.file[index]; 526 const auto function = petscstack.function[index]; 527 const auto line = petscstack.line[index]; 528 #else 529 constexpr const char *file = nullptr; 530 constexpr const char *function = nullptr; 531 constexpr auto line = 0; 532 #endif 533 534 PetscFunctionBegin; 535 PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx)); 536 if (name) PetscValidCharPointer(name, 4); 537 PetscCall(marked_object_map.register_finalize()); 538 PetscCall(PetscLogEventBegin(DCONTEXT_Mark, dctx, nullptr, nullptr, nullptr)); 539 PetscCall(PetscDeviceContextMarkIntentFromID_Private(dctx, id, mode, MarkedObjectMap::snapshot_type::frame_type{file, function, line}, name ? name : "unknown object")); 540 PetscCall(PetscLogEventEnd(DCONTEXT_Mark, dctx, nullptr, nullptr, nullptr)); 541 PetscFunctionReturn(0); 542 } 543 544 #if defined(__clang__) 545 #pragma clang diagnostic pop 546 #endif 547