Blender V5.0
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
8
9#pragma once
10
11#include <atomic>
12
13#include "BLI_array.hh"
14
15namespace blender {
16
27 private:
28 /* Can generally used relaxed memory order with this algorithm. */
29 static constexpr auto relaxed = std::memory_order_relaxed;
30
31 struct Item {
32 int parent;
33 int rank;
34 };
35
39 mutable Array<std::atomic<Item>> items_;
40
41 public:
45 AtomicDisjointSet(const int size);
46
51 void join(int x, int y)
52 {
53 while (true) {
54 x = this->find_root(x);
55 y = this->find_root(y);
56
57 if (x == y) {
58 /* They are in the same set already. */
59 return;
60 }
61
62 int x_rank = items_[x].load(relaxed).rank;
63 int y_rank = items_[y].load(relaxed).rank;
64
65 if (
66 /* Implement union by rank heuristic. */
67 x_rank > y_rank
68 /* If the rank is the same, make a consistent decision. */
69 || (x_rank == y_rank && x < y))
70 {
71 std::swap(x_rank, y_rank);
72 std::swap(x, y);
73 }
74
75 /* Update parent of item x. */
76 Item x_item_old = {x, x_rank};
77 const Item x_item_new{y, x_rank};
78 if (!items_[x].compare_exchange_strong(x_item_old, x_item_new, relaxed)) {
79 /* Another thread has updated item x, start again. */
80 continue;
81 }
82
83 if (x_rank == y_rank) {
84 /* Increase rank of item y. This may fail when another thread has updated item y in the
85 * meantime. That may lead to worse behavior with the union by rank heurist, but seems to
86 * be ok in practice. */
87 Item y_item_old{y, y_rank};
88 const Item y_item_new{y, y_rank + 1};
89 items_[y].compare_exchange_weak(y_item_old, y_item_new, relaxed);
90 }
91
92 return;
93 }
94 }
95
99 bool in_same_set(int x, int y) const
100 {
101 while (true) {
102 x = this->find_root(x);
103 y = this->find_root(y);
104 if (x == y) {
105 return true;
106 }
107 if (items_[x].load(relaxed).parent == x) {
108 return false;
109 }
110 }
111 }
112
116 int find_root(int x) const
117 {
118 while (true) {
119 const Item item = items_[x].load(relaxed);
120 if (x == item.parent) {
121 return x;
122 }
123 const int new_parent = items_[item.parent].load(relaxed).parent;
124 if (item.parent != new_parent) {
125 /* This halves the path for faster future lookups. That fail but that does not change
126 * correctness. */
127 Item expected = item;
128 const Item desired{new_parent, item.rank};
129 items_[x].compare_exchange_weak(expected, desired, relaxed);
130 }
131 x = new_parent;
132 }
133 }
134
138 bool is_root(const int x) const
139 {
140 const Item item = items_[x].load(relaxed);
141 return item.parent == x;
142 }
143
151
155 int count_sets() const;
156};
157
158} // namespace blender
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
int calc_reduced_ids(MutableSpan< int > result) const
bool in_same_set(int x, int y) const