Skip to content

Fix crashes on saving scores with parts#33618

Open
miiizen wants to merge 1 commit into
musescore:mainfrom
miiizen:33516-partSaveCrash
Open

Fix crashes on saving scores with parts#33618
miiizen wants to merge 1 commit into
musescore:mainfrom
miiizen:33516-partSaveCrash

Conversation

@miiizen
Copy link
Copy Markdown
Contributor

@miiizen miiizen commented May 28, 2026

Resolves: #33516

This PR makes EIDRegister and EID::newUnique thread safe, as during saving we create and register new EIDs.
I have also made sure MasterScore::checkMidiMapping is only called for the master score. There's no need to repeat this for each part.

The crashes were introduced in #32702

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 28, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6c1701a2-c0c3-4545-8015-980f37c32d7b

📥 Commits

Reviewing files that changed from the base of the PR and between ec4ac8c and acc3f9a.

📒 Files selected for processing (6)
  • muse
  • src/engraving/infrastructure/eid.cpp
  • src/engraving/infrastructure/eidregister.cpp
  • src/engraving/infrastructure/eidregister.h
  • src/engraving/rw/write/writer.cpp
  • src/engraving/rw/write/writer.h
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/engraving/infrastructure/eidregister.cpp
  • muse
  • src/engraving/rw/write/writer.cpp
  • src/engraving/infrastructure/eidregister.h
  • src/engraving/rw/write/writer.h

📝 Walkthrough

Walkthrough

This PR advances the muse submodule and makes several concurrency-related changes: EID::newUnique() uses thread-local RNG and distribution; EIDRegister adds a mutable std::shared_mutex and takes a std::lock_guard in registerItemEID; Writer replaces GlobalInject with GlobalThreadSafeInject for IApplication and runs masterScore()->checkMidiMapping() only when score->isMaster().

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main objective of the PR: fixing crashes that occur when saving scores with parts.
Description check ✅ Passed The description follows the template structure, includes the issue link, explains the changes clearly, and references the regression PR. All key sections are present and complete.
Linked Issues check ✅ Passed The PR addresses the root cause of issue #33516 by making EIDRegister and EID::newUnique thread-safe to handle concurrent access during saving, and prevents unnecessary duplicate MIDI mapping checks for parts.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing thread-safety issues during saving and optimizing MIDI mapping checks; the muse submodule update may be an incidental dependency change required for thread-safe injection.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/engraving/infrastructure/eidregister.cpp`:
- Line 43: The methods accessing the shared maps are inconsistently
synchronized: add appropriate locking around all accesses to m_itemToEid and
m_eidToItem—use an exclusive lock (std::lock_guard or std::unique_lock on
m_mutex) at the start of removeItem (since it modifies both maps) and use shared
locks (std::shared_lock on m_mutex) at the start of itemFromEID and EIDFromItem
(since they only read); ensure the locks are acquired before any map access in
the functions registerItemEID, removeItem, itemFromEID, and EIDFromItem to
prevent data races.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 858bb581-22f9-43bc-aca7-4cb8a773a10c

📥 Commits

Reviewing files that changed from the base of the PR and between 37b61ae and ec4ac8c.

📒 Files selected for processing (6)
  • muse
  • src/engraving/infrastructure/eid.cpp
  • src/engraving/infrastructure/eidregister.cpp
  • src/engraving/infrastructure/eidregister.h
  • src/engraving/rw/write/writer.cpp
  • src/engraving/rw/write/writer.h

IF_ASSERT_FAILED(eid.isValid() && item) {
return;
}
std::lock_guard lock(m_mutex);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Incomplete synchronization: other methods also need locks.

While registerItemEID now locks m_mutex, the other methods (removeItem, itemFromEID, EIDFromItem) that access the shared maps remain unsynchronized. Concurrent access from multiple threads can still cause data races and corruption.

  • removeItem (lines 60-78) modifies both maps and needs an exclusive lock (std::lock_guard or std::unique_lock)
  • itemFromEID (lines 80-87) reads m_eidToItem and needs a shared lock (std::shared_lock)
  • EIDFromItem (lines 89-93) reads m_itemToEid and needs a shared lock (std::shared_lock)
🔒 Proposed fix to add synchronization to all methods

Add to removeItem:

 void EIDRegister::removeItem(const EngravingObject* item)
 {
+    std::lock_guard lock(m_mutex);
     // NOTE: needed only when elements are removed during read (e.g. broken spanners)

Add to itemFromEID:

 EngravingObject* EIDRegister::itemFromEID(const EID& eid) const
 {
+    std::shared_lock lock(m_mutex);
     auto iter = m_eidToItem.find(eid);

Add to EIDFromItem:

 EID EIDRegister::EIDFromItem(const EngravingObject* item) const
 {
+    std::shared_lock lock(m_mutex);
     auto iter = m_itemToEid.find(const_cast<EngravingObject*>(item));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/engraving/infrastructure/eidregister.cpp` at line 43, The methods
accessing the shared maps are inconsistently synchronized: add appropriate
locking around all accesses to m_itemToEid and m_eidToItem—use an exclusive lock
(std::lock_guard or std::unique_lock on m_mutex) at the start of removeItem
(since it modifies both maps) and use shared locks (std::shared_lock on m_mutex)
at the start of itemFromEID and EIDFromItem (since they only read); ensure the
locks are acquired before any map access in the functions registerItemEID,
removeItem, itemFromEID, and EIDFromItem to prevent data races.

@miiizen miiizen force-pushed the 33516-partSaveCrash branch from ec4ac8c to acc3f9a Compare May 28, 2026 15:42
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 28, 2026

Actionable comments posted: 0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Crash saving project with parts

2 participants