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

Improve creational correctness involving tracks and clips #7594

Open
wants to merge 20 commits into
base: master
Choose a base branch
from

Conversation

sakertooth
Copy link
Contributor

@sakertooth sakertooth commented Nov 23, 2024

Currently, we add tracks and clips prematurely to there respective containers, i.e., they are added within the base class. This has seemed to have caused a number of problems involving calls to pure virtual functions (because the object wasn't fully created yet), to dynamic_cast failures and ultimately crashes. This PR fixes this by only adding a track or clip to their respective containers after the object has been fully constructed.

There are also fixes that address potential issues that can happen when creating the corresponding view for a track or clip. What happens is that currently, we use Qt::QueuedConnection when signaling that the view should be made after the object was created. However, this gives a deadly grace period where in between those two events, the object could be deleted before Qt returns to the event loop and creates the view. To fix this, we use Qt::DirectConnection instead, which eliminates this grace period. Removing tracks and clips still must use Qt::QueuedConnection (which they currently do) or QObject::deleteLater since Qt could be using the view during that time.

In addition, I added some minor refactoring improvements like loosening the coupling between specific track types and the base track class by moving the type information outside Track and making Track::type pure virtual. This improvement could not be moved into another PR because callers currently call Track::type before the track is fully created.

Also moved specific pattern clip/pattern store functionality into Qt signals within PatternStore.

The change to moving to smart pointers is involved and will pollute this PR. I'm moving the improvement of ownership semantics to another PR after this one is merged. This one is focusing on making sure objects are fully initialized before adding them to their parents.

To simplify the Track constructor and loosen the coupling between specific Track types and the base Track class
If there is contention, where one thread wants to do something with the track and the other is trying to destroy it, we are betting that the destructor will win over the lock last, which wont always happen. The real fix in this instance is to ensure that when the destructor is called, the object is no longer in use by anything else.

By removing the lock, this isn't undoing a problem. The problem was always still there. The lock will only help 50% of the time (i.e., in the scenario mentioned above), with the other 50% crashing the program. The chances get slimmer as the contention increases, but this doesn't change the fact that this is still error-prone.
We can keep things simple and use the signal already provided by Qt when destroying QObject's.
Same with destroyedTrack, we can use the destroyed signal that Qt provides us to make things simpler.
No need for a function call.
QueuedConnections were used previously. Because this kind of connection only invokes the slot when Qt returns to the event loop, this could be a problem if the track or clip was deleted in between the time when the signal that a track or clip was added was emitted, and when the slot to add the corresponding track or clip view was invoked.

An alternative solution that could be even better overall is to use weak pointers in the view classes and manage the objects using shared pointers. This is a more involved task, however.
Since we moved the adding of tracks and clips out of the constructors, we have to call the functions to add them ourselves within AutomationTrackTest
include/AutomationTrack.h Outdated Show resolved Hide resolved
Copy link
Contributor

@Rossmaxx Rossmaxx left a comment

Choose a reason for hiding this comment

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

A few thoughts. Another doubt I have is that, does this fix that crash with deleting a track while playing?

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

A few thoughts. Another doubt I have is that, does this fix that crash with deleting a track while playing?

I believe crashes like those happen because of synchronization issues involving mutexes (which I believe should be fixed, albeit not ideally). What I am addressing here with this PR is the problem where we are adding clips to tracks and tracks to track containers (like the song editor) way too early. If I created a SampleTrack object for example, it would add itself to the song editor when its constructor hasn't completed yet. This has caused a number of bugs when processing the Song.

@sakertooth
Copy link
Contributor Author

sakertooth commented Nov 23, 2024

A few thoughts. Another doubt I have is that, does this fix that crash with deleting a track while playing?

Wow, I discovered something. When tracks are deleted, only the base destructor is called (Track::~Track) and not the derived one (maybe something like SampleTrack::~SampleTrack for example) because the destructor isn't virtual. Destructors like SampleTrack::~SampleTrack remove all play handles associated with that track, but it may not be ran when deleting it at all. I'm just realizing how many issues need to be fixed here.

Wait, nevermind, whatever Track inherits have virtual destructors already. I do remember that we covered these cases with a clang-tidy PR awhile back actually.

@sakertooth sakertooth changed the title Improve the creational correctness of tracks and clips Improve ownership semantics involving tracks and clips Nov 23, 2024
@sakertooth sakertooth changed the title Improve ownership semantics involving tracks and clips Improve creational correctness involving tracks and clips Nov 23, 2024
…om it

This behavior of removing the child from its parent in the child's destructor can be removed once we start using smart pointers.
@sakertooth sakertooth added needs code review A functional code review is currently required for this PR needs testing This pull request needs more testing labels Nov 23, 2024
@sakertooth sakertooth removed the needs code review A functional code review is currently required for this PR label Nov 23, 2024
@@ -98,35 +88,34 @@ Track::~Track()
* \param tt The type of track to create
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe you should change these 2 parameters here too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs testing This pull request needs more testing
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants