| 273 | } |
| 274 | |
| 275 | array::ArrayDesc::~ArrayDesc() { |
| 276 | // When an array description is destroyed it will delete a bunch of arrays |
| 277 | // that may also destroy their corresponding descriptions and so on and so |
| 278 | // forth. |
| 279 | // |
| 280 | // This calls recursively the destructor and can result in stack overflow, we |
| 281 | // instead put them in a vector and destroy them one at a time resulting in a |
| 282 | // max stack depth of 2. |
| 283 | if (inputs.empty()) { |
| 284 | return; |
| 285 | } |
| 286 | |
| 287 | std::vector<std::shared_ptr<ArrayDesc>> for_deletion; |
| 288 | |
| 289 | auto append_deletable_inputs = [&for_deletion](ArrayDesc& ad) { |
| 290 | std::unordered_map<std::uintptr_t, array> input_map; |
| 291 | for (array& a : ad.inputs) { |
| 292 | if (a.array_desc_) { |
| 293 | input_map.insert({a.id(), a}); |
| 294 | for (auto& s : a.siblings()) { |
| 295 | input_map.insert({s.id(), s}); |
| 296 | } |
| 297 | } |
| 298 | } |
| 299 | ad.inputs.clear(); |
| 300 | for (auto& [_, a] : input_map) { |
| 301 | bool is_deletable = |
| 302 | (a.array_desc_.use_count() <= a.siblings().size() + 1); |
| 303 | // An array with siblings is deletable only if all of its siblings |
| 304 | // are deletable |
| 305 | for (auto& s : a.siblings()) { |
| 306 | if (!is_deletable) { |
| 307 | break; |
| 308 | } |
| 309 | int is_input = (input_map.find(s.id()) != input_map.end()); |
| 310 | is_deletable &= |
| 311 | s.array_desc_.use_count() <= a.siblings().size() + is_input; |
| 312 | } |
| 313 | if (is_deletable) { |
| 314 | for_deletion.push_back(std::move(a.array_desc_)); |
| 315 | } |
| 316 | } |
| 317 | }; |
| 318 | |
| 319 | append_deletable_inputs(*this); |
| 320 | |
| 321 | while (!for_deletion.empty()) { |
| 322 | // top is going to be deleted at the end of the block *after* the arrays |
| 323 | // with inputs have been moved into the vector |
| 324 | auto top = std::move(for_deletion.back()); |
| 325 | for_deletion.pop_back(); |
| 326 | append_deletable_inputs(*top); |
| 327 | |
| 328 | // Clear out possible siblings to break circular references |
| 329 | for (auto& s : top->siblings) { |
| 330 | // Set to null here to avoid descending into top-level |
| 331 | // array destructor for siblings |
| 332 | s.array_desc_ = nullptr; |