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:
LoRd_MuldeR 2012-05-12 02:51:24 +02:00
parent 68d2a28c7b
commit 193488e26f
5 changed files with 58 additions and 34 deletions

View File

@ -43,7 +43,7 @@
WorkingBanner::WorkingBanner(QWidget *parent)
:
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
setupUi(this);
@ -226,25 +226,41 @@ void WorkingBanner::setText(const QString &text)
}
labelStatus->setText(choppedText);
}
/*
if(this->isVisible())
{
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
labelStatus->repaint();
}
*/
}
void WorkingBanner::setProgressMax(unsigned int max)
{
m_progressMax = max;
updateProgress();
}
void WorkingBanner::setProgressVal(unsigned int val)
{
m_progressVal = val;
updateProgress();
}
////////////////////////////////////////////////////////////
// Private
////////////////////////////////////////////////////////////
void WorkingBanner::updateProgress(void)
{
if(m_progressMax > 0)
{
int progress = qRound(qBound(0.0, static_cast<double>(m_progressVal) / static_cast<double>(m_progressMax), 1.0) * 100.0);
m_progress->setText(QString::number(progress));
int newProgress = qRound(qBound(0.0, static_cast<double>(m_progressVal) / static_cast<double>(m_progressMax), 1.0) * 100.0);
if(m_progressInt != newProgress)
{
m_progressInt = newProgress;
m_progress->setText(QString::number(m_progressInt));
if(this->isVisible())
{
labelStatus->repaint();
}
}
}
}

View File

@ -42,6 +42,7 @@ public:
private:
QMovie *m_working;
bool m_canClose;
void updateProgress(void);
public slots:
void setText(const QString &text);
@ -62,4 +63,5 @@ protected:
QFontMetrics *m_metrics;
unsigned int m_progressMax;
unsigned int m_progressVal;
unsigned int m_progressInt;
};

View File

@ -133,14 +133,7 @@ void FileAnalyzer::run()
QThreadPool *pool = new QThreadPool();
QThread::msleep(333);
if(QThread::idealThreadCount() > 1)
{
pool->setMaxThreadCount((QThread::idealThreadCount() * 3) / 2);
}
else
{
pool->setMaxThreadCount(2);
}
pool->setMaxThreadCount(qBound(2, ((QThread::idealThreadCount() * 3) / 2), 12));
while(!(m_inputFiles.isEmpty() || m_bAborted))
{
@ -156,10 +149,7 @@ void FileAnalyzer::run()
while(!pool->tryStart(task))
{
if(!AnalyzeTask::waitForOneThread(1250))
{
qWarning("FileAnalyzer: Timeout, retrying!");
}
AnalyzeTask::waitForOneThread();
if(m_abortFlag)
{
@ -175,7 +165,8 @@ void FileAnalyzer::run()
MessageBeep(MB_ICONERROR);
m_bAborted = true;
}
else
if(!m_bAborted)
{
if(int count = AnalyzeTask::getAdditionalFiles(m_inputFiles))
{

View File

@ -56,7 +56,11 @@ unsigned int AnalyzeTask::s_filesDenied;
unsigned int AnalyzeTask::s_filesDummyCDDA;
unsigned int AnalyzeTask::s_filesCueSheet;
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
@ -126,6 +130,7 @@ void AnalyzeTask::run_ex(void)
}
if(fileType == fileTypeSkip)
{
waitForPreviousThreads();
qWarning("File was recently added, skipping!");
return;
}
@ -133,6 +138,8 @@ void AnalyzeTask::run_ex(void)
{
QWriteLocker lock(&s_lock);
s_filesDenied++;
lock.unlock();
waitForPreviousThreads();
qWarning("Cannot access file for reading, skipping!");
return;
}
@ -140,10 +147,13 @@ void AnalyzeTask::run_ex(void)
{
QWriteLocker lock(&s_lock);
s_filesDummyCDDA++;
lock.unlock();
waitForPreviousThreads();
qWarning("Dummy CDDA file detected, skipping!");
return;
}
//Handle files with *incomplete* meida info
if(file.fileName().isEmpty() || file.formatContainerType().isEmpty() || file.formatAudioType().isEmpty())
{
QStringList fileList;
@ -167,9 +177,8 @@ void AnalyzeTask::run_ex(void)
{
QWriteLocker lock(&s_lock);
s_filesAccepted++;
s_recentlyAdded.append(file.filePath());
s_recentlyAdded.insert(file.filePath().toLower());
lock.unlock();
waitForPreviousThreads();
emit fileAnalyzed(file);
}
@ -186,14 +195,15 @@ void AnalyzeTask::run_ex(void)
qDebug("Rejected file of unknown type: %s", file.filePath().toUtf8().constData());
s_filesRejected++;
}
waitForPreviousThreads();
return;
}
//Emit the file now!
QWriteLocker lock(&s_lock);
s_filesAccepted++;
s_recentlyAdded.append(file.filePath());
s_recentlyAdded.insert(file.filePath().toLower());
lock.unlock();
waitForPreviousThreads();
emit fileAnalyzed(file);
}
@ -208,7 +218,7 @@ const AudioFileModel AnalyzeTask::analyzeFile(const QString &filePath, int *type
AudioFileModel audioFile(filePath);
QReadLocker readLock(&s_lock);
if(s_recentlyAdded.contains(filePath, Qt::CaseInsensitive))
if(s_recentlyAdded.contains(filePath.toLower()))
{
*type = fileTypeSkip;
return audioFile;
@ -726,23 +736,27 @@ void AnalyzeTask::waitForPreviousThreads(void)
//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!
for(int i = 0; i < 64; i++)
int retryCount = 0;
while(retryCount < MAX_RETRIES)
{
s_waitMutex.lock();
if((s_threadIdx_finished >= m_threadIdx) || *m_abortFlag)
{
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();
}
qWarning("AnalyzeTask Timeout, will proceed anyway !!!");
}
////////////////////////////////////////////////////////////
@ -797,12 +811,12 @@ int AnalyzeTask::getAdditionalFiles(QStringList &fileList)
return 0;
}
bool AnalyzeTask::waitForOneThread(unsigned long timeout)
bool AnalyzeTask::waitForOneThread(void)
{
bool ret = false;
s_waitMutex.lock();
ret = s_waitCond.wait(&s_waitMutex, timeout);
ret = s_waitCond.wait(&s_waitMutex, WAITCOND_TIMEOUT);
s_waitMutex.unlock();
return ret;
@ -825,6 +839,7 @@ void AnalyzeTask::reset(void)
s_threadIdx_finished = 0;
s_waitMutex.unlock();
}
////////////////////////////////////////////////////////////
// EVENTS
////////////////////////////////////////////////////////////

View File

@ -54,7 +54,7 @@ public:
static unsigned int filesCueSheet(void);
//Wait till the next running thread terminates
static bool waitForOneThread(unsigned long timeout);
static bool waitForOneThread(void);
signals:
void fileSelected(const QString &fileName);
@ -111,7 +111,7 @@ private:
static unsigned int s_filesDenied;
static unsigned int s_filesDummyCDDA;
static unsigned int s_filesCueSheet;
static QStringList s_recentlyAdded;
static QSet<QString> s_recentlyAdded;
static QStringList s_additionalFiles;
static unsigned __int64 makeThreadIdx(void);