Line data Source code
1 1 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : /// \file
5 : /// Defines class template GlobalCache.
6 :
7 : #pragma once
8 :
9 : #include <charm++.h>
10 : #include <memory>
11 : #include <mutex>
12 : #include <numeric>
13 : #include <optional>
14 : #include <pup.h>
15 : #include <string>
16 : #include <tuple>
17 : #include <unordered_map>
18 : #include <utility>
19 : #include <vector>
20 :
21 : #include "DataStructures/DataBox/Tag.hpp"
22 : #include "DataStructures/DataBox/TagTraits.hpp"
23 : #include "Parallel/ArrayComponentId.hpp"
24 : #include "Parallel/Callback.hpp"
25 : #include "Parallel/CharmRegistration.hpp"
26 : #include "Parallel/Info.hpp"
27 : #include "Parallel/Local.hpp"
28 : #include "Parallel/ParallelComponentHelpers.hpp"
29 : #include "Parallel/ResourceInfo.hpp"
30 : #include "Parallel/Tags/ResourceInfo.hpp"
31 : #include "Utilities/Algorithm.hpp"
32 : #include "Utilities/ErrorHandling/Assert.hpp"
33 : #include "Utilities/ErrorHandling/Error.hpp"
34 : #include "Utilities/Gsl.hpp"
35 : #include "Utilities/Numeric.hpp"
36 : #include "Utilities/PrettyType.hpp"
37 : #include "Utilities/Requires.hpp"
38 : #include "Utilities/Serialization/PupStlCpp17.hpp"
39 : #include "Utilities/Serialization/Serialize.hpp"
40 : #include "Utilities/StdHelpers/RetrieveUniquePtr.hpp"
41 : #include "Utilities/System/ParallelInfo.hpp"
42 : #include "Utilities/TMPL.hpp"
43 : #include "Utilities/TaggedTuple.hpp"
44 : #include "Utilities/TypeTraits/CreateGetTypeAliasOrDefault.hpp"
45 : #include "Utilities/TypeTraits/IsA.hpp"
46 :
47 : #include "Parallel/GlobalCache.decl.h"
48 : #include "Parallel/Main.decl.h"
49 :
50 : /// \cond
51 : namespace Parallel {
52 : template <typename Metavariables>
53 : struct GlobalCache;
54 : } // namespace Parallel
55 : namespace mem_monitor {
56 : template <typename Metavariables>
57 : struct MemoryMonitor;
58 : template <typename ContributingComponent>
59 : struct ContributeMemoryData;
60 : } // namespace mem_monitor
61 : /// \endcond
62 :
63 : namespace Parallel {
64 :
65 : namespace GlobalCache_detail {
66 :
67 : template <class GlobalCacheTag, class Metavariables>
68 : using get_matching_tag = typename matching_tag_helper<
69 : GlobalCacheTag,
70 : tmpl::append<get_const_global_cache_tags<Metavariables>,
71 : get_mutable_global_cache_tags<Metavariables>>>::type;
72 :
73 : template <class GlobalCacheTag, class Metavariables>
74 : using type_for_get = typename type_for_get_helper<
75 : typename get_matching_tag<GlobalCacheTag, Metavariables>::type>::type;
76 :
77 : CREATE_GET_TYPE_ALIAS_OR_DEFAULT(component_being_mocked)
78 :
79 : template <typename... Tags>
80 : auto make_mutable_cache_tag_storage(tuples::TaggedTuple<Tags...>&& input) {
81 : return tuples::TaggedTuple<MutableCacheTag<Tags>...>(std::make_tuple(
82 : std::move(tuples::get<Tags>(input)),
83 : std::unordered_map<Parallel::ArrayComponentId,
84 : std::vector<std::unique_ptr<Callback>>>{})...);
85 : }
86 :
87 : template <typename ParallelComponent, typename ComponentList>
88 : auto get_component_if_mocked_impl() {
89 : if constexpr (tmpl::list_contains_v<ComponentList, ParallelComponent>) {
90 : return ParallelComponent{};
91 : } else {
92 : using mock_find = tmpl::find<
93 : ComponentList,
94 : std::is_same<get_component_being_mocked_or_default<tmpl::_1, void>,
95 : tmpl::pin<ParallelComponent>>>;
96 : static_assert(
97 : tmpl::size<mock_find>::value > 0,
98 : "The requested parallel component (first template argument) is not a "
99 : "known parallel component (second template argument) or a component "
100 : "being mocked by one of those components.");
101 : return tmpl::front<mock_find>{};
102 : }
103 : }
104 :
105 : /// In order to be able to use a mock action testing framework we need to be
106 : /// able to get the correct parallel component from the global cache even when
107 : /// the correct component is a mock. We do this by having the mocked
108 : /// components have a member type alias `component_being_mocked`, and having
109 : /// `Parallel::get_component` check if the component to be retrieved is in the
110 : /// `metavariables::component_list`. If it is not in the `component_list` then
111 : /// we search for a mock component that is mocking the component we are trying
112 : /// to retrieve.
113 : template <typename ComponentList, typename ParallelComponent>
114 : using get_component_if_mocked =
115 : decltype(get_component_if_mocked_impl<ParallelComponent, ComponentList>());
116 :
117 : /// This class replaces the Charm++ base class of the GlobalCache in unit tests
118 : /// that don't have a Charm++ main function.
119 : struct MockGlobalCache {
120 : MockGlobalCache() = default;
121 : explicit MockGlobalCache(CkMigrateMessage* /*msg*/) {}
122 : virtual ~MockGlobalCache() = default;
123 : virtual void pup(PUP::er& /*p*/) {}
124 :
125 : static bool isIrreducible() { return true; }
126 :
127 : #if defined(__GNUC__) && !defined(__clang__)
128 : #pragma GCC diagnostic push
129 : #pragma GCC diagnostic ignored "-Wsuggest-attribute=noreturn"
130 : #elif defined(__clang__)
131 : #pragma GCC diagnostic push
132 : #pragma GCC diagnostic ignored "-Wmissing-noreturn"
133 : #endif // defined(__GNUC__) && !defined(__clang__)
134 : template <typename T>
135 : static void contribute(T&& /*unused*/) {
136 : ERROR("Not implemented.");
137 : }
138 : #if defined(__GNUC__) || defined(__clang__)
139 : #pragma GCC diagnostic pop
140 : #endif
141 : };
142 :
143 : #ifdef SPECTRE_CHARM_HAS_MAIN
144 : static constexpr bool mock_global_cache = false;
145 : #else
146 : static constexpr bool mock_global_cache = true;
147 : #endif // SPECTRE_CHARM_HAS_MAIN
148 :
149 : } // namespace GlobalCache_detail
150 :
151 : /// \cond
152 : template <typename ParallelComponentTag, typename Metavariables>
153 : auto get_parallel_component(GlobalCache<Metavariables>& cache)
154 : -> Parallel::proxy_from_parallel_component<
155 : GlobalCache_detail::get_component_if_mocked<
156 : typename Metavariables::component_list, ParallelComponentTag>>&;
157 :
158 : template <typename ParallelComponentTag, typename Metavariables>
159 : auto get_parallel_component(const GlobalCache<Metavariables>& cache)
160 : -> const Parallel::proxy_from_parallel_component<
161 : GlobalCache_detail::get_component_if_mocked<
162 : typename Metavariables::component_list, ParallelComponentTag>>&;
163 : /// \endcond
164 :
165 : /*!
166 : * \ingroup ParallelGroup
167 : * \brief A Charm++ chare that caches global data once per Charm++ node.
168 : *
169 : * \details There are two types of global data that are stored; const data and
170 : * mutable data. Once the GlobalCache is created, const data cannot be edited
171 : * but mutable data can be edited using `Parallel::mutate`.
172 : *
173 : * The template parameter `Metavariables` must define the following type
174 : * aliases:
175 : * - `component_list` typelist of ParallelComponents
176 : * - `const_global_cache_tags` (possibly empty) typelist of tags of
177 : * constant data
178 : * - `mutable_global_cache_tags` (possibly empty) typelist of tags of
179 : * non-constant data
180 : *
181 : * The tag lists for the const items added to the GlobalCache is created by
182 : * combining the following tag lists:
183 : * - `Metavariables::const_global_cache_tags` which should contain only those
184 : * tags that cannot be added from the other tag lists below.
185 : * - `Component::const_global_cache_tags` for each `Component` in
186 : * `Metavariables::component_list` which should contain the tags needed by
187 : * any simple actions called on the Component, as well as tags need by the
188 : * `allocate_array` function of an array component. The type alias may be
189 : * omitted for an empty list.
190 : * - `Action::const_global_cache_tags` for each `Action` in the
191 : * `phase_dependent_action_list` of each `Component` of
192 : * `Metavariables::component_list` which should contain the tags needed by
193 : * that Action. The type alias may be omitted for an empty list.
194 : *
195 : * The tag lists for the mutable items added to the GlobalCache is created
196 : * by combining exactly the same tag lists as for the const items, except with
197 : * `const_global_cache_tags` replaced by `mutable_global_cache_tags`.
198 : *
199 : * The tags in the `const_global_cache_tags` and
200 : * `mutable_global_cache_tags` type lists are db::SimpleTag%s that
201 : * have a `using option_tags` type alias and a static function
202 : * `create_from_options` that are used to create the constant data (or initial
203 : * mutable data) from input file options.
204 : *
205 : * References to const items in the GlobalCache are also added to the
206 : * db::DataBox of each `Component` in the
207 : * `Metavariables::component_list` with the same tag with which they
208 : * were inserted into the GlobalCache. References to mutable items
209 : * in the GlobalCache are not added to the db::DataBox.
210 : *
211 : * Since mutable data is stored once per Charm++ node, we require that
212 : * data structures held by mutable tags have some sort of thread-safety.
213 : * Particularly, we require data structures in mutable tags be Single
214 : * Producer-Multiple Consumer. This means that the data structure should be
215 : * readable/accessible by multiple threads at once, even while being mutated
216 : * (multiple consumer), but will not be edited/mutated simultaneously on
217 : * multiple threads (single producer).
218 : */
219 : template <typename Metavariables>
220 1 : class GlobalCache
221 : : public std::conditional_t<GlobalCache_detail::mock_global_cache,
222 : GlobalCache_detail::MockGlobalCache,
223 : CBase_GlobalCache<Metavariables>> {
224 0 : using Base = std::conditional_t<GlobalCache_detail::mock_global_cache,
225 : GlobalCache_detail::MockGlobalCache,
226 : CBase_GlobalCache<Metavariables>>;
227 0 : using parallel_component_tag_list = tmpl::transform<
228 : typename Metavariables::component_list,
229 : tmpl::bind<
230 : tmpl::type_,
231 : tmpl::bind<Parallel::proxy_from_parallel_component, tmpl::_1>>>;
232 0 : using ParallelComponentTuple =
233 : tuples::tagged_tuple_from_typelist<parallel_component_tag_list>;
234 :
235 : public:
236 0 : static constexpr bool is_mocked = GlobalCache_detail::mock_global_cache;
237 0 : using proxy_type = CProxy_GlobalCache<Metavariables>;
238 0 : using main_proxy_type = CProxy_Main<Metavariables>;
239 : /// Access to the Metavariables template parameter
240 1 : using metavariables = Metavariables;
241 : /// Typelist of the ParallelComponents stored in the GlobalCache
242 1 : using component_list = typename Metavariables::component_list;
243 : // Even though the GlobalCache doesn't run the Algorithm, this type alias
244 : // helps in identifying that the GlobalCache is a Nodegroup using
245 : // Parallel::is_nodegroup_v
246 0 : using chare_type = Parallel::Algorithms::Nodegroup;
247 0 : using const_tags_list = get_const_global_cache_tags<Metavariables>;
248 0 : using ConstTagsTuple = tuples::tagged_tuple_from_typelist<const_tags_list>;
249 0 : using ConstTagsStorage = ConstTagsTuple;
250 0 : using mutable_tags_list = get_mutable_global_cache_tags<Metavariables>;
251 0 : using MutableTagsTuple =
252 : tuples::tagged_tuple_from_typelist<mutable_tags_list>;
253 0 : using MutableTagsStorage = tuples::tagged_tuple_from_typelist<
254 : get_mutable_global_cache_tag_storage<Metavariables>>;
255 :
256 : /// Constructor meant to be used in the ActionTesting framework.
257 1 : GlobalCache(ConstTagsTuple const_global_cache,
258 : MutableTagsTuple mutable_global_cache = {},
259 : std::vector<size_t> procs_per_node = {1}, const int my_proc = 0,
260 : const int my_node = 0, const int my_local_rank = 0);
261 :
262 : /// Constructor meant to be used in charm-aware settings (with a Main proxy).
263 1 : GlobalCache(ConstTagsTuple const_global_cache,
264 : MutableTagsTuple mutable_global_cache,
265 : std::optional<main_proxy_type> main_proxy);
266 :
267 0 : explicit GlobalCache(CkMigrateMessage* msg) : Base(msg) {}
268 :
269 0 : ~GlobalCache() override {
270 : (void)Parallel::charmxx::RegisterChare<
271 : GlobalCache<Metavariables>,
272 : CkIndex_GlobalCache<Metavariables>>::registrar;
273 : }
274 : /// \cond
275 : GlobalCache() = default;
276 : GlobalCache(const GlobalCache&) = default;
277 : GlobalCache& operator=(const GlobalCache&) = default;
278 : GlobalCache(GlobalCache&&) = default;
279 : GlobalCache& operator=(GlobalCache&&) = default;
280 : /// \endcond
281 :
282 : /// Entry method to set the ParallelComponents (should only be called once)
283 1 : void set_parallel_components(ParallelComponentTuple&& parallel_components,
284 : const CkCallback& callback);
285 :
286 : /*!
287 : * \brief Returns whether the object referred to by `GlobalCacheTag`
288 : * (which must be a mutable cache tag) is ready to be accessed by a
289 : * `get` call.
290 : *
291 : * \details `function` is a user-defined invokable that:
292 : * - takes one argument: a const reference to the object referred to by the
293 : * `GlobalCacheTag`.
294 : * - if the data is ready, returns a default constructed
295 : * `std::unique_ptr<CallBack>`
296 : * - if the data is not ready, returns a `std::unique_ptr<CallBack>`,
297 : * where the `Callback` will re-invoke the current action on the
298 : * current parallel component. This callback should be a
299 : * `Parallel::PerformAlgorithmCallback`. Other types of callbacks are not
300 : * supported at this time.
301 : *
302 : * \parblock
303 : * \warning The `function` may be called twice so it should not modify any
304 : * state in its scope.
305 : * \endparblock
306 : *
307 : * \parblock
308 : * \warning If there has already been a callback registered for the given
309 : * `array_component_id`, then the callback returned by `function` will **not**
310 : * be registered or called.
311 : * \endparblock
312 : */
313 : template <typename GlobalCacheTag, typename Function>
314 1 : bool mutable_cache_item_is_ready(
315 : const Parallel::ArrayComponentId& array_component_id,
316 : const Function& function);
317 :
318 : /// Mutates the non-const object identified by GlobalCacheTag.
319 : /// \requires `GlobalCacheTag` is a tag in `mutable_global_cache_tags`
320 : /// defined by the Metavariables and in Actions.
321 : ///
322 : /// Internally calls `Function::apply()`, where `Function` is a
323 : /// user-defined struct and `Function::apply()` is a user-defined
324 : /// static function that mutates the object. `Function::apply()`
325 : /// takes as its first argument a `gsl::not_null` pointer to the
326 : /// object named by the GlobalCacheTag (or if that object is a
327 : /// `std::unique_ptr<T>`, a `gsl::not_null<T*>`), and takes the contents of
328 : /// `args` as subsequent arguments.
329 : template <typename GlobalCacheTag, typename Function, typename... Args>
330 1 : void mutate(const std::tuple<Args...>& args);
331 :
332 : /// Entry method that computes the size of the local branch of the
333 : /// GlobalCache and sends it to the MemoryMonitor parallel component.
334 : ///
335 : /// \note This can only be called if the MemoryMonitor component is in the
336 : /// `component_list` of the metavariables. Also can't be called in the testing
337 : /// framework. Trying to do either of these will result in an ERROR.
338 1 : void compute_size_for_memory_monitor(const double time);
339 :
340 : /// Entry method that will set the value of the Parallel::Tags::ResourceInfo
341 : /// tag to the value passed in (if the tag exists in the GlobalCache)
342 : ///
343 : /// This is only meant to be called once.
344 1 : void set_resource_info(
345 : const Parallel::ResourceInfo<Metavariables>& resource_info);
346 :
347 : /// Entry method to print the mutable global cache callbacks upon a deadlock
348 : /// to a file.
349 : ///
350 : /// For each mutable cache tag, this prints
351 : ///
352 : /// - Tag name
353 : /// - Node number
354 : /// - Number of callbacks registered
355 : /// - For each callback, the ArrayComponentId and the callback name
356 1 : void print_mutable_cache_callbacks(const std::string& file_name);
357 :
358 : /// Retrieve the resource_info
359 1 : const Parallel::ResourceInfo<Metavariables>& get_resource_info() const {
360 : return resource_info_;
361 : }
362 :
363 : /// Overlay new data onto a subset of the GlobalCache's tags.
364 : ///
365 : /// This is used when reparsing an input file to update option values during
366 : /// a restart from a checkpoint.
367 1 : void overlay_cache_data(const tuples::tagged_tuple_from_typelist<
368 : Parallel::get_overlayable_option_list<Metavariables>>&
369 : data_to_overlay);
370 :
371 : /// Retrieve the proxy to the global cache
372 1 : proxy_type get_this_proxy();
373 :
374 0 : void pup(PUP::er& p) override; // NOLINT
375 :
376 : /// Retrieve the proxy to the Main chare (or std::nullopt if the proxy has not
377 : /// been set, i.e. we are not charm-aware).
378 1 : std::optional<main_proxy_type> get_main_proxy();
379 :
380 : /// @{
381 : /// Wrappers for charm++ informational functions.
382 :
383 : /// Number of processing elements
384 1 : int number_of_procs() const;
385 : /// Number of nodes.
386 1 : int number_of_nodes() const;
387 : /// Number of processing elements on the given node.
388 1 : int procs_on_node(const int node_index) const;
389 : /// %Index of first processing element on the given node.
390 1 : int first_proc_on_node(const int node_index) const;
391 : /// %Index of the node for the given processing element.
392 1 : int node_of(const int proc_index) const;
393 : /// The local index for the given processing element on its node.
394 1 : int local_rank_of(const int proc_index) const;
395 : /// %Index of my processing element.
396 1 : int my_proc() const;
397 : /// %Index of my node.
398 1 : int my_node() const;
399 : /// The local index of my processing element on my node.
400 : /// This is in the interval 0, ..., procs_on_node(my_node()) - 1.
401 1 : int my_local_rank() const;
402 : /// @}
403 :
404 : private:
405 : // clang-tidy: false positive, redundant declaration
406 : template <typename GlobalCacheTag, typename MV>
407 0 : friend auto get(const GlobalCache<MV>& cache) // NOLINT
408 : -> const GlobalCache_detail::type_for_get<GlobalCacheTag, MV>&;
409 :
410 : // clang-tidy: false positive, redundant declaration
411 : template <typename ParallelComponentTag, typename MV>
412 0 : friend auto get_parallel_component( // NOLINT
413 : GlobalCache<MV>& cache)
414 : -> Parallel::proxy_from_parallel_component<
415 : GlobalCache_detail::get_component_if_mocked<
416 : typename MV::component_list, ParallelComponentTag>>&;
417 :
418 : // clang-tidy: false positive, redundant declaration
419 : template <typename ParallelComponentTag, typename MV>
420 0 : friend auto get_parallel_component( // NOLINT
421 : const GlobalCache<MV>& cache)
422 : -> const Parallel::proxy_from_parallel_component<
423 : GlobalCache_detail::get_component_if_mocked<
424 : typename MV::component_list,
425 : ParallelComponentTag>>&; // NOLINT
426 :
427 0 : ConstTagsStorage const_global_cache_{};
428 0 : MutableTagsStorage mutable_global_cache_{};
429 : // Wrap mutable tags in Parallel::MutexTag. The type of MutexTag is a
430 : // pair<mutex, mutex>. The first mutex is for editing the value of the mutable
431 : // tag. The second mutex is for editing the vector of callbacks associated
432 : // with the mutable tag.
433 : tuples::tagged_tuple_from_typelist<
434 : tmpl::transform<MutableTagsStorage, tmpl::bind<MutexTag, tmpl::_1>>>
435 0 : mutexes_{};
436 0 : ParallelComponentTuple parallel_components_{};
437 0 : Parallel::ResourceInfo<Metavariables> resource_info_{};
438 0 : bool parallel_components_have_been_set_{false};
439 0 : bool resource_info_has_been_set_{false};
440 0 : std::optional<main_proxy_type> main_proxy_;
441 : // Defaults for testing framework
442 0 : int my_proc_{0};
443 0 : int my_node_{0};
444 0 : int my_local_rank_{0};
445 0 : std::vector<size_t> procs_per_node_{1};
446 : };
447 :
448 : template <typename Metavariables>
449 : GlobalCache<Metavariables>::GlobalCache(ConstTagsTuple const_global_cache,
450 : MutableTagsTuple mutable_global_cache,
451 : std::vector<size_t> procs_per_node,
452 : const int my_proc, const int my_node,
453 : const int my_local_rank)
454 : : const_global_cache_(std::move(const_global_cache)),
455 : mutable_global_cache_(GlobalCache_detail::make_mutable_cache_tag_storage(
456 : std::move(mutable_global_cache))),
457 : main_proxy_(std::nullopt),
458 : my_proc_(my_proc),
459 : my_node_(my_node),
460 : my_local_rank_(my_local_rank),
461 : procs_per_node_(std::move(procs_per_node)) {}
462 :
463 : template <typename Metavariables>
464 : GlobalCache<Metavariables>::GlobalCache(
465 : ConstTagsTuple const_global_cache, MutableTagsTuple mutable_global_cache,
466 : std::optional<main_proxy_type> main_proxy)
467 : : const_global_cache_(std::move(const_global_cache)),
468 : mutable_global_cache_(GlobalCache_detail::make_mutable_cache_tag_storage(
469 : std::move(mutable_global_cache))),
470 : main_proxy_(std::move(main_proxy)) {}
471 :
472 : template <typename Metavariables>
473 : void GlobalCache<Metavariables>::set_parallel_components(
474 : ParallelComponentTuple&& parallel_components, const CkCallback& callback) {
475 : ASSERT(!parallel_components_have_been_set_,
476 : "Can only set the parallel_components once");
477 : parallel_components_ = std::move(parallel_components);
478 : parallel_components_have_been_set_ = true;
479 : this->contribute(callback);
480 : }
481 :
482 : template <typename Metavariables>
483 : template <typename GlobalCacheTag, typename Function>
484 : bool GlobalCache<Metavariables>::mutable_cache_item_is_ready(
485 : const Parallel::ArrayComponentId& array_component_id,
486 : const Function& function) {
487 : using tag = MutableCacheTag<GlobalCache_detail::get_matching_mutable_tag<
488 : GlobalCacheTag, Metavariables>>;
489 : std::unique_ptr<Callback> optional_callback{};
490 : // Returns true if a callback was returned from `function`. Returns false if
491 : // nullptr was returned
492 : const auto callback_was_registered = [this, &function,
493 : &optional_callback]() -> bool {
494 : // Reads don't need a lock.
495 : if constexpr (tt::is_a_v<std::unique_ptr, typename tag::tag::type>) {
496 : optional_callback =
497 : function(*(std::get<0>(tuples::get<tag>(mutable_global_cache_))));
498 : } else {
499 : optional_callback =
500 : function(std::get<0>(tuples::get<tag>(mutable_global_cache_)));
501 : }
502 :
503 : return optional_callback != nullptr;
504 : };
505 :
506 : if (callback_was_registered()) {
507 : optional_callback->register_with_charm();
508 : // Second mutex is for vector of callbacks
509 : std::mutex& mutex = tuples::get<MutexTag<tag>>(mutexes_).second;
510 : const std::unique_ptr<Callback> clone_of_optional_callback =
511 : optional_callback->get_clone();
512 : {
513 : // Scoped for lock guard
514 : const std::lock_guard<std::mutex> lock(mutex);
515 : std::unordered_map<Parallel::ArrayComponentId,
516 : std::vector<std::unique_ptr<Callback>>>& callbacks =
517 : std::get<1>(tuples::get<tag>(mutable_global_cache_));
518 :
519 : if (callbacks.contains(array_component_id)) {
520 : // If this array component id already exists, we don't want to add
521 : // multiple of the same callback, so we loop over the existing callbacks
522 : // and only if none of the existing callbacks are equal to the optional
523 : // callback do we move the optional callback into the vector
524 : auto& vec_callbacks = callbacks.at(array_component_id);
525 : if (alg::none_of(vec_callbacks,
526 : [&](const std::unique_ptr<Callback>& local_callback) {
527 : return local_callback->is_equal_to(
528 : *optional_callback);
529 : })) {
530 : vec_callbacks.emplace_back(std::move(optional_callback));
531 : }
532 : } else {
533 : // If we don't have this array component id, then we create the vector
534 : // and move the optional callback into the vector
535 : callbacks[array_component_id] =
536 : std::vector<std::unique_ptr<Callback>>(1);
537 : callbacks.at(array_component_id)[0] = std::move(optional_callback);
538 : }
539 : }
540 :
541 : // We must check if the tag is ready again. Consider the following example:
542 : //
543 : // We have two writers, A and B preparing to make independent changes to a
544 : // cache object. We have an element E with a callback waiting for the change
545 : // B is going to make. Suppose the following sequence of events:
546 : //
547 : // 1. A mutates the object, copies the callback list (below in `mutate`),
548 : // and starts the callback for element E.
549 : // 2. E checks the current value and determines it is not ready.
550 : // 3. B mutates the object, copies the empty callback list, and returns with
551 : // nothing to do.
552 : // 4. E returns a new callback, which is added to the callback list.
553 : // 5. A returns.
554 : //
555 : // We now have E waiting on a change that has already happened. This will
556 : // most certainly result in a deadlock if E is blocking the Algorithm. Thus
557 : // we must do another check for whether the cache object is ready or not. In
558 : // the order of events, this check happens sometime after 4. When this check
559 : // happens, E concludes that the cache object *is* ready (because 3 is when
560 : // the object was mutated) and E can continue on.
561 : //
562 : // If this second check reveals that the object *is* ready, then we have an
563 : // unecessary callback in our map, so we remove it.
564 : //
565 : // If this second check reveals that the object *isn't* ready, then we don't
566 : // bother adding another callback to the map because one already exists. No
567 : // need to call a callback twice.
568 : //
569 : // This function returns true if no callback was registered and false if one
570 : // was registered.
571 : const bool cache_item_is_ready = not callback_was_registered();
572 : if (cache_item_is_ready) {
573 : const std::lock_guard<std::mutex> lock(mutex);
574 : std::unordered_map<Parallel::ArrayComponentId,
575 : std::vector<std::unique_ptr<Callback>>>& callbacks =
576 : std::get<1>(tuples::get<tag>(mutable_global_cache_));
577 :
578 : // It's possible that no new callbacks were registered, so make sure this
579 : // array component id still has callbacks before trying to remove them.
580 : if (callbacks.contains(array_component_id)) {
581 : // If this callback was a duplicate, we'll have to search through all
582 : // callbacks to determine which to remove. If it wasn't a duplicate,
583 : // then it'll just be the last callback in the vector.
584 : auto& vec_callbacks = callbacks.at(array_component_id);
585 : std::erase_if(vec_callbacks,
586 : [&clone_of_optional_callback](const auto& t) {
587 : return t->is_equal_to(*clone_of_optional_callback);
588 : });
589 :
590 : if (callbacks.at(array_component_id).empty()) {
591 : callbacks.erase(array_component_id);
592 : }
593 : }
594 : }
595 :
596 : return cache_item_is_ready;
597 : } else {
598 : // The user-defined `function` didn't specify a callback, which
599 : // means that the item is ready.
600 : return true;
601 : }
602 : }
603 :
604 : template <typename Metavariables>
605 : template <typename GlobalCacheTag, typename Function, typename... Args>
606 : void GlobalCache<Metavariables>::mutate(const std::tuple<Args...>& args) {
607 : (void)Parallel::charmxx::RegisterGlobalCacheMutate<
608 : Metavariables, GlobalCacheTag, Function, Args...>::registrar;
609 : using tag = MutableCacheTag<GlobalCache_detail::get_matching_mutable_tag<
610 : GlobalCacheTag, Metavariables>>;
611 :
612 : // Do the mutate.
613 : std::apply(
614 : [this](const auto&... local_args) {
615 : // First mutex is for value of mutable tag
616 : std::mutex& mutex = tuples::get<MutexTag<tag>>(mutexes_).first;
617 : const std::lock_guard<std::mutex> lock(mutex);
618 : Function::apply(make_not_null(&StdHelpers::retrieve(std::get<0>(
619 : tuples::get<tag>(mutable_global_cache_)))),
620 : local_args...);
621 : },
622 : args);
623 :
624 : // A callback might call mutable_cache_item_is_ready, which might add yet
625 : // another callback to the vector of callbacks. We don't want to immediately
626 : // invoke this new callback as it might just add another callback again (and
627 : // again in an infinite loop). And we don't want to remove it from the map of
628 : // callbacks before it is invoked otherwise we could get a deadlock.
629 : // Therefore, after locking it, we std::move the map of callbacks into a
630 : // temporary map, clear the original map, and invoke the callbacks in the
631 : // temporary map.
632 : std::unordered_map<Parallel::ArrayComponentId,
633 : std::vector<std::unique_ptr<Callback>>>
634 : callbacks{};
635 : // Second mutex is for map of callbacks
636 : std::mutex& mutex = tuples::get<MutexTag<tag>>(mutexes_).second;
637 : {
638 : // Scoped for lock guard
639 : const std::lock_guard<std::mutex> lock(mutex);
640 : callbacks = std::move(std::get<1>(tuples::get<tag>(mutable_global_cache_)));
641 : std::get<1>(tuples::get<tag>(mutable_global_cache_)).clear();
642 : }
643 :
644 : // Invoke the callbacks. Any new callbacks that are added to the
645 : // list (if a callback calls mutable_cache_item_is_ready) will be
646 : // saved and will not be invoked here.
647 : for (auto& [array_component_id, vec_callbacks] : callbacks) {
648 : for (auto& callback : vec_callbacks) {
649 : callback->invoke();
650 : }
651 : }
652 : }
653 :
654 : template <typename Metavariables>
655 : void GlobalCache<Metavariables>::overlay_cache_data(
656 : const tuples::tagged_tuple_from_typelist<
657 : Parallel::get_overlayable_option_list<Metavariables>>&
658 : data_to_overlay) {
659 : // Entries in `get_overlayable_tag_list` and `get_overlayable_option_list`
660 : // have a 1-to-1 mapping: one is the option tag for the other. We loop
661 : // over the tag because it's easy to call tag::option_tag but not vice versa.
662 : tmpl::for_each<Parallel::get_overlayable_tag_list<Metavariables>>(
663 : [this, &data_to_overlay](auto tag) {
664 : using Tag = typename decltype(tag)::type;
665 : static_assert(tmpl::size<typename Tag::option_tags>::value == 1,
666 : "The current implementation can only update tags "
667 : "constructed from a single option tag.");
668 : using OptionTag = tmpl::front<typename Tag::option_tags>;
669 : tuples::get<Tag>(const_global_cache_) =
670 : Tag::create_from_options(tuples::get<OptionTag>(data_to_overlay));
671 : });
672 : }
673 :
674 : #if defined(__GNUC__) && !defined(__clang__)
675 : #pragma GCC diagnostic push
676 : #pragma GCC diagnostic ignored "-Wsuggest-attribute=noreturn"
677 : #endif // defined(__GNUC__) && !defined(__clang__)
678 : template <typename Metavariables>
679 : void GlobalCache<Metavariables>::compute_size_for_memory_monitor(
680 : const double time) {
681 : if constexpr (tmpl::list_contains_v<
682 : typename Metavariables::component_list,
683 : mem_monitor::MemoryMonitor<Metavariables>>) {
684 : const double size_in_bytes =
685 : static_cast<double>(size_of_object_in_bytes(*this));
686 : const double size_in_MB = size_in_bytes / 1.0e6;
687 :
688 : auto& mem_monitor_proxy = Parallel::get_parallel_component<
689 : mem_monitor::MemoryMonitor<Metavariables>>(*this);
690 :
691 : const int my_node = Parallel::my_node<int>(*this);
692 :
693 : Parallel::simple_action<
694 : mem_monitor::ContributeMemoryData<GlobalCache<Metavariables>>>(
695 : mem_monitor_proxy, time, my_node, size_in_MB);
696 : } else {
697 : (void)time;
698 : ERROR(
699 : "GlobalCache::compute_size_for_memory_monitor can only be called if "
700 : "the MemoryMonitor is in the component list in the metavariables.\n");
701 : }
702 : }
703 :
704 : template <typename Metavariables>
705 : void GlobalCache<Metavariables>::set_resource_info(
706 : const Parallel::ResourceInfo<Metavariables>& resource_info) {
707 : ASSERT(not resource_info_has_been_set_,
708 : "Can only set the resource info once");
709 : resource_info_ = resource_info;
710 : resource_info_has_been_set_ = true;
711 : }
712 :
713 : template <typename Metavariables>
714 : void GlobalCache<Metavariables>::print_mutable_cache_callbacks(
715 : const std::string& file_name) {
716 : std::stringstream ss{};
717 : ss << std::setprecision(std::numeric_limits<double>::digits10 + 4)
718 : << std::scientific;
719 :
720 : ss << "========== BEGIN CALLBACKS ON NODE " << this->my_node()
721 : << " ==========\n";
722 :
723 : tmpl::for_each<typename MutableTagsStorage::tags_list>(
724 : [this, &ss](auto tag_v) {
725 : using Tag = tmpl::type_from<decltype(tag_v)>;
726 :
727 : const auto& callbacks =
728 : std::get<1>(tuples::get<Tag>(mutable_global_cache_));
729 :
730 : ss << "For tag " << pretty_type::name<typename Tag::tag>()
731 : << ", callbacks ("
732 : << alg::accumulate(callbacks, 0_st,
733 : [](const size_t cur_size, const auto& v) {
734 : return cur_size + v.second.size();
735 : })
736 : << "):\n";
737 :
738 : for (const auto& [array_component_id, vec_callbacks] : callbacks) {
739 : for (const auto& callback : vec_callbacks) {
740 : ss << " ArrayComponentId " << array_component_id << ": "
741 : << callback->name() << "\n";
742 : }
743 : }
744 : });
745 :
746 : ss << "========== END CALLBACKS ON NODE " << this->my_node()
747 : << " ============\n";
748 :
749 : Parallel::fprintf(file_name, "%s\n", ss.str());
750 : }
751 :
752 : #if defined(__GNUC__) && !defined(__clang__)
753 : #pragma GCC diagnostic pop
754 : #endif // defined(__GNUC__) && !defined(__clang__)
755 :
756 : template <typename Metavariables>
757 : typename Parallel::GlobalCache<Metavariables>::proxy_type
758 : GlobalCache<Metavariables>::get_this_proxy() {
759 : if constexpr (is_mocked) {
760 : // The proxy is not used in the testing framework
761 : return Parallel::GlobalCache<Metavariables>::proxy_type{};
762 : } else {
763 : return this->thisProxy;
764 : }
765 : }
766 :
767 : template <typename Metavariables>
768 : std::optional<typename Parallel::GlobalCache<Metavariables>::main_proxy_type>
769 : GlobalCache<Metavariables>::get_main_proxy() {
770 : return main_proxy_;
771 : }
772 :
773 : // For all these functions, if the main proxy is set (meaning we are
774 : // charm-aware) then just call the sys:: functions. Otherwise, use the values
775 : // set for the testing framework (or the defaults).
776 : template <typename Metavariables>
777 : int GlobalCache<Metavariables>::number_of_procs() const {
778 : return main_proxy_.has_value()
779 : ? sys::number_of_procs()
780 : : static_cast<int>(alg::accumulate(procs_per_node_, 0_st));
781 : }
782 :
783 : template <typename Metavariables>
784 : int GlobalCache<Metavariables>::number_of_nodes() const {
785 : return main_proxy_.has_value() ? sys::number_of_nodes()
786 : : static_cast<int>(procs_per_node_.size());
787 : }
788 :
789 : template <typename Metavariables>
790 : int GlobalCache<Metavariables>::procs_on_node(const int node_index) const {
791 : return main_proxy_.has_value()
792 : ? sys::procs_on_node(node_index)
793 : : static_cast<int>(
794 : procs_per_node_[static_cast<size_t>(node_index)]);
795 : }
796 :
797 : template <typename Metavariables>
798 : int GlobalCache<Metavariables>::first_proc_on_node(const int node_index) const {
799 : return main_proxy_.has_value()
800 : ? sys::first_proc_on_node(node_index)
801 : : static_cast<int>(
802 : std::accumulate(procs_per_node_.begin(),
803 : procs_per_node_.begin() + node_index, 0_st));
804 : }
805 :
806 : template <typename Metavariables>
807 : int GlobalCache<Metavariables>::node_of(const int proc_index) const {
808 : if (main_proxy_.has_value()) {
809 : // For some reason gcov doesn't think this line is tested even though it is
810 : // in Test_AlgorithmGlobalCache.cpp
811 : return sys::node_of(proc_index); // LCOV_EXCL_LINE
812 : } else {
813 : size_t procs_so_far = 0;
814 : size_t node = 0;
815 : while (procs_so_far <= static_cast<size_t>(proc_index)) {
816 : procs_so_far += procs_per_node_[node];
817 : ++node;
818 : }
819 : return static_cast<int>(--node);
820 : }
821 : }
822 :
823 : template <typename Metavariables>
824 : int GlobalCache<Metavariables>::local_rank_of(const int proc_index) const {
825 : return main_proxy_.has_value()
826 : ? sys::local_rank_of(proc_index)
827 : : proc_index - first_proc_on_node(node_of(proc_index));
828 : }
829 :
830 : template <typename Metavariables>
831 : int GlobalCache<Metavariables>::my_proc() const {
832 : return main_proxy_.has_value() ? sys::my_proc() : my_proc_;
833 : }
834 :
835 : template <typename Metavariables>
836 : int GlobalCache<Metavariables>::my_node() const {
837 : return main_proxy_.has_value() ? sys::my_node() : my_node_;
838 : }
839 :
840 : template <typename Metavariables>
841 : int GlobalCache<Metavariables>::my_local_rank() const {
842 : return main_proxy_.has_value() ? sys::my_local_rank() : my_local_rank_;
843 : }
844 :
845 : template <typename Metavariables>
846 : void GlobalCache<Metavariables>::pup(PUP::er& p) {
847 : p | const_global_cache_;
848 : p | parallel_components_;
849 : p | mutable_global_cache_;
850 : p | main_proxy_;
851 : p | parallel_components_have_been_set_;
852 : p | resource_info_has_been_set_;
853 : p | my_proc_;
854 : p | my_node_;
855 : p | my_local_rank_;
856 : p | procs_per_node_;
857 : }
858 :
859 : /// @{
860 : /// \ingroup ParallelGroup
861 : /// \brief Access the Charm++ proxy associated with a ParallelComponent
862 : ///
863 : /// \requires ParallelComponentTag is a tag in component_list
864 : ///
865 : /// \returns a Charm++ proxy that can be used to call an entry method on the
866 : /// chare(s)
867 : template <typename ParallelComponentTag, typename Metavariables>
868 1 : auto get_parallel_component(GlobalCache<Metavariables>& cache)
869 : -> Parallel::proxy_from_parallel_component<
870 : GlobalCache_detail::get_component_if_mocked<
871 : typename Metavariables::component_list, ParallelComponentTag>>& {
872 : return tuples::get<tmpl::type_<Parallel::proxy_from_parallel_component<
873 : GlobalCache_detail::get_component_if_mocked<
874 : typename Metavariables::component_list, ParallelComponentTag>>>>(
875 : cache.parallel_components_);
876 : }
877 :
878 : template <typename ParallelComponentTag, typename Metavariables>
879 1 : auto get_parallel_component(const GlobalCache<Metavariables>& cache)
880 : -> const Parallel::proxy_from_parallel_component<
881 : GlobalCache_detail::get_component_if_mocked<
882 : typename Metavariables::component_list, ParallelComponentTag>>& {
883 : return tuples::get<tmpl::type_<Parallel::proxy_from_parallel_component<
884 : GlobalCache_detail::get_component_if_mocked<
885 : typename Metavariables::component_list, ParallelComponentTag>>>>(
886 : cache.parallel_components_);
887 : }
888 : /// @}
889 :
890 : /// @{
891 : /// \ingroup ParallelGroup
892 : /// \brief Access data in the cache
893 : ///
894 : /// \requires GlobalCacheTag is a tag in the `mutable_global_cache_tags`
895 : /// or `const_global_cache_tags` defined by the Metavariables and in Actions.
896 : ///
897 : /// \returns a constant reference to an object in the cache
898 : template <typename GlobalCacheTag, typename Metavariables>
899 1 : auto get(const GlobalCache<Metavariables>& cache)
900 : -> const GlobalCache_detail::type_for_get<GlobalCacheTag, Metavariables>& {
901 : constexpr bool is_mutable =
902 : is_in_mutable_global_cache<Metavariables, GlobalCacheTag>;
903 : // We check if the tag is to be retrieved directly or via a base class
904 : using tmp_tag =
905 : GlobalCache_detail::get_matching_tag<GlobalCacheTag, Metavariables>;
906 : using tag =
907 : tmpl::conditional_t<is_mutable, MutableCacheTag<tmp_tag>, tmp_tag>;
908 : if constexpr (is_mutable) {
909 : // Tag is not in the const tags, so use mutable_global_cache_. No locks here
910 : // because we require all mutable tags to be able to be read at all times
911 : // (even when being written to)
912 : if constexpr (tt::is_a_v<std::unique_ptr, typename tag::tag::type>) {
913 : return *std::get<0>(tuples::get<tag>(cache.mutable_global_cache_));
914 : } else {
915 : return std::get<0>(tuples::get<tag>(cache.mutable_global_cache_));
916 : }
917 : } else {
918 : // Tag is in the const tags, so use const_global_cache_
919 : if constexpr (tt::is_a_v<std::unique_ptr, typename tag::type>) {
920 : return *(tuples::get<tag>(cache.const_global_cache_));
921 : } else {
922 : return tuples::get<tag>(cache.const_global_cache_);
923 : }
924 : }
925 : }
926 :
927 : /// \ingroup ParallelGroup
928 : /// \brief Returns whether the object identified by `GlobalCacheTag`
929 : /// is ready to be accessed by `get`.
930 : ///
931 : /// \requires `GlobalCacheTag` is a tag in `mutable_global_cache_tags`
932 : /// defined by the Metavariables and in Actions.
933 : ///
934 : /// \requires `function` is a user-defined invokable that takes one argument:
935 : /// a const reference to the object referred to by the
936 : /// `GlobalCacheTag`. `function` returns a
937 : /// `std::unique_ptr<CallBack>` that determines the readiness. To
938 : /// indicate that the item is ready, the `std::unique_ptr` returned
939 : /// by `function` must be nullptr; in this case
940 : /// `mutable_cache_item_is_ready` returns true. To indicate that the
941 : /// item is not ready, the `std::unique_ptr` returned by `function`
942 : /// must be valid; in this case, `mutable_cache_item_is_ready`
943 : /// appends the `std::unique_ptr<Callback>` to an
944 : /// internal list of callbacks to be called on `mutate`, and then
945 : /// returns false.
946 : ///
947 : /// \note If `function` is returning a valid callback, it should only return a
948 : /// `Parallel::PerformAlgorithmCallback`. Other types of callbacks are not
949 : /// supported at this time.
950 : template <typename GlobalCacheTag, typename Function, typename Metavariables>
951 1 : bool mutable_cache_item_is_ready(
952 : GlobalCache<Metavariables>& cache,
953 : const Parallel::ArrayComponentId& array_component_id,
954 : const Function& function) {
955 : return cache.template mutable_cache_item_is_ready<GlobalCacheTag>(
956 : array_component_id, function);
957 : }
958 :
959 : /// \ingroup ParallelGroup
960 : ///
961 : /// \brief Mutates non-const data in the cache, by calling `Function::apply()`
962 : ///
963 : /// \requires `GlobalCacheTag` is a tag in tag_list.
964 : /// \requires `Function` is a struct with a static void `apply()`
965 : /// function that mutates the object. `Function::apply()` takes as its
966 : /// first argument a `gsl::not_null` pointer to the object named by
967 : /// the `GlobalCacheTag`, and takes `args` as
968 : /// subsequent arguments.
969 : template <typename GlobalCacheTag, typename Function, typename Metavariables,
970 : typename... Args>
971 1 : void mutate(GlobalCache<Metavariables>& cache, Args&&... args) {
972 : if (cache.get_main_proxy().has_value()) {
973 : if constexpr (not GlobalCache<Metavariables>::is_mocked) {
974 : cache.thisProxy.template mutate<GlobalCacheTag, Function>(
975 : std::make_tuple<Args...>(std::forward<Args>(args)...));
976 : } else {
977 : ERROR(
978 : "Main proxy is set but global cache is being mocked. This is "
979 : "currently not implemented.");
980 : }
981 : } else {
982 : cache.template mutate<GlobalCacheTag, Function>(
983 : std::make_tuple<Args...>(std::forward<Args>(args)...));
984 : }
985 : }
986 :
987 : namespace Tags {
988 : template <class Metavariables>
989 0 : struct GlobalCacheProxy : db::SimpleTag {
990 0 : using type = CProxy_GlobalCache<Metavariables>;
991 : };
992 :
993 : /// \ingroup DataBoxTagsGroup
994 : /// \ingroup ParallelGroup
995 : /// Tag to retrieve the `Parallel::GlobalCache` from the DataBox.
996 : template <class Metavariables>
997 1 : struct GlobalCache : db::SimpleTag {
998 0 : using type = Parallel::GlobalCache<Metavariables>*;
999 : };
1000 :
1001 : template <class Metavariables>
1002 0 : struct GlobalCacheCompute : GlobalCache<Metavariables>, db::ComputeTag {
1003 0 : using base = GlobalCache<Metavariables>;
1004 0 : using argument_tags = tmpl::list<GlobalCacheProxy<Metavariables>>;
1005 0 : using return_type = Parallel::GlobalCache<Metavariables>*;
1006 0 : static void function(
1007 : const gsl::not_null<Parallel::GlobalCache<Metavariables>**>
1008 : local_branch_of_global_cache,
1009 : const CProxy_GlobalCache<Metavariables>& global_cache_proxy) {
1010 : *local_branch_of_global_cache = Parallel::local_branch(global_cache_proxy);
1011 : }
1012 : };
1013 :
1014 : /// \ingroup DataBoxTagsGroup
1015 : /// \ingroup ParallelGroup
1016 : /// Tag used to retrieve data from the `Parallel::GlobalCache`. This is the
1017 : /// recommended way for compute tags to retrieve data out of the global cache.
1018 : template <class CacheTag, class Metavariables>
1019 1 : struct FromGlobalCache : CacheTag, db::ReferenceTag {
1020 : static_assert(db::is_simple_tag_v<CacheTag>);
1021 0 : using base = CacheTag;
1022 0 : using argument_tags = tmpl::list<GlobalCache<Metavariables>>;
1023 :
1024 0 : static const auto& get(
1025 : const Parallel::GlobalCache<Metavariables>* const& cache) {
1026 : return Parallel::get<CacheTag>(*cache);
1027 : }
1028 : };
1029 :
1030 : template <typename Metavariables>
1031 0 : struct ResourceInfoReference : ResourceInfo<Metavariables>, db::ReferenceTag {
1032 0 : using base = ResourceInfo<Metavariables>;
1033 0 : using argument_tags = tmpl::list<GlobalCache<Metavariables>>;
1034 :
1035 0 : static const auto& get(
1036 : const Parallel::GlobalCache<Metavariables>* const& cache) {
1037 : return cache->get_resource_info();
1038 : }
1039 : };
1040 : } // namespace Tags
1041 : } // namespace Parallel
1042 :
1043 : namespace PUP {
1044 : /// \cond
1045 : // Warning! This is an invalid and kludgey pupper, because when unpacking it
1046 : // produces a `nullptr` rather than a valid `GlobalCache*`. This function is
1047 : // provided _only_ to enable putting a `GlobalCache*` in the DataBox.
1048 : //
1049 : // SpECTRE parallel components with a `GlobalCache*` in their DataBox should
1050 : // set this pointer using a compute item that calls `Parallel::local_branch` on
1051 : // a stored proxy. When deserializing the DataBox, these components should call
1052 : // `db:mutate<GlobalCacheProxy>(box)` to force the DataBox to update the
1053 : // pointer from the new Charm++ proxy.
1054 : //
1055 : // We do not currently anticipate needing to (de)serialize a `GlobalCache*`
1056 : // outside of a DataBox. But if this need arises, it will be necessary to
1057 : // provide a non-kludgey pupper here.
1058 : //
1059 : // Correctly (de)serializing the `GlobalCache*` would require obtaining a
1060 : // `CProxy_GlobalCache` item and calling `Parallel::local_branch` on it ---
1061 : // just as in the workflow described above, but within the pupper vs in the
1062 : // DataBox. But this strategy fails when restarting from a checkpoint file,
1063 : // because calling `Parallel::local_branch` may not be well-defined in the
1064 : // unpacking pup call when all Charm++ components may not yet be fully restored.
1065 : // This difficulty is why we instead write an invalid pupper here.
1066 : //
1067 : // In future versions of Charm++, the pup function may know whether it is
1068 : // called in the context of checkpointing, load balancing, etc. This knowledge
1069 : // would enable us to write a valid pupper for non-checkpoint contexts, and
1070 : // return a `nullptr` only when restoring from checkpoint.
1071 : template <typename Metavariables>
1072 : inline void operator|(PUP::er& p, // NOLINT
1073 : Parallel::GlobalCache<Metavariables>*& t) {
1074 : if (p.isUnpacking()) {
1075 : t = nullptr;
1076 : }
1077 : }
1078 : /// \endcond
1079 : } // namespace PUP
1080 :
1081 0 : #define CK_TEMPLATES_ONLY
1082 : #include "Parallel/GlobalCache.def.h"
1083 : #undef CK_TEMPLATES_ONLY
|