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