Skip to content
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

Revamp synchronization with the audio engine #6881

Merged
merged 5 commits into from
Nov 18, 2023

Conversation

sakertooth
Copy link
Contributor

@sakertooth sakertooth commented Sep 21, 2023

Revamps synchronization of the audio engine. I'm not too sure why the current implementation of requestChangesInModel/doneChangeInModel did not make the master audio thread wait as expected, but I presume it had something to do with the boolean variables being used, making certain calls to requestChangesInModel only apply once. All in all, this PR intends to:

  • Actually make the other threads wait when changes are running.
  • Bring code that is easier to follow.
  • Not cause data races within the synchronization mechanism itself (TSan reported 0 data races involving these functions with these changes applied).
  • Bring use of STL's std::mutex and std::condition_variable std::shared_mutex over equivalent Qt types.

The revamp consists of one lock. Both calls to requestChangeInModel and renderNextBuffer will try and acquire the lock. Waiting on the lock on the thread requesting the change is either because there is already another change that has locked the mutex, or the audio thread has locked it for rendering. Waiting on the lock on the audio thread is because a change is currently running.

It is also important to mention that we can also be passing in certain changes to be executed on the master audio thread without using locks, under the condition that we do not create Qt objects (or call most Qt functions) on those threads, as such operations should be done on the main thread. Because of this, maybe it will be beneficial to add this functionality alongside requestChangesInModel/doneChangesInModel for non-Qt related operations that need to synchronize with the master audio thread in the future.

Copy link
Member

@DomClark DomClark left a comment

Choose a reason for hiding this comment

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

Bring use of STL's std::mutex and std::condition_variable over equivalent Qt types.

I like this change - chipping away at the Qt usage in the core is always good.

Not cause data races within the synchronization mechanism itself (TSan reported 0 data races involving these functions with these changes applied).

Also very much a good goal. The two data races that stand out in the old synchronisation mechanism to me (by reading, that is, not by a tool) are the read of m_changesSignal in AudioEngine::runChangesInModel and the two writes to m_isProcessing, all of which are unguarded by any mutex. I suspect in general that ThreadSanitizer doesn't understand Qt's synchronisation objects, and as such reports a number of false positives, which is another reason to move to standard C++ types for this purpose.

src/core/AudioEngine.cpp Outdated Show resolved Hide resolved
src/core/AudioEngine.cpp Outdated Show resolved Hide resolved
src/core/AudioEngine.cpp Show resolved Hide resolved
src/core/AudioEngine.cpp Outdated Show resolved Hide resolved
@sakertooth
Copy link
Contributor Author

I really have to find a simpler design for this mechanism. It either seems overly complicated, or I'm just not that good at mentally keeping track of what we have to worry about when synchronizing with other threads concurrently.

@sakertooth sakertooth marked this pull request as draft October 9, 2023 06:21
@sakertooth sakertooth marked this pull request as ready for review October 10, 2023 03:32
@sakertooth
Copy link
Contributor Author

Okay, I believe I have something a lot cleaner and more robust. @DomClark, if you don't mind taking another look, that would be great.

@sakertooth
Copy link
Contributor Author

Changed this a bit. I just went with the most obvious and simple design using just two locks. The design is so simple and robust I'm looking to merge this soon (unless there is some odd reason why we can't lock at the beginning of the rendering period).

@sakertooth sakertooth force-pushed the revamp-changes-in-model branch from adfb473 to 330548c Compare October 25, 2023 22:18
@JohannesLorenz JohannesLorenz self-requested a review October 26, 2023 06:52
@JohannesLorenz
Copy link
Contributor

Give me a few days to review this one, please.

Copy link
Contributor

@JohannesLorenz JohannesLorenz left a comment

Choose a reason for hiding this comment

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

Code-Review is OK, only editorials.

Note: A few things should be tested, such as:

  • Comparing CPU load to master with a "heavy" song
  • Exporting to wave
  • Doing things while playback is active: Moving knobs, adding an effect, saving a file etc

include/AudioEngine.h Outdated Show resolved Hide resolved
src/core/AudioEngine.cpp Show resolved Hide resolved
src/core/AudioEngine.cpp Show resolved Hide resolved
@sakertooth
Copy link
Contributor Author

Let me know if you still plan to review this a second time @DomClark.

It makes more sense for s_runningChange to be true for a thread only
if it had already acquired the lock, and same can be said for when
it should be false after it releases the lock.
@sakertooth
Copy link
Contributor Author

Note: A few things should be tested, such as:
Comparing CPU load to master with a "heavy" song
Exporting to wave
Doing things while playback is active: Moving knobs, adding an effect, saving a file etc

CPU before and after playback in Greippi - Krem Kaakkuja demo for this PR:
image
image

Now for master:
image
image

Of course, the CPU did jump around a bit each time (0%-3% when I sampled it for values) for both cases, so the screenshots above should be taken with a grain of salt as there were fluctuations. In any case however, there does seem to be somewhat of an improvement.

Exporting to wave seems fine. No problem there really. I had a crash when exporting, but also got crashes for the same demo on master, so it doesn't seem like a problem with this PR specifically.

Moving/updating knobs, faders, etc, all work well.

Will merge today if no one objects.

@sakertooth sakertooth merged commit 7268827 into LMMS:master Nov 18, 2023
@sakertooth sakertooth deleted the revamp-changes-in-model branch November 18, 2023 20:28
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.

3 participants