Fix: In order to ensure the "correct" ordering, *all* threads need to wait for the previous threads to finish - not only the threads that will emit a file. If both, thread n and n+1, want to emit a file and thus n+1 has to wait for n, we additionally must allow thread n+2 to terminate before n and n+1, even if n+2 does *not* emit a file. That's because as soon as n+2 finishes, it unblocks all threads x with x < n+2, which includes n and n+1. If that happens and n+1 is still waiting for n, we effectively allow n+1 to emit its file *before* n. By blocking all threads x with x > n+1 until n+1 has finished (regardless of whether they want to emit a file or not) this problem is resolved. As long as we assume that most threads actually *do* emit a file, this commit shouldn't hurt the performance.
This commit is contained in:
parent
68d2a28c7b
commit
193488e26f
@ -43,7 +43,7 @@
|
|||||||
WorkingBanner::WorkingBanner(QWidget *parent)
|
WorkingBanner::WorkingBanner(QWidget *parent)
|
||||||
:
|
:
|
||||||
QDialog(parent, Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint),
|
QDialog(parent, Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint),
|
||||||
m_progressMax(0), m_progressVal(0), m_metrics(NULL)
|
m_progressMax(0), m_progressVal(0), m_progressInt(0), m_metrics(NULL)
|
||||||
{
|
{
|
||||||
//Init the dialog, from the .ui file
|
//Init the dialog, from the .ui file
|
||||||
setupUi(this);
|
setupUi(this);
|
||||||
@ -226,25 +226,41 @@ void WorkingBanner::setText(const QString &text)
|
|||||||
}
|
}
|
||||||
labelStatus->setText(choppedText);
|
labelStatus->setText(choppedText);
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
if(this->isVisible())
|
if(this->isVisible())
|
||||||
{
|
{
|
||||||
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
labelStatus->repaint();
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorkingBanner::setProgressMax(unsigned int max)
|
void WorkingBanner::setProgressMax(unsigned int max)
|
||||||
{
|
{
|
||||||
m_progressMax = max;
|
m_progressMax = max;
|
||||||
|
updateProgress();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorkingBanner::setProgressVal(unsigned int val)
|
void WorkingBanner::setProgressVal(unsigned int val)
|
||||||
{
|
{
|
||||||
m_progressVal = val;
|
m_progressVal = val;
|
||||||
|
updateProgress();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
// Private
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void WorkingBanner::updateProgress(void)
|
||||||
|
{
|
||||||
if(m_progressMax > 0)
|
if(m_progressMax > 0)
|
||||||
{
|
{
|
||||||
int progress = qRound(qBound(0.0, static_cast<double>(m_progressVal) / static_cast<double>(m_progressMax), 1.0) * 100.0);
|
int newProgress = qRound(qBound(0.0, static_cast<double>(m_progressVal) / static_cast<double>(m_progressMax), 1.0) * 100.0);
|
||||||
m_progress->setText(QString::number(progress));
|
if(m_progressInt != newProgress)
|
||||||
|
{
|
||||||
|
m_progressInt = newProgress;
|
||||||
|
m_progress->setText(QString::number(m_progressInt));
|
||||||
|
if(this->isVisible())
|
||||||
|
{
|
||||||
|
labelStatus->repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
QMovie *m_working;
|
QMovie *m_working;
|
||||||
bool m_canClose;
|
bool m_canClose;
|
||||||
|
void updateProgress(void);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setText(const QString &text);
|
void setText(const QString &text);
|
||||||
@ -62,4 +63,5 @@ protected:
|
|||||||
QFontMetrics *m_metrics;
|
QFontMetrics *m_metrics;
|
||||||
unsigned int m_progressMax;
|
unsigned int m_progressMax;
|
||||||
unsigned int m_progressVal;
|
unsigned int m_progressVal;
|
||||||
|
unsigned int m_progressInt;
|
||||||
};
|
};
|
||||||
|
@ -133,14 +133,7 @@ void FileAnalyzer::run()
|
|||||||
QThreadPool *pool = new QThreadPool();
|
QThreadPool *pool = new QThreadPool();
|
||||||
QThread::msleep(333);
|
QThread::msleep(333);
|
||||||
|
|
||||||
if(QThread::idealThreadCount() > 1)
|
pool->setMaxThreadCount(qBound(2, ((QThread::idealThreadCount() * 3) / 2), 12));
|
||||||
{
|
|
||||||
pool->setMaxThreadCount((QThread::idealThreadCount() * 3) / 2);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pool->setMaxThreadCount(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
while(!(m_inputFiles.isEmpty() || m_bAborted))
|
while(!(m_inputFiles.isEmpty() || m_bAborted))
|
||||||
{
|
{
|
||||||
@ -156,10 +149,7 @@ void FileAnalyzer::run()
|
|||||||
|
|
||||||
while(!pool->tryStart(task))
|
while(!pool->tryStart(task))
|
||||||
{
|
{
|
||||||
if(!AnalyzeTask::waitForOneThread(1250))
|
AnalyzeTask::waitForOneThread();
|
||||||
{
|
|
||||||
qWarning("FileAnalyzer: Timeout, retrying!");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(m_abortFlag)
|
if(m_abortFlag)
|
||||||
{
|
{
|
||||||
@ -175,7 +165,8 @@ void FileAnalyzer::run()
|
|||||||
MessageBeep(MB_ICONERROR);
|
MessageBeep(MB_ICONERROR);
|
||||||
m_bAborted = true;
|
m_bAborted = true;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if(!m_bAborted)
|
||||||
{
|
{
|
||||||
if(int count = AnalyzeTask::getAdditionalFiles(m_inputFiles))
|
if(int count = AnalyzeTask::getAdditionalFiles(m_inputFiles))
|
||||||
{
|
{
|
||||||
|
@ -56,7 +56,11 @@ unsigned int AnalyzeTask::s_filesDenied;
|
|||||||
unsigned int AnalyzeTask::s_filesDummyCDDA;
|
unsigned int AnalyzeTask::s_filesDummyCDDA;
|
||||||
unsigned int AnalyzeTask::s_filesCueSheet;
|
unsigned int AnalyzeTask::s_filesCueSheet;
|
||||||
QStringList AnalyzeTask::s_additionalFiles;
|
QStringList AnalyzeTask::s_additionalFiles;
|
||||||
QStringList AnalyzeTask::s_recentlyAdded;
|
QSet<QString> AnalyzeTask::s_recentlyAdded;
|
||||||
|
|
||||||
|
/*constants*/
|
||||||
|
const int WAITCOND_TIMEOUT = 2500;
|
||||||
|
const int MAX_RETRIES = 60000 / WAITCOND_TIMEOUT;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
// Constructor
|
// Constructor
|
||||||
@ -126,6 +130,7 @@ void AnalyzeTask::run_ex(void)
|
|||||||
}
|
}
|
||||||
if(fileType == fileTypeSkip)
|
if(fileType == fileTypeSkip)
|
||||||
{
|
{
|
||||||
|
waitForPreviousThreads();
|
||||||
qWarning("File was recently added, skipping!");
|
qWarning("File was recently added, skipping!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -133,6 +138,8 @@ void AnalyzeTask::run_ex(void)
|
|||||||
{
|
{
|
||||||
QWriteLocker lock(&s_lock);
|
QWriteLocker lock(&s_lock);
|
||||||
s_filesDenied++;
|
s_filesDenied++;
|
||||||
|
lock.unlock();
|
||||||
|
waitForPreviousThreads();
|
||||||
qWarning("Cannot access file for reading, skipping!");
|
qWarning("Cannot access file for reading, skipping!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -140,10 +147,13 @@ void AnalyzeTask::run_ex(void)
|
|||||||
{
|
{
|
||||||
QWriteLocker lock(&s_lock);
|
QWriteLocker lock(&s_lock);
|
||||||
s_filesDummyCDDA++;
|
s_filesDummyCDDA++;
|
||||||
|
lock.unlock();
|
||||||
|
waitForPreviousThreads();
|
||||||
qWarning("Dummy CDDA file detected, skipping!");
|
qWarning("Dummy CDDA file detected, skipping!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Handle files with *incomplete* meida info
|
||||||
if(file.fileName().isEmpty() || file.formatContainerType().isEmpty() || file.formatAudioType().isEmpty())
|
if(file.fileName().isEmpty() || file.formatContainerType().isEmpty() || file.formatAudioType().isEmpty())
|
||||||
{
|
{
|
||||||
QStringList fileList;
|
QStringList fileList;
|
||||||
@ -167,9 +177,8 @@ void AnalyzeTask::run_ex(void)
|
|||||||
{
|
{
|
||||||
QWriteLocker lock(&s_lock);
|
QWriteLocker lock(&s_lock);
|
||||||
s_filesAccepted++;
|
s_filesAccepted++;
|
||||||
s_recentlyAdded.append(file.filePath());
|
s_recentlyAdded.insert(file.filePath().toLower());
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
waitForPreviousThreads();
|
waitForPreviousThreads();
|
||||||
emit fileAnalyzed(file);
|
emit fileAnalyzed(file);
|
||||||
}
|
}
|
||||||
@ -186,14 +195,15 @@ void AnalyzeTask::run_ex(void)
|
|||||||
qDebug("Rejected file of unknown type: %s", file.filePath().toUtf8().constData());
|
qDebug("Rejected file of unknown type: %s", file.filePath().toUtf8().constData());
|
||||||
s_filesRejected++;
|
s_filesRejected++;
|
||||||
}
|
}
|
||||||
|
waitForPreviousThreads();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Emit the file now!
|
||||||
QWriteLocker lock(&s_lock);
|
QWriteLocker lock(&s_lock);
|
||||||
s_filesAccepted++;
|
s_filesAccepted++;
|
||||||
s_recentlyAdded.append(file.filePath());
|
s_recentlyAdded.insert(file.filePath().toLower());
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
waitForPreviousThreads();
|
waitForPreviousThreads();
|
||||||
emit fileAnalyzed(file);
|
emit fileAnalyzed(file);
|
||||||
}
|
}
|
||||||
@ -208,7 +218,7 @@ const AudioFileModel AnalyzeTask::analyzeFile(const QString &filePath, int *type
|
|||||||
AudioFileModel audioFile(filePath);
|
AudioFileModel audioFile(filePath);
|
||||||
|
|
||||||
QReadLocker readLock(&s_lock);
|
QReadLocker readLock(&s_lock);
|
||||||
if(s_recentlyAdded.contains(filePath, Qt::CaseInsensitive))
|
if(s_recentlyAdded.contains(filePath.toLower()))
|
||||||
{
|
{
|
||||||
*type = fileTypeSkip;
|
*type = fileTypeSkip;
|
||||||
return audioFile;
|
return audioFile;
|
||||||
@ -726,23 +736,27 @@ void AnalyzeTask::waitForPreviousThreads(void)
|
|||||||
//This function will block until all threads with a *lower* index have terminated.
|
//This function will block until all threads with a *lower* index have terminated.
|
||||||
//Required to make sure that the files will be added in the "correct" order!
|
//Required to make sure that the files will be added in the "correct" order!
|
||||||
|
|
||||||
for(int i = 0; i < 64; i++)
|
int retryCount = 0;
|
||||||
|
|
||||||
|
while(retryCount < MAX_RETRIES)
|
||||||
{
|
{
|
||||||
s_waitMutex.lock();
|
s_waitMutex.lock();
|
||||||
|
|
||||||
if((s_threadIdx_finished >= m_threadIdx) || *m_abortFlag)
|
if((s_threadIdx_finished >= m_threadIdx) || *m_abortFlag)
|
||||||
{
|
{
|
||||||
s_waitMutex.unlock();
|
s_waitMutex.unlock();
|
||||||
break;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!s_waitCond.wait(&s_waitMutex, 1250))
|
if(!s_waitCond.wait(&s_waitMutex, WAITCOND_TIMEOUT))
|
||||||
{
|
{
|
||||||
qWarning("FileAnalyzerTask: Timeout, retrying!");
|
retryCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
s_waitMutex.unlock();
|
s_waitMutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qWarning("AnalyzeTask Timeout, will proceed anyway !!!");
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
@ -797,12 +811,12 @@ int AnalyzeTask::getAdditionalFiles(QStringList &fileList)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AnalyzeTask::waitForOneThread(unsigned long timeout)
|
bool AnalyzeTask::waitForOneThread(void)
|
||||||
{
|
{
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
|
|
||||||
s_waitMutex.lock();
|
s_waitMutex.lock();
|
||||||
ret = s_waitCond.wait(&s_waitMutex, timeout);
|
ret = s_waitCond.wait(&s_waitMutex, WAITCOND_TIMEOUT);
|
||||||
s_waitMutex.unlock();
|
s_waitMutex.unlock();
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -825,6 +839,7 @@ void AnalyzeTask::reset(void)
|
|||||||
s_threadIdx_finished = 0;
|
s_threadIdx_finished = 0;
|
||||||
s_waitMutex.unlock();
|
s_waitMutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
// EVENTS
|
// EVENTS
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
|
@ -54,7 +54,7 @@ public:
|
|||||||
static unsigned int filesCueSheet(void);
|
static unsigned int filesCueSheet(void);
|
||||||
|
|
||||||
//Wait till the next running thread terminates
|
//Wait till the next running thread terminates
|
||||||
static bool waitForOneThread(unsigned long timeout);
|
static bool waitForOneThread(void);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void fileSelected(const QString &fileName);
|
void fileSelected(const QString &fileName);
|
||||||
@ -111,7 +111,7 @@ private:
|
|||||||
static unsigned int s_filesDenied;
|
static unsigned int s_filesDenied;
|
||||||
static unsigned int s_filesDummyCDDA;
|
static unsigned int s_filesDummyCDDA;
|
||||||
static unsigned int s_filesCueSheet;
|
static unsigned int s_filesCueSheet;
|
||||||
static QStringList s_recentlyAdded;
|
static QSet<QString> s_recentlyAdded;
|
||||||
static QStringList s_additionalFiles;
|
static QStringList s_additionalFiles;
|
||||||
|
|
||||||
static unsigned __int64 makeThreadIdx(void);
|
static unsigned __int64 makeThreadIdx(void);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user