1/*
2 * Copyright (C) 2006 Aaron Seigo <aseigo@kde.org>
3 * Copyright (C) 2007, 2009 Ryan P. Bitanga <ryan.bitanga@gmail.com>
4 * Copyright (C) 2008 Jordi Polo <mumismo@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Library General Public License as
8 * published by the Free Software Foundation; either version 2, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 */
21
22#include "runnermanager.h"
23
24#include "config-plasma.h"
25
26#include <QMutex>
27#include <QTimer>
28#include <QCoreApplication>
29
30#include <kdebug.h>
31#include <kplugininfo.h>
32#include <kservicetypetrader.h>
33#include <kstandarddirs.h>
34
35#ifndef PLASMA_NO_SOLID
36#include <solid/device.h>
37#include <solid/deviceinterface.h>
38#endif
39
40#include <Weaver/DebuggingAids.h>
41#include <Weaver/State.h>
42#include <Weaver/Thread.h>
43#include <Weaver/ThreadWeaver.h>
44
45#include "private/runnerjobs_p.h"
46#include "pluginloader.h"
47#include "querymatch.h"
48
49using ThreadWeaver::Weaver;
50using ThreadWeaver::Job;
51
52//#define MEASURE_PREPTIME
53
54namespace Plasma
55{
56
57/*****************************************************
58* RunnerManager::Private class
59*
60*****************************************************/
61class RunnerManagerPrivate
62{
63public:
64
65 RunnerManagerPrivate(RunnerManager *parent)
66 : q(parent),
67 deferredRun(0),
68 currentSingleRunner(0),
69 prepped(false),
70 allRunnersPrepped(false),
71 singleRunnerPrepped(false),
72 teardownRequested(false),
73 singleMode(false),
74 singleRunnerWasLoaded(false)
75 {
76 matchChangeTimer.setSingleShot(true);
77 delayTimer.setSingleShot(true);
78
79 QObject::connect(&matchChangeTimer, SIGNAL(timeout()), q, SLOT(matchesChanged()));
80 QObject::connect(&context, SIGNAL(matchesChanged()), q, SLOT(scheduleMatchesChanged()));
81 QObject::connect(&delayTimer, SIGNAL(timeout()), q, SLOT(unblockJobs()));
82 }
83
84 ~RunnerManagerPrivate()
85 {
86 KConfigGroup config = configGroup();
87 context.save(config);
88 }
89
90 void scheduleMatchesChanged()
91 {
92 matchChangeTimer.start(100);
93 }
94
95 void matchesChanged()
96 {
97 emit q->matchesChanged(context.matches());
98 }
99
100 void loadConfiguration()
101 {
102 KConfigGroup config = configGroup();
103
104 //The number of threads used scales with the number of processors.
105#ifndef PLASMA_NO_SOLID
106 const int numProcs =
107 qMax(Solid::Device::listFromType(Solid::DeviceInterface::Processor).count(), 1);
108#else
109 const int numProcs = 1;
110#endif
111 //This entry allows to define a hard upper limit independent of the number of processors.
112 const int maxThreads = config.readEntry("maxThreads", 16);
113 const int numThreads = qMin(maxThreads, 2 + ((numProcs - 1) * 2));
114 //kDebug() << "setting up" << numThreads << "threads for" << numProcs << "processors";
115 if (numThreads > Weaver::instance()->maximumNumberOfThreads()) {
116 Weaver::instance()->setMaximumNumberOfThreads(numThreads);
117 }
118 // Limit the number of instances of a single normal speed runner and all of the slow runners
119 // to half the number of threads
120 const int cap = qMax(2, numThreads/2);
121 DefaultRunnerPolicy::instance().setCap(cap);
122
123 context.restore(config);
124 }
125
126 KConfigGroup configGroup()
127 {
128 return conf.isValid() ? conf : KConfigGroup(KGlobal::config(), "PlasmaRunnerManager");
129 }
130
131 void clearSingleRunner()
132 {
133 if (singleRunnerWasLoaded) {
134 delete currentSingleRunner;
135 }
136
137 currentSingleRunner = 0;
138 }
139
140 void loadSingleRunner()
141 {
142 if (!singleMode || singleModeRunnerId.isEmpty()) {
143 clearSingleRunner();
144 return;
145 }
146
147 if (currentSingleRunner) {
148 if (currentSingleRunner->id() == singleModeRunnerId) {
149 return;
150 }
151
152 clearSingleRunner();
153 }
154
155 AbstractRunner *loadedRunner = q->runner(singleModeRunnerId);
156 if (loadedRunner) {
157 singleRunnerWasLoaded = false;
158 currentSingleRunner = loadedRunner;
159 return;
160 }
161
162 KService::List offers = KServiceTypeTrader::self()->query("Plasma/Runner", QString("[X-KDE-PluginInfo-Name] == '%1'").arg(singleModeRunnerId));
163 if (!offers.isEmpty()) {
164 const KService::Ptr &service = offers[0];
165 currentSingleRunner = loadInstalledRunner(service);
166
167 if (currentSingleRunner) {
168 emit currentSingleRunner->prepare();
169 singleRunnerWasLoaded = true;
170 }
171 }
172 }
173
174 void loadRunners()
175 {
176 KConfigGroup config = configGroup();
177 KPluginInfo::List offers = RunnerManager::listRunnerInfo();
178
179 const bool loadAll = config.readEntry("loadAll", false);
180 const QStringList whiteList = config.readEntry("pluginWhiteList", QStringList());
181 const bool noWhiteList = whiteList.isEmpty();
182 KConfigGroup pluginConf;
183 if (conf.isValid()) {
184 pluginConf = KConfigGroup(&conf, "Plugins");
185 } else {
186 pluginConf = KConfigGroup(KGlobal::config(), "Plugins");
187 }
188
189 advertiseSingleRunnerIds.clear();
190
191 QSet<AbstractRunner *> deadRunners;
192 QMutableListIterator<KPluginInfo> it(offers);
193 while (it.hasNext()) {
194 KPluginInfo &description = it.next();
195 //kDebug() << "Loading runner: " << service->name() << service->storageId();
196 QString tryExec = description.property("TryExec").toString();
197 //kDebug() << "TryExec is" << tryExec;
198 if (!tryExec.isEmpty() && KStandardDirs::findExe(tryExec).isEmpty()) {
199 // we don't actually have this application!
200 continue;
201 }
202
203 const QString runnerName = description.pluginName();
204 description.load(pluginConf);
205
206 const bool loaded = runners.contains(runnerName);
207 const bool selected = loadAll || (description.isPluginEnabled() && (noWhiteList || whiteList.contains(runnerName)));
208
209 const bool singleQueryModeEnabled = description.property("X-Plasma-AdvertiseSingleRunnerQueryMode").toBool();
210
211 if (singleQueryModeEnabled) {
212 advertiseSingleRunnerIds.insert(runnerName, description.name());
213 }
214
215 //kDebug() << loadAll << description.isPluginEnabled() << noWhiteList << whiteList.contains(runnerName);
216 if (selected) {
217 if (!loaded) {
218 AbstractRunner *runner = loadInstalledRunner(description.service());
219
220 if (runner) {
221 runners.insert(runnerName, runner);
222 }
223 }
224 } else if (loaded) {
225 //Remove runner
226 deadRunners.insert(runners.take(runnerName));
227 kDebug() << "Removing runner: " << runnerName;
228 }
229 }
230
231 if (!deadRunners.isEmpty()) {
232 QSet<FindMatchesJob *> deadJobs;
233 foreach (FindMatchesJob *job, searchJobs) {
234 if (deadRunners.contains(job->runner())) {
235 QObject::disconnect(job, SIGNAL(done(ThreadWeaver::Job*)), q, SLOT(jobDone(ThreadWeaver::Job*)));
236 searchJobs.remove(job);
237 deadJobs.insert(job);
238 }
239 }
240
241 foreach (FindMatchesJob *job, oldSearchJobs) {
242 if (deadRunners.contains(job->runner())) {
243 oldSearchJobs.remove(job);
244 deadJobs.insert(job);
245 }
246 }
247
248 if (deadJobs.isEmpty()) {
249 qDeleteAll(deadRunners);
250 } else {
251 new DelayedJobCleaner(deadJobs, deadRunners);
252 }
253 }
254
255 if (!singleRunnerWasLoaded) {
256 // in case we deleted it up above
257 clearSingleRunner();
258 }
259
260 kDebug() << "All runners loaded, total:" << runners.count();
261 }
262
263 AbstractRunner *loadInstalledRunner(const KService::Ptr service)
264 {
265 if (!service) {
266 return 0;
267 }
268
269 AbstractRunner *runner = PluginLoader::pluginLoader()->loadRunner(service->property("X-KDE-PluginInfo-Name", QVariant::String).toString());
270
271 if (runner) {
272 runner->setParent(q);
273 } else {
274 const QString api = service->property("X-Plasma-API").toString();
275
276 if (api.isEmpty()) {
277 QVariantList args;
278 args << service->storageId();
279 if (Plasma::isPluginVersionCompatible(KPluginLoader(*service).pluginVersion())) {
280 QString error;
281 runner = service->createInstance<AbstractRunner>(q, args, &error);
282 if (!runner) {
283 kDebug() << "Failed to load runner:" << service->name() << ". error reported:" << error;
284 }
285 }
286 } else {
287 //kDebug() << "got a script runner known as" << api;
288 runner = new AbstractRunner(service, q);
289 }
290 }
291
292 if (runner) {
293 kDebug() << "================= loading runner:" << service->name() << "=================";
294 QObject::connect(runner, SIGNAL(matchingSuspended(bool)), q, SLOT(runnerMatchingSuspended(bool)));
295 QMetaObject::invokeMethod(runner, "init");
296 if (prepped) {
297 emit runner->prepare();
298 }
299 }
300
301 return runner;
302 }
303
304 void jobDone(ThreadWeaver::Job *job)
305 {
306 FindMatchesJob *runJob = dynamic_cast<FindMatchesJob *>(job);
307
308 if (!runJob) {
309 return;
310 }
311
312 if (deferredRun.isEnabled() && runJob->runner() == deferredRun.runner()) {
313 //kDebug() << "job actually done, running now **************";
314 QueryMatch tmpRun = deferredRun;
315 deferredRun = QueryMatch(0);
316 tmpRun.run(context);
317 }
318
319 searchJobs.remove(runJob);
320 oldSearchJobs.remove(runJob);
321 runJob->deleteLater();
322
323 if (searchJobs.isEmpty() && context.matches().isEmpty()) {
324 // we finished our run, and there are no valid matches, and so no
325 // signal will have been sent out. so we need to emit the signal
326 // ourselves here
327 emit q->matchesChanged(context.matches());
328 }
329
330 checkTearDown();
331 }
332
333 void checkTearDown()
334 {
335 //kDebug() << prepped << teardownRequested << searchJobs.count() << oldSearchJobs.count();
336
337 if (!prepped || !teardownRequested) {
338 return;
339 }
340
341 if (Weaver::instance()->isIdle()) {
342 qDeleteAll(searchJobs);
343 searchJobs.clear();
344 qDeleteAll(oldSearchJobs);
345 oldSearchJobs.clear();
346 }
347
348 if (searchJobs.isEmpty() && oldSearchJobs.isEmpty()) {
349 if (allRunnersPrepped) {
350 foreach (AbstractRunner *runner, runners) {
351 emit runner->teardown();
352 }
353
354 allRunnersPrepped = false;
355 }
356
357 if (singleRunnerPrepped) {
358 if (currentSingleRunner) {
359 emit currentSingleRunner->teardown();
360 }
361
362 singleRunnerPrepped = false;
363 }
364
365 emit q->queryFinished();
366
367 prepped = false;
368 teardownRequested = false;
369 }
370 }
371
372 void unblockJobs()
373 {
374 // WORKAROUND: Queue an empty job to force ThreadWeaver to awaken threads
375 if (searchJobs.isEmpty() && Weaver::instance()->isIdle()) {
376 qDeleteAll(oldSearchJobs);
377 oldSearchJobs.clear();
378 checkTearDown();
379 return;
380 }
381
382 DummyJob *dummy = new DummyJob(q);
383 Weaver::instance()->enqueue(dummy);
384 QObject::connect(dummy, SIGNAL(done(ThreadWeaver::Job*)), dummy, SLOT(deleteLater()));
385 }
386
387 void runnerMatchingSuspended(bool suspended)
388 {
389 if (suspended || !prepped || teardownRequested) {
390 return;
391 }
392
393 AbstractRunner *runner = qobject_cast<AbstractRunner *>(q->sender());
394
395 if (runner) {
396 startJob(runner);
397 }
398 }
399
400 void startJob(AbstractRunner *runner)
401 {
402 if ((runner->ignoredTypes() & context.type()) == 0) {
403 FindMatchesJob *job = new FindMatchesJob(runner, &context, Weaver::instance());
404 QObject::connect(job, SIGNAL(done(ThreadWeaver::Job*)), q, SLOT(jobDone(ThreadWeaver::Job*)));
405 if (runner->speed() == AbstractRunner::SlowSpeed) {
406 job->setDelayTimer(&delayTimer);
407 }
408 Weaver::instance()->enqueue(job);
409 searchJobs.insert(job);
410 }
411 }
412
413 // Delay in ms before slow runners are allowed to run
414 static const int slowRunDelay = 400;
415
416 RunnerManager *q;
417 QueryMatch deferredRun;
418 RunnerContext context;
419 QTimer matchChangeTimer;
420 QTimer delayTimer; // Timer to control when to run slow runners
421 QHash<QString, AbstractRunner*> runners;
422 QHash<QString, QString> advertiseSingleRunnerIds;
423 AbstractRunner* currentSingleRunner;
424 QSet<FindMatchesJob*> searchJobs;
425 QSet<FindMatchesJob*> oldSearchJobs;
426 KConfigGroup conf;
427 QString singleModeRunnerId;
428 bool loadAll : 1;
429 bool prepped : 1;
430 bool allRunnersPrepped : 1;
431 bool singleRunnerPrepped : 1;
432 bool teardownRequested : 1;
433 bool singleMode : 1;
434 bool singleRunnerWasLoaded : 1;
435};
436
437/*****************************************************
438* RunnerManager::Public class
439*
440*****************************************************/
441RunnerManager::RunnerManager(QObject *parent)
442 : QObject(parent),
443 d(new RunnerManagerPrivate(this))
444{
445 d->loadConfiguration();
446 //ThreadWeaver::setDebugLevel(true, 4);
447}
448
449RunnerManager::RunnerManager(KConfigGroup &c, QObject *parent)
450 : QObject(parent),
451 d(new RunnerManagerPrivate(this))
452{
453 // Should this be really needed? Maybe d->loadConfiguration(c) would make
454 // more sense.
455 d->conf = KConfigGroup(&c, "PlasmaRunnerManager");
456 d->loadConfiguration();
457 //ThreadWeaver::setDebugLevel(true, 4);
458}
459
460RunnerManager::~RunnerManager()
461{
462 if (!qApp->closingDown() && (!d->searchJobs.isEmpty() || !d->oldSearchJobs.isEmpty())) {
463 new DelayedJobCleaner(d->searchJobs + d->oldSearchJobs);
464 }
465
466 delete d;
467}
468
469void RunnerManager::reloadConfiguration()
470{
471 d->loadConfiguration();
472 d->loadRunners();
473}
474
475void RunnerManager::setAllowedRunners(const QStringList &runners)
476{
477 KConfigGroup config = d->configGroup();
478 config.writeEntry("pluginWhiteList", runners);
479
480 if (!d->runners.isEmpty()) {
481 // this has been called with runners already created. so let's do an instant reload
482 d->loadRunners();
483 }
484}
485
486QStringList RunnerManager::allowedRunners() const
487{
488 KConfigGroup config = d->configGroup();
489 return config.readEntry("pluginWhiteList", QStringList());
490}
491
492void RunnerManager::loadRunner(const KService::Ptr service)
493{
494 KPluginInfo description(service);
495 const QString runnerName = description.pluginName();
496 if (!runnerName.isEmpty() && !d->runners.contains(runnerName)) {
497 AbstractRunner *runner = d->loadInstalledRunner(service);
498 if (runner) {
499 d->runners.insert(runnerName, runner);
500 }
501 }
502}
503
504void RunnerManager::loadRunner(const QString &path)
505{
506 if (!d->runners.contains(path)) {
507 AbstractRunner *runner = new AbstractRunner(this, path);
508 connect(runner, SIGNAL(matchingSuspended(bool)), this, SLOT(runnerMatchingSuspended(bool)));
509 d->runners.insert(path, runner);
510 }
511}
512
513AbstractRunner* RunnerManager::runner(const QString &name) const
514{
515 if (d->runners.isEmpty()) {
516 d->loadRunners();
517 }
518
519 return d->runners.value(name, 0);
520}
521
522AbstractRunner *RunnerManager::singleModeRunner() const
523{
524 return d->currentSingleRunner;
525}
526
527void RunnerManager::setSingleModeRunnerId(const QString &id)
528{
529 d->singleModeRunnerId = id;
530 d->loadSingleRunner();
531}
532
533QString RunnerManager::singleModeRunnerId() const
534{
535 return d->singleModeRunnerId;
536}
537
538bool RunnerManager::singleMode() const
539{
540 return d->singleMode;
541}
542
543void RunnerManager::setSingleMode(bool singleMode)
544{
545 if (d->singleMode == singleMode) {
546 return;
547 }
548
549
550 Plasma::AbstractRunner *prevSingleRunner = d->currentSingleRunner;
551 d->singleMode = singleMode;
552 d->loadSingleRunner();
553 d->singleMode = d->currentSingleRunner;
554
555 if (prevSingleRunner != d->currentSingleRunner) {
556 if (d->prepped) {
557 matchSessionComplete();
558
559 if (d->singleMode) {
560 setupMatchSession();
561 }
562 }
563 }
564}
565
566QList<AbstractRunner *> RunnerManager::runners() const
567{
568 return d->runners.values();
569}
570
571QStringList RunnerManager::singleModeAdvertisedRunnerIds() const
572{
573 return d->advertiseSingleRunnerIds.keys();
574}
575
576QString RunnerManager::runnerName(const QString &id) const
577{
578 if (runner(id)) {
579 return runner(id)->name();
580 } else {
581 return d->advertiseSingleRunnerIds.value(id, QString());
582 }
583}
584
585RunnerContext* RunnerManager::searchContext() const
586{
587 return &d->context;
588}
589
590//Reordering is here so data is not reordered till strictly needed
591QList<QueryMatch> RunnerManager::matches() const
592{
593 return d->context.matches();
594}
595
596void RunnerManager::run(const QString &id)
597{
598 run(d->context.match(id));
599}
600
601void RunnerManager::run(const QueryMatch &match)
602{
603 if (!match.isEnabled()) {
604 return;
605 }
606
607 //TODO: this function is not const as it may be used for learning
608 AbstractRunner *runner = match.runner();
609
610 foreach (FindMatchesJob *job, d->searchJobs) {
611 if (job->runner() == runner && !job->isFinished()) {
612 kDebug() << "deferred run";
613 d->deferredRun = match;
614 return;
615 }
616 }
617
618 if (d->deferredRun.isValid()) {
619 d->deferredRun = QueryMatch(0);
620 }
621
622 d->context.run(match);
623}
624
625QList<QAction*> RunnerManager::actionsForMatch(const QueryMatch &match)
626{
627 AbstractRunner *runner = match.runner();
628 if (runner) {
629 return runner->actionsForMatch(match);
630 }
631
632 return QList<QAction*>();
633}
634
635QMimeData * RunnerManager::mimeDataForMatch(const QString &id) const
636{
637 return mimeDataForMatch(d->context.match(id));
638}
639
640
641QMimeData * RunnerManager::mimeDataForMatch(const QueryMatch &match) const
642{
643 AbstractRunner *runner = match.runner();
644 QMimeData *mimeData;
645 if (runner && QMetaObject::invokeMethod(
646 runner,
647 "mimeDataForMatch", Qt::DirectConnection,
648 Q_RETURN_ARG(QMimeData*, mimeData),
649 Q_ARG(const Plasma::QueryMatch *, &match)
650 )) {
651 return mimeData;
652 }
653
654 return 0;
655}
656
657KPluginInfo::List RunnerManager::listRunnerInfo(const QString &parentApp)
658{
659 return PluginLoader::pluginLoader()->listRunnerInfo(parentApp);
660}
661
662void RunnerManager::setupMatchSession()
663{
664 d->teardownRequested = false;
665
666 if (d->prepped) {
667 return;
668 }
669
670 d->prepped = true;
671 if (d->singleMode) {
672 if (d->currentSingleRunner) {
673 emit d->currentSingleRunner->prepare();
674 d->singleRunnerPrepped = true;
675 }
676 } else {
677 foreach (AbstractRunner *runner, d->runners) {
678#ifdef MEASURE_PREPTIME
679 QTime t;
680 t.start();
681#endif
682 emit runner->prepare();
683#ifdef MEASURE_PREPTIME
684 kDebug() << t.elapsed() << runner->name();
685#endif
686 }
687
688 d->allRunnersPrepped = true;
689 }
690}
691
692void RunnerManager::matchSessionComplete()
693{
694 if (!d->prepped) {
695 return;
696 }
697
698 d->teardownRequested = true;
699 d->checkTearDown();
700}
701
702void RunnerManager::launchQuery(const QString &term)
703{
704 launchQuery(term, QString());
705}
706
707void RunnerManager::launchQuery(const QString &untrimmedTerm, const QString &runnerName)
708{
709 setupMatchSession();
710 QString term = untrimmedTerm.trimmed();
711
712 setSingleModeRunnerId(runnerName);
713 setSingleMode(!runnerName.isEmpty());
714
715 if (term.isEmpty()) {
716 if (d->singleMode && d->currentSingleRunner && d->currentSingleRunner->defaultSyntax()) {
717 term = d->currentSingleRunner->defaultSyntax()->exampleQueries().first().remove(QRegExp(":q:"));
718 } else {
719 reset();
720 return;
721 }
722 }
723
724 if (d->context.query() == term) {
725 // we already are searching for this!
726 return;
727 }
728
729 if (d->singleMode && !d->currentSingleRunner) {
730 reset();
731 return;
732 }
733
734 if (d->runners.isEmpty()) {
735 d->loadRunners();
736 }
737
738 reset();
739// kDebug() << "runners searching for" << term << "on" << runnerName;
740 d->context.setQuery(term);
741
742 QHash<QString, AbstractRunner*> runable;
743
744 //if the name is not empty we will launch only the specified runner
745 if (d->singleMode && d->currentSingleRunner) {
746 runable.insert(QString(), d->currentSingleRunner);
747 d->context.setSingleRunnerQueryMode(true);
748 } else {
749 runable = d->runners;
750 }
751
752 foreach (Plasma::AbstractRunner *r, runable) {
753 if (r->isMatchingSuspended()) {
754 continue;
755 }
756
757 d->startJob(r);
758 }
759
760 // Start timer to unblock slow runners
761 d->delayTimer.start(RunnerManagerPrivate::slowRunDelay);
762}
763
764bool RunnerManager::execQuery(const QString &term)
765{
766 return execQuery(term, QString());
767}
768
769bool RunnerManager::execQuery(const QString &untrimmedTerm, const QString &runnerName)
770{
771 QString term = untrimmedTerm.trimmed();
772
773 if (term.isEmpty()) {
774 reset();
775 return false;
776 }
777
778 if (d->runners.isEmpty()) {
779 d->loadRunners();
780 }
781
782 if (d->context.query() == term) {
783 // we already are searching for this!
784 emit matchesChanged(d->context.matches());
785 return false;
786 }
787
788 reset();
789 //kDebug() << "executing query about " << term << "on" << runnerName;
790 d->context.setQuery(term);
791 AbstractRunner *r = runner(runnerName);
792
793 if (!r) {
794 //kDebug() << "failed to find the runner";
795 return false;
796 }
797
798 if ((r->ignoredTypes() & d->context.type()) != 0) {
799 //kDebug() << "ignored!";
800 return false;
801 }
802
803 r->performMatch(d->context);
804 //kDebug() << "succeeded with" << d->context.matches().count() << "results";
805 emit matchesChanged(d->context.matches());
806 return true;
807}
808
809QString RunnerManager::query() const
810{
811 return d->context.query();
812}
813
814void RunnerManager::reset()
815{
816 // If ThreadWeaver is idle, it is safe to clear previous jobs
817 if (Weaver::instance()->isIdle()) {
818 qDeleteAll(d->searchJobs);
819 qDeleteAll(d->oldSearchJobs);
820 d->oldSearchJobs.clear();
821 } else {
822 Q_FOREACH(FindMatchesJob *job, d->searchJobs) {
823 Weaver::instance()->dequeue(job);
824 }
825 d->oldSearchJobs += d->searchJobs;
826 }
827
828 d->searchJobs.clear();
829
830 if (d->deferredRun.isEnabled()) {
831 //kDebug() << "job actually done, running now **************";
832 QueryMatch tmpRun = d->deferredRun;
833 d->deferredRun = QueryMatch(0);
834 tmpRun.run(d->context);
835 }
836
837 d->context.reset();
838}
839
840} // Plasma namespace
841
842#include "runnermanager.moc"
843