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
|