Line data Source code
1 0 : // Distributed under the MIT License. 2 : // See LICENSE.txt for details. 3 : 4 : #pragma once 5 : 6 : #include <charm++.h> 7 : #include <cstddef> 8 : #include <limits> 9 : #include <pup.h> 10 : #include <string> 11 : #include <unordered_map> 12 : 13 : #include "Domain/Structure/ElementId.hpp" 14 : #include "Parallel/NodeLock.hpp" 15 : #include "Parallel/Phase.hpp" 16 : #include "Utilities/Gsl.hpp" 17 : #include "Utilities/Serialization/CharmPupable.hpp" 18 : 19 : namespace Parallel { 20 : /*! 21 : * \brief The base class of a member of an DG element array/map on a nodegroup. 22 : * 23 : * The nodegroup DgElementCollection stores all the elements on a node. Each 24 : * of those elements is a `DgElementArrayMember` and has this base class to 25 : * make access easier so it can be used in a type-erased context since 26 : * `DgElementArrayMember` depends on the metavariables, 27 : * phase-dependent-action-list, and the simple tags needed from options. 28 : * 29 : * This class essentially mimicks a lot of the functionality of 30 : * `Parallel::DistributedObject` but does not involve Charm++ beyond 31 : * serialization. 32 : */ 33 : template <size_t Dim> 34 1 : class DgElementArrayMemberBase : PUP::able { 35 : public: 36 0 : DgElementArrayMemberBase() = default; 37 : 38 0 : DgElementArrayMemberBase(const DgElementArrayMemberBase& /*rhs*/) = default; 39 0 : DgElementArrayMemberBase& operator=(const DgElementArrayMemberBase& /*rhs*/) = 40 : default; 41 0 : DgElementArrayMemberBase(DgElementArrayMemberBase&& /*rhs*/) = default; 42 0 : DgElementArrayMemberBase& operator=(DgElementArrayMemberBase&& /*rhs*/) = 43 : default; 44 0 : ~DgElementArrayMemberBase() override = default; 45 : 46 0 : WRAPPED_PUPable_abstract(DgElementArrayMemberBase); // NOLINT 47 : 48 0 : explicit DgElementArrayMemberBase(CkMigrateMessage* msg); 49 : 50 : /// Start execution of the phase-dependent action list in `next_phase`. If 51 : /// `next_phase` has already been visited, execution will resume at the point 52 : /// where the previous execution of the same phase left off. 53 : /// 54 : /// If \p force is true, then regardless of how this component terminated 55 : /// (error or deadlock), it will resume. 56 : /// 57 : /// \warning Don't set \p force to true unless you are absolutely sure you 58 : /// want to. This can have very unintended consequences if used incorrectly. 59 1 : virtual void start_phase(Parallel::Phase next_phase, bool force = false) = 0; 60 : 61 : /// Get the current phase 62 1 : Parallel::Phase phase() const; 63 : 64 : /// Tell the Algorithm it should no longer execute the algorithm. This does 65 : /// not mean that the execution of the program is terminated, but only that 66 : /// the algorithm has terminated. An algorithm can be restarted by passing 67 : /// `true` as the second argument to the `receive_data` method or by calling 68 : /// perform_algorithm(true). 69 1 : void set_terminate(gsl::not_null<size_t*> number_of_elements_terminated, 70 : gsl::not_null<Parallel::NodeLock*> nodegroup_lock, 71 : bool terminate); 72 : 73 : /// Check if an algorithm should continue being evaluated 74 1 : bool get_terminate() const; 75 : 76 : /// The zero-indexed step in the algorithm. 77 1 : size_t algorithm_step() const; 78 : 79 : /// Start evaluating the algorithm until it is stopped by an action. 80 1 : virtual void perform_algorithm() = 0; 81 : 82 : /// Print the expanded type aliases 83 1 : virtual std::string print_types() const = 0; 84 : 85 : /// Print the current state of the algorithm 86 1 : std::string print_state() const; 87 : 88 : /// Print the current contents of the inboxes 89 1 : virtual std::string print_inbox() const = 0; 90 : 91 : /// Print the current contents of the DataBox 92 1 : virtual std::string print_databox() const = 0; 93 : 94 : /// \brief The `inbox_lock()` only locks the inbox, nothing else. The inbox is 95 : /// unsafe to access without this lock. 96 : /// 97 : /// Use `element_lock()` to lock the rest of the element. 98 : /// 99 : /// This should always be managed by `std::unique_lock` or `std::lock_guard`. 100 1 : Parallel::NodeLock& inbox_lock(); 101 : 102 : /// \brief Locks the element, except for the inbox, which is guarded by the 103 : /// `inbox_lock()`. 104 : /// 105 : /// This should always be managed by `std::unique_lock` or `std::lock_guard`. 106 1 : Parallel::NodeLock& element_lock(); 107 : 108 : /// \brief Set which core this element should pretend to be bound to. 109 1 : void set_core(size_t core); 110 : 111 : /// \brief Get which core this element should pretend to be bound to. 112 1 : size_t get_core() const; 113 : 114 : /// Returns the name of the last "next iterable action" to be run before a 115 : /// deadlock occurred. 116 1 : const std::string& deadlock_analysis_next_iterable_action() const { 117 : return deadlock_analysis_next_iterable_action_; 118 : } 119 : 120 0 : void pup(PUP::er& p) override; 121 : 122 : protected: 123 0 : DgElementArrayMemberBase(ElementId<Dim> element_id, size_t node_number); 124 : 125 0 : Parallel::NodeLock inbox_lock_{}; 126 0 : Parallel::NodeLock element_lock_{}; 127 0 : bool performing_action_ = false; 128 0 : Parallel::Phase phase_{Parallel::Phase::Initialization}; 129 0 : std::unordered_map<Parallel::Phase, size_t> phase_bookmarks_{}; 130 0 : std::size_t algorithm_step_ = 0; 131 : 132 0 : bool terminate_{true}; 133 0 : bool halt_algorithm_until_next_phase_{false}; 134 : 135 : // Records the name of the next action to be called so that during deadlock 136 : // analysis we can print this out. 137 0 : std::string deadlock_analysis_next_iterable_action_{}; 138 0 : ElementId<Dim> element_id_; 139 0 : size_t my_node_{std::numeric_limits<size_t>::max()}; 140 : // There is no associated core. However, we use this as a method of 141 : // interoperating with core-aware concepts like the interpolation 142 : // framework. Once that framework is core-agnostic we will remove my_core_. 143 0 : size_t my_core_{std::numeric_limits<size_t>::max()}; 144 : }; 145 : } // namespace Parallel