fix(DlgPrefController): add missing layout #14606
Conversation
| &ControllerManager::slotApplyMapping); | ||
|
|
||
| // Update GUI | ||
| connect(m_pControllerManager.get(), | ||
| &ControllerManager::mappingApplied, | ||
| this, | ||
| &DlgPrefController::enableWizardAndIOTabs); | ||
|
|
||
| connect(m_pControllerManager.get(), | ||
| &ControllerManager::mappingApplied, | ||
| this, | ||
| [this](bool) { | ||
| // shortcut for creating and assigning required I/O table models | ||
| showMapping(m_pMapping); | ||
| }); |
There was a problem hiding this comment.
I suspect this fix is incomplete, but I'm not sure what the Qt::BlockingQueuedConnection was intending to fix. @daschuer do you remember?
There was a problem hiding this comment.
Yes, I know. Probably the comment was not clear enough. We have two copies of the mapping, one is the shared copy in the preferences, the user can edit. The other is the immutable copy that is used in the controllers thread.
We must prevent that the mapping is edited in the main thread while copying it. Else we have a corrupted copy that may lead to a crash.
The blocking connection stops the main thread that the mapping cannot he edited.
I think a solution her is to introduce two calls. One copying the mapping and a second one that interacts with the GUI thread after we have the immutable copy.
There was a problem hiding this comment.
Do you think my fix is sufficient then? IIUC, this change will load and bind the GUI component once the controller has acknowledge the newly copied mapping. Or do it it further changes?
There was a problem hiding this comment.
Without the blocking connection this:
Is called from the controller thread while the GUI thread has still write access to the mapping.
A workaround would be to restore the blocking call and do the controller GUI initialization in a second slot without a lock.
The copying is done in the controller thread, because the controller thread is this way also like locked implicit.
An alternative would be to clone the mapping twice.
First create a copy in the GUI thread. Than we have a temporary immutable mapping. Than copy it again in the controller thread by swapping pointers.
I like to avoid locking in the controller thread, because the mapping loockups happen on a high rate.
There was a problem hiding this comment.
A workaround would be to restore the blocking call and do the controller GUI initialization in a second slot without a lock.
This is not possible. Here is sequence diagram of the event loop
sequenceDiagram
GUI->>Controller: applyMapping
activate Controller
critical setup resources in main thread
Controller-->>GUI: setup resources in main thread
end
This means that there is no way we can keep applyMapping a blocking call and keep this working. Please bare in mind that the lock in controller is out of our remit (see doc)
I like to avoid locking in the controller thread, because the mapping loockups happen on a high rate.
I'm not sure to understand what this has to do here? The deadlock doesn't happen at loockup but atapplyMapping(a.k.a controller initailisation)
Basically, we use applyMapping in 3 occurrences in the preference screens:
- MIDI mapping (which seems to justify you adding that blocking connection)
- Mapping selection (which will deadlock if loading a screen mapping
- Default mapping selection (which will deadlock if loading a screen mapping
There was a problem hiding this comment.
The lockinh needs to be installed whenever the mapping is accessed, because of of these calls can be happen and it causes a crash.
A workaround would be to restore the blocking call and do the controller GUI initialization in a second slot without a lock.
This is not possible. Here is sequence diagram of the event loop
For my understanding the workflow can remain. We just need to take care that the mapping is not altered during setting up the resources in main. That's why I think that one blocking call for the mutable stuff and one other call for the rest will work.
Whenever the user changes a preferences option that affecte the mapping, we need to repeat the trick. Editing the mapping in the GUI thread an apply in the blocking call.
There was a problem hiding this comment.
Could we add a mutex to slotApplyMapping then? Since the deadlock comes from blocking the whole thread's event loop, restricting the critical section should fix this issue
|
Could you please retarget the fix for the deadlock introduced by #14159 to 2.5. |
a234657 to
8e6f75c
Compare
|
Done - #14006 to be addressed separately |
8e6f75c to
a234657
Compare
|
Actually, I reverted the PR for 2.6. The deadlock only impact that branch as this is related to QML screen controller (see comment here), so we could release 2.5.1 without this change. WDYT? |
|
Ok, makes sense! |
a234657 to
57d4abc
Compare
|
Please resolve the merge conflicts! |
57d4abc to
28761d7
Compare
You can see a backtrace here |
|
Okay, so let's sum up where we have:
This means we cannot using Here are the solutions I can thing about:
From this, it feels like the "least worse" option in order would be:
wdyt? |
|
We already have 3. for editing the mappings itself. So a natural solution would be to extend this for the controller preferences as well.
Can you explain that a bit more? Which API will be changed? I think we need to separate two things. Applying the preferences to the JS Code and on the fly changes of values. Do we have the later? For my understanding we always apply all settings at once. |
Can you clarify this? I understand that there is some space where we clone the mapping, but these are localised usecase. Are we sure this won't have any impact otherwise?
Sure, my fear is that current we rely on a If you are 100% sure it won't, I'm happy to go down this road.
Oh yeah, for now it's the latter, tho there was discussions to bring the former as an option in the future (and a split would likely cripple this feature opportunity) |
We had an issue with writing a used copy of the mapping which was fixed here: I have tried to explain how it works in the commit description. Is this understandable of do you have questions?
This is used for ownership tracking only. I think this is the significant part: mixxx/src/controllers/bulk/bulkcontroller.cpp Lines 100 to 103 in 6adb012 The controller receives from the settings a shared pointer which tracks that it is not deleted when one thread outlives the other. In addition we do a deep copy into
I don't have a crystal ball to be that. :-/
Like in the mapping itself the values are still mutable only the control structures are not. So runtime changes can be still applied via atomic variables inside the otherwise immutable version of the controller preferences. Like control objects. (or reuse them). The crasher here does is not cased by access to preferences values it is cased because the whole preferences structure has changed behind the back. |
|
I tried to implement the cloning for the last 2h, but couldn't get anything to work that wouldn't be a major refactor. Would you want to push what you had in mind maybe? |
|
Can you point me to you experimental branch which not works? Why does cloning not work? |
|
Unfortunately, I didn't commit everything and discarded all the changes (twice actually, after hitting round blocks) I guess I could keep the |
|
I am following this PR with interest would like to understand these issues better and try to help work out a solution. Would you two be open to doing a call to look at the code in depth? Concurrency is one of the things Mixxx struggles with and we want to ensure we don't create technical debt or bad bugs. |
|
Good shout, I'm up for it. Will create a thread on Zulip! |
|
Superseded by #15524 |
|
Actually, there is 2ed412f that still need merging! |
28761d7 to
2f3c49b
Compare
Fix asserts introduced in #14006 and deadlock introduced in #14159