Transfer iterator result ownership

Generated with the assistance of an AI coding tool.
This commit is contained in:
Thomas Krijnen
2026-08-09 04:54:51 +02:00
parent be3c2ee770
commit dcfc22e29e
5 changed files with 77 additions and 18 deletions
-4
View File
@@ -79,7 +79,6 @@ namespace ifcopenshell::geom {
std::vector<std::shared_ptr<const ifcopenshell::geom::element>> _parent_storage; std::vector<std::shared_ptr<const ifcopenshell::geom::element>> _parent_storage;
friend class iterator; friend class iterator;
virtual std::unique_ptr<element> clone() const { return std::make_unique<element>(*this); }
void set_parents(std::vector<std::unique_ptr<ifcopenshell::geom::element>>&& newparents) { void set_parents(std::vector<std::unique_ptr<ifcopenshell::geom::element>>&& newparents) {
_parents.clear(); _parents.clear();
_parent_storage.clear(); _parent_storage.clear();
@@ -177,7 +176,6 @@ namespace ifcopenshell::geom {
native_element(const native_element& other) = default; native_element(const native_element& other) = default;
private: private:
native_element& operator=(const native_element& other); native_element& operator=(const native_element& other);
std::unique_ptr<element> clone() const override { return std::make_unique<native_element>(*this); }
}; };
class triangulation_element : public element { class triangulation_element : public element {
@@ -197,7 +195,6 @@ namespace ifcopenshell::geom {
triangulation_element(const triangulation_element& other) = default; triangulation_element(const triangulation_element& other) = default;
private: private:
triangulation_element& operator=(const triangulation_element& other); triangulation_element& operator=(const triangulation_element& other);
std::unique_ptr<element> clone() const override { return std::make_unique<triangulation_element>(*this); }
}; };
class serialized_element : public element { class serialized_element : public element {
@@ -212,7 +209,6 @@ namespace ifcopenshell::geom {
serialized_element(const serialized_element& other) = default; serialized_element(const serialized_element& other) = default;
private: private:
serialized_element& operator=(const serialized_element& other); serialized_element& operator=(const serialized_element& other);
std::unique_ptr<element> clone() const override { return std::make_unique<serialized_element>(*this); }
}; };
} }
+33 -9
View File
@@ -509,10 +509,17 @@ express::base ifcopenshell::geom::iterator::next() {
using std::chrono::high_resolution_clock; using std::chrono::high_resolution_clock;
validate_iterator_state(); validate_iterator_state();
if (*native_task_result_iterator_ != *task_result_iterator_) { {
delete* native_task_result_iterator_; std::lock_guard<std::mutex> lock(element_ready_mutex_);
auto* element = *task_result_iterator_;
auto* native_element = *native_task_result_iterator_;
*task_result_iterator_ = nullptr;
*native_task_result_iterator_ = nullptr;
if (native_element != element) {
delete native_element;
}
delete element;
} }
delete* task_result_iterator_;
if (num_threads_ != 1) { if (num_threads_ != 1) {
if (!wait_for_element()) { if (!wait_for_element()) {
@@ -523,6 +530,7 @@ express::base ifcopenshell::geom::iterator::next() {
return express::base{}; return express::base{};
} }
std::lock_guard<std::mutex> lock(element_ready_mutex_);
task_result_iterator_++; task_result_iterator_++;
native_task_result_iterator_++; native_task_result_iterator_++;
@@ -540,6 +548,7 @@ express::base ifcopenshell::geom::iterator::next() {
} }
} }
std::lock_guard<std::mutex> lock(element_ready_mutex_);
task_result_iterator_++; task_result_iterator_++;
native_task_result_iterator_++; native_task_result_iterator_++;
@@ -552,7 +561,18 @@ std::unique_ptr<ifcopenshell::geom::element> ifcopenshell::geom::iterator::get()
{ {
validate_iterator_state(); validate_iterator_state();
auto ret = *task_result_iterator_; std::unique_ptr<ifcopenshell::geom::element> ret;
{
std::lock_guard<std::mutex> lock(element_ready_mutex_);
ret.reset(*task_result_iterator_);
if (!ret) {
throw std::runtime_error("current element has already been retrieved");
}
*task_result_iterator_ = nullptr;
if (*native_task_result_iterator_ == ret.get()) {
*native_task_result_iterator_ = nullptr;
}
}
// If we want to organize the element considering their hierarchy // If we want to organize the element considering their hierarchy
if (settings_.get<ifcopenshell::geom::settings::UseElementHierarchy>().get()) { if (settings_.get<ifcopenshell::geom::settings::UseElementHierarchy>().get()) {
@@ -603,7 +623,7 @@ std::unique_ptr<ifcopenshell::geom::element> ifcopenshell::geom::iterator::get()
} }
} }
return ret->clone(); return ret;
} }
std::unique_ptr<ifcopenshell::geom::element> ifcopenshell::geom::iterator::get_object(int id) { std::unique_ptr<ifcopenshell::geom::element> ifcopenshell::geom::iterator::get_object(int id) {
@@ -842,11 +862,15 @@ ifcopenshell::geom::iterator::~iterator() {
} }
if (task_result_ptr_initialized) { if (task_result_ptr_initialized) {
while (task_result_iterator_ != --all_processed_elements_.end()) { std::lock_guard<std::mutex> lock(element_ready_mutex_);
if (*native_task_result_iterator_ != *task_result_iterator_) { while (task_result_iterator_ != all_processed_elements_.end()) {
delete* native_task_result_iterator_; auto* element = *task_result_iterator_;
auto* native_element = *native_task_result_iterator_;
if (native_element != element) {
delete native_element;
} }
delete* task_result_iterator_++; delete element;
task_result_iterator_++;
native_task_result_iterator_++; native_task_result_iterator_++;
} }
} }
+13 -4
View File
@@ -44,7 +44,7 @@
* at least a single representation will process successfully * * at least a single representation will process successfully *
* * * *
* ifcopenshell::geom::iterator::get() * * ifcopenshell::geom::iterator::get() *
* returns an owned copy of the current ifcopenshell::geom::element * * transfers ownership of the current ifcopenshell::geom::element *
* * * *
* ifcopenshell::geom::iterator::next() * * ifcopenshell::geom::iterator::next() *
* returns true iff a following entity is available for a successive call to * * returns true iff a following entity is available for a successive call to *
@@ -115,8 +115,8 @@ namespace ifcopenshell::geom {
std::list<ifcopenshell::geom::element*> all_processed_elements_; std::list<ifcopenshell::geom::element*> all_processed_elements_;
std::list<ifcopenshell::geom::native_element*> all_processed_native_elements_; std::list<ifcopenshell::geom::native_element*> all_processed_native_elements_;
std::list<ifcopenshell::geom::element*>::const_iterator task_result_iterator_; std::list<ifcopenshell::geom::element*>::iterator task_result_iterator_;
std::list<ifcopenshell::geom::native_element*>::const_iterator native_task_result_iterator_; std::list<ifcopenshell::geom::native_element*>::iterator native_task_result_iterator_;
std::mutex element_ready_mutex_; std::mutex element_ready_mutex_;
bool task_result_ptr_initialized = false; bool task_result_ptr_initialized = false;
@@ -297,7 +297,16 @@ namespace ifcopenshell::geom {
std::unique_ptr<native_element> get_native() std::unique_ptr<native_element> get_native()
{ {
validate_iterator_state(); validate_iterator_state();
return std::make_unique<native_element>(**native_task_result_iterator_); std::lock_guard<std::mutex> lock(element_ready_mutex_);
auto* result = *native_task_result_iterator_;
if (!result) {
throw std::runtime_error("current native element has already been retrieved");
}
*native_task_result_iterator_ = nullptr;
if (*task_result_iterator_ == result) {
*task_result_iterator_ = nullptr;
}
return std::unique_ptr<native_element>(result);
} }
std::unique_ptr<element> get_object(int id); std::unique_ptr<element> get_object(int id);
@@ -203,6 +203,36 @@ def test_iterator():
assert iterator.initialize() assert iterator.initialize()
@pytest.mark.parametrize("num_threads", [1, 2])
def test_iterator_get_transfers_ownership(num_threads):
settings = ifcopenshell.geom.settings()
iterator = ifcopenshell.geom.iterator(settings, fn, num_threads)
assert iterator.initialize()
element = iterator.get()
element_id = element.id
with pytest.raises(RuntimeError, match="already been retrieved"):
iterator.get()
iterator.next()
assert element.id == element_id
@pytest.mark.parametrize("num_threads", [1, 2])
def test_iterator_get_native_transfers_ownership(num_threads):
settings = ifcopenshell.geom.settings()
iterator = ifcopenshell.geom.iterator(settings, fn, num_threads)
assert iterator.initialize()
element = iterator.get_native()
element_id = element.id
with pytest.raises(RuntimeError, match="already been retrieved"):
iterator.get_native()
iterator.next()
assert element.id == element_id
def test_logging(): def test_logging():
assert ifcopenshell.logger assert ifcopenshell.logger
logger = ifcopenshell.logger() logger = ifcopenshell.logger()
+1 -1
View File
@@ -68,7 +68,7 @@
} }
// Use RTTI to return the most specialized element proxy. The ownership flag is // Use RTTI to return the most specialized element proxy. The ownership flag is
// supplied by the wrapped function, so iterator copies can transfer ownership // supplied by the wrapped function, so iterator results can transfer ownership
// while borrowed serializer results remain non-owning. // while borrowed serializer results remain non-owning.
%typemap(out) ifcopenshell::geom::element* { %typemap(out) ifcopenshell::geom::element* {
ifcopenshell::geom::serialized_element* serialized_elem = dynamic_cast<ifcopenshell::geom::serialized_element*>($1); ifcopenshell::geom::serialized_element* serialized_elem = dynamic_cast<ifcopenshell::geom::serialized_element*>($1);