Skip to content

Commit 9f8a6d4

Browse files
committed
fixed csg tree cache concurrency bug (#356)
1 parent f6ff8c3 commit 9f8a6d4

File tree

3 files changed

+44
-32
lines changed

3 files changed

+44
-32
lines changed

src/manifold/src/csg_tree.cpp

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -223,17 +223,19 @@ CsgOpNode::CsgOpNode() {}
223223

224224
CsgOpNode::CsgOpNode(const std::vector<std::shared_ptr<CsgNode>> &children,
225225
OpType op)
226-
: impl_(std::make_shared<Impl>()) {
227-
impl_->children_ = children;
226+
: impl_(Impl{}) {
227+
auto impl = impl_.GetGuard();
228+
impl->children_ = children;
228229
SetOp(op);
229230
// opportunistically flatten the tree without costly evaluation
230231
GetChildren(false);
231232
}
232233

233234
CsgOpNode::CsgOpNode(std::vector<std::shared_ptr<CsgNode>> &&children,
234235
OpType op)
235-
: impl_(std::make_shared<Impl>()) {
236-
impl_->children_ = children;
236+
: impl_(Impl{}) {
237+
auto impl = impl_.GetGuard();
238+
impl->children_ = children;
237239
SetOp(op);
238240
// opportunistically flatten the tree without costly evaluation
239241
GetChildren(false);
@@ -243,27 +245,30 @@ std::shared_ptr<CsgNode> CsgOpNode::Transform(const glm::mat4x3 &m) const {
243245
auto node = std::make_shared<CsgOpNode>();
244246
node->impl_ = impl_;
245247
node->transform_ = m * glm::mat4(transform_);
248+
node->op_ = op_;
246249
return node;
247250
}
248251

249252
std::shared_ptr<CsgLeafNode> CsgOpNode::ToLeafNode() const {
250253
if (cache_ != nullptr) return cache_;
251-
if (impl_->children_.empty()) return nullptr;
252254
// turn the children into leaf nodes
253255
GetChildren();
254-
auto &children_ = impl_->children_;
256+
auto impl = impl_.GetGuard();
257+
auto &children_ = impl->children_;
255258
if (children_.size() > 1) {
256-
switch (impl_->op_) {
259+
switch (op_) {
257260
case CsgNodeType::Union:
258261
BatchUnion();
259262
break;
260263
case CsgNodeType::Intersection: {
261264
std::vector<std::shared_ptr<const Manifold::Impl>> impls;
262265
for (auto &child : children_) {
263-
impls.push_back(std::dynamic_pointer_cast<CsgLeafNode>(child)->GetImpl());
266+
impls.push_back(
267+
std::dynamic_pointer_cast<CsgLeafNode>(child)->GetImpl());
264268
}
265269
children_.clear();
266-
children_.push_back(std::make_shared<CsgLeafNode>(BatchBoolean(OpType::Intersect, impls)));
270+
children_.push_back(std::make_shared<CsgLeafNode>(
271+
BatchBoolean(OpType::Intersect, impls)));
267272
break;
268273
};
269274
case CsgNodeType::Difference: {
@@ -283,6 +288,8 @@ std::shared_ptr<CsgLeafNode> CsgOpNode::ToLeafNode() const {
283288
// unreachable
284289
break;
285290
}
291+
} else if (children_.size() == 0) {
292+
return nullptr;
286293
}
287294
// children_ must contain only one CsgLeafNode now, and its Transform will
288295
// give CsgLeafNode as well
@@ -345,7 +352,8 @@ void CsgOpNode::BatchUnion() const {
345352
// If the number of children exceeded this limit, we will operate on chunks
346353
// with size kMaxUnionSize.
347354
constexpr int kMaxUnionSize = 1000;
348-
auto &children_ = impl_->children_;
355+
auto impl = impl_.GetGuard();
356+
auto &children_ = impl->children_;
349357
while (children_.size() > 1) {
350358
const int start = (children_.size() > kMaxUnionSize)
351359
? (children_.size() - kMaxUnionSize)
@@ -392,7 +400,8 @@ void CsgOpNode::BatchUnion() const {
392400
}
393401
}
394402
children_.erase(children_.begin() + start, children_.end());
395-
children_.push_back(std::make_shared<CsgLeafNode>(BatchBoolean(OpType::Add, impls)));
403+
children_.push_back(
404+
std::make_shared<CsgLeafNode>(BatchBoolean(OpType::Add, impls)));
396405
// move it to the front as we process from the back, and the newly added
397406
// child should be quite complicated
398407
std::swap(children_.front(), children_.back());
@@ -408,18 +417,18 @@ void CsgOpNode::BatchUnion() const {
408417
*/
409418
std::vector<std::shared_ptr<CsgNode>> &CsgOpNode::GetChildren(
410419
bool finalize) const {
411-
auto &children_ = impl_->children_;
412-
if (children_.empty() || (impl_->simplified_ && !finalize) ||
413-
impl_->flattened_)
420+
auto impl = impl_.GetGuard();
421+
auto &children_ = impl->children_;
422+
if (children_.empty() || (impl->simplified_ && !finalize) || impl->flattened_)
414423
return children_;
415-
impl_->simplified_ = true;
416-
impl_->flattened_ = finalize;
424+
impl->simplified_ = true;
425+
impl->flattened_ = finalize;
417426
std::vector<std::shared_ptr<CsgNode>> newChildren;
418427

419-
CsgNodeType op = impl_->op_;
428+
CsgNodeType op = op_;
420429
for (auto &child : children_) {
421430
if (child->GetNodeType() == op && child.use_count() == 1 &&
422-
std::dynamic_pointer_cast<CsgOpNode>(child)->impl_.use_count() == 1) {
431+
std::dynamic_pointer_cast<CsgOpNode>(child)->impl_.UseCount() == 1) {
423432
auto grandchildren =
424433
std::dynamic_pointer_cast<CsgOpNode>(child)->GetChildren(finalize);
425434
int start = children_.size();
@@ -444,13 +453,13 @@ std::vector<std::shared_ptr<CsgNode>> &CsgOpNode::GetChildren(
444453
void CsgOpNode::SetOp(OpType op) {
445454
switch (op) {
446455
case OpType::Add:
447-
impl_->op_ = CsgNodeType::Union;
456+
op_ = CsgNodeType::Union;
448457
break;
449458
case OpType::Subtract:
450-
impl_->op_ = CsgNodeType::Difference;
459+
op_ = CsgNodeType::Difference;
451460
break;
452461
case OpType::Intersect:
453-
impl_->op_ = CsgNodeType::Intersection;
462+
op_ = CsgNodeType::Intersection;
454463
break;
455464
}
456465
}

src/manifold/src/csg_tree.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,18 +72,18 @@ class CsgOpNode final : public CsgNode {
7272

7373
std::shared_ptr<CsgLeafNode> ToLeafNode() const override;
7474

75-
CsgNodeType GetNodeType() const override { return impl_->op_; }
75+
CsgNodeType GetNodeType() const override { return op_; }
7676

7777
glm::mat4x3 GetTransform() const override;
7878

7979
private:
8080
struct Impl {
81-
CsgNodeType op_;
82-
mutable std::vector<std::shared_ptr<CsgNode>> children_;
83-
mutable bool simplified_ = false;
84-
mutable bool flattened_ = false;
81+
std::vector<std::shared_ptr<CsgNode>> children_;
82+
bool simplified_ = false;
83+
bool flattened_ = false;
8584
};
86-
std::shared_ptr<Impl> impl_ = nullptr;
85+
mutable ConcurrentSharedPtr<Impl> impl_ = ConcurrentSharedPtr<Impl>(Impl{});
86+
CsgNodeType op_;
8787
glm::mat4x3 transform_ = glm::mat4x3(1.0f);
8888
// the following fields are for lazy evaluation, so they are mutable
8989
mutable std::shared_ptr<CsgLeafNode> cache_ = nullptr;

src/utilities/include/utils.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -194,11 +194,12 @@ class strided_range {
194194
template <typename T>
195195
class ConcurrentSharedPtr {
196196
public:
197-
ConcurrentSharedPtr(T value) : impl(std::make_shared<T>(value)), mutex(std::make_shared<std::mutex>()) {}
198-
ConcurrentSharedPtr(const ConcurrentSharedPtr<T>& other) : impl(other.impl), mutex(other.mutex) {}
197+
ConcurrentSharedPtr(T value) : impl(std::make_shared<T>(value)) {}
198+
ConcurrentSharedPtr(const ConcurrentSharedPtr<T>& other)
199+
: impl(other.impl), mutex(other.mutex) {}
199200
class SharedPtrGuard {
200201
public:
201-
SharedPtrGuard(std::mutex* mutex, T* content)
202+
SharedPtrGuard(std::recursive_mutex* mutex, T* content)
202203
: mutex(mutex), content(content) {
203204
mutex->lock();
204205
}
@@ -208,14 +209,16 @@ class ConcurrentSharedPtr {
208209
T* operator->() { return content; }
209210

210211
private:
211-
std::mutex* mutex;
212+
std::recursive_mutex* mutex;
212213
T* content;
213214
};
214215
SharedPtrGuard GetGuard() { return SharedPtrGuard(mutex.get(), impl.get()); };
216+
unsigned int UseCount() { return impl.use_count(); };
215217

216218
private:
217219
std::shared_ptr<T> impl;
218-
std::shared_ptr<std::mutex> mutex;
220+
std::shared_ptr<std::recursive_mutex> mutex =
221+
std::make_shared<std::recursive_mutex>();
219222
};
220223
/** @} */
221224
} // namespace manifold

0 commit comments

Comments
 (0)