Actual source code: mark_dcontext.cxx

  1: #include "petscdevice_interface_internal.hpp" /*I <petscdevice.h> I*/

  3: #include <petsc/private/cpp/object_pool.hpp>
  4: #include <petsc/private/cpp/utility.hpp>

  6: #include <unordered_map>
  7: #include <algorithm> // std::remove_if(), std::find_if()
  8: #include <vector>
  9: #include <string>
 10: #include <sstream> // std::ostringstream

 12: #if defined(__clang__)
 13:   #pragma clang diagnostic push
 14:   #pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
 15: #endif

 17: // ==========================================================================================
 18: // PetscEvent
 19: // ==========================================================================================

 21: struct PetscEventAllocator : public Petsc::AllocatorBase<PetscEvent> {
 22:   PETSC_NODISCARD static PetscErrorCode create(PetscEvent *event) noexcept
 23:   {
 24:     PetscNew(event);
 25:     return 0;
 26:   }

 28:   PETSC_NODISCARD static PetscErrorCode destroy(PetscEvent event) noexcept
 29:   {
 30:     reset(event);
 31:     PetscFree(event);
 32:     return 0;
 33:   }

 35:   PETSC_NODISCARD static PetscErrorCode reset(PetscEvent event) noexcept
 36:   {
 37:     if (auto &destroy = event->destroy) {
 38:       (*destroy)(event);
 39:       destroy = nullptr;
 40:     }
 41:     PetscAssert(!event->data, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Event failed to destroy its data member: %p", event->data);
 42:     event->dctx_id    = 0;
 43:     event->dctx_state = 0;
 44:     event->dtype      = PETSC_DEVICE_DEFAULT();
 45:     return 0;
 46:   }
 47: };

 49: static Petsc::ObjectPool<PetscEvent, PetscEventAllocator> event_pool;

 51: static PetscErrorCode PetscDeviceContextCreateEvent_Private(PetscDeviceContext dctx, PetscEvent *event)
 52: {
 55:   event_pool.allocate(event);
 56:   PetscDeviceContextGetDeviceType(dctx, &(*event)->dtype);
 57:   PetscTryTypeMethod(dctx, createevent, *event);
 58:   return 0;
 59: }

 61: static PetscErrorCode PetscEventDestroy_Private(PetscEvent *event)
 62: {
 64:   if (*event) event_pool.deallocate(Petsc::util::exchange(*event, nullptr));
 65:   return 0;
 66: }

 68: static PetscErrorCode PetscDeviceContextRecordEvent_Private(PetscDeviceContext dctx, PetscEvent event)
 69: {
 70:   PetscObjectId    id;
 71:   PetscObjectState state;

 75:   id    = PetscObjectCast(dctx)->id;
 76:   state = PetscObjectCast(dctx)->state;
 77:   // technically state can never be less than event->dctx_state (only equal) but we include
 78:   // it in the check just in case
 79:   if ((id == event->dctx_id) && (state <= event->dctx_state)) return 0;
 80:   if (dctx->ops->recordevent) {
 81:     // REVIEW ME:
 82:     // TODO maybe move this to impls, as they can determine whether they can interoperate with
 83:     // other device types more readily
 84:     if (PetscDefined(USE_DEBUG) && (event->dtype != PETSC_DEVICE_HOST)) {
 85:       PetscDeviceType dtype;

 87:       PetscDeviceContextGetDeviceType(dctx, &dtype);
 89:     }
 90:     PetscUseTypeMethod(dctx, recordevent, event);
 91:   }
 92:   event->dctx_id    = id;
 93:   event->dctx_state = state;
 94:   return 0;
 95: }

 97: static PetscErrorCode PetscDeviceContextWaitForEvent_Private(PetscDeviceContext dctx, PetscEvent event)
 98: {
101:   // empty data implies you cannot wait on this event
102:   if (!event->data) return 0;
103:   if (PetscDefined(USE_DEBUG)) {
104:     const auto      etype = event->dtype;
105:     PetscDeviceType dtype;

107:     PetscDeviceContextGetDeviceType(dctx, &dtype);
109:   }
110:   if (PetscObjectCast(dctx)->id == event->dctx_id) return 0;
111:   PetscTryTypeMethod(dctx, waitforevent, event);
112:   return 0;
113: }

115: // ==========================================================================================
116: // PetscStackFrame
117: //
118: // A helper class that (when debugging is enabled) contains the stack frame from which
119: // PetscDeviceContextMakrIntentFromID(). It is intended to be derived from, since this enables
120: // empty-base-class optimization to kick in when debugging is disabled.
121: // ==========================================================================================

123: template <bool use_debug>
124: struct PetscStackFrame;

126: template <>
127: struct PetscStackFrame</* use_debug = */ true> {
128:   std::string file{};
129:   std::string function{};
130:   int         line{};

132:   PetscStackFrame() = default;

134:   PetscStackFrame(const char *file_, const char *func_, int line_) noexcept : file(split_on_petsc_path_(file_)), function(func_), line(line_) { }

136:   bool operator==(const PetscStackFrame &other) const noexcept { return line == other.line && file == other.file && function == other.function; }

138:   PETSC_NODISCARD std::string to_string() const noexcept
139:   {
140:     std::string ret;

142:     ret = '(' + function + "() at " + file + ':' + std::to_string(line) + ')';
143:     return ret;
144:   }

146: private:
147:   static std::string split_on_petsc_path_(std::string &&in) noexcept
148:   {
149:     auto pos = in.find("petsc/src");

151:     if (pos == std::string::npos) pos = in.find("petsc/include");
152:     if (pos == std::string::npos) pos = 0;
153:     return in.substr(pos);
154:   }

156:   friend std::ostream &operator<<(std::ostream &os, const PetscStackFrame &frame)
157:   {
158:     os << frame.to_string();
159:     return os;
160:   }

162:   friend void swap(PetscStackFrame &lhs, PetscStackFrame &rhs) noexcept
163:   {
164:     using std::swap;

166:     swap(lhs.file, rhs.file);
167:     swap(lhs.function, rhs.function);
168:     swap(lhs.line, rhs.line);
169:   }
170: };

172: template <>
173: struct PetscStackFrame</* use_debug = */ false> {
174:   template <typename... T>
175:   constexpr PetscStackFrame(T &&...) noexcept
176:   {
177:   }

179:   constexpr bool operator==(const PetscStackFrame &) const noexcept { return true; }

181:   PETSC_NODISCARD static std::string to_string() noexcept { return "(unknown)"; }

183:   friend std::ostream &operator<<(std::ostream &os, const PetscStackFrame &) noexcept
184:   {
185:     os << "(unknown)";
186:     return os;
187:   }
188: };

190: // ==========================================================================================
191: // MarkedObjectMap
192: //
193: // A mapping from a PetscObjectId to a PetscEvent and (if debugging is enabled) a
194: // PetscStackFrame containing the location where PetscDeviceContextMarkIntentFromID was called
195: // ==========================================================================================

197: class MarkedObjectMap : public Petsc::RegisterFinalizeable<MarkedObjectMap> {
198: public:
199:   // Note we derive from PetscStackFrame so that the empty base class optimization can kick
200:   // in. If it were just a member it would still take up storage in optimized builds
201:   class snapshot_type : private PetscStackFrame<PetscDefined(USE_DEBUG)> {
202:   public:
203:     using frame_type = PetscStackFrame<PetscDefined(USE_DEBUG)>;

205:     snapshot_type() = default;
206:     snapshot_type(PetscDeviceContext, frame_type) noexcept;

208:     ~snapshot_type() noexcept;

210:     // movable
211:     snapshot_type(snapshot_type &&) noexcept;
212:     snapshot_type &operator=(snapshot_type &&) noexcept;

214:     // not copyable
215:     snapshot_type(const snapshot_type &) noexcept            = delete;
216:     snapshot_type &operator=(const snapshot_type &) noexcept = delete;

218:     PETSC_NODISCARD PetscEvent        event() const noexcept { return event_; }
219:     PETSC_NODISCARD const frame_type &frame() const noexcept { return *this; }
220:     PETSC_NODISCARD frame_type       &frame() noexcept { return *this; }

222:     PETSC_NODISCARD PetscObjectId dctx_id() const noexcept
223:     {
224:       PetscAssertAbort(event(), PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Snapshot %s does not contain an event!", frame().to_string().c_str());
225:       PetscFunctionReturn(event()->dctx_id);
226:     }

228:     PETSC_NODISCARD PetscErrorCode ensure_event(PetscDeviceContext) noexcept;

230:     friend void swap(snapshot_type &, snapshot_type &) noexcept;

232:   private:
233:     PetscEvent event_{}; // the state of device context when this snapshot was recorded

235:     PETSC_NODISCARD static PetscEvent init_event_(PetscDeviceContext) noexcept;
236:   };

238:   // the "value" each key maps to
239:   struct mapped_type {
240:     using dependency_type = std::vector<snapshot_type>;

242:     PetscMemoryAccessMode mode = PETSC_MEMORY_ACCESS_READ;
243:     snapshot_type         last_write{};
244:     dependency_type       dependencies{};
245:   };

247:   using map_type = std::unordered_map<PetscObjectId, mapped_type>;

249:   map_type map;

251: private:
252:   friend RegisterFinalizeable;

254:   PETSC_NODISCARD PetscErrorCode finalize_() noexcept;
255: };

257: // ==========================================================================================
258: // MarkedObejctMap Private API
259: // ==========================================================================================

261: inline PetscErrorCode MarkedObjectMap::finalize_() noexcept
262: {
263:   PetscInfo(nullptr, "Finalizing marked object map\n");
264:   map.clear();
265:   return 0;
266: }

268: // ==========================================================================================
269: // MarkedObejctMap::snapshot_type Private API
270: // ==========================================================================================

272: inline PetscEvent MarkedObjectMap::snapshot_type::init_event_(PetscDeviceContext dctx) noexcept
273: {
274:   PetscEvent event = nullptr;

276:   PETSC_COMM_SELF, PetscDeviceContextCreateEvent_Private(dctx, &event);
277:   PETSC_COMM_SELF, PetscDeviceContextRecordEvent_Private(dctx, event);
278:   return event;
279: }

281: // ==========================================================================================
282: // MarkedObejctMap::snapshot_type Public API
283: // ==========================================================================================

285: MarkedObjectMap::snapshot_type::snapshot_type(PetscDeviceContext dctx, frame_type frame) noexcept : frame_type(std::move(frame)), event_(init_event_(dctx)) { }

287: MarkedObjectMap::snapshot_type::~snapshot_type() noexcept
288: {
289:   PETSC_COMM_SELF, PetscEventDestroy_Private(&event_);
290:   return;
291: }

293: // movable
294: MarkedObjectMap::snapshot_type::snapshot_type(snapshot_type &&other) noexcept : frame_type(std::move(other)), event_(Petsc::util::exchange(other.event_, nullptr)) { }

296: MarkedObjectMap::snapshot_type &MarkedObjectMap::snapshot_type::operator=(snapshot_type &&other) noexcept
297: {
298:   if (this != &other) {
299:     frame_type::operator=(std::move(other));
300:     PETSC_COMM_SELF, PetscEventDestroy_Private(&event_);
301:     event_ = Petsc::util::exchange(other.event_, nullptr);
302:   }
303:   PetscFunctionReturn(*this);
304: }

306: PetscErrorCode MarkedObjectMap::snapshot_type::ensure_event(PetscDeviceContext dctx) noexcept
307: {
308:   if (PetscUnlikely(!event_)) PetscDeviceContextCreateEvent_Private(dctx, &event_);
309:   return 0;
310: }

312: void swap(MarkedObjectMap::snapshot_type &lhs, MarkedObjectMap::snapshot_type &rhs) noexcept
313: {
314:   using std::swap;

316:   swap(lhs.frame(), rhs.frame());
317:   swap(lhs.event_, rhs.event_);
318: }

320: // A mapping between PetscObjectId (i.e. some PetscObject) to the list of PetscEvent's encoding
321: // the last time the PetscObject was accessed
322: static MarkedObjectMap marked_object_map;

324: // ==========================================================================================
325: // Utility Functions
326: // ==========================================================================================

328: template <typename T>
329: static PetscErrorCode PetscDeviceContextMapIterVisitor(PetscDeviceContext dctx, T &&callback) noexcept
330: {
331:   const auto dctx_id    = PetscObjectCast(dctx)->id;
332:   auto      &dctx_deps  = CxxDataCast(dctx)->deps;
333:   auto      &object_map = marked_object_map.map;

335:   for (auto &&dep : dctx_deps) {
336:     const auto mapit = object_map.find(dep);

338:     // Need this check since the final PetscDeviceContext may run through this *after* the map
339:     // has been finalized (and cleared), and hence might fail to find its dependencies. This is
340:     // perfectly valid since the user no longer cares about dangling dependencies after PETSc
341:     // is finalized
342:     if (PetscLikely(mapit != object_map.end())) {
343:       auto      &deps = mapit->second.dependencies;
344:       const auto end  = deps.end();
345:       const auto it   = std::remove_if(deps.begin(), end, [&](const MarkedObjectMap::snapshot_type &obj) { return obj.dctx_id() == dctx_id; });

347:       callback(mapit, deps.cbegin(), static_cast<decltype(deps.cend())>(it));
348:       // remove ourselves
349:       deps.erase(it, end);
350:       // continue to next object, but erase this one if it has no more dependencies
351:       if (deps.empty()) object_map.erase(mapit);
352:     }
353:   }
354:   dctx_deps.clear();
355:   return 0;
356: }

358: PetscErrorCode PetscDeviceContextSyncClearMap_Internal(PetscDeviceContext dctx)
359: {
360:   using map_iterator = MarkedObjectMap::map_type::const_iterator;
361:   using dep_iterator = MarkedObjectMap::mapped_type::dependency_type::const_iterator;

363:   PetscCall(PetscDeviceContextMapIterVisitor(dctx, [&](map_iterator mapit, dep_iterator it, dep_iterator end) {
364:     if (PetscDefined(USE_DEBUG_AND_INFO)) {
365:       std::ostringstream oss;
366:       const auto         mode = PetscMemoryAccessModeToString(mapit->second.mode);

368:       oss << "synced dctx " << PetscObjectCast(dctx)->id << ", remaining leaves for obj " << mapit->first << ": {";
369:       while (it != end) {
370:         oss << "[dctx " << it->dctx_id() << ", " << mode << ' ' << it->frame() << ']';
371:         if (++it != end) oss << ", ";
372:       }
373:       oss << '}';
374:       PetscInfo(nullptr, "%s\n", oss.str().c_str());
375:     }
376:     return 0;
377:   }));
378:   {
379:     // the recursive sync clear map call is unbounded in case of a dependenct loop so we make a
380:     // copy
381:     // clang-format off
382:     const std::vector<CxxData::upstream_type::value_type> upstream_copy(
383:       std::make_move_iterator(CxxDataCast(dctx)->upstream.begin()),
384:       std::make_move_iterator(CxxDataCast(dctx)->upstream.end())
385:     );
386:     // clang-format on

388:     // aftermath, clear our set of parents (to avoid infinite recursion) and mark ourselves as no
389:     // longer contained (while the empty graph technically *is* always contained, it is not what
390:     // we mean by it)
391:     CxxDataCast(dctx)->clear();
392:     //dctx->contained = PETSC_FALSE;
393:     for (auto &&upstrm : upstream_copy) {
394:       // check that this parent still points to what we originally thought it was
396:       PetscDeviceContextSyncClearMap_Internal(upstrm.first);
397:     }
398:   }
399:   return 0;
400: }

402: PetscErrorCode PetscDeviceContextCheckNotOrphaned_Internal(PetscDeviceContext dctx)
403: {
404:   std::ostringstream oss;
405:   //const auto         allow = dctx->options.allow_orphans, contained = dctx->contained;
406:   const auto allow = true, contained = true;
407:   auto       wrote_to_oss = false;
408:   using map_iterator      = MarkedObjectMap::map_type::const_iterator;
409:   using dep_iterator      = MarkedObjectMap::mapped_type::dependency_type::const_iterator;

411:   PetscCall(PetscDeviceContextMapIterVisitor(dctx, [&](map_iterator mapit, dep_iterator it, dep_iterator end) {
412:     if (allow || contained) return 0;
413:     wrote_to_oss = true;
414:     oss << "- PetscObject (id " << mapit->first << "), intent " << PetscMemoryAccessModeToString(mapit->second.mode) << ' ' << it->frame();
415:     if (std::distance(it, end) == 0) oss << " (orphaned)"; // we were the only dependency
416:     oss << '\n';
417:     return 0;
418:   }));
420:              PetscObjectCast(dctx)->name ? PetscObjectCast(dctx)->name : "unnamed", PetscObjectCast(dctx)->id, oss.str().c_str());
421:   CxxDataCast(dctx)->clear();
422:   return 0;
423: }

425: #define DEBUG_INFO(mess, ...) PetscDebugInfo(dctx, "dctx %" PetscInt64_FMT " (%s) - obj %" PetscInt64_FMT " (%s): " mess, PetscObjectCast(dctx)->id, PetscObjectCast(dctx)->name ? PetscObjectCast(dctx)->name : "unnamed", id, name, ##__VA_ARGS__)

427: // The current mode is compatible with the previous mode (i.e. read-read) so we need only
428: // update the existing version and possibly appeand ourselves to the dependency list

430: template <bool use_debug>
431: static PetscErrorCode MarkFromID_CompatibleModes(MarkedObjectMap::mapped_type &marked, PetscDeviceContext dctx, PetscObjectId id, PetscMemoryAccessMode mode, PetscStackFrame<use_debug> &frame, const char *PETSC_UNUSED name, bool *update_object_dependencies)
432: {
433:   const auto dctx_id             = PetscObjectCast(dctx)->id;
434:   auto      &object_dependencies = marked.dependencies;
435:   const auto end                 = object_dependencies.end();
436:   const auto it                  = std::find_if(object_dependencies.begin(), end, [&](const MarkedObjectMap::snapshot_type &obj) { return obj.dctx_id() == dctx_id; });

438:   DEBUG_INFO("new mode (%s) COMPATIBLE with %s mode (%s), no need to serialize\n", PetscMemoryAccessModeToString(mode), object_dependencies.empty() ? "default" : "old", PetscMemoryAccessModeToString(marked.mode));
439:   if (it != end) {
440:     using std::swap;

442:     // we have been here before, all we must do is update our entry then we can bail
443:     DEBUG_INFO("found old self as dependency, updating\n");
444:     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);
445:     swap(it->frame(), frame);
446:     PetscDeviceContextRecordEvent_Private(dctx, it->event());
447:     *update_object_dependencies = false;
448:     return 0;
449:   }

451:   // we have not been here before, need to serialize with the last write event (if it exists)
452:   // and add ourselves to the dependency list
453:   if (const auto event = marked.last_write.event()) PetscDeviceContextWaitForEvent_Private(dctx, event);
454:   return 0;
455: }

457: template <bool use_debug>
458: static PetscErrorCode MarkFromID_IncompatibleModes_UpdateLastWrite(MarkedObjectMap::mapped_type &marked, PetscDeviceContext dctx, PetscObjectId id, PetscMemoryAccessMode mode, PetscStackFrame<use_debug> &frame, const char *PETSC_UNUSED name, bool *update_object_dependencies)
459: {
460:   const auto      dctx_id    = PetscObjectCast(dctx)->id;
461:   auto           &last_write = marked.last_write;
462:   auto           &last_dep   = marked.dependencies.back();
463:   PetscDeviceType dtype;

465:   PetscAssert(marked.dependencies.size() == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Can only have a single writer as dependency, have %zu!", marked.dependencies.size());
466:   PetscDeviceContextGetDeviceType(dctx, &dtype);
467:   if (last_dep.event()->dtype != dtype) {
468:     DEBUG_INFO("moving last write dependency (intent %s)\n", PetscMemoryAccessModeToString(marked.mode));
469:     last_write = std::move(last_dep);
470:     return 0;
471:   }

473:   // we match the device type of the dependency, we can reuse its event!
474:   auto      &dctx_upstream_deps     = CxxDataCast(dctx)->deps;
475:   const auto last_write_was_also_us = last_write.event() && (last_write.dctx_id() == dctx_id);
476:   using std::swap;

478:   DEBUG_INFO("we matched the previous write dependency's (intent %s) device type (%s), swapping last dependency with last write\n", PetscMemoryAccessModeToString(marked.mode), PetscDeviceTypes[dtype]);
479:   if (last_dep.event()->dctx_id != dctx_id) dctx_upstream_deps.emplace(id);
480:   PetscAssert(dctx_upstream_deps.find(id) != dctx_upstream_deps.end(), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Did not find id %" PetscInt64_FMT "in object dependencies, but we have apparently recorded the last dependency %s!", id,
481:               last_write.frame().to_string().c_str());
482:   swap(last_write, last_dep);
483:   if (last_write_was_also_us) {
484:     DEBUG_INFO("we were also the last write event (intent %s), updating\n", PetscMemoryAccessModeToString(mode));
485:     // we are both the last to write *and* the last to leave a write event. This is the
486:     // fast path, we only need to update the frame and update the recorded event
487:     swap(last_dep.frame(), frame);
488:     // last used to be last_write which is not guaranteed to have an event, so must
489:     // create it now
490:     last_dep.ensure_event(dctx);
491:     PetscDeviceContextRecordEvent_Private(dctx, last_dep.event());
492:     *update_object_dependencies = false;
493:   }
494:   return 0;
495: }

497: // The current mode is NOT compatible with the previous mode. We must serialize with all events
498: // in the dependency list, possibly clear it, and update the previous write event

500: template <bool use_debug>
501: static PetscErrorCode MarkFromID_IncompatibleModes(MarkedObjectMap::mapped_type &marked, PetscDeviceContext dctx, PetscObjectId id, PetscMemoryAccessMode mode, PetscStackFrame<use_debug> &frame, const char *name, bool *update_object_dependencies)
502: {
503:   auto &old_mode            = marked.mode;
504:   auto &object_dependencies = marked.dependencies;

506:   // we are NOT compatible  with the previous mode
507:   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),
508:                        object_dependencies.size(), object_dependencies.size() == 1 ? "dependency" : "dependencies"));

510:   for (const auto &dep : object_dependencies) PetscDeviceContextWaitForEvent_Private(dctx, dep.event());
511:   // if the previous mode wrote, update the last write node with it
512:   if (PetscMemoryAccessWrite(old_mode)) MarkFromID_IncompatibleModes_UpdateLastWrite(marked, dctx, id, mode, frame, name, update_object_dependencies);

514:   old_mode = mode;
515:   // clear out the old dependencies if are about to append ourselves
516:   if (*update_object_dependencies) object_dependencies.clear();
517:   return 0;
518: }

520: template <bool use_debug>
521: static PetscErrorCode PetscDeviceContextMarkIntentFromID_Private(PetscDeviceContext dctx, PetscObjectId id, PetscMemoryAccessMode mode, PetscStackFrame<use_debug> frame, const char *name)
522: {
523:   auto &marked                     = marked_object_map.map[id];
524:   auto &object_dependencies        = marked.dependencies;
525:   auto  update_object_dependencies = true;

527:   if ((marked.mode == PETSC_MEMORY_ACCESS_READ) && (mode == PETSC_MEMORY_ACCESS_READ)) {
528:     MarkFromID_CompatibleModes(marked, dctx, id, mode, frame, name, &update_object_dependencies);
529:   } else {
530:     MarkFromID_IncompatibleModes(marked, dctx, id, mode, frame, name, &update_object_dependencies);
531:   }
532:   if (update_object_dependencies) {
533:     // become the new leaf by appending ourselves
534:     DEBUG_INFO("%s with intent %s\n", object_dependencies.empty() ? "dependency list is empty, creating new leaf" : "appending to existing leaves", PetscMemoryAccessModeToString(mode));
535:     object_dependencies.emplace_back(dctx, std::move(frame));
536:     CxxDataCast(dctx)->deps.emplace(id);
537:   }
538:   return 0;
539: }

541: #undef DEBUG_INFO

543: /*@C
544:   PetscDeviceContextMarkIntentFromID - Indicate a `PetscDeviceContext`s access intent to the
545:   auto-dependency system

547:   Not Collective

549:   Input Parameters:
550: + dctx - The `PetscDeviceContext`
551: . id   - The `PetscObjectId` to mark
552: . mode - The desired access intent
553: - name - The object name (for debug purposes, ignored in optimized builds)

555:   Notes:
556:   This routine formally informs the dependency system that `dctx` will access the object
557:   represented by `id` with `mode` and adds `dctx` to `id`'s list of dependencies (termed
558:   "leaves").

560:   If the existing set of leaves have an incompatible `PetscMemoryAccessMode` to `mode`, `dctx`
561:   will be serialized against them.

563:   Level: intermediate

565: .seealso: `PetscDeviceContextWaitForContext()`, `PetscDeviceContextSynchronize()`,
566: `PetscObjectGetId()`, `PetscMemoryAccessMode`
567: @*/
568: PetscErrorCode PetscDeviceContextMarkIntentFromID(PetscDeviceContext dctx, PetscObjectId id, PetscMemoryAccessMode mode, const char name[])
569: {
570: #if PetscDefined(USE_DEBUG)
571:   const auto index    = petscstack.currentsize > 2 ? petscstack.currentsize - 2 : 0;
572:   const auto file     = petscstack.file[index];
573:   const auto function = petscstack.function[index];
574:   const auto line     = petscstack.line[index];
575: #else
576:   constexpr const char *file     = nullptr;
577:   constexpr const char *function = nullptr;
578:   constexpr auto        line     = 0;
579: #endif

581:   PetscDeviceContextGetOptionalNullContext_Internal(&dctx);
583:   marked_object_map.register_finalize();
584:   PetscLogEventBegin(DCONTEXT_Mark, dctx, nullptr, nullptr, nullptr);
585:   PetscDeviceContextMarkIntentFromID_Private(dctx, id, mode, MarkedObjectMap::snapshot_type::frame_type{file, function, line}, name ? name : "unknown object");
586:   PetscLogEventEnd(DCONTEXT_Mark, dctx, nullptr, nullptr, nullptr);
587:   return 0;
588: }

590: #if defined(__clang__)
591:   #pragma clang diagnostic pop
592: #endif