-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
library BrowseFeature: add threading for IO #3623
base: main
Are you sure you want to change the base?
Conversation
Not strictly related, but there's a performance regression with the whole |
Thank you for picking this up.
Between which versions? Is it related to https://bugs.launchpad.net/mixxx/+bug/1905124 |
i'm testing based off this patch would actually make the app more usable the scenario mentioned in 1905124, but the actual fix should be somewhere else. this patch just makes it sure that we never block UI because of IO calls/delays. |
i'm guessing based on the CI error, that you dont want to explicitly require |
Not all supported platforms provide the same level of features and compatibility. We are not excluding anything explicitly. |
By the way, can you please sign https://docs.google.com/a/mixxx.org/spreadsheet/viewform?formkey=dEpYN2NkVEFnWWQzbkFfM0ZYYUZ5X2c6MQ |
gotcha! then i'm just gonna be leaving the old code. although i guess i could check with macros if std::filesystem is available and use that and fall back to current code, but that would at a lot of noise to the code and there's literally no reason (although haven't checked whether there are any performance differences, but dont expect too much) to replace the currently working solution. |
This is why we have CI. :) mixxxdj/website#205 |
🐐 it's GPLv2, no? :) dont get me wrong i dont have anything against signing things, i'm just not really sure why it's necessary :) |
We don't currently distribute Mixxx via the Mac App Store and have not for years, but we ask people to sign the CLA anyway in case so we could in the future and we have contact information for everyone in case we want to make any other changes to the license. |
ok after spending way too much time in order to fix all the requested changes (using signals/slots, adding a mutex), the whole thing blew up. namely spin ball and blocked UI is back. i'm really not sure what's happening, but i have a strong feeling that this whole TreeItemModel (especially the FolderItemModel) is flawed by design. as none of the UI action items should ever block basically rendering a TreeView. i'm gonna leave this patch here as it at least works and makes the current main usable. it's up to you guys what to do with it... |
namely the problem is that |
@uklotzde just for the reference based on your and this suggestion patching the following:
where scanDir is connected to or you had something else in mind? |
@vigsterkr have you tried forcing a queued connection ? |
I think you should create a real background thread that does the io querying and emitting the results. I did something similar in: |
this is a queued connection, or? in case yes, then i've tried it... |
yeah i'm guessing that having an internal queue that handles the requests of reads should be there... note this is becoming even more shitty coz then the list passed around needs to be copied (so far passing around references). but that still does not solve the main issue |
as a quick has i've tried moving every IO logic into a separate worker thread that works on a queue to process all the IO operations and then fill out the model. same brokenness with the UI: spinball on mac, and UI blocks... i really reckon that even though the IO should really move into a background thread, there is something wrong with the View/Model design as now the only thing that can cause this behaviour is when in the worker thread i call any ideas is appreciated.... note even though this current patch is apparently not following Qt's standards are actually working as expected compared to the solution that Qt suggests (signals and slots and all that event loop magic) |
If the current design of |
apparently indirection of indirection should solve this problem: http://blog.hostilefork.com/qt-model-view-different-threads/ |
so, i'm hoping that this is the Qt standard solution for the problem i was trying to fix. unfortunately this solution results in an as bad as experience that this PR was intended to solve. i'm really not sure why is the UI thread gets blocked when a i have 2 guesses but would be super interested to hear about anybody else's opinion:
|
e265836
to
de8f99d
Compare
de8f99d
to
c602db4
Compare
&FolderTreeModel::newChildren, | ||
this, | ||
&FolderTreeModel::addChildren, | ||
Qt::QueuedConnection); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Explicit queued connections should only be used if there is a reason, i.e. to prevent recursive signal/slot cascades within the same thread. Qt implicitly uses queued connections between threads.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
imo this is a good enough reason: https://stackoverflow.com/a/1145628
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The answer is wrong. This connection is implicitly queued.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cool man then just propose a change...? 🙃
i'm sorry man this is a bit ridicolous how you are handling an open source project's reviewing... could be more welcoming... this is not really encouraging new contributors to the project.... just my 2 cents...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and note this is in retrospect with our whole conversations in this PR...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @vigsterkr, Sorry you had a bad experience in this pull request. Mixxx is a very large, very complex, very old codebase, and the developers here have a lot of experience with it. There is also a lot to do on this project and it's not always possible to give full explanations or help new contributors work through every change. We do appreciate your input, as shown by the large amount of communication on this pull request.
I would encourage you to stick with contributing, with a smaller change that focuses on just one small fix rather than a whole group of changes. Then you can get a better feel for how Mixxx is put together and how we do pull requests.
999fc8e
to
847dfa1
Compare
@JoergAtGithub took waaaaaaaaaaaaaaaaay longer than expected, but finally done. |
e1538e2
to
46d2fbb
Compare
DO NOT MERGE as this fully blocks the thread UI upon insertion of children
the thread should die
Qt 5.14.0 already defines itself std::hash<QString>
resolve issue with const signal
this way one still needs to refresh the subtree manually...
46d2fbb
to
47ada1f
Compare
What happened with this PR? I can see there is still a few |
As far as I can see Viktor didn't received any feedback to his last commits and stopped working on this PR. @vigsterkr are you still interested to finish this PR and get a detailed code review therefore?. |
patiently waiting for the review still
|
Great, could you respond to the TODO and FIXME question by Antoine above. Do you think this needs to be done before merging this PR? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for picking this up. The review slipped through because @uklotzde has left the project, sorry. Let's continue.
"your library. Would you like to rescan now?")); | ||
QPushButton* scanButton = msgBox.addButton( | ||
tr("Scan"), QMessageBox::AcceptRole); | ||
msgBox.setText( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR is hard to review because all the minor code style changes. In general, mass reformating is not permitted in Mixxx. Please make sure you use pre-commit, which will take care of formating the touched lines only. Configure your editor to not reformat untouched files.
There is no need to remove the changes here though.
auto title = m_childModel.data(index, Qt::DisplayRole); | ||
m_childModel.setData(index, QString("loading")); | ||
m_childModel.triggerRepaint(index); | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the status here?
&FolderTreeModel::newChildren, | ||
this, | ||
&FolderTreeModel::addChildren, | ||
Qt::QueuedConnection); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Strange. I have no Idea how these issue are related.
The default Qt::AutoConnection
should do the right thing here. Hover explicit Qt::QueuedConnection
adds a bit of documentation. Normally it would be better to use Auto, but if it is a workaround of a Qt issue please add a comment.
In addition you may add DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
into FolderTreeModel::addChildren
if (!folders->isEmpty()) { | ||
sync.waitForFinished(); | ||
m_cacheLock.unlock(); | ||
emit newChildren(parent, folders); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is not clear from the name if it is an imperative slot or a signal. Can you rename it to something more signal like?
For example newChildFoldersFound()
or such?
connect(this, | ||
&FolderTreeModel::newChildren, | ||
this, | ||
&FolderTreeModel::addChildren, | ||
Qt::QueuedConnection); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The connect called form the "Thread (pooled)"! This means that the new object is has probably not the main thread affinity as desired. Please assert your assumptions using DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this);
It can be adjusted by moveToThread(thread);
connect(this, | ||
&FolderTreeModel::hasSubDirectory, | ||
this, | ||
&FolderTreeModel::onHasSubDirectory); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this become a direct call?
if (m_directoryCache.count(str)) { | ||
if (!QFileInfo::exists(str)) { | ||
m_cacheLock.lockForWrite(); | ||
// TODO: when m_directoryCache is a prefix tree |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO?
QThreadPool m_pool; | ||
mutable FolderQueue m_folderQueue; | ||
mutable std::mutex m_queueLock; | ||
std::atomic<bool> m_isRunning; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks more like a query variable for something else. How about invert it and call it m_stopFolderThread
mutable FolderQueue m_folderQueue; | ||
mutable std::mutex m_queueLock; | ||
std::atomic<bool> m_isRunning; | ||
QFuture<void> m_folderProcess; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
QFuture handles threads, not processes. How about m_folderFuture
or m_folderThread
m_cacheLock.unlock(); | ||
} | ||
} | ||
QThread::msleep(100); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Busy looking at the m_folderQueue
is wasting CPU. Can we replace it with a single Future that is started for every folder added? This was we would get rid of the m_folderQueue altogether. This would also solve the issue with the signal from the Future. It would be just replaced with a QFuture::result()
Removing |
It's never a good idea to call IO in a UI thread as anything can go wrong, see for example this bug.
some other details of this patch:
std::filesystem
std::unordered_map
instead ofQHash
inFolderTreeModel
for performance reasonsTasks that still needs to be done:
handle the returned Future ofWONTFIXQtConcurrent::run
and update the UI based on a timer if loading takes more time than n secondsFolderTreeModel::m_directoryCache
QFileSystemWatcher
logic for monitoring FS changes.std::hash<QString>
when needed (avoid redefinition error)