Skip to content

Fix data race in AbstractInvoker #23806

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

cbjeukendrup
Copy link
Member

Includes #23805

This data race was reproducible in Address Sanitizer builds, when you opened and closed the Mixer extremely often and quickly during playback. It is also reported by Thread Sanitizer.

This fix has the risk of introducing deadlocks or performance issues. Before we merge this, we should convince ourselves more that this solution is indeed desirable. I am in contact with Igor to discuss the details, and when we reach a conclusion I will probably post that here.

Like with #23805, it is questionable whether this fix is worth it, even regardless of its correctness and efficiency. The data race that it solves was quite difficult to reproduce (that is, reproduce in such way that the effects were noticeable in the form of an ASan error, and not even a real crash).

("very quickly" as in "keeping the keyboard shortcut pressed so that it fires repeatedly")

Or when quitting app during playback with mixer open
found using Thread Sanitizer
the `m_callbacks` member was not protected at all.
@cbjeukendrup
Copy link
Member Author

I managed to create a deadlock with this solution, when closing a score. I thought it was funny enough to share:

  1. NotationViewInputController is deleted when closing the score
  2. It calls its Asyncable::disconnectAll
  3. That calls AbstractInvoker::disconnectAsync, which locks the new mutex
  4. deleteCall is called, which deletes the callback for the notation->interaction()->selectionChanged() handler registered in NotationViewInputController::onNotationChanged()
  5. This callback had captured a copy of the INotationPtr, but that is released now; and apparently, it was the last one who had the INotationPtr, so the INotation is destructed
  6. This causes the NotationMidiInput to be destructed
  7. That causes the NotationInteraction to be released
  8. That causes the NotationInteraction's m_selectionChanged channel to be deleted
  9. That results in that Channel's ChannelInvoker being released
  10. In the ChannelInvoker destructor, AbstractInvoker::removeAllCallBacks() is called
  11. That also tries to lock the mutex, but it was already locked in step 3

I think we can fix it by doing some big restructurings around doRemoveCallback.

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.

1 participant