Exemple #1
0
 AutoSem(QSemaphore* sem) : s(sem) { s->release();}
Exemple #2
0
void GenerateMultipleThread::run()
{
	genMulti.abortOptimization=false;
	
	time(&initial_time);

	for(int i=0; i<nTimetables; i++){
		time(&start_time);
	
		bool impossible;
		bool timeExceeded;
		
		for(int qq=0; qq<gt.rules.nInternalActivities; qq++)
			permutation[qq]=savedPermutation[qq];
			
		emit(timetableStarted(i+1));
		semaphoreTimetableStarted.acquire();

		genMulti.generate(timeLimit, impossible, timeExceeded, true); //true means threaded

		QString s;
		
		bool ok;

		myMutex.lock();
		if(genMulti.abortOptimization){
			myMutex.unlock();
			return;
		}
		else if(impossible){
			s=tr("Timetable impossible to generate");
			s+=QString(".");
			ok=false;
		}
		else if(timeExceeded){
			s=tr("Time exceeded for current timetable");

			////////2011-05-26
			int mact=maxActivitiesPlaced;
			int mseconds=genMulti.timeToHighestStage;

			bool zero=false;
			if(mseconds==0)
				zero=true;
			int hh=mseconds/3600;
			mseconds%=3600;
			int mm=mseconds/60;
			mseconds%=60;
			int ss=mseconds;

			QString tim;
			if(hh>0){
				tim+=" ";
				tim+=tr("%1 h", "hours").arg(hh);
			}
			if(mm>0){
				tim+=" ";
				tim+=tr("%1 m", "minutes").arg(mm);
			}
			if(ss>0 || zero){
				tim+=" ";
				tim+=tr("%1 s", "seconds").arg(ss);
			}
			tim.remove(0, 1);
			s+=QString(". ");
			s+=tr("Max placed activities: %1 (at %2)", "%1 represents the maximum number of activities placed, %2 is a time interval").arg(mact).arg(tim);
			///////

			s+=QString(".");

			ok=false;
		}
		else{
			ok=true;
			
			time_t finish_time;
			time(&finish_time);
			int seconds=int(finish_time-start_time);
			int hours=seconds/3600;
			seconds%=3600;
			int minutes=seconds/60;
			seconds%=60;
			
			QString tmp;
			genMulti.c.fitness(gt.rules, &tmp);
			
			s=tr("Timetable has %1 soft conflicts factor and was generated in %2 hours, %3 minutes and %4 seconds")
			 .arg(CustomFETString::number(genMulti.c.conflictsTotal))
			 .arg(hours)
			 .arg(minutes)
			 .arg(seconds);

			s+=QString(".");
		}
		myMutex.unlock();
		
		emit(timetableGenerated(i+1, s, ok));
		semaphoreTimetableFinished.acquire();
	}
	
	emit(finished());
}
 void waitForTest()
 {
     semaphore.release();
     testSemaphore.acquire();
 }
Exemple #4
0
void tst_QSemaphore::tryAcquireWithTimeout()
{
    QFETCH(int, timeout);

    QSemaphore semaphore;
    QTime time;


    QCOMPARE(semaphore.available(), 0);

    semaphore.release();
    QCOMPARE(semaphore.available(), 1);
    time.start();
    QVERIFY(!semaphore.tryAcquire(2, timeout));
    QVERIFY(time.elapsed() >= timeout);
    QCOMPARE(semaphore.available(), 1);

    semaphore.release();
    QCOMPARE(semaphore.available(), 2);
    time.start();
    QVERIFY(!semaphore.tryAcquire(3, timeout));
    QVERIFY(time.elapsed() >= timeout);
    QCOMPARE(semaphore.available(), 2);

    semaphore.release(10);
    QCOMPARE(semaphore.available(), 12);
    time.start();
    QVERIFY(!semaphore.tryAcquire(100, timeout));
    QVERIFY(time.elapsed() >= timeout);
    QCOMPARE(semaphore.available(), 12);

    semaphore.release(10);
    QCOMPARE(semaphore.available(), 22);
    time.start();
    QVERIFY(!semaphore.tryAcquire(100, timeout));
    QVERIFY(time.elapsed() >= timeout);
    QCOMPARE(semaphore.available(), 22);

    time.start();
    QVERIFY(semaphore.tryAcquire(1, timeout));
    QVERIFY(time.elapsed() <= timeout);
    QCOMPARE(semaphore.available(), 21);

    time.start();
    QVERIFY(semaphore.tryAcquire(1, timeout));
    QVERIFY(time.elapsed() <= timeout);
    QCOMPARE(semaphore.available(), 20);

    time.start();
    QVERIFY(semaphore.tryAcquire(10, timeout));
    QVERIFY(time.elapsed() <= timeout);
    QCOMPARE(semaphore.available(), 10);

    time.start();
    QVERIFY(semaphore.tryAcquire(10, timeout));
    QVERIFY(time.elapsed() <= timeout);
    QCOMPARE(semaphore.available(), 0);

    // should not be able to acquire more
    time.start();
    QVERIFY(!semaphore.tryAcquire(1, timeout));
    QVERIFY(time.elapsed() >= timeout);
    QCOMPARE(semaphore.available(), 0);

    time.start();
    QVERIFY(!semaphore.tryAcquire(1, timeout));
    QVERIFY(time.elapsed() >= timeout);
    QCOMPARE(semaphore.available(), 0);

    time.start();
    QVERIFY(!semaphore.tryAcquire(10, timeout));
    QVERIFY(time.elapsed() >= timeout);
    QCOMPARE(semaphore.available(), 0);

    time.start();
    QVERIFY(!semaphore.tryAcquire(10, timeout));
    QVERIFY(time.elapsed() >= timeout);
    QCOMPARE(semaphore.available(), 0);
}
Exemple #5
0
void TimetableGenerateMultipleForm::timetableStarted(int timetable)
{
	TimetableExport::writeRandomSeed(this, timetable, true); //true represents 'before' state
	
	semaphoreTimetableStarted.release();
}
Exemple #6
0
void TimetableGenerateForm::simulationFinished()
{
	if(!simulation_running){
		return;
	}

	simulation_running=false;

	finishedSemaphore.acquire();

	TimetableExport::writeRandomSeed(this, false); //false represents 'before' state

	Solution& c=gen.c;

	//needed to find the conflicts strings
	QString tmp;
	c.fitness(gt.rules, &tmp);

	TimetableExport::getStudentsTimetable(c);
	TimetableExport::getTeachersTimetable(c);
	TimetableExport::getRoomsTimetable(c);

	//update the string representing the conflicts
	conflictsStringTitle=TimetableGenerateForm::tr("Soft conflicts", "Title of dialog");
	conflictsString="";
	conflictsString+=tr("Total soft conflicts:");
	conflictsString+=" ";
	conflictsString+=CustomFETString::number(c.conflictsTotal);
	conflictsString+="\n";
	conflictsString+=TimetableGenerateForm::tr("Soft conflicts listing (in decreasing order):");
	conflictsString+="\n";

	foreach(QString t, c.conflictsDescriptionList)
		conflictsString+=t+"\n";

	TimetableExport::writeSimulationResults(this);

	QString kk;
	kk=FILE_SEP;
	if(INPUT_FILENAME_XML=="")
		kk.append("unnamed");
	else{
		kk.append(INPUT_FILENAME_XML.right(INPUT_FILENAME_XML.length()-INPUT_FILENAME_XML.lastIndexOf(FILE_SEP)-1));

		if(kk.right(4)==".fet")
			kk=kk.left(kk.length()-4);
	}
	kk.append("-single");

/*	QMessageBox::information(this, TimetableGenerateForm::tr("FET information"),
		TimetableGenerateForm::tr("Allocation terminated successfully, remaining %1 weighted"
		" soft conflicts from constraints with weight percentage lower than 100%"
		" (see menu Timetable/Show soft conflicts or the text file in"
		" the output directory for details)."
		"\n\nSimulation results should be now written. You may check now Timetable/View."
		" The results are also saved in the directory %2 in"
		" html and xml mode and the soft conflicts in txt mode").arg(c.conflictsTotal).arg(QDir::toNativeSeparators(OUTPUT_DIR+FILE_SEP+"timetables"+kk))
		+". "+tr("Data+timetable is saved as a .fet data file (with activities locked by constraints)"
		", so that you can open/modify/regenerate the current timetable later"));
*/

	QString s=QString("");
	s+=tr("Generation successful!");
	s+=QString("\n\n");
	s+=tr("Weighted soft conflicts: %1").arg(CustomFETString::number(c.conflictsTotal));
	s+=QString("\n\n");
	s+=tr("Results were saved in the directory %1").arg(QDir::toNativeSeparators(OUTPUT_DIR+FILE_SEP+"timetables"+kk));
	QMessageBox::information(this, TimetableGenerateForm::tr("FET information"), s);

	startPushButton->setEnabled(true);
	stopPushButton->setDisabled(true);
	stopHighestPushButton->setDisabled(true);
	closePushButton->setEnabled(true);
	writeResultsPushButton->setDisabled(true);
	writeHighestStagePushButton->setDisabled(true);
	seeImpossiblePushButton->setDisabled(true);
}
 void run()
 {
     recycledThread = QThread::currentThread();
     threadRecyclingSemaphore.release();
 }
void tst_QReadWriteLock::tryReadLock()
{
    QReadWriteLock rwlock;
    QVERIFY(rwlock.tryLockForRead());
    rwlock.unlock();
    QVERIFY(rwlock.tryLockForRead());
    rwlock.unlock();

    rwlock.lockForRead();
    rwlock.lockForRead();
    QVERIFY(rwlock.tryLockForRead());
    rwlock.unlock();
    rwlock.unlock();
    rwlock.unlock();

    rwlock.lockForWrite();
    QVERIFY(!rwlock.tryLockForRead());
    rwlock.unlock();

    // functionality test
    {
        class Thread : public QThread
        {
        public:
            void run()
            {
                testsTurn.release();

                threadsTurn.acquire();
                QVERIFY(!readWriteLock.tryLockForRead());
                testsTurn.release();

                threadsTurn.acquire();
                QVERIFY(readWriteLock.tryLockForRead());
                lockCount.ref();
                QVERIFY(readWriteLock.tryLockForRead());
                lockCount.ref();
                lockCount.deref();
                readWriteLock.unlock();
                lockCount.deref();
                readWriteLock.unlock();
                testsTurn.release();

                threadsTurn.acquire();
                QTime timer;
                timer.start();
                QVERIFY(!readWriteLock.tryLockForRead(1000));
                QVERIFY(timer.elapsed() >= 1000);
                testsTurn.release();

                threadsTurn.acquire();
                timer.start();
                QVERIFY(readWriteLock.tryLockForRead(1000));
                QVERIFY(timer.elapsed() <= 1000);
                lockCount.ref();
                QVERIFY(readWriteLock.tryLockForRead(1000));
                lockCount.ref();
                lockCount.deref();
                readWriteLock.unlock();
                lockCount.deref();
                readWriteLock.unlock();
                testsTurn.release();

                threadsTurn.acquire();
            }
        };

        Thread thread;
        thread.start();

        testsTurn.acquire();
        readWriteLock.lockForWrite();
        QVERIFY(lockCount.testAndSetRelaxed(0, 1));
        threadsTurn.release();

        testsTurn.acquire();
        QVERIFY(lockCount.testAndSetRelaxed(1, 0));
        readWriteLock.unlock();
        threadsTurn.release();

        testsTurn.acquire();
        readWriteLock.lockForWrite();
        QVERIFY(lockCount.testAndSetRelaxed(0, 1));
        threadsTurn.release();

        testsTurn.acquire();
        QVERIFY(lockCount.testAndSetRelaxed(1, 0));
        readWriteLock.unlock();
        threadsTurn.release();

        // stop thread
        testsTurn.acquire();
        threadsTurn.release();
        thread.wait();
    }
}
void tst_QReadWriteLock::tryWriteLock()
{
    {
        QReadWriteLock rwlock;
        QVERIFY(rwlock.tryLockForWrite());
        rwlock.unlock();
        QVERIFY(rwlock.tryLockForWrite());
        rwlock.unlock();

        rwlock.lockForWrite();
        QVERIFY(!rwlock.tryLockForWrite());
        QVERIFY(!rwlock.tryLockForWrite());
        rwlock.unlock();

        rwlock.lockForRead();
        QVERIFY(!rwlock.tryLockForWrite());
        rwlock.unlock();
    }

    {
        QReadWriteLock rwlock(QReadWriteLock::Recursive);
        QVERIFY(rwlock.tryLockForWrite());
        rwlock.unlock();
        QVERIFY(rwlock.tryLockForWrite());
        rwlock.unlock();

        rwlock.lockForWrite();
        QVERIFY(rwlock.tryLockForWrite());
        QVERIFY(rwlock.tryLockForWrite());
        rwlock.unlock();
        rwlock.unlock();
        rwlock.unlock();

        rwlock.lockForRead();
        QVERIFY(!rwlock.tryLockForWrite());
        rwlock.unlock();
    }

    // functionality test
    {
        class Thread : public QThread
        {
        public:
            Thread() : failureCount(0) { }
            void run()
            {
                testsTurn.release();

                threadsTurn.acquire();
                if (readWriteLock.tryLockForWrite())
                    failureCount++;
                testsTurn.release();

                threadsTurn.acquire();
                if (!readWriteLock.tryLockForWrite())
                    failureCount++;
                if (!lockCount.testAndSetRelaxed(0, 1))
                    failureCount++;
                if (!lockCount.testAndSetRelaxed(1, 0))
                    failureCount++;
                readWriteLock.unlock();
                testsTurn.release();

                threadsTurn.acquire();
                if (readWriteLock.tryLockForWrite(1000))
                    failureCount++;
                testsTurn.release();

                threadsTurn.acquire();
                if (!readWriteLock.tryLockForWrite(1000))
                    failureCount++;
                if (!lockCount.testAndSetRelaxed(0, 1))
                    failureCount++;
                if (!lockCount.testAndSetRelaxed(1, 0))
                    failureCount++;
                readWriteLock.unlock();
                testsTurn.release();

                threadsTurn.acquire();
            }

            int failureCount;
        };

        Thread thread;
        thread.start();

        testsTurn.acquire();
        readWriteLock.lockForRead();
        lockCount.ref();
        threadsTurn.release();

        testsTurn.acquire();
        lockCount.deref();
        readWriteLock.unlock();
        threadsTurn.release();

        testsTurn.acquire();
        readWriteLock.lockForRead();
        lockCount.ref();
        threadsTurn.release();

        testsTurn.acquire();
        lockCount.deref();
        readWriteLock.unlock();
        threadsTurn.release();

        // stop thread
        testsTurn.acquire();
        threadsTurn.release();
        thread.wait();

        QCOMPARE(thread.failureCount, 0);
    }
}
 void wait()
 {
     semaphore.acquire();
 }
 void run()
 {
     semaphore.release();
 }
 void run()
 {
     semaphore.acquire();
     count.ref();
 }
 void run()
 {
     waitForStarted.release();
     waitToFinish.acquire();
 }
 void run()
 {
     thread = QThread::currentThread();
     ++runCount;
     semaphore.release();
 }
Exemple #15
0
 ~AutoSem() {
     if (s->available() > 0)
         s->acquire(s->available());
 }
Exemple #16
0
void tst_QSemaphore::acquire()
{
    {
        // old incrementOne() test
        QVERIFY(!semaphore);
        semaphore = new QSemaphore;
        // make some "thing" available
        semaphore->release();

        ThreadOne t1;
        ThreadOne t2;

        t1.start();
        t2.start();

        QVERIFY(t1.wait(4000));
        QVERIFY(t2.wait(4000));

        delete semaphore;
        semaphore = 0;
    }

    // old incrementN() test
    {
        QVERIFY(!semaphore);
        semaphore = new QSemaphore;
        // make 4 "things" available
        semaphore->release(4);

        ThreadN t1(2);
        ThreadN t2(3);

        t1.start();
        t2.start();

        QVERIFY(t1.wait(4000));
        QVERIFY(t2.wait(4000));

        delete semaphore;
        semaphore = 0;
    }

    QSemaphore semaphore;

    QCOMPARE(semaphore.available(), 0);
    semaphore.release();
    QCOMPARE(semaphore.available(), 1);
    semaphore.release();
    QCOMPARE(semaphore.available(), 2);
    semaphore.release(10);
    QCOMPARE(semaphore.available(), 12);
    semaphore.release(10);
    QCOMPARE(semaphore.available(), 22);

    semaphore.acquire();
    QCOMPARE(semaphore.available(), 21);
    semaphore.acquire();
    QCOMPARE(semaphore.available(), 20);
    semaphore.acquire(10);
    QCOMPARE(semaphore.available(), 10);
    semaphore.acquire(10);
    QCOMPARE(semaphore.available(), 0);
}
void tst_QMutex::tryLock()
{
    // test non-recursive mutex
    {
        class Thread : public QThread
        {
        public:
            void run()
            {
                testsTurn.release();

                threadsTurn.acquire();
                QVERIFY(!normalMutex.tryLock());
                testsTurn.release();

                threadsTurn.acquire();
                QVERIFY(normalMutex.tryLock());
                QVERIFY(lockCount.testAndSetRelaxed(0, 1));
                QVERIFY(!normalMutex.tryLock());
                QVERIFY(lockCount.testAndSetRelaxed(1, 0));
                normalMutex.unlock();
                testsTurn.release();

                threadsTurn.acquire();
                QTime timer;
                timer.start();
                QVERIFY(!normalMutex.tryLock(1000));
                QVERIFY(timer.elapsed() >= 1000);
                testsTurn.release();

                threadsTurn.acquire();
                timer.start();
                QVERIFY(normalMutex.tryLock(1000));
                QVERIFY(timer.elapsed() <= 1000);
                QVERIFY(lockCount.testAndSetRelaxed(0, 1));
                timer.start();
                QVERIFY(!normalMutex.tryLock(1000));
                QVERIFY(timer.elapsed() >= 1000);
                QVERIFY(lockCount.testAndSetRelaxed(1, 0));
                normalMutex.unlock();
                testsTurn.release();

                threadsTurn.acquire();
            }
        };

        Thread thread;
        thread.start();

        testsTurn.acquire();
        normalMutex.lock();
        QVERIFY(lockCount.testAndSetRelaxed(0, 1));
        threadsTurn.release();

        testsTurn.acquire();
        QVERIFY(lockCount.testAndSetRelaxed(1, 0));
        normalMutex.unlock();
        threadsTurn.release();

        testsTurn.acquire();
        normalMutex.lock();
        QVERIFY(lockCount.testAndSetRelaxed(0, 1));
        threadsTurn.release();

        testsTurn.acquire();
        QVERIFY(lockCount.testAndSetRelaxed(1, 0));
        normalMutex.unlock();
        threadsTurn.release();

        // wait for thread to finish
        testsTurn.acquire();
        threadsTurn.release();
        thread.wait();
    }

    // test recursive mutex
    {
        class Thread : public QThread
        {
        public:
            void run()
            {
                testsTurn.release();

                threadsTurn.acquire();
                QVERIFY(!recursiveMutex.tryLock());
                testsTurn.release();

                threadsTurn.acquire();
                QVERIFY(recursiveMutex.tryLock());
                QVERIFY(lockCount.testAndSetRelaxed(0, 1));
                QVERIFY(recursiveMutex.tryLock());
                QVERIFY(lockCount.testAndSetRelaxed(1, 2));
                QVERIFY(lockCount.testAndSetRelaxed(2, 1));
                recursiveMutex.unlock();
                QVERIFY(lockCount.testAndSetRelaxed(1, 0));
                recursiveMutex.unlock();
                testsTurn.release();

                threadsTurn.acquire();
                QTime timer;
                timer.start();
                QVERIFY(!recursiveMutex.tryLock(1000));
                QVERIFY(timer.elapsed() >= 1000);
                testsTurn.release();

                threadsTurn.acquire();
                timer.start();
                QVERIFY(recursiveMutex.tryLock(1000));
                QVERIFY(timer.elapsed() <= 1000);
                QVERIFY(lockCount.testAndSetRelaxed(0, 1));
                QVERIFY(recursiveMutex.tryLock(1000));
                QVERIFY(lockCount.testAndSetRelaxed(1, 2));
                QVERIFY(lockCount.testAndSetRelaxed(2, 1));
                recursiveMutex.unlock();
                QVERIFY(lockCount.testAndSetRelaxed(1, 0));
                recursiveMutex.unlock();
                testsTurn.release();

                threadsTurn.acquire();
            }
        };

        Thread thread;
        thread.start();

        testsTurn.acquire();
        recursiveMutex.lock();
        QVERIFY(lockCount.testAndSetRelaxed(0, 1));
        recursiveMutex.lock();
        QVERIFY(lockCount.testAndSetRelaxed(1, 2));
        threadsTurn.release();

        testsTurn.acquire();
        QVERIFY(lockCount.testAndSetRelaxed(2, 1));
        recursiveMutex.unlock();
        QVERIFY(lockCount.testAndSetRelaxed(1, 0));
        recursiveMutex.unlock();
        threadsTurn.release();

        testsTurn.acquire();
        recursiveMutex.lock();
        QVERIFY(lockCount.testAndSetRelaxed(0, 1));
        recursiveMutex.lock();
        QVERIFY(lockCount.testAndSetRelaxed(1, 2));
        threadsTurn.release();

        testsTurn.acquire();
        QVERIFY(lockCount.testAndSetRelaxed(2, 1));
        recursiveMutex.unlock();
        QVERIFY(lockCount.testAndSetRelaxed(1, 0));
        recursiveMutex.unlock();
        threadsTurn.release();

        // stop thread
        testsTurn.acquire();
        threadsTurn.release();
        thread.wait();
    }
}
Exemple #18
0
void tst_QSemaphore::tryAcquire()
{
    QSemaphore semaphore;

    QCOMPARE(semaphore.available(), 0);

    semaphore.release();
    QCOMPARE(semaphore.available(), 1);
    QVERIFY(!semaphore.tryAcquire(2));
    QCOMPARE(semaphore.available(), 1);

    semaphore.release();
    QCOMPARE(semaphore.available(), 2);
    QVERIFY(!semaphore.tryAcquire(3));
    QCOMPARE(semaphore.available(), 2);

    semaphore.release(10);
    QCOMPARE(semaphore.available(), 12);
    QVERIFY(!semaphore.tryAcquire(100));
    QCOMPARE(semaphore.available(), 12);

    semaphore.release(10);
    QCOMPARE(semaphore.available(), 22);
    QVERIFY(!semaphore.tryAcquire(100));
    QCOMPARE(semaphore.available(), 22);

    QVERIFY(semaphore.tryAcquire());
    QCOMPARE(semaphore.available(), 21);

    QVERIFY(semaphore.tryAcquire());
    QCOMPARE(semaphore.available(), 20);

    QVERIFY(semaphore.tryAcquire(10));
    QCOMPARE(semaphore.available(), 10);

    QVERIFY(semaphore.tryAcquire(10));
    QCOMPARE(semaphore.available(), 0);

    // should not be able to acquire more
    QVERIFY(!semaphore.tryAcquire());
    QCOMPARE(semaphore.available(), 0);

    QVERIFY(!semaphore.tryAcquire());
    QCOMPARE(semaphore.available(), 0);

    QVERIFY(!semaphore.tryAcquire(10));
    QCOMPARE(semaphore.available(), 0);

    QVERIFY(!semaphore.tryAcquire(10));
    QCOMPARE(semaphore.available(), 0);
}
Exemple #19
0
void TimetableGenerateForm::activityPlaced(int na){
	assert(gt.rules.initialized && gt.rules.internalStructureComputed);

	myMutex.lock();
	int t=gen.searchTime; //seconds
	int mact=maxActivitiesPlaced;
	int seconds=gen.timeToHighestStage;
	myMutex.unlock();

	//write to the Qt interface
	QString s;
	s+=TimetableGenerateForm::tr("%1 out of %2 activities placed").arg(na).arg(gt.rules.nInternalActivities)+"\n";

	s+=TimetableGenerateForm::tr("Elapsed time:");
	int h=t/3600;
	if(h>0){
		s+=" ";
		s+=TimetableGenerateForm::tr("%1 h", "hours").arg(h);
	}
	t=t%3600;
	int m=t/60;
	if(m>0){
		s+=" ";
		s+=TimetableGenerateForm::tr("%1 m", "minutes").arg(m);
	}
	t=t%60;
	if(t>0){
		s+=" ";
		s+=TimetableGenerateForm::tr("%1 s", "seconds").arg(t);
	}
	
	bool zero=false;
	if(seconds==0)
		zero=true;
	int hh=seconds/3600;
	seconds%=3600;
	int mm=seconds/60;
	seconds%=60;
	int ss=seconds;

	QString tim;
	if(hh>0){
		tim+=" ";
		tim+=tr("%1 h", "hours").arg(hh);
	}
	if(mm>0){
		tim+=" ";
		tim+=tr("%1 m", "minutes").arg(mm);
	}
	if(ss>0 || zero){
		tim+=" ";
		tim+=tr("%1 s", "seconds").arg(ss);
	}
	tim.remove(0, 1);
	s+="\n\n";
	s+=tr("Max placed activities: %1 (at %2)", "%1 represents the maximum number of activities placed, %2 is a time interval").arg(mact).arg(tim);

	currentResultsTextEdit->setPlainText(s);

	semaphorePlacedActivity.release();
}
void SearchOperation::doSearch( SearchData& searchData, LineNumber initialLine )
{
    const auto& config = Persistable::get<Configuration>();

    const auto nbSourceLines = sourceLogData_->getNbLine();
    LineLength maxLength = 0_length;
    LinesCount nbMatches = searchData.getNbMatches();

    const auto nbLinesInChunk = LinesCount( config.searchReadBufferSizeLines() );

    LOG( logDEBUG ) << "Searching from line " << initialLine << " to " << nbSourceLines;

    if ( initialLine < startLine_ ) {
        initialLine = startLine_;
    }

    const auto endLine = qMin( LineNumber( nbSourceLines.get() ), endLine_ );

    using namespace std::chrono;
    high_resolution_clock::time_point t1 = high_resolution_clock::now();

    QSemaphore searchCompleted;
    QSemaphore blocksDone;

    using SearchBlockQueue = moodycamel::BlockingConcurrentQueue<SearchBlockData>;
    using ProcessMatchQueue = moodycamel::BlockingConcurrentQueue<PartialSearchResults>;

    SearchBlockQueue searchBlockQueue;
    ProcessMatchQueue processMatchQueue;

    std::vector<QFuture<void>> matchers;

    const auto matchingThreadsCount = [&config]()
    {
        if ( !config.useParallelSearch() ) {
            return 1;
        }

        return qMax( 1,  config.searchThreadPoolSize() == 0 
                        ? QThread::idealThreadCount() - 1 
                        : static_cast<int>( config.searchThreadPoolSize() ) );    
    }();

    LOG( logINFO ) << "Using " << matchingThreadsCount << " matching threads";

    auto localThreadPool = std::make_unique<QThreadPool>();
    localThreadPool->setMaxThreadCount( matchingThreadsCount + 2 );

    const auto makeMatcher = [this, &searchBlockQueue, &processMatchQueue,
                              pool = localThreadPool.get()]( size_t index ) {
        // copy and optimize regex for each thread
        auto regexp = QRegularExpression{ regexp_.pattern(), regexp_.patternOptions() };
        regexp.optimize();
        return QtConcurrent::run( pool, [regexp, index, &searchBlockQueue, &processMatchQueue]() {
            auto cToken = moodycamel::ConsumerToken{ searchBlockQueue };
            auto pToken = moodycamel::ProducerToken{ processMatchQueue };

            for ( ;; ) {
                SearchBlockData blockData;
                searchBlockQueue.wait_dequeue( cToken, blockData );

                LOG( logDEBUG ) << "Searcher " << index << " " << blockData.chunkStart;

                const auto lastBlock = blockData.lines.empty();

                if ( !lastBlock ) {
                    blockData.results
                        = filterLines( regexp, blockData.lines, blockData.chunkStart );
                }

                LOG( logDEBUG ) << "Searcher " << index << " sending matches "
                                << blockData.results.matchingLines.size();

                processMatchQueue.enqueue( pToken, std::move( blockData.results ) );

                if ( lastBlock ) {
                    LOG( logDEBUG ) << "Searcher " << index << " last block";
                    return;
                }
            }
        } );
    };

    for ( int i = 0; i < matchingThreadsCount; ++i ) {
        matchers.emplace_back( makeMatcher( i ) );
    }

    auto processMatches = QtConcurrent::run( localThreadPool.get(), [&]() {
        auto cToken = moodycamel::ConsumerToken{ processMatchQueue };

        size_t matchersDone = 0;

        int reportedPercentage = 0;
        auto reportedMatches = nbMatches;
        LinesCount totalProcessedLines = 0_lcount;

        const auto totalLines = endLine - initialLine;

        for ( ;; ) {
            PartialSearchResults matchResults;
            processMatchQueue.wait_dequeue( cToken, matchResults );

            LOG( logDEBUG ) << "Combining match results from " << matchResults.chunkStart;

            if ( matchResults.processedLines.get() ) {

                maxLength = qMax( maxLength, matchResults.maxLength );
                nbMatches += LinesCount(
                    static_cast<LinesCount::UnderlyingType>( matchResults.matchingLines.size() ) );

                const auto processedLines = LinesCount{ matchResults.chunkStart.get()
                                                        + matchResults.processedLines.get() };

                totalProcessedLines += matchResults.processedLines;

                // After each block, copy the data to shared data
                // and update the client
                searchData.addAll( maxLength, matchResults.matchingLines, processedLines );

                LOG( logDEBUG ) << "done Searching chunk starting at " << matchResults.chunkStart
                                << ", " << matchResults.processedLines << " lines read.";

                blocksDone.release( matchResults.processedLines.get() );
            }
            else {
                matchersDone++;
            }

            const int percentage 
                = static_cast<int>( std::floor( 100.f * ( totalProcessedLines ).get() / totalLines.get() ) );

            if ( percentage > reportedPercentage || nbMatches > reportedMatches ) {

                emit searchProgressed( nbMatches, std::min( 99, percentage ), initialLine );

                reportedPercentage = percentage;
                reportedMatches = nbMatches;
            }

            if ( matchersDone == matchers.size() ) {
                searchCompleted.release();
                return;
            }
        }
    } );

    auto pToken =  moodycamel::ProducerToken{ searchBlockQueue };

    blocksDone.release( nbLinesInChunk.get() * ( static_cast<uint32_t>( matchers.size() ) + 1 ) );

    for ( auto chunkStart = initialLine; chunkStart < endLine;
          chunkStart = chunkStart + nbLinesInChunk ) {
        if ( *interruptRequested_ )
            break;

        LOG( logDEBUG ) << "Reading chunk starting at " << chunkStart;

        const auto linesInChunk
            = LinesCount( qMin( nbLinesInChunk.get(), ( endLine - chunkStart ).get() ) );
        auto lines = sourceLogData_->getLines( chunkStart, linesInChunk );

        LOG( logDEBUG ) << "Sending chunk starting at " << chunkStart << ", " << lines.size()
                        << " lines read.";

        blocksDone.acquire( static_cast<uint32_t>( lines.size() ) );

        searchBlockQueue.enqueue( pToken,
                                  { chunkStart, std::move( lines ), PartialSearchResults{} } );

        LOG( logDEBUG ) << "Sent chunk starting at " << chunkStart << ", " << lines.size()
                        << " lines read.";
    }

    for ( size_t i = 0; i < matchers.size(); ++i ) {
        searchBlockQueue.enqueue( pToken,
                                  { endLine, std::vector<QString>{}, PartialSearchResults{} } );
    }

    searchCompleted.acquire();

    high_resolution_clock::time_point t2 = high_resolution_clock::now();
    auto duration = duration_cast<microseconds>( t2 - t1 ).count();

    LOG( logINFO ) << "Searching done, took " << duration / 1000.f << " ms";
    LOG( logINFO ) << "Searching perf "
                   << static_cast<uint32_t>(
                          std::floor( 1000 * 1000.f * ( endLine - initialLine ).get() / duration ) )
                   << " lines/s";

    emit searchProgressed( nbMatches, 100, initialLine );
}