| 543 | } |
| 544 | |
| 545 | void SessionManager::registerObjectTopicParser(ObjectTopicId id, std::unique_ptr<MessageParserHandle> parser) { |
| 546 | const bool incoming_valid = parser != nullptr && parser->valid(); |
| 547 | // The old slot is moved out under the lock and destroyed AFTER it releases: |
| 548 | // its dtor runs MessageParserHandle teardown (potentially dlclose), which must |
| 549 | // never run while holding object_parsers_mutex_. |
| 550 | ObjectParserSlot replaced_slot; |
| 551 | { |
| 552 | std::unique_lock lock(object_parsers_mutex_); |
| 553 | if (!incoming_valid) { |
| 554 | // Do not silently drop a previously valid registration just because the |
| 555 | // caller handed us an invalid replacement — that produced "topic suddenly |
| 556 | // can't be decoded anymore" with no diagnostic. Keep the prior parser and |
| 557 | // warn loudly so the operator can see something is wrong with the binding. |
| 558 | const auto existing = object_topic_parsers_.find(id.id); |
| 559 | if (existing != object_topic_parsers_.end() && existing->second.handle != nullptr && |
| 560 | existing->second.handle->valid()) { |
| 561 | qCWarning(lcSession) << "registerObjectTopicParser: ignoring invalid replacement for topic" << id.id |
| 562 | << "(previous valid parser preserved)"; |
| 563 | return; |
| 564 | } |
| 565 | qCWarning(lcSession) << "registerObjectTopicParser: invalid parser for topic" << id.id |
| 566 | << "— erasing registration"; |
| 567 | if (existing != object_topic_parsers_.end()) { |
| 568 | replaced_slot = std::move(existing->second); |
| 569 | object_topic_parsers_.erase(existing); |
| 570 | } |
| 571 | return; |
| 572 | } |
| 573 | // Fresh mutex per registration: a re-registration swaps in a new parser, but |
| 574 | // existing consumers still guard the old one. Reusing the lock would let the |
| 575 | // new caller race with leftover work on the old parser pointer. |
| 576 | ObjectParserSlot fresh{std::shared_ptr<MessageParserHandle>(std::move(parser)), std::make_shared<std::mutex>()}; |
| 577 | auto& slot = object_topic_parsers_[id.id]; |
| 578 | replaced_slot = std::move(slot); // keep the old handle off the lock-held dtor path |
| 579 | slot = std::move(fresh); |
| 580 | } |
| 581 | // replaced_slot destructs here, after the lock is released. |
| 582 | } |
| 583 | |
| 584 | const SessionManager::ObjectParserSlot* SessionManager::findValidParserSlotLocked(ObjectTopicId id) const { |
| 585 | auto it = object_topic_parsers_.find(id.id); |