xref: /petsc/src/sys/objects/device/interface/mark_dcontext.cxx (revision 7c441f3aff93c611491d4ea0564d57010b1fd4e9)
10e6b6b59SJacob Faibussowitsch #include "petscdevice_interface_internal.hpp" /*I <petscdevice.h> I*/
20e6b6b59SJacob Faibussowitsch 
30e6b6b59SJacob Faibussowitsch #include <petsc/private/cpp/object_pool.hpp>
40e6b6b59SJacob Faibussowitsch #include <petsc/private/cpp/utility.hpp>
50e6b6b59SJacob Faibussowitsch 
60e6b6b59SJacob Faibussowitsch #include <unordered_map>
70e6b6b59SJacob Faibussowitsch #include <algorithm> // std::remove_if(), std::find_if()
80e6b6b59SJacob Faibussowitsch #include <vector>
90e6b6b59SJacob Faibussowitsch #include <string>
100e6b6b59SJacob Faibussowitsch #include <sstream> // std::ostringstream
110e6b6b59SJacob Faibussowitsch 
12a966a306SPierre Jolivet #if defined(__clang__)
13a966a306SPierre Jolivet   #pragma clang diagnostic push
14a966a306SPierre Jolivet   #pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
15a966a306SPierre Jolivet #endif
16a966a306SPierre Jolivet 
170e6b6b59SJacob Faibussowitsch // ==========================================================================================
180e6b6b59SJacob Faibussowitsch // PetscEvent
190e6b6b59SJacob Faibussowitsch // ==========================================================================================
200e6b6b59SJacob Faibussowitsch 
210e6b6b59SJacob Faibussowitsch struct PetscEventAllocator : public Petsc::AllocatorBase<PetscEvent> {
22d71ae5a4SJacob Faibussowitsch   PETSC_NODISCARD static PetscErrorCode create(PetscEvent *event) noexcept
23d71ae5a4SJacob Faibussowitsch   {
240e6b6b59SJacob Faibussowitsch     PetscFunctionBegin;
250e6b6b59SJacob Faibussowitsch     PetscCall(PetscNew(event));
260e6b6b59SJacob Faibussowitsch     PetscFunctionReturn(0);
270e6b6b59SJacob Faibussowitsch   }
280e6b6b59SJacob Faibussowitsch 
29d71ae5a4SJacob Faibussowitsch   PETSC_NODISCARD static PetscErrorCode destroy(PetscEvent event) noexcept
30d71ae5a4SJacob Faibussowitsch   {
310e6b6b59SJacob Faibussowitsch     PetscFunctionBegin;
320e6b6b59SJacob Faibussowitsch     PetscCall(reset(event));
330e6b6b59SJacob Faibussowitsch     PetscCall(PetscFree(event));
340e6b6b59SJacob Faibussowitsch     PetscFunctionReturn(0);
350e6b6b59SJacob Faibussowitsch   }
360e6b6b59SJacob Faibussowitsch 
37d71ae5a4SJacob Faibussowitsch   PETSC_NODISCARD static PetscErrorCode reset(PetscEvent event, bool zero = true) noexcept
38d71ae5a4SJacob Faibussowitsch   {
390e6b6b59SJacob Faibussowitsch     PetscFunctionBegin;
400e6b6b59SJacob Faibussowitsch     if (zero) {
410e6b6b59SJacob Faibussowitsch       if (auto &destroy = event->destroy) {
420e6b6b59SJacob Faibussowitsch         PetscCall((*destroy)(event));
430e6b6b59SJacob Faibussowitsch         destroy = nullptr;
440e6b6b59SJacob Faibussowitsch       }
450e6b6b59SJacob Faibussowitsch       event->dctx_id    = 0;
460e6b6b59SJacob Faibussowitsch       event->dctx_state = 0;
470e6b6b59SJacob Faibussowitsch       PetscAssert(!event->data, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Event failed to destroy its data member: %p", event->data);
480e6b6b59SJacob Faibussowitsch     }
490e6b6b59SJacob Faibussowitsch     event->dtype = PETSC_DEVICE_DEFAULT();
500e6b6b59SJacob Faibussowitsch     PetscFunctionReturn(0);
510e6b6b59SJacob Faibussowitsch   }
520e6b6b59SJacob Faibussowitsch };
530e6b6b59SJacob Faibussowitsch 
540e6b6b59SJacob Faibussowitsch static Petsc::ObjectPool<PetscEvent, PetscEventAllocator> event_pool;
550e6b6b59SJacob Faibussowitsch 
56d71ae5a4SJacob Faibussowitsch static PetscErrorCode PetscDeviceContextCreateEvent_Private(PetscDeviceContext dctx, PetscEvent *event)
57d71ae5a4SJacob Faibussowitsch {
580e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
590e6b6b59SJacob Faibussowitsch   PetscValidDeviceContext(dctx, 1);
600e6b6b59SJacob Faibussowitsch   PetscValidPointer(event, 2);
610e6b6b59SJacob Faibussowitsch   PetscCall(event_pool.allocate(event));
620e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceContextGetDeviceType(dctx, &(*event)->dtype));
630e6b6b59SJacob Faibussowitsch   PetscTryTypeMethod(dctx, createevent, *event);
640e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
650e6b6b59SJacob Faibussowitsch }
660e6b6b59SJacob Faibussowitsch 
67d71ae5a4SJacob Faibussowitsch static PetscErrorCode PetscEventDestroy_Private(PetscEvent *event)
68d71ae5a4SJacob Faibussowitsch {
690e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
700e6b6b59SJacob Faibussowitsch   PetscValidPointer(event, 1);
710e6b6b59SJacob Faibussowitsch   if (*event) PetscCall(event_pool.deallocate(Petsc::util::exchange(*event, nullptr)));
720e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
730e6b6b59SJacob Faibussowitsch }
740e6b6b59SJacob Faibussowitsch 
75d71ae5a4SJacob Faibussowitsch static PetscErrorCode PetscDeviceContextRecordEvent_Private(PetscDeviceContext dctx, PetscEvent event)
76d71ae5a4SJacob Faibussowitsch {
770e6b6b59SJacob Faibussowitsch   PetscObjectId    id;
780e6b6b59SJacob Faibussowitsch   PetscObjectState state;
790e6b6b59SJacob Faibussowitsch 
800e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
810e6b6b59SJacob Faibussowitsch   PetscValidDeviceContext(dctx, 1);
820e6b6b59SJacob Faibussowitsch   PetscValidPointer(event, 2);
830e6b6b59SJacob Faibussowitsch   id    = PetscObjectCast(dctx)->id;
840e6b6b59SJacob Faibussowitsch   state = PetscObjectCast(dctx)->state;
850e6b6b59SJacob Faibussowitsch   // technically state can never be less than event->dctx_state (only equal) but we include
860e6b6b59SJacob Faibussowitsch   // it in the check just in case
870e6b6b59SJacob Faibussowitsch   if ((id == event->dctx_id) && (state <= event->dctx_state)) PetscFunctionReturn(0);
880e6b6b59SJacob Faibussowitsch   if (dctx->ops->recordevent) {
890e6b6b59SJacob Faibussowitsch     // REVIEW ME:
900e6b6b59SJacob Faibussowitsch     // TODO maybe move this to impls, as they can determine whether they can interoperate with
910e6b6b59SJacob Faibussowitsch     // other device types more readily
920e6b6b59SJacob Faibussowitsch     if (PetscDefined(USE_DEBUG) && (event->dtype != PETSC_DEVICE_HOST)) {
930e6b6b59SJacob Faibussowitsch       PetscDeviceType dtype;
940e6b6b59SJacob Faibussowitsch 
950e6b6b59SJacob Faibussowitsch       PetscCall(PetscDeviceContextGetDeviceType(dctx, &dtype));
960e6b6b59SJacob Faibussowitsch       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]);
970e6b6b59SJacob Faibussowitsch     }
980e6b6b59SJacob Faibussowitsch     PetscUseTypeMethod(dctx, recordevent, event);
990e6b6b59SJacob Faibussowitsch   }
1000e6b6b59SJacob Faibussowitsch   event->dctx_id    = id;
1010e6b6b59SJacob Faibussowitsch   event->dctx_state = state;
1020e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
1030e6b6b59SJacob Faibussowitsch }
1040e6b6b59SJacob Faibussowitsch 
105d71ae5a4SJacob Faibussowitsch static PetscErrorCode PetscDeviceContextWaitForEvent_Private(PetscDeviceContext dctx, PetscEvent event)
106d71ae5a4SJacob Faibussowitsch {
1070e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
1080e6b6b59SJacob Faibussowitsch   PetscValidDeviceContext(dctx, 1);
1090e6b6b59SJacob Faibussowitsch   PetscValidPointer(event, 2);
1100e6b6b59SJacob Faibussowitsch   // empty data implies you cannot wait on this event
1110e6b6b59SJacob Faibussowitsch   if (!event->data) PetscFunctionReturn(0);
1120e6b6b59SJacob Faibussowitsch   if (PetscDefined(USE_DEBUG)) {
1130e6b6b59SJacob Faibussowitsch     const auto      etype = event->dtype;
1140e6b6b59SJacob Faibussowitsch     PetscDeviceType dtype;
1150e6b6b59SJacob Faibussowitsch 
1160e6b6b59SJacob Faibussowitsch     PetscCall(PetscDeviceContextGetDeviceType(dctx, &dtype));
1170e6b6b59SJacob Faibussowitsch     PetscCheck(etype == dtype, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Event type %s does not match device context type %s", PetscDeviceTypes[etype], PetscDeviceTypes[dtype]);
1180e6b6b59SJacob Faibussowitsch   }
1190e6b6b59SJacob Faibussowitsch   if (PetscObjectCast(dctx)->id == event->dctx_id) PetscFunctionReturn(0);
1200e6b6b59SJacob Faibussowitsch   PetscTryTypeMethod(dctx, waitforevent, event);
1210e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
1220e6b6b59SJacob Faibussowitsch }
1230e6b6b59SJacob Faibussowitsch 
1240e6b6b59SJacob Faibussowitsch // ==========================================================================================
1250e6b6b59SJacob Faibussowitsch // PetscStackFrame
1260e6b6b59SJacob Faibussowitsch //
1270e6b6b59SJacob Faibussowitsch // A helper class that (when debugging is enabled) contains the stack frame from which
1280e6b6b59SJacob Faibussowitsch // PetscDeviceContextMakrIntentFromID(). It is intended to be derived from, since this enables
1290e6b6b59SJacob Faibussowitsch // empty-base-class optimization to kick in when debugging is disabled.
1300e6b6b59SJacob Faibussowitsch // ==========================================================================================
1310e6b6b59SJacob Faibussowitsch 
1320e6b6b59SJacob Faibussowitsch template <bool use_debug>
1330e6b6b59SJacob Faibussowitsch struct PetscStackFrame;
1340e6b6b59SJacob Faibussowitsch 
1350e6b6b59SJacob Faibussowitsch template <>
1360e6b6b59SJacob Faibussowitsch struct PetscStackFrame</* use_debug = */ true> {
1370e6b6b59SJacob Faibussowitsch   std::string file{};
1380e6b6b59SJacob Faibussowitsch   std::string function{};
1390e6b6b59SJacob Faibussowitsch   int         line{};
1400e6b6b59SJacob Faibussowitsch 
1410e6b6b59SJacob Faibussowitsch   PetscStackFrame() = default;
1420e6b6b59SJacob Faibussowitsch 
1430e6b6b59SJacob Faibussowitsch   PetscStackFrame(const char *file_, const char *func_, int line_) noexcept : file(split_on_petsc_path_(file_)), function(func_), line(line_) { }
1440e6b6b59SJacob Faibussowitsch 
1450e6b6b59SJacob Faibussowitsch   bool operator==(const PetscStackFrame &other) const noexcept { return line == other.line && file == other.file && function == other.function; }
1460e6b6b59SJacob Faibussowitsch 
147*7c441f3aSJacob Faibussowitsch   PETSC_NODISCARD std::string to_string() const noexcept
148*7c441f3aSJacob Faibussowitsch   {
149*7c441f3aSJacob Faibussowitsch     std::string ret;
150*7c441f3aSJacob Faibussowitsch 
151*7c441f3aSJacob Faibussowitsch     ret = '(' + function + "() at " + file + ':' + std::to_string(line) + ')';
152*7c441f3aSJacob Faibussowitsch     return ret;
153*7c441f3aSJacob Faibussowitsch   }
154*7c441f3aSJacob Faibussowitsch 
1550e6b6b59SJacob Faibussowitsch private:
156d71ae5a4SJacob Faibussowitsch   static std::string split_on_petsc_path_(std::string &&in) noexcept
157d71ae5a4SJacob Faibussowitsch   {
1580e6b6b59SJacob Faibussowitsch     auto pos = in.find("petsc/src");
1590e6b6b59SJacob Faibussowitsch 
1600e6b6b59SJacob Faibussowitsch     if (pos == std::string::npos) pos = in.find("petsc/include");
1610e6b6b59SJacob Faibussowitsch     if (pos == std::string::npos) pos = 0;
1620e6b6b59SJacob Faibussowitsch     return in.substr(pos);
1630e6b6b59SJacob Faibussowitsch   }
1640e6b6b59SJacob Faibussowitsch 
165d71ae5a4SJacob Faibussowitsch   friend std::ostream &operator<<(std::ostream &os, const PetscStackFrame &frame)
166d71ae5a4SJacob Faibussowitsch   {
167*7c441f3aSJacob Faibussowitsch     os << frame.to_string();
1680e6b6b59SJacob Faibussowitsch     return os;
1690e6b6b59SJacob Faibussowitsch   }
170*7c441f3aSJacob Faibussowitsch 
171*7c441f3aSJacob Faibussowitsch   friend void swap(PetscStackFrame &lhs, PetscStackFrame &rhs) noexcept
172*7c441f3aSJacob Faibussowitsch   {
173*7c441f3aSJacob Faibussowitsch     using std::swap;
174*7c441f3aSJacob Faibussowitsch 
175*7c441f3aSJacob Faibussowitsch     swap(lhs.file, rhs.file);
176*7c441f3aSJacob Faibussowitsch     swap(lhs.function, rhs.function);
177*7c441f3aSJacob Faibussowitsch     swap(lhs.line, rhs.line);
178*7c441f3aSJacob Faibussowitsch   }
1790e6b6b59SJacob Faibussowitsch };
1800e6b6b59SJacob Faibussowitsch 
1810e6b6b59SJacob Faibussowitsch template <>
1820e6b6b59SJacob Faibussowitsch struct PetscStackFrame</* use_debug = */ false> {
1830e6b6b59SJacob Faibussowitsch   template <typename... T>
184d71ae5a4SJacob Faibussowitsch   constexpr PetscStackFrame(T &&...) noexcept
185d71ae5a4SJacob Faibussowitsch   {
186d71ae5a4SJacob Faibussowitsch   }
1870e6b6b59SJacob Faibussowitsch 
1880e6b6b59SJacob Faibussowitsch   constexpr bool operator==(const PetscStackFrame &) const noexcept { return true; }
1890e6b6b59SJacob Faibussowitsch 
190*7c441f3aSJacob Faibussowitsch   PETSC_NODISCARD static std::string to_string() noexcept { return "(unknown)"; }
191*7c441f3aSJacob Faibussowitsch 
192d71ae5a4SJacob Faibussowitsch   friend std::ostream &operator<<(std::ostream &os, const PetscStackFrame &) noexcept
193d71ae5a4SJacob Faibussowitsch   {
1940e6b6b59SJacob Faibussowitsch     os << "(unknown)";
1950e6b6b59SJacob Faibussowitsch     return os;
1960e6b6b59SJacob Faibussowitsch   }
1970e6b6b59SJacob Faibussowitsch };
1980e6b6b59SJacob Faibussowitsch 
1990e6b6b59SJacob Faibussowitsch // ==========================================================================================
2000e6b6b59SJacob Faibussowitsch // MarkedObjectMap
2010e6b6b59SJacob Faibussowitsch //
2020e6b6b59SJacob Faibussowitsch // A mapping from a PetscObjectId to a PetscEvent and (if debugging is enabled) a
2030e6b6b59SJacob Faibussowitsch // PetscStackFrame containing the location where PetscDeviceContextMarkIntentFromID was called
2040e6b6b59SJacob Faibussowitsch // ==========================================================================================
2050e6b6b59SJacob Faibussowitsch 
2060e6b6b59SJacob Faibussowitsch class MarkedObjectMap : public Petsc::RegisterFinalizeable<MarkedObjectMap> {
2070e6b6b59SJacob Faibussowitsch public:
2080e6b6b59SJacob Faibussowitsch   // Note we derive from PetscStackFrame so that the empty base class optimization can kick
2090e6b6b59SJacob Faibussowitsch   // in. If it were just a member it would still take up storage in optimized builds
2100e6b6b59SJacob Faibussowitsch   class snapshot_type : private PetscStackFrame<PetscDefined(USE_DEBUG)> {
2110e6b6b59SJacob Faibussowitsch   public:
2120e6b6b59SJacob Faibussowitsch     using frame_type = PetscStackFrame<PetscDefined(USE_DEBUG)>;
2130e6b6b59SJacob Faibussowitsch 
2140e6b6b59SJacob Faibussowitsch     snapshot_type() = default;
2150e6b6b59SJacob Faibussowitsch     snapshot_type(PetscDeviceContext, frame_type) noexcept;
2160e6b6b59SJacob Faibussowitsch 
2170e6b6b59SJacob Faibussowitsch     ~snapshot_type() noexcept;
2180e6b6b59SJacob Faibussowitsch 
2190e6b6b59SJacob Faibussowitsch     // movable
2200e6b6b59SJacob Faibussowitsch     snapshot_type(snapshot_type &&) noexcept;
2210e6b6b59SJacob Faibussowitsch     snapshot_type &operator=(snapshot_type &&) noexcept;
2220e6b6b59SJacob Faibussowitsch 
2230e6b6b59SJacob Faibussowitsch     // not copyable
2240e6b6b59SJacob Faibussowitsch     snapshot_type(const snapshot_type &) noexcept            = delete;
2250e6b6b59SJacob Faibussowitsch     snapshot_type &operator=(const snapshot_type &) noexcept = delete;
2260e6b6b59SJacob Faibussowitsch 
2270e6b6b59SJacob Faibussowitsch     PETSC_NODISCARD PetscEvent        event() const noexcept { return event_; }
2280e6b6b59SJacob Faibussowitsch     PETSC_NODISCARD const frame_type &frame() const noexcept { return *this; }
2290e6b6b59SJacob Faibussowitsch     PETSC_NODISCARD frame_type       &frame() noexcept { return *this; }
230*7c441f3aSJacob Faibussowitsch 
231*7c441f3aSJacob Faibussowitsch     PETSC_NODISCARD PetscObjectId dctx_id() const noexcept
232*7c441f3aSJacob Faibussowitsch     {
233*7c441f3aSJacob Faibussowitsch       PetscFunctionBegin;
234*7c441f3aSJacob Faibussowitsch       PetscAssertAbort(event(), PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Snapshot %s does not contain an event!", frame().to_string().c_str());
235*7c441f3aSJacob Faibussowitsch       PetscFunctionReturn(event()->dctx_id);
236*7c441f3aSJacob Faibussowitsch     }
237*7c441f3aSJacob Faibussowitsch 
238*7c441f3aSJacob Faibussowitsch     PETSC_NODISCARD PetscErrorCode ensure_event(PetscDeviceContext) noexcept;
239*7c441f3aSJacob Faibussowitsch 
240*7c441f3aSJacob Faibussowitsch     friend void swap(snapshot_type &, snapshot_type &) noexcept;
2410e6b6b59SJacob Faibussowitsch 
2420e6b6b59SJacob Faibussowitsch   private:
2430e6b6b59SJacob Faibussowitsch     PetscEvent event_{}; // the state of device context when this snapshot was recorded
2440e6b6b59SJacob Faibussowitsch 
2450e6b6b59SJacob Faibussowitsch     PETSC_NODISCARD static PetscEvent init_event_(PetscDeviceContext) noexcept;
2460e6b6b59SJacob Faibussowitsch   };
2470e6b6b59SJacob Faibussowitsch 
2480e6b6b59SJacob Faibussowitsch   // the "value" each key maps to
2490e6b6b59SJacob Faibussowitsch   struct mapped_type {
2500e6b6b59SJacob Faibussowitsch     using dependency_type = std::vector<snapshot_type>;
2510e6b6b59SJacob Faibussowitsch 
2520e6b6b59SJacob Faibussowitsch     PetscMemoryAccessMode mode = PETSC_MEMORY_ACCESS_READ;
2530e6b6b59SJacob Faibussowitsch     snapshot_type         last_write{};
2540e6b6b59SJacob Faibussowitsch     dependency_type       dependencies{};
2550e6b6b59SJacob Faibussowitsch   };
2560e6b6b59SJacob Faibussowitsch 
2570e6b6b59SJacob Faibussowitsch   using map_type = std::unordered_map<PetscObjectId, mapped_type>;
2580e6b6b59SJacob Faibussowitsch 
2590e6b6b59SJacob Faibussowitsch   map_type map;
2600e6b6b59SJacob Faibussowitsch 
2610e6b6b59SJacob Faibussowitsch private:
2620e6b6b59SJacob Faibussowitsch   friend class RegisterFinalizeable<MarkedObjectMap>;
2630e6b6b59SJacob Faibussowitsch 
2640e6b6b59SJacob Faibussowitsch   PETSC_NODISCARD PetscErrorCode finalize_() noexcept;
2650e6b6b59SJacob Faibussowitsch };
2660e6b6b59SJacob Faibussowitsch 
2670e6b6b59SJacob Faibussowitsch // ==========================================================================================
2680e6b6b59SJacob Faibussowitsch // MarkedObejctMap Private API
2690e6b6b59SJacob Faibussowitsch // ==========================================================================================
2700e6b6b59SJacob Faibussowitsch 
271d71ae5a4SJacob Faibussowitsch inline PetscErrorCode MarkedObjectMap::finalize_() noexcept
272d71ae5a4SJacob Faibussowitsch {
2730e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
2740e6b6b59SJacob Faibussowitsch   PetscCall(PetscInfo(nullptr, "Finalizing marked object map\n"));
2750e6b6b59SJacob Faibussowitsch   if (PetscDefined(USE_DEBUG)) {
2760e6b6b59SJacob Faibussowitsch     std::ostringstream oss;
2770e6b6b59SJacob Faibussowitsch     auto               wrote_to_oss = false;
2780e6b6b59SJacob Faibussowitsch     const auto         end          = this->map.cend();
2790e6b6b59SJacob Faibussowitsch     PetscMPIInt        rank;
2800e6b6b59SJacob Faibussowitsch 
2810e6b6b59SJacob Faibussowitsch     PetscCallMPI(MPI_Comm_rank(PETSC_COMM_WORLD, &rank));
2820e6b6b59SJacob Faibussowitsch     for (auto it = this->map.cbegin(); it != end; ++it) {
2830e6b6b59SJacob Faibussowitsch       // need a temporary since we want to prepend "object xxx has orphaned dependencies" if
2840e6b6b59SJacob Faibussowitsch       // any of the dependencies have orphans. but we also need to check that in the loop, so
2850e6b6b59SJacob Faibussowitsch       // use a temporary to accumulate and then build the rest from it.
2860e6b6b59SJacob Faibussowitsch       std::ostringstream oss_tmp;
2870e6b6b59SJacob Faibussowitsch       auto               wrote_to_oss_tmp = false;
2880e6b6b59SJacob Faibussowitsch       //const auto        &mapped           = it->second;
2890e6b6b59SJacob Faibussowitsch       //const auto         mode             = PetscMemoryAccessModes(mapped.mode);
2900e6b6b59SJacob Faibussowitsch 
2910e6b6b59SJacob Faibussowitsch       // for (auto &&dep : mapped.dependencies) {
2920e6b6b59SJacob Faibussowitsch       //   // if (!dep.ctx->options.allow_orphans) {
2930e6b6b59SJacob Faibussowitsch       //   //   wrote_to_oss_tmp = true;
2940e6b6b59SJacob Faibussowitsch       //   //   oss_tmp<<"  ["<<rank<<"] dctx "<<dep.ctx<<" (id "<<dep.dctx_id()<<", state "<<dep.dctx_state<<", intent "<<mode<<' '<<dep.frame()<<")\n";
2950e6b6b59SJacob Faibussowitsch       //   // }
2960e6b6b59SJacob Faibussowitsch       // }
2970e6b6b59SJacob Faibussowitsch       // check if we wrote to it
2980e6b6b59SJacob Faibussowitsch       if (wrote_to_oss_tmp) {
2990e6b6b59SJacob Faibussowitsch         oss << '[' << rank << "] object " << it->first << " has orphaned dependencies:\n" << oss_tmp.str();
3000e6b6b59SJacob Faibussowitsch         wrote_to_oss = true;
3010e6b6b59SJacob Faibussowitsch       }
3020e6b6b59SJacob Faibussowitsch     }
3030e6b6b59SJacob Faibussowitsch     if (wrote_to_oss) {
3040e6b6b59SJacob Faibussowitsch       //PetscCall((*PetscErrorPrintf)("%s\n",oss.str().c_str()));
3050e6b6b59SJacob Faibussowitsch       //SETERRQ(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Orphaned dependencies found, see above");
3060e6b6b59SJacob Faibussowitsch     }
3070e6b6b59SJacob Faibussowitsch   }
3080e6b6b59SJacob Faibussowitsch   // replace with new map, since clear() does not necessarily free memory
3090e6b6b59SJacob Faibussowitsch   PetscCallCXX(this->map = map_type{});
3100e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
3110e6b6b59SJacob Faibussowitsch }
3120e6b6b59SJacob Faibussowitsch 
3130e6b6b59SJacob Faibussowitsch // ==========================================================================================
3140e6b6b59SJacob Faibussowitsch // MarkedObejctMap::snapshot_type Private API
3150e6b6b59SJacob Faibussowitsch // ==========================================================================================
3160e6b6b59SJacob Faibussowitsch 
317d71ae5a4SJacob Faibussowitsch inline PetscEvent MarkedObjectMap::snapshot_type::init_event_(PetscDeviceContext dctx) noexcept
318d71ae5a4SJacob Faibussowitsch {
3190e6b6b59SJacob Faibussowitsch   PetscEvent event = nullptr;
3200e6b6b59SJacob Faibussowitsch 
3210e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
3220e6b6b59SJacob Faibussowitsch   PetscCallAbort(PETSC_COMM_SELF, PetscDeviceContextCreateEvent_Private(dctx, &event));
3230e6b6b59SJacob Faibussowitsch   PetscCallAbort(PETSC_COMM_SELF, PetscDeviceContextRecordEvent_Private(dctx, event));
3240e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(event);
3250e6b6b59SJacob Faibussowitsch }
3260e6b6b59SJacob Faibussowitsch 
3270e6b6b59SJacob Faibussowitsch // ==========================================================================================
3280e6b6b59SJacob Faibussowitsch // MarkedObejctMap::snapshot_type Public API
3290e6b6b59SJacob Faibussowitsch // ==========================================================================================
3300e6b6b59SJacob Faibussowitsch 
3310e6b6b59SJacob Faibussowitsch MarkedObjectMap::snapshot_type::snapshot_type(PetscDeviceContext dctx, frame_type frame) noexcept : frame_type(std::move(frame)), event_(init_event_(dctx)) { }
3320e6b6b59SJacob Faibussowitsch 
333d71ae5a4SJacob Faibussowitsch MarkedObjectMap::snapshot_type::~snapshot_type() noexcept
334d71ae5a4SJacob Faibussowitsch {
3350e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
3360e6b6b59SJacob Faibussowitsch   PetscCallAbort(PETSC_COMM_SELF, PetscEventDestroy_Private(&event_));
3370e6b6b59SJacob Faibussowitsch   PetscFunctionReturnVoid();
3380e6b6b59SJacob Faibussowitsch }
3390e6b6b59SJacob Faibussowitsch 
3400e6b6b59SJacob Faibussowitsch // movable
3410e6b6b59SJacob Faibussowitsch MarkedObjectMap::snapshot_type::snapshot_type(snapshot_type &&other) noexcept : frame_type(std::move(other)), event_(Petsc::util::exchange(other.event_, nullptr)) { }
3420e6b6b59SJacob Faibussowitsch 
343d71ae5a4SJacob Faibussowitsch MarkedObjectMap::snapshot_type &MarkedObjectMap::snapshot_type::operator=(snapshot_type &&other) noexcept
344d71ae5a4SJacob Faibussowitsch {
3450e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
3460e6b6b59SJacob Faibussowitsch   if (this != &other) {
3470e6b6b59SJacob Faibussowitsch     frame_type::operator=(std::move(other));
3480e6b6b59SJacob Faibussowitsch     PetscCallAbort(PETSC_COMM_SELF, PetscEventDestroy_Private(&event_));
3490e6b6b59SJacob Faibussowitsch     event_ = Petsc::util::exchange(other.event_, nullptr);
3500e6b6b59SJacob Faibussowitsch   }
3510e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(*this);
3520e6b6b59SJacob Faibussowitsch }
3530e6b6b59SJacob Faibussowitsch 
354*7c441f3aSJacob Faibussowitsch PetscErrorCode MarkedObjectMap::snapshot_type::ensure_event(PetscDeviceContext dctx) noexcept
355*7c441f3aSJacob Faibussowitsch {
356*7c441f3aSJacob Faibussowitsch   PetscFunctionBegin;
357*7c441f3aSJacob Faibussowitsch   if (PetscUnlikely(!event_)) PetscCall(PetscDeviceContextCreateEvent_Private(dctx, &event_));
358*7c441f3aSJacob Faibussowitsch   PetscFunctionReturn(0);
359*7c441f3aSJacob Faibussowitsch }
360*7c441f3aSJacob Faibussowitsch 
361*7c441f3aSJacob Faibussowitsch void swap(MarkedObjectMap::snapshot_type &lhs, MarkedObjectMap::snapshot_type &rhs) noexcept
362*7c441f3aSJacob Faibussowitsch {
363*7c441f3aSJacob Faibussowitsch   using std::swap;
364*7c441f3aSJacob Faibussowitsch 
365*7c441f3aSJacob Faibussowitsch   swap(lhs.frame(), rhs.frame());
366*7c441f3aSJacob Faibussowitsch   swap(lhs.event_, rhs.event_);
367*7c441f3aSJacob Faibussowitsch }
368*7c441f3aSJacob Faibussowitsch 
3690e6b6b59SJacob Faibussowitsch // A mapping between PetscObjectId (i.e. some PetscObject) to the list of PetscEvent's encoding
3700e6b6b59SJacob Faibussowitsch // the last time the PetscObject was accessed
3710e6b6b59SJacob Faibussowitsch static MarkedObjectMap marked_object_map;
3720e6b6b59SJacob Faibussowitsch 
3730e6b6b59SJacob Faibussowitsch // ==========================================================================================
3740e6b6b59SJacob Faibussowitsch // Utility Functions
3750e6b6b59SJacob Faibussowitsch // ==========================================================================================
3760e6b6b59SJacob Faibussowitsch 
3770e6b6b59SJacob Faibussowitsch template <typename T>
378d71ae5a4SJacob Faibussowitsch static PetscErrorCode PetscDeviceContextMapIterVisitor(PetscDeviceContext dctx, T &&callback) noexcept
379d71ae5a4SJacob Faibussowitsch {
3800e6b6b59SJacob Faibussowitsch   const auto dctx_id    = PetscObjectCast(dctx)->id;
3810e6b6b59SJacob Faibussowitsch   auto      &dctx_deps  = CxxDataCast(dctx)->deps;
3820e6b6b59SJacob Faibussowitsch   auto      &object_map = marked_object_map.map;
3830e6b6b59SJacob Faibussowitsch 
3840e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
3850e6b6b59SJacob Faibussowitsch   for (auto &&dep : dctx_deps) {
3860e6b6b59SJacob Faibussowitsch     const auto mapit = object_map.find(dep);
3870e6b6b59SJacob Faibussowitsch 
3880e6b6b59SJacob Faibussowitsch     // Need this check since the final PetscDeviceContext may run through this *after* the map
3890e6b6b59SJacob Faibussowitsch     // has been finalized (and cleared), and hence might fail to find its dependencies. This is
3900e6b6b59SJacob Faibussowitsch     // perfectly valid since the user no longer cares about dangling dependencies after PETSc
3910e6b6b59SJacob Faibussowitsch     // is finalized
3920e6b6b59SJacob Faibussowitsch     if (PetscLikely(mapit != object_map.end())) {
3930e6b6b59SJacob Faibussowitsch       auto      &deps = mapit->second.dependencies;
3940e6b6b59SJacob Faibussowitsch       const auto end  = deps.end();
3950e6b6b59SJacob Faibussowitsch       const auto it   = std::remove_if(deps.begin(), end, [&](const MarkedObjectMap::snapshot_type &obj) { return obj.dctx_id() == dctx_id; });
3960e6b6b59SJacob Faibussowitsch 
3970e6b6b59SJacob Faibussowitsch       PetscCall(callback(mapit, deps.cbegin(), static_cast<decltype(deps.cend())>(it)));
3980e6b6b59SJacob Faibussowitsch       // remove ourselves
3990e6b6b59SJacob Faibussowitsch       PetscCallCXX(deps.erase(it, end));
4000e6b6b59SJacob Faibussowitsch       // continue to next object, but erase this one if it has no more dependencies
4010e6b6b59SJacob Faibussowitsch       if (deps.empty()) PetscCallCXX(object_map.erase(mapit));
4020e6b6b59SJacob Faibussowitsch     }
4030e6b6b59SJacob Faibussowitsch   }
4040e6b6b59SJacob Faibussowitsch   PetscCallCXX(dctx_deps.clear());
4050e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
4060e6b6b59SJacob Faibussowitsch }
4070e6b6b59SJacob Faibussowitsch 
408d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDeviceContextSyncClearMap_Internal(PetscDeviceContext dctx)
409d71ae5a4SJacob Faibussowitsch {
4100e6b6b59SJacob Faibussowitsch   using map_iterator = MarkedObjectMap::map_type::const_iterator;
4110e6b6b59SJacob Faibussowitsch   using dep_iterator = MarkedObjectMap::mapped_type::dependency_type::const_iterator;
4120e6b6b59SJacob Faibussowitsch 
4130e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
4140e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceContextMapIterVisitor(dctx, [&](map_iterator mapit, dep_iterator it, dep_iterator end) {
4150e6b6b59SJacob Faibussowitsch     PetscFunctionBegin;
4160e6b6b59SJacob Faibussowitsch     if (PetscDefined(USE_DEBUG_AND_INFO)) {
4170e6b6b59SJacob Faibussowitsch       std::ostringstream oss;
4180e6b6b59SJacob Faibussowitsch       const auto         mode = PetscMemoryAccessModeToString(mapit->second.mode);
4190e6b6b59SJacob Faibussowitsch 
4200e6b6b59SJacob Faibussowitsch       oss << "synced dctx " << PetscObjectCast(dctx)->id << ", remaining leaves for obj " << mapit->first << ": {";
4210e6b6b59SJacob Faibussowitsch       while (it != end) {
4220e6b6b59SJacob Faibussowitsch         oss << "[dctx " << it->dctx_id() << ", " << mode << ' ' << it->frame() << ']';
4230e6b6b59SJacob Faibussowitsch         if (++it != end) oss << ", ";
4240e6b6b59SJacob Faibussowitsch       }
4250e6b6b59SJacob Faibussowitsch       oss << '}';
4260e6b6b59SJacob Faibussowitsch       PetscCall(PetscInfo(nullptr, "%s\n", oss.str().c_str()));
4270e6b6b59SJacob Faibussowitsch     }
4280e6b6b59SJacob Faibussowitsch     PetscFunctionReturn(0);
4290e6b6b59SJacob Faibussowitsch   }));
4300e6b6b59SJacob Faibussowitsch   {
4310e6b6b59SJacob Faibussowitsch     // the recursive sync clear map call is unbounded in case of a dependenct loop so we make a
4320e6b6b59SJacob Faibussowitsch     // copy
4330e6b6b59SJacob Faibussowitsch     // clang-format off
4340e6b6b59SJacob Faibussowitsch     const std::vector<CxxData::upstream_type::value_type> upstream_copy(
4350e6b6b59SJacob Faibussowitsch       std::make_move_iterator(CxxDataCast(dctx)->upstream.begin()),
4360e6b6b59SJacob Faibussowitsch       std::make_move_iterator(CxxDataCast(dctx)->upstream.end())
4370e6b6b59SJacob Faibussowitsch     );
4380e6b6b59SJacob Faibussowitsch     // clang-format on
4390e6b6b59SJacob Faibussowitsch 
4400e6b6b59SJacob Faibussowitsch     // aftermath, clear our set of parents (to avoid infinite recursion) and mark ourselves as no
4410e6b6b59SJacob Faibussowitsch     // longer contained (while the empty graph technically *is* always contained, it is not what
4420e6b6b59SJacob Faibussowitsch     // we mean by it)
4430e6b6b59SJacob Faibussowitsch     PetscCall(CxxDataCast(dctx)->clear());
4440e6b6b59SJacob Faibussowitsch     //dctx->contained = PETSC_FALSE;
4450e6b6b59SJacob Faibussowitsch     for (auto &&upstrm : upstream_copy) {
4460e6b6b59SJacob Faibussowitsch       // check that this parent still points to what we originally thought it was
4470e6b6b59SJacob Faibussowitsch       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);
4480e6b6b59SJacob Faibussowitsch       PetscCall(PetscDeviceContextSyncClearMap_Internal(upstrm.first));
4490e6b6b59SJacob Faibussowitsch     }
4500e6b6b59SJacob Faibussowitsch   }
4510e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
4520e6b6b59SJacob Faibussowitsch }
4530e6b6b59SJacob Faibussowitsch 
454d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDeviceContextCheckNotOrphaned_Internal(PetscDeviceContext dctx)
455d71ae5a4SJacob Faibussowitsch {
4560e6b6b59SJacob Faibussowitsch   std::ostringstream oss;
4570e6b6b59SJacob Faibussowitsch   //const auto         allow = dctx->options.allow_orphans, contained = dctx->contained;
4580e6b6b59SJacob Faibussowitsch   const auto allow = true, contained = true;
4590e6b6b59SJacob Faibussowitsch   auto       wrote_to_oss = false;
4600e6b6b59SJacob Faibussowitsch   using map_iterator      = MarkedObjectMap::map_type::const_iterator;
4610e6b6b59SJacob Faibussowitsch   using dep_iterator      = MarkedObjectMap::mapped_type::dependency_type::const_iterator;
4620e6b6b59SJacob Faibussowitsch 
4630e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
4640e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceContextMapIterVisitor(dctx, [&](map_iterator mapit, dep_iterator it, dep_iterator end) {
4650e6b6b59SJacob Faibussowitsch     PetscFunctionBegin;
4660e6b6b59SJacob Faibussowitsch     if (allow || contained) PetscFunctionReturn(0);
4670e6b6b59SJacob Faibussowitsch     wrote_to_oss = true;
4680e6b6b59SJacob Faibussowitsch     oss << "- PetscObject (id " << mapit->first << "), intent " << PetscMemoryAccessModeToString(mapit->second.mode) << ' ' << it->frame();
4690e6b6b59SJacob Faibussowitsch     if (std::distance(it, end) == 0) oss << " (orphaned)"; // we were the only dependency
4700e6b6b59SJacob Faibussowitsch     oss << '\n';
4710e6b6b59SJacob Faibussowitsch     PetscFunctionReturn(0);
4720e6b6b59SJacob Faibussowitsch   }));
4730e6b6b59SJacob Faibussowitsch   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",
4740e6b6b59SJacob Faibussowitsch              PetscObjectCast(dctx)->name ? PetscObjectCast(dctx)->name : "unnamed", PetscObjectCast(dctx)->id, oss.str().c_str());
4750e6b6b59SJacob Faibussowitsch   PetscCall(CxxDataCast(dctx)->clear());
4760e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
4770e6b6b59SJacob Faibussowitsch }
4780e6b6b59SJacob Faibussowitsch 
479*7c441f3aSJacob Faibussowitsch #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__)
4800e6b6b59SJacob Faibussowitsch 
481*7c441f3aSJacob Faibussowitsch // The current mode is compatible with the previous mode (i.e. read-read) so we need only
482*7c441f3aSJacob Faibussowitsch // update the existing version and possibly appeand ourselves to the dependency list
483*7c441f3aSJacob Faibussowitsch 
484*7c441f3aSJacob Faibussowitsch template <bool use_debug>
485*7c441f3aSJacob Faibussowitsch 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)
486*7c441f3aSJacob Faibussowitsch {
487*7c441f3aSJacob Faibussowitsch   const auto dctx_id             = PetscObjectCast(dctx)->id;
488*7c441f3aSJacob Faibussowitsch   auto      &object_dependencies = marked.dependencies;
4890e6b6b59SJacob Faibussowitsch   const auto end                 = object_dependencies.end();
4900e6b6b59SJacob Faibussowitsch   const auto it                  = std::find_if(object_dependencies.begin(), end, [&](const MarkedObjectMap::snapshot_type &obj) { return obj.dctx_id() == dctx_id; });
4910e6b6b59SJacob Faibussowitsch 
492*7c441f3aSJacob Faibussowitsch   PetscFunctionBegin;
493*7c441f3aSJacob Faibussowitsch   PetscCall(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)));
4940e6b6b59SJacob Faibussowitsch   if (it != end) {
495*7c441f3aSJacob Faibussowitsch     using std::swap;
496*7c441f3aSJacob Faibussowitsch 
4970e6b6b59SJacob Faibussowitsch     // we have been here before, all we must do is update our entry then we can bail
4980e6b6b59SJacob Faibussowitsch     PetscCall(DEBUG_INFO("found old self as dependency, updating\n"));
4990e6b6b59SJacob Faibussowitsch     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);
500*7c441f3aSJacob Faibussowitsch     swap(it->frame(), frame);
5010e6b6b59SJacob Faibussowitsch     PetscCall(PetscDeviceContextRecordEvent_Private(dctx, it->event()));
502*7c441f3aSJacob Faibussowitsch     *update_object_dependencies = false;
5030e6b6b59SJacob Faibussowitsch     PetscFunctionReturn(0);
5040e6b6b59SJacob Faibussowitsch   }
5050e6b6b59SJacob Faibussowitsch 
5060e6b6b59SJacob Faibussowitsch   // we have not been here before, need to serialize with the last write event (if it exists)
5070e6b6b59SJacob Faibussowitsch   // and add ourselves to the dependency list
5080e6b6b59SJacob Faibussowitsch   if (const auto event = marked.last_write.event()) PetscCall(PetscDeviceContextWaitForEvent_Private(dctx, event));
509*7c441f3aSJacob Faibussowitsch   PetscFunctionReturn(0);
510*7c441f3aSJacob Faibussowitsch }
511*7c441f3aSJacob Faibussowitsch 
512*7c441f3aSJacob Faibussowitsch template <bool use_debug>
513*7c441f3aSJacob Faibussowitsch 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)
514*7c441f3aSJacob Faibussowitsch {
515*7c441f3aSJacob Faibussowitsch   const auto      dctx_id    = PetscObjectCast(dctx)->id;
516*7c441f3aSJacob Faibussowitsch   auto           &last_write = marked.last_write;
517*7c441f3aSJacob Faibussowitsch   auto           &last_dep   = marked.dependencies.back();
518*7c441f3aSJacob Faibussowitsch   PetscDeviceType dtype;
519*7c441f3aSJacob Faibussowitsch 
520*7c441f3aSJacob Faibussowitsch   PetscFunctionBegin;
521*7c441f3aSJacob Faibussowitsch   PetscAssert(marked.dependencies.size() == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Can only have a single writer as dependency, have %zu!", marked.dependencies.size());
522*7c441f3aSJacob Faibussowitsch   PetscCall(PetscDeviceContextGetDeviceType(dctx, &dtype));
523*7c441f3aSJacob Faibussowitsch   if (last_dep.event()->dtype != dtype) {
524*7c441f3aSJacob Faibussowitsch     PetscCall(DEBUG_INFO("moving last write dependency (intent %s)\n", PetscMemoryAccessModeToString(marked.mode)));
525*7c441f3aSJacob Faibussowitsch     last_write = std::move(last_dep);
526*7c441f3aSJacob Faibussowitsch     PetscFunctionReturn(0);
527*7c441f3aSJacob Faibussowitsch   }
528*7c441f3aSJacob Faibussowitsch 
529*7c441f3aSJacob Faibussowitsch   // we match the device type of the dependency, we can reuse its event!
530*7c441f3aSJacob Faibussowitsch   auto      &dctx_upstream_deps     = CxxDataCast(dctx)->deps;
531*7c441f3aSJacob Faibussowitsch   const auto last_write_was_also_us = last_write.event() && (last_write.dctx_id() == dctx_id);
532*7c441f3aSJacob Faibussowitsch   using std::swap;
533*7c441f3aSJacob Faibussowitsch 
534*7c441f3aSJacob Faibussowitsch   PetscCall(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]));
535*7c441f3aSJacob Faibussowitsch   if (last_dep.event()->dctx_id != dctx_id) dctx_upstream_deps.emplace(id);
536*7c441f3aSJacob Faibussowitsch   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,
537*7c441f3aSJacob Faibussowitsch               last_write.frame().to_string().c_str());
538*7c441f3aSJacob Faibussowitsch   swap(last_write, last_dep);
539*7c441f3aSJacob Faibussowitsch   if (last_write_was_also_us) {
540*7c441f3aSJacob Faibussowitsch     PetscCall(DEBUG_INFO("we were also the last write event (intent %s), updating\n", PetscMemoryAccessModeToString(mode)));
541*7c441f3aSJacob Faibussowitsch     // we are both the last to write *and* the last to leave a write event. This is the
542*7c441f3aSJacob Faibussowitsch     // fast path, we only need to update the frame and update the recorded event
543*7c441f3aSJacob Faibussowitsch     swap(last_dep.frame(), frame);
544*7c441f3aSJacob Faibussowitsch     // last used to be last_write which is not guaranteed to have an event, so must
545*7c441f3aSJacob Faibussowitsch     // create it now
546*7c441f3aSJacob Faibussowitsch     PetscCall(last_dep.ensure_event(dctx));
547*7c441f3aSJacob Faibussowitsch     PetscCall(PetscDeviceContextRecordEvent_Private(dctx, last_dep.event()));
548*7c441f3aSJacob Faibussowitsch     *update_object_dependencies = false;
549*7c441f3aSJacob Faibussowitsch   }
550*7c441f3aSJacob Faibussowitsch   PetscFunctionReturn(0);
551*7c441f3aSJacob Faibussowitsch }
552*7c441f3aSJacob Faibussowitsch 
553*7c441f3aSJacob Faibussowitsch // The current mode is NOT compatible with the previous mode. We must serialize with all events
554*7c441f3aSJacob Faibussowitsch // in the dependency list, possibly clear it, and update the previous write event
555*7c441f3aSJacob Faibussowitsch 
556*7c441f3aSJacob Faibussowitsch template <bool use_debug>
557*7c441f3aSJacob Faibussowitsch 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)
558*7c441f3aSJacob Faibussowitsch {
559*7c441f3aSJacob Faibussowitsch   auto &old_mode            = marked.mode;
560*7c441f3aSJacob Faibussowitsch   auto &object_dependencies = marked.dependencies;
561*7c441f3aSJacob Faibussowitsch 
562*7c441f3aSJacob Faibussowitsch   PetscFunctionBegin;
563*7c441f3aSJacob Faibussowitsch   // we are NOT compatible  with the previous mode
5640e6b6b59SJacob Faibussowitsch   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),
5650e6b6b59SJacob Faibussowitsch                        object_dependencies.size(), object_dependencies.size() == 1 ? "dependency" : "dependencies"));
5660e6b6b59SJacob Faibussowitsch 
567*7c441f3aSJacob Faibussowitsch   for (const auto &dep : object_dependencies) PetscCall(PetscDeviceContextWaitForEvent_Private(dctx, dep.event()));
568*7c441f3aSJacob Faibussowitsch   // if the previous mode wrote, update the last write node with it
569*7c441f3aSJacob Faibussowitsch   if (PetscMemoryAccessWrite(old_mode)) PetscCall(MarkFromID_IncompatibleModes_UpdateLastWrite(marked, dctx, id, mode, frame, name, update_object_dependencies));
5700e6b6b59SJacob Faibussowitsch 
5710e6b6b59SJacob Faibussowitsch   old_mode = mode;
572*7c441f3aSJacob Faibussowitsch   // clear out the old dependencies if are about to append ourselves
573*7c441f3aSJacob Faibussowitsch   if (*update_object_dependencies) object_dependencies.clear();
574*7c441f3aSJacob Faibussowitsch   PetscFunctionReturn(0);
5750e6b6b59SJacob Faibussowitsch }
576*7c441f3aSJacob Faibussowitsch 
577*7c441f3aSJacob Faibussowitsch template <bool use_debug>
578*7c441f3aSJacob Faibussowitsch static PetscErrorCode PetscDeviceContextMarkIntentFromID_Private(PetscDeviceContext dctx, PetscObjectId id, PetscMemoryAccessMode mode, PetscStackFrame<use_debug> frame, const char *name)
579*7c441f3aSJacob Faibussowitsch {
580*7c441f3aSJacob Faibussowitsch   auto &marked                     = marked_object_map.map[id];
581*7c441f3aSJacob Faibussowitsch   auto &object_dependencies        = marked.dependencies;
582*7c441f3aSJacob Faibussowitsch   auto  update_object_dependencies = true;
583*7c441f3aSJacob Faibussowitsch 
584*7c441f3aSJacob Faibussowitsch   PetscFunctionBegin;
585*7c441f3aSJacob Faibussowitsch   if ((marked.mode == PETSC_MEMORY_ACCESS_READ) && (mode == PETSC_MEMORY_ACCESS_READ)) {
586*7c441f3aSJacob Faibussowitsch     PetscCall(MarkFromID_CompatibleModes(marked, dctx, id, mode, frame, name, &update_object_dependencies));
587*7c441f3aSJacob Faibussowitsch   } else {
588*7c441f3aSJacob Faibussowitsch     PetscCall(MarkFromID_IncompatibleModes(marked, dctx, id, mode, frame, name, &update_object_dependencies));
589*7c441f3aSJacob Faibussowitsch   }
590*7c441f3aSJacob Faibussowitsch   if (update_object_dependencies) {
5910e6b6b59SJacob Faibussowitsch     // become the new leaf by appending ourselves
5920e6b6b59SJacob Faibussowitsch     PetscCall(DEBUG_INFO("%s with intent %s\n", object_dependencies.empty() ? "dependency list is empty, creating new leaf" : "appending to existing leaves", PetscMemoryAccessModeToString(mode)));
5930e6b6b59SJacob Faibussowitsch     PetscCallCXX(object_dependencies.emplace_back(dctx, std::move(frame)));
5940e6b6b59SJacob Faibussowitsch     PetscCallCXX(CxxDataCast(dctx)->deps.emplace(id));
5950e6b6b59SJacob Faibussowitsch   }
596*7c441f3aSJacob Faibussowitsch   PetscFunctionReturn(0);
597*7c441f3aSJacob Faibussowitsch }
598*7c441f3aSJacob Faibussowitsch 
599*7c441f3aSJacob Faibussowitsch #undef DEBUG_INFO
6000e6b6b59SJacob Faibussowitsch 
6010e6b6b59SJacob Faibussowitsch /*@C
6020e6b6b59SJacob Faibussowitsch   PetscDeviceContextMarkIntentFromID - Indicate a `PetscDeviceContext`s access intent to the
6030e6b6b59SJacob Faibussowitsch   auto-dependency system
6040e6b6b59SJacob Faibussowitsch 
6050e6b6b59SJacob Faibussowitsch   Not Collective
6060e6b6b59SJacob Faibussowitsch 
6070e6b6b59SJacob Faibussowitsch   Input Parameters:
6080e6b6b59SJacob Faibussowitsch + dctx - The `PetscDeviceContext`
6090e6b6b59SJacob Faibussowitsch . id   - The `PetscObjectId` to mark
6100e6b6b59SJacob Faibussowitsch . mode - The desired access intent
6110e6b6b59SJacob Faibussowitsch - name - The object name (for debug purposes, ignored in optimized builds)
6120e6b6b59SJacob Faibussowitsch 
6130e6b6b59SJacob Faibussowitsch   Notes:
6140e6b6b59SJacob Faibussowitsch   This routine formally informs the dependency system that `dctx` will access the object
6150e6b6b59SJacob Faibussowitsch   represented by `id` with `mode` and adds `dctx` to `id`'s list of dependencies (termed
6160e6b6b59SJacob Faibussowitsch   "leaves").
6170e6b6b59SJacob Faibussowitsch 
6180e6b6b59SJacob Faibussowitsch   If the existing set of leaves have an incompatible `PetscMemoryAccessMode` to `mode`, `dctx`
6190e6b6b59SJacob Faibussowitsch   will be serialized against them.
6200e6b6b59SJacob Faibussowitsch 
6210e6b6b59SJacob Faibussowitsch   Level: intermediate
6220e6b6b59SJacob Faibussowitsch 
6230e6b6b59SJacob Faibussowitsch .seealso: `PetscDeviceContextWaitForContext()`, `PetscDeviceContextSynchronize()`,
6240e6b6b59SJacob Faibussowitsch `PetscObjectGetId()`, `PetscMemoryAccessMode`
6250e6b6b59SJacob Faibussowitsch @*/
626d71ae5a4SJacob Faibussowitsch PetscErrorCode PetscDeviceContextMarkIntentFromID(PetscDeviceContext dctx, PetscObjectId id, PetscMemoryAccessMode mode, const char name[])
627d71ae5a4SJacob Faibussowitsch {
6280e6b6b59SJacob Faibussowitsch #if PetscDefined(USE_DEBUG)
6290e6b6b59SJacob Faibussowitsch   const auto index    = petscstack.currentsize > 2 ? petscstack.currentsize - 2 : 0;
6300e6b6b59SJacob Faibussowitsch   const auto file     = petscstack.file[index];
6310e6b6b59SJacob Faibussowitsch   const auto function = petscstack.function[index];
6320e6b6b59SJacob Faibussowitsch   const auto line     = petscstack.line[index];
6330e6b6b59SJacob Faibussowitsch #else
6340e6b6b59SJacob Faibussowitsch   constexpr const char *file     = nullptr;
6350e6b6b59SJacob Faibussowitsch   constexpr const char *function = nullptr;
6360e6b6b59SJacob Faibussowitsch   constexpr auto        line     = 0;
6370e6b6b59SJacob Faibussowitsch #endif
6380e6b6b59SJacob Faibussowitsch 
6390e6b6b59SJacob Faibussowitsch   PetscFunctionBegin;
6400e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx));
6410e6b6b59SJacob Faibussowitsch   if (name) PetscValidCharPointer(name, 4);
6420e6b6b59SJacob Faibussowitsch   PetscCall(marked_object_map.register_finalize());
6436a4a1270SPierre Jolivet   PetscCall(PetscLogEventBegin(DCONTEXT_Mark, dctx, nullptr, nullptr, nullptr));
6440e6b6b59SJacob Faibussowitsch   PetscCall(PetscDeviceContextMarkIntentFromID_Private(dctx, id, mode, MarkedObjectMap::snapshot_type::frame_type{file, function, line}, name ? name : "unknown object"));
6456a4a1270SPierre Jolivet   PetscCall(PetscLogEventEnd(DCONTEXT_Mark, dctx, nullptr, nullptr, nullptr));
6460e6b6b59SJacob Faibussowitsch   PetscFunctionReturn(0);
6470e6b6b59SJacob Faibussowitsch }
648a966a306SPierre Jolivet 
649a966a306SPierre Jolivet #if defined(__clang__)
650a966a306SPierre Jolivet   #pragma clang diagnostic pop
651a966a306SPierre Jolivet #endif
652