Wildmeshing Toolkit
Loading...
Searching...
No Matches
LocalizedRetry.hpp
1#pragma once
2
3#include <wmtk/ExecutionScheduler.hpp>
4#include <wmtk/threading/collector.hpp>
5
6#include <cstdint>
7#include <utility>
8#include <vector>
9
10namespace wmtk {
11
45template <class Mesh>
46size_t run_localized_to_convergence(
47 Mesh& m,
48 ExecutePass<Mesh>& executor,
49 std::vector<std::pair<Op, typename Mesh::Tuple>> ops,
50 size_t max_passes = 0)
51{
52 using Tuple = typename Mesh::Tuple;
53
54 // vertex_epoch[v] = the last round in which v was in some successful operation's
55 // modified region. Capacity is fixed within a phase (storage is preallocated), and
56 // any vertex created during the phase (splits) has an id below this capacity.
57 std::vector<uint64_t> vertex_epoch(m.vert_capacity(), 0);
58 uint64_t round = 0;
59 threading::collector<std::pair<Op, Tuple>> failures;
60
61 auto edge_epoch = [&vertex_epoch](const Mesh& m_, const Tuple& t) -> uint64_t {
62 const size_t a = t.vid(m_);
63 const size_t b = t.switch_vertex(m_).vid(m_);
64 const uint64_t ea = a < vertex_epoch.size() ? vertex_epoch[a] : 0;
65 const uint64_t eb = b < vertex_epoch.size() ? vertex_epoch[b] : 0;
66 return std::max(ea, eb);
67 };
68
69 // Wrap the driver-provided renewal: keep its behavior (re-enqueue affected tuples
70 // within the pass) and additionally stamp those tuples' vertices with the current
71 // round so the between-pass filter can find the failures adjacent to them.
72 auto driver_renew = executor.renew_neighbor_tuples;
73 executor.renew_neighbor_tuples =
74 [&, driver_renew](const Mesh& m_, Op op, const std::vector<Tuple>& newts) {
75 auto tups = driver_renew(m_, op, newts);
76 for (const auto& [_, t] : tups) {
77 const size_t a = t.vid(m_);
78 const size_t b = t.switch_vertex(m_).vid(m_);
79 if (a < vertex_epoch.size()) {
80 // this is thread-safe because each vertex is only ever modified by one
81 // operation at a time (the two-ring lock)
82 vertex_epoch[a] = round;
83 }
84 if (b < vertex_epoch.size()) {
85 vertex_epoch[b] = round;
86 }
87 }
88 return tups;
89 };
90 executor.on_fail = [&failures](const Mesh&, Op op, const Tuple& t) {
91 failures.emplace_back(op, t);
92 };
93
94 size_t total_success = 0;
95 do {
96 ++round;
97 failures.clear();
98 executor(m, ops);
99 total_success += static_cast<size_t>(executor.get_cnt_success());
100 ops.clear();
101 for (const auto& pr : failures) {
102 const Tuple& t = pr.second;
103 if (!t.is_valid(m)) {
104 continue;
105 }
106 // retry only if this failure's neighborhood was modified during this round
107 if (edge_epoch(m, t) == round) {
108 ops.emplace_back(pr);
109 }
110 }
111 } while (executor.get_cnt_success() > 0 && !ops.empty() &&
112 (max_passes == 0 || round < max_passes));
113 return total_success;
114}
115
116} // namespace wmtk