Blender V4.3
BLI_atomic_disjoint_set.hh
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#pragma once
6
7#include <atomic>
8
9#include "BLI_array.hh"
10
11namespace blender {
12
23 private:
24 /* Can generally used relaxed memory order with this algorithm. */
25 static constexpr auto relaxed = std::memory_order_relaxed;
26
27 struct Item {
28 int parent;
29 int rank;
30 };
31
35 mutable Array<std::atomic<Item>> items_;
36
37 public:
41 AtomicDisjointSet(const int size);
42
47 void join(int x, int y)
48 {
49 while (true) {
50 x = this->find_root(x);
51 y = this->find_root(y);
52
53 if (x == y) {
54 /* They are in the same set already. */
55 return;
56 }
57
58 int x_rank = items_[x].load(relaxed).rank;
59 int y_rank = items_[y].load(relaxed).rank;
60
61 if (
62 /* Implement union by rank heuristic. */
63 x_rank > y_rank
64 /* If the rank is the same, make a consistent decision. */
65 || (x_rank == y_rank && x < y))
66 {
67 std::swap(x_rank, y_rank);
68 std::swap(x, y);
69 }
70
71 /* Update parent of item x. */
72 Item x_item_old = {x, x_rank};
73 const Item x_item_new{y, x_rank};
74 if (!items_[x].compare_exchange_strong(x_item_old, x_item_new, relaxed)) {
75 /* Another thread has updated item x, start again. */
76 continue;
77 }
78
79 if (x_rank == y_rank) {
80 /* Increase rank of item y. This may fail when another thread has updated item y in the
81 * meantime. That may lead to worse behavior with the union by rank heurist, but seems to
82 * be ok in practice. */
83 Item y_item_old{y, y_rank};
84 const Item y_item_new{y, y_rank + 1};
85 items_[y].compare_exchange_weak(y_item_old, y_item_new, relaxed);
86 }
87
88 return;
89 }
90 }
91
95 bool in_same_set(int x, int y) const
96 {
97 while (true) {
98 x = this->find_root(x);
99 y = this->find_root(y);
100 if (x == y) {
101 return true;
102 }
103 if (items_[x].load(relaxed).parent == x) {
104 return false;
105 }
106 }
107 }
108
112 int find_root(int x) const
113 {
114 while (true) {
115 const Item item = items_[x].load(relaxed);
116 if (x == item.parent) {
117 return x;
118 }
119 const int new_parent = items_[item.parent].load(relaxed).parent;
120 if (item.parent != new_parent) {
121 /* This halves the path for faster future lookups. That fail but that does not change
122 * correctness. */
123 Item expected = item;
124 const Item desired{new_parent, item.rank};
125 items_[x].compare_exchange_weak(expected, desired, relaxed);
126 }
127 x = new_parent;
128 }
129 }
130
134 bool is_root(const int x) const
135 {
136 const Item item = items_[x].load(relaxed);
137 return item.parent == x;
138 }
139
146 int calc_reduced_ids(MutableSpan<int> result) const;
147
151 int count_sets() const;
152};
153
154} // namespace blender
int calc_reduced_ids(MutableSpan< int > result) const
bool in_same_set(int x, int y) const