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